From a32c51effda8682b292d04863aae7811f78abf7e Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Fri, 10 Jul 2015 14:29:40 +0200 Subject: cmd, core, eth, common: genesis preparation Implemented the --genesis flag thru which we can set a custom genesis block, including the official Ethereum genesis block. --- core/bench_test.go | 4 +- core/block_processor_test.go | 4 +- core/chain_makers.go | 4 +- core/chain_makers_test.go | 4 +- core/chain_manager.go | 85 ++++++++++++++------------- core/chain_manager_test.go | 46 +++++++-------- core/chain_util.go | 25 ++++++++ core/genesis.go | 134 +++++++++++++++++++++++++++++++------------ 8 files changed, 196 insertions(+), 110 deletions(-) (limited to 'core') diff --git a/core/bench_test.go b/core/bench_test.go index 645df48c2..018d27d8d 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -162,13 +162,13 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { // Generate a chain of b.N blocks using the supplied block // generator function. - genesis := GenesisBlockForTesting(db, benchRootAddr, benchRootFunds) + genesis := WriteGenesisBlockForTesting(db, benchRootAddr, benchRootFunds) chain := GenerateChain(genesis, db, b.N, gen) // Time the insertion of the new chain. // State and blocks are stored in the same DB. evmux := new(event.TypeMux) - chainman, _ := NewChainManager(genesis, db, db, db, FakePow{}, evmux) + chainman, _ := NewChainManager(db, db, db, FakePow{}, evmux) chainman.SetProcessor(NewBlockProcessor(db, db, FakePow{}, chainman, evmux)) defer chainman.Stop() b.ReportAllocs() diff --git a/core/block_processor_test.go b/core/block_processor_test.go index 4250b897b..dab5be974 100644 --- a/core/block_processor_test.go +++ b/core/block_processor_test.go @@ -33,8 +33,8 @@ func proc() (*BlockProcessor, *ChainManager) { db, _ := ethdb.NewMemDatabase() var mux event.TypeMux - genesis := GenesisBlock(0, db) - chainMan, err := NewChainManager(genesis, db, db, db, thePow(), &mux) + WriteTestNetGenesisBlock(db, db, 0) + chainMan, err := NewChainManager(db, db, db, thePow(), &mux) if err != nil { fmt.Println(err) } diff --git a/core/chain_makers.go b/core/chain_makers.go index 501fe7a92..93f381e78 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -183,7 +183,9 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header { // InsertChain on the result of makeChain. func newCanonical(n int, db common.Database) (*BlockProcessor, error) { evmux := &event.TypeMux{} - chainman, _ := NewChainManager(GenesisBlock(0, db), db, db, db, FakePow{}, evmux) + + WriteTestNetGenesisBlock(db, db, 0) + chainman, _ := NewChainManager(db, db, db, FakePow{}, evmux) bman := NewBlockProcessor(db, db, FakePow{}, chainman, evmux) bman.bc.SetProcessor(bman) parent := bman.bc.CurrentBlock() diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index 2f001be9b..ddd54d217 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -39,7 +39,7 @@ func ExampleGenerateChain() { ) // Ensure that key1 has some funds in the genesis block. - genesis := GenesisBlockForTesting(db, addr1, big.NewInt(1000000)) + genesis := WriteGenesisBlockForTesting(db, addr1, big.NewInt(1000000)) // This call generates a chain of 5 blocks. The function runs for // each block and adds different features to gen based on the @@ -74,7 +74,7 @@ func ExampleGenerateChain() { // Import the chain. This runs all block validation rules. evmux := &event.TypeMux{} - chainman, _ := NewChainManager(genesis, db, db, db, FakePow{}, evmux) + chainman, _ := NewChainManager(db, db, db, FakePow{}, evmux) chainman.SetProcessor(NewBlockProcessor(db, db, FakePow{}, chainman, evmux)) if i, err := chainman.InsertChain(chain); err != nil { fmt.Printf("insert error (block %d): %v\n", i, err) diff --git a/core/chain_manager.go b/core/chain_manager.go index 3c5eb0e8a..8fa13ddec 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -18,6 +18,7 @@ package core import ( + "errors" "fmt" "io" "math/big" @@ -34,7 +35,6 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/pow" - "github.com/ethereum/go-ethereum/rlp" "github.com/hashicorp/golang-lru" ) @@ -42,10 +42,9 @@ var ( chainlogger = logger.NewLogger("CHAIN") jsonlogger = logger.NewJsonLogger() - blockHashPre = []byte("block-hash-") - blockNumPre = []byte("block-num-") - blockInsertTimer = metrics.NewTimer("chain/inserts") + + ErrNoGenesis = errors.New("Genesis not found in chain") ) const ( @@ -88,25 +87,32 @@ type ChainManager struct { pow pow.PoW } -func NewChainManager(genesis *types.Block, blockDb, stateDb, extraDb common.Database, pow pow.PoW, mux *event.TypeMux) (*ChainManager, error) { +func NewChainManager(blockDb, stateDb, extraDb common.Database, pow pow.PoW, mux *event.TypeMux) (*ChainManager, error) { cache, _ := lru.New(blockCacheLimit) bc := &ChainManager{ - blockDb: blockDb, - stateDb: stateDb, - extraDb: extraDb, - genesisBlock: GenesisBlock(42, stateDb), - eventMux: mux, - quit: make(chan struct{}), - cache: cache, - pow: pow, - } - // Check the genesis block given to the chain manager. If the genesis block mismatches block number 0 - // throw an error. If no block or the same block's found continue. - if g := bc.GetBlockByNumber(0); g != nil && g.Hash() != genesis.Hash() { - return nil, fmt.Errorf("Genesis mismatch. Maybe different nonce (%d vs %d)? %x / %x", g.Nonce(), genesis.Nonce(), g.Hash().Bytes()[:4], genesis.Hash().Bytes()[:4]) - } - bc.genesisBlock = genesis - bc.setLastState() + blockDb: blockDb, + stateDb: stateDb, + extraDb: extraDb, + eventMux: mux, + quit: make(chan struct{}), + cache: cache, + pow: pow, + } + + bc.genesisBlock = bc.GetBlockByNumber(0) + if bc.genesisBlock == nil { + // XXX Uncomment me before Frontier + //return nil, ErrNoGenesis + genesis, err := WriteTestNetGenesisBlock(bc.stateDb, bc.blockDb, 42) + if err != nil { + glog.Fatalln("genisis err", err) + } + bc.genesisBlock = genesis + } + + if err := bc.setLastState(); err != nil { + return nil, err + } // Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain for hash, _ := range BadHashes { @@ -226,7 +232,7 @@ func (bc *ChainManager) recover() bool { return false } -func (bc *ChainManager) setLastState() { +func (bc *ChainManager) setLastState() error { data, _ := bc.blockDb.Get([]byte("LastBlock")) if len(data) != 0 { block := bc.GetBlock(common.BytesToHash(data)) @@ -250,6 +256,8 @@ func (bc *ChainManager) setLastState() { if glog.V(logger.Info) { glog.Infof("Last block (#%v) %x TD=%v\n", bc.currentBlock.Number(), bc.currentBlock.Hash(), bc.td) } + + return nil } func (bc *ChainManager) makeCache() { @@ -272,7 +280,11 @@ func (bc *ChainManager) Reset() { bc.cache, _ = lru.New(blockCacheLimit) // Prepare the genesis block - bc.write(bc.genesisBlock) + err := WriteBlock(bc.blockDb, bc.genesisBlock) + if err != nil { + glog.Fatalln("db err:", err) + } + bc.insert(bc.genesisBlock) bc.currentBlock = bc.genesisBlock bc.makeCache() @@ -295,7 +307,12 @@ func (bc *ChainManager) ResetWithGenesisBlock(gb *types.Block) { // Prepare the genesis block gb.Td = gb.Difficulty() bc.genesisBlock = gb - bc.write(bc.genesisBlock) + + err := WriteBlock(bc.blockDb, bc.genesisBlock) + if err != nil { + glog.Fatalln("db err:", err) + } + bc.insert(bc.genesisBlock) bc.currentBlock = bc.genesisBlock bc.makeCache() @@ -357,21 +374,6 @@ func (bc *ChainManager) insert(block *types.Block) { bc.lastBlockHash = block.Hash() } -func (bc *ChainManager) write(block *types.Block) { - tstart := time.Now() - - enc, _ := rlp.EncodeToBytes((*types.StorageBlock)(block)) - key := append(blockHashPre, block.Hash().Bytes()...) - err := bc.blockDb.Put(key, enc) - if err != nil { - glog.Fatal("db write fail:", err) - } - - if glog.V(logger.Debug) { - glog.Infof("wrote block #%v %s. Took %v\n", block.Number(), common.PP(block.Hash().Bytes()), time.Since(tstart)) - } -} - // Accessors func (bc *ChainManager) Genesis() *types.Block { return bc.genesisBlock @@ -535,7 +537,10 @@ func (self *ChainManager) WriteBlock(block *types.Block, queued bool) (status wr status = SideStatTy } - self.write(block) + err = WriteBlock(self.blockDb, block) + if err != nil { + glog.Fatalln("db err:", err) + } // Delete from future blocks self.futureBlocks.Remove(block.Hash()) diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go index 92f080f01..5f8cfd4f4 100644 --- a/core/chain_manager_test.go +++ b/core/chain_manager_test.go @@ -48,8 +48,8 @@ func thePow() pow.PoW { func theChainManager(db common.Database, t *testing.T) *ChainManager { var eventMux event.TypeMux - genesis := GenesisBlock(0, db) - chainMan, err := NewChainManager(genesis, db, db, db, thePow(), &eventMux) + WriteTestNetGenesisBlock(db, db, 0) + chainMan, err := NewChainManager(db, db, db, thePow(), &eventMux) if err != nil { t.Error("failed creating chainmanager:", err) t.FailNow() @@ -125,7 +125,7 @@ func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) { bman.bc.mu.Lock() { - bman.bc.write(block) + WriteBlock(bman.bc.blockDb, block) } bman.bc.mu.Unlock() } @@ -362,25 +362,6 @@ func TestChainMultipleInsertions(t *testing.T) { } } -func TestGetBlocksFromHash(t *testing.T) { - t.Skip("Skipped: outdated test files") - - db, _ := ethdb.NewMemDatabase() - chainMan := theChainManager(db, t) - chain, err := loadChain("valid1", t) - if err != nil { - fmt.Println(err) - t.FailNow() - } - - for _, block := range chain { - chainMan.write(block) - } - - blocks := chainMan.GetBlocksFromHash(chain[len(chain)-1].Hash(), 4) - fmt.Println(blocks) -} - type bproc struct{} func (bproc) Process(*types.Block) (state.Logs, types.Receipts, error) { return nil, nil, nil } @@ -418,7 +399,12 @@ func chm(genesis *types.Block, db common.Database) *ChainManager { func TestReorgLongest(t *testing.T) { db, _ := ethdb.NewMemDatabase() - genesis := GenesisBlock(0, db) + + genesis, err := WriteTestNetGenesisBlock(db, db, 0) + if err != nil { + t.Error(err) + t.FailNow() + } bc := chm(genesis, db) chain1 := makeChainWithDiff(genesis, []int{1, 2, 4}, 10) @@ -437,7 +423,11 @@ func TestReorgLongest(t *testing.T) { func TestReorgShortest(t *testing.T) { db, _ := ethdb.NewMemDatabase() - genesis := GenesisBlock(0, db) + genesis, err := WriteTestNetGenesisBlock(db, db, 0) + if err != nil { + t.Error(err) + t.FailNow() + } bc := chm(genesis, db) chain1 := makeChainWithDiff(genesis, []int{1, 2, 3, 4}, 10) @@ -457,7 +447,11 @@ func TestReorgShortest(t *testing.T) { func TestInsertNonceError(t *testing.T) { for i := 1; i < 25 && !t.Failed(); i++ { db, _ := ethdb.NewMemDatabase() - genesis := GenesisBlock(0, db) + genesis, err := WriteTestNetGenesisBlock(db, db, 0) + if err != nil { + t.Error(err) + t.FailNow() + } bc := chm(genesis, db) bc.processor = NewBlockProcessor(db, db, bc.pow, bc, bc.eventMux) blocks := makeChain(bc.currentBlock, i, db, 0) @@ -491,6 +485,7 @@ func TestInsertNonceError(t *testing.T) { } } +/* func TestGenesisMismatch(t *testing.T) { db, _ := ethdb.NewMemDatabase() var mux event.TypeMux @@ -505,6 +500,7 @@ func TestGenesisMismatch(t *testing.T) { t.Error("expected genesis mismatch error") } } +*/ // failpow returns false from Verify for a certain block number. type failpow struct{ num uint64 } diff --git a/core/chain_util.go b/core/chain_util.go index 7e3d8eba8..253396ed7 100644 --- a/core/chain_util.go +++ b/core/chain_util.go @@ -19,6 +19,7 @@ package core import ( "bytes" "math/big" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -28,6 +29,11 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) +var ( + blockHashPre = []byte("block-hash-") + blockNumPre = []byte("block-num-") +) + // CalcDifficulty is the difficulty adjustment algorithm. It returns // the difficulty that a new block b should have when created at time // given the parent block's time and difficulty. @@ -118,3 +124,22 @@ func WriteHead(db common.Database, block *types.Block) error { } return nil } + +// WriteBlock writes a block to the database +func WriteBlock(db common.Database, block *types.Block) error { + tstart := time.Now() + + enc, _ := rlp.EncodeToBytes((*types.StorageBlock)(block)) + key := append(blockHashPre, block.Hash().Bytes()...) + err := db.Put(key, enc) + if err != nil { + glog.Fatal("db write fail:", err) + return err + } + + if glog.V(logger.Debug) { + glog.Infof("wrote block #%v %s. Took %v\n", block.Number(), common.PP(block.Hash().Bytes()), time.Since(tstart)) + } + + return nil +} diff --git a/core/genesis.go b/core/genesis.go index 2d369aae0..8c9a21c7e 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -19,8 +19,10 @@ package core import ( "encoding/json" "fmt" + "io" + "io/ioutil" "math/big" - "os" + "strings" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" @@ -28,51 +30,71 @@ import ( "github.com/ethereum/go-ethereum/params" ) -// GenesisBlock creates a genesis block with the given nonce. -func GenesisBlock(nonce uint64, db common.Database) *types.Block { - var accounts map[string]struct { - Balance string - Code string - } - err := json.Unmarshal(GenesisAccounts, &accounts) +// WriteGenesisBlock writes the genesis block to the database as block number 0 +func WriteGenesisBlock(stateDb, blockDb common.Database, reader io.Reader) (*types.Block, error) { + contents, err := ioutil.ReadAll(reader) if err != nil { - fmt.Println("unable to decode genesis json data:", err) - os.Exit(1) + return nil, err } - statedb := state.New(common.Hash{}, db) - for addr, account := range accounts { - codedAddr := common.Hex2Bytes(addr) - accountState := statedb.CreateAccount(common.BytesToAddress(codedAddr)) - accountState.SetBalance(common.Big(account.Balance)) - accountState.SetCode(common.FromHex(account.Code)) - statedb.UpdateStateObject(accountState) + + var genesis struct { + Nonce string + Timestamp string + ParentHash string + ExtraData string + GasLimit string + Difficulty string + Mixhash string + Coinbase string + Alloc map[string]struct { + Code string + Storage map[string]string + Balance string + } } - statedb.Sync() + if err := json.Unmarshal(contents, &genesis); err != nil { + return nil, err + } + + statedb := state.New(common.Hash{}, stateDb) + for addr, account := range genesis.Alloc { + address := common.HexToAddress(addr) + statedb.AddBalance(address, common.String2Big(account.Balance)) + statedb.SetCode(address, common.Hex2Bytes(account.Code)) + for key, value := range account.Storage { + statedb.SetState(address, common.HexToHash(key), common.HexToHash(value)) + } + } + statedb.SyncObjects() + + difficulty := common.String2Big(genesis.Difficulty) block := types.NewBlock(&types.Header{ - Difficulty: params.GenesisDifficulty, - GasLimit: params.GenesisGasLimit, - Nonce: types.EncodeNonce(nonce), + Nonce: types.EncodeNonce(common.String2Big(genesis.Nonce).Uint64()), + Time: common.String2Big(genesis.Timestamp).Uint64(), + ParentHash: common.HexToHash(genesis.ParentHash), + Extra: common.Hex2Bytes(genesis.ExtraData), + GasLimit: common.String2Big(genesis.GasLimit), + Difficulty: difficulty, + MixDigest: common.HexToHash(genesis.Mixhash), + Coinbase: common.HexToAddress(genesis.Coinbase), Root: statedb.Root(), }, nil, nil, nil) - block.Td = params.GenesisDifficulty - return block -} + block.Td = difficulty -var GenesisAccounts = []byte(`{ - "0000000000000000000000000000000000000001": {"balance": "1"}, - "0000000000000000000000000000000000000002": {"balance": "1"}, - "0000000000000000000000000000000000000003": {"balance": "1"}, - "0000000000000000000000000000000000000004": {"balance": "1"}, - "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, - "e4157b34ea9615cfbde6b4fda419828124b70c78": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, - "b9c015918bdaba24b4ff057a92a3873d6eb201be": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, - "6c386a4b26f73c802f34673f7248bb118f97424a": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, - "cd2a3d9f938e13cd947ec05abc7fe734df8dd826": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, - "2ef47100e0787b915105fd5e3f4ff6752079d5cb": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, - "e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, - "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"} -}`) + statedb.Sync() + + err = WriteBlock(blockDb, block) + if err != nil { + return nil, err + } + err = WriteHead(blockDb, block) + if err != nil { + return nil, err + } + + return block, nil +} // GenesisBlockForTesting creates a block in which addr has the given wei balance. // The state trie of the block is written to db. @@ -90,3 +112,39 @@ func GenesisBlockForTesting(db common.Database, addr common.Address, balance *bi block.Td = params.GenesisDifficulty return block } + +func WriteGenesisBlockForTesting(db common.Database, addr common.Address, balance *big.Int) *types.Block { + testGenesis := fmt.Sprintf(`{ + "nonce":"0x%x", + "gasLimit":"0x%x", + "difficulty":"0x%x", + "alloc": { + "0x%x":{"balance":"0x%x"} + } +}`, types.EncodeNonce(0), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes(), addr, balance.Bytes()) + block, _ := WriteGenesisBlock(db, db, strings.NewReader(testGenesis)) + return block +} + +func WriteTestNetGenesisBlock(stateDb, blockDb common.Database, nonce uint64) (*types.Block, error) { + testGenesis := fmt.Sprintf(`{ + "nonce":"0x%x", + "gasLimit":"0x%x", + "difficulty":"0x%x", + "alloc": { + "0000000000000000000000000000000000000001": {"balance": "1"}, + "0000000000000000000000000000000000000002": {"balance": "1"}, + "0000000000000000000000000000000000000003": {"balance": "1"}, + "0000000000000000000000000000000000000004": {"balance": "1"}, + "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "e4157b34ea9615cfbde6b4fda419828124b70c78": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "b9c015918bdaba24b4ff057a92a3873d6eb201be": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "6c386a4b26f73c802f34673f7248bb118f97424a": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "cd2a3d9f938e13cd947ec05abc7fe734df8dd826": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "2ef47100e0787b915105fd5e3f4ff6752079d5cb": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"} + } +}`, types.EncodeNonce(nonce), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes()) + return WriteGenesisBlock(stateDb, blockDb, strings.NewReader(testGenesis)) +} -- cgit v1.2.3