core: refactor genesis handling

This commit solves several issues concerning the genesis block:

* Genesis/ChainConfig loading was handled by cmd/geth code. This left
  library users in the cold. They could specify a JSON-encoded
  string and overwrite the config, but didn't get any of the additional
  checks performed by geth.
* Decoding and writing of genesis JSON was conflated in
  WriteGenesisBlock. This made it a lot harder to embed the genesis
  block into the forthcoming config file loader. This commit changes
  things so there is a single Genesis type that represents genesis
  blocks. All uses of Write*Genesis* are changed to use the new type
  instead.
* If the chain config supplied by the user was incompatible with the
  current chain (i.e. the chain had already advanced beyond a scheduled
  fork), it got overwritten. This is not an issue in practice because
  previous forks have always had the highest total difficulty. It might
  matter in the future though. The new code reverts the local chain to
  the point of the fork when upgrading configuration.

The change to genesis block data removes compression library
dependencies from package core.
This commit is contained in:
Felix Lange
2017-03-02 14:03:33 +01:00
parent 67c47459f2
commit 37dd9086ec
45 changed files with 1379 additions and 1275 deletions

View File

@ -20,7 +20,6 @@ import (
"context"
"fmt"
"math/big"
"runtime"
"testing"
"github.com/ethereum/go-ethereum/common"
@ -30,7 +29,6 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/pow"
"github.com/hashicorp/golang-lru"
)
// So we can deterministically seed different blockchains
@ -59,14 +57,10 @@ func testChainConfig() *params.ChainConfig {
// chain. Depending on the full flag, if creates either a full block chain or a
// header only chain.
func newCanonical(n int) (ethdb.Database, *LightChain, error) {
// Create te new chain database
db, _ := ethdb.NewMemDatabase()
evmux := &event.TypeMux{}
// Initialize a fresh chain with only a genesis block
genesis, _ := core.WriteTestNetGenesisBlock(db)
blockchain, _ := NewLightChain(&dummyOdr{db: db}, testChainConfig(), pow.FakePow{}, evmux)
gspec := core.Genesis{Config: testChainConfig()}
genesis := gspec.MustCommit(db)
blockchain, _ := NewLightChain(&dummyOdr{db: db}, gspec.Config, pow.FakePow{}, new(event.TypeMux))
// Create and inject the requested chain
if n == 0 {
return db, blockchain, nil
@ -77,21 +71,20 @@ func newCanonical(n int) (ethdb.Database, *LightChain, error) {
return db, blockchain, err
}
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
}
func theLightChain(db ethdb.Database, t *testing.T) *LightChain {
var eventMux event.TypeMux
core.WriteTestNetGenesisBlock(db)
LightChain, err := NewLightChain(&dummyOdr{db: db}, testChainConfig(), pow.NewTestEthash(), &eventMux)
if err != nil {
t.Error("failed creating LightChain:", err)
t.FailNow()
return nil
// newTestLightChain creates a LightChain that doesn't validate anything.
func newTestLightChain() *LightChain {
db, _ := ethdb.NewMemDatabase()
gspec := &core.Genesis{
Difficulty: big.NewInt(1),
Config: testChainConfig(),
}
return LightChain
gspec.MustCommit(db)
lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, pow.NewTestEthash(), new(event.TypeMux))
if err != nil {
panic(err)
}
lc.SetValidator(bproc{})
return lc
}
// Test fork of length N starting from block i
@ -302,20 +295,6 @@ func (odr *dummyOdr) Retrieve(ctx context.Context, req OdrRequest) error {
return nil
}
func chm(genesis *types.Block, db ethdb.Database) *LightChain {
odr := &dummyOdr{db: db}
var eventMux event.TypeMux
bc := &LightChain{odr: odr, chainDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: pow.FakePow{}}
bc.hc, _ = core.NewHeaderChain(db, testChainConfig(), bc.Validator, bc.getProcInterrupt)
bc.bodyCache, _ = lru.New(100)
bc.bodyRLPCache, _ = lru.New(100)
bc.blockCache, _ = lru.New(100)
bc.SetValidator(bproc{})
bc.ResetWithGenesisBlock(genesis)
return bc
}
// Tests that reorganizing a long difficult chain after a short easy one
// overwrites the canonical numbers and links in the database.
func TestReorgLongHeaders(t *testing.T) {
@ -329,14 +308,11 @@ func TestReorgShortHeaders(t *testing.T) {
}
func testReorg(t *testing.T, first, second []int, td int64) {
// Create a pristine block chain
db, _ := ethdb.NewMemDatabase()
genesis, _ := core.WriteTestNetGenesisBlock(db)
bc := chm(genesis, db)
bc := newTestLightChain()
// Insert an easy and a difficult chain afterwards
bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, first, 11), 1)
bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, second, 22), 1)
bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, first, 11), 1)
bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, second, 22), 1)
// Check that the chain is valid number and link wise
prev := bc.CurrentHeader()
for header := bc.GetHeaderByNumber(bc.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, bc.GetHeaderByNumber(header.Number.Uint64()-1) {
@ -345,7 +321,7 @@ func testReorg(t *testing.T, first, second []int, td int64) {
}
}
// Make sure the chain total difficulty is the correct one
want := new(big.Int).Add(genesis.Difficulty(), big.NewInt(td))
want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td))
if have := bc.GetTdByHash(bc.CurrentHeader().Hash()); have.Cmp(want) != 0 {
t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
}
@ -353,14 +329,11 @@ func testReorg(t *testing.T, first, second []int, td int64) {
// Tests that the insertion functions detect banned hashes.
func TestBadHeaderHashes(t *testing.T) {
// Create a pristine block chain
db, _ := ethdb.NewMemDatabase()
genesis, _ := core.WriteTestNetGenesisBlock(db)
bc := chm(genesis, db)
bc := newTestLightChain()
// Create a chain, ban a hash and try to import
var err error
headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 4}, 10)
headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10)
core.BadHashes[headers[2].Hash()] = true
_, err = bc.InsertHeaderChain(headers, 1)
if !core.IsBadHashError(err) {
@ -371,13 +344,10 @@ func TestBadHeaderHashes(t *testing.T) {
// Tests that bad hashes are detected on boot, and the chan rolled back to a
// good state prior to the bad hash.
func TestReorgBadHeaderHashes(t *testing.T) {
// Create a pristine block chain
db, _ := ethdb.NewMemDatabase()
genesis, _ := core.WriteTestNetGenesisBlock(db)
bc := chm(genesis, db)
bc := newTestLightChain()
// Create a chain, import and ban aferwards
headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 3, 4}, 10)
headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10)
if _, err := bc.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to import headers: %v", err)
@ -387,8 +357,9 @@ func TestReorgBadHeaderHashes(t *testing.T) {
}
core.BadHashes[headers[3].Hash()] = true
defer func() { delete(core.BadHashes, headers[3].Hash()) }()
// Create a new chain manager and check it rolled back the state
ncm, err := NewLightChain(&dummyOdr{db: db}, testChainConfig(), pow.FakePow{}, new(event.TypeMux))
// Create a new LightChain and check that it rolled back the state.
ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, testChainConfig(), pow.FakePow{}, new(event.TypeMux))
if err != nil {
t.Fatalf("failed to create new chain manager: %v", err)
}