From fce218f7f83cbee98781f7f81adce1cd69222128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Mon, 31 Aug 2015 20:21:02 +0300 Subject: [PATCH 1/3] core, eth: split the db blocks into headers and bodies --- core/chain_manager.go | 7 +- core/chain_util.go | 158 ++++++++++++++++++++++++++++++++++++------ core/types/block.go | 4 ++ eth/backend.go | 108 ++++++++++++----------------- 4 files changed, 185 insertions(+), 92 deletions(-) diff --git a/core/chain_manager.go b/core/chain_manager.go index c8127951ea..8dccd04c55 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -361,12 +361,7 @@ func (bc *ChainManager) Genesis() *types.Block { // Block fetching methods func (bc *ChainManager) HasBlock(hash common.Hash) bool { - if bc.cache.Contains(hash) { - return true - } - - data, _ := bc.chainDb.Get(append(blockHashPre, hash[:]...)) - return len(data) != 0 + return bc.GetBlock(hash) != nil } func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) (chain []common.Hash) { diff --git a/core/chain_util.go b/core/chain_util.go index 84b462ce3d..19ea243a45 100644 --- a/core/chain_util.go +++ b/core/chain_util.go @@ -19,7 +19,6 @@ package core import ( "bytes" "math/big" - "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -30,9 +29,12 @@ import ( ) var ( - blockHashPre = []byte("block-hash-") + headerHashPre = []byte("header-hash-") + bodyHashPre = []byte("body-hash-") blockNumPre = []byte("block-num-") ExpDiffPeriod = big.NewInt(100000) + + blockHashPre = []byte("block-hash-") // [deprecated by eth/63] ) // CalcDifficulty is the difficulty adjustment algorithm. It returns @@ -112,27 +114,91 @@ func CalcGasLimit(parent *types.Block) *big.Int { return gl } -// GetBlockByHash returns the block corresponding to the hash or nil if not found -func GetBlockByHash(db common.Database, hash common.Hash) *types.Block { - data, _ := db.Get(append(blockHashPre, hash[:]...)) +// storageBody is the block body encoding used for the database. +type storageBody struct { + Transactions []*types.Transaction + Uncles []*types.Header +} + +// GetHeaderRLPByHash retrieves a block header in its raw RLP database encoding, +// or nil if the header's not found. +func GetHeaderRLPByHash(db common.Database, hash common.Hash) []byte { + data, _ := db.Get(append(headerHashPre, hash[:]...)) + return data +} + +// GetHeaderByHash retrieves the block header corresponding to the hash, nil if +// none found. +func GetHeaderByHash(db common.Database, hash common.Hash) *types.Header { + data := GetHeaderRLPByHash(db, hash) if len(data) == 0 { return nil } - var block types.StorageBlock - if err := rlp.Decode(bytes.NewReader(data), &block); err != nil { - glog.V(logger.Error).Infof("invalid block RLP for hash %x: %v", hash, err) + header := new(types.Header) + if err := rlp.Decode(bytes.NewReader(data), header); err != nil { + glog.V(logger.Error).Infof("invalid block header RLP for hash %x: %v", hash, err) return nil } - return (*types.Block)(&block) + return header } -// GetBlockByHash returns the canonical block by number or nil if not found +// GetBodyRLPByHash retrieves the block body (transactions and uncles) in RLP +// encoding, and the associated total difficulty. +func GetBodyRLPByHash(db common.Database, hash common.Hash) ([]byte, *big.Int) { + combo, _ := db.Get(append(bodyHashPre, hash[:]...)) + if len(combo) == 0 { + return nil, nil + } + buffer := bytes.NewBuffer(combo) + + td := new(big.Int) + if err := rlp.Decode(buffer, td); err != nil { + glog.V(logger.Error).Infof("invalid block td RLP for hash %x: %v", hash, err) + return nil, nil + } + return buffer.Bytes(), td +} + +// GetBodyByHash retrieves the block body (transactons, uncles, total difficulty) +// corresponding to the hash, nils if none found. +func GetBodyByHash(db common.Database, hash common.Hash) ([]*types.Transaction, []*types.Header, *big.Int) { + data, td := GetBodyRLPByHash(db, hash) + if len(data) == 0 || td == nil { + return nil, nil, nil + } + body := new(storageBody) + if err := rlp.Decode(bytes.NewReader(data), body); err != nil { + glog.V(logger.Error).Infof("invalid block body RLP for hash %x: %v", hash, err) + return nil, nil, nil + } + return body.Transactions, body.Uncles, td +} + +// GetBlockByHash retrieves an entire block corresponding to the hash, assembling +// it back from the stored header and body. +func GetBlockByHash(db common.Database, hash common.Hash) *types.Block { + // Retrieve the block header and body contents + header := GetHeaderByHash(db, hash) + if header == nil { + return nil + } + transactions, uncles, td := GetBodyByHash(db, hash) + if td == nil { + return nil + } + // Reassemble the block and return + block := types.NewBlockWithHeader(header).WithBody(transactions, uncles) + block.Td = td + + return block +} + +// GetBlockByNumber returns the canonical block by number or nil if not found. func GetBlockByNumber(db common.Database, number uint64) *types.Block { key, _ := db.Get(append(blockNumPre, big.NewInt(int64(number)).Bytes()...)) if len(key) == 0 { return nil } - return GetBlockByHash(db, common.BytesToHash(key)) } @@ -159,21 +225,67 @@ 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) +// WriteHeader serializes a block header into the database. +func WriteHeader(db common.Database, header *types.Header) error { + data, err := rlp.EncodeToBytes(header) 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)) + key := append(headerHashPre, header.Hash().Bytes()...) + if err := db.Put(key, data); err != nil { + glog.Fatalf("failed to store header into database: %v", err) + return err } - + glog.V(logger.Debug).Infof("stored header #%v [%x…]", header.Number, header.Hash().Bytes()[:4]) return nil } + +// WriteBody serializes the body of a block into the database. +func WriteBody(db common.Database, block *types.Block) error { + body, err := rlp.EncodeToBytes(&storageBody{block.Transactions(), block.Uncles()}) + if err != nil { + return err + } + td, err := rlp.EncodeToBytes(block.Td) + if err != nil { + return err + } + key := append(bodyHashPre, block.Hash().Bytes()...) + if err := db.Put(key, append(td, body...)); err != nil { + glog.Fatalf("failed to store block body into database: %v", err) + return err + } + glog.V(logger.Debug).Infof("stored block body #%v [%x…]", block.Number, block.Hash().Bytes()[:4]) + return nil +} + +// WriteBlock serializes a block into the database, header and body separately. +func WriteBlock(db common.Database, block *types.Block) error { + // Store the body first to retain database consistency + if err := WriteBody(db, block); err != nil { + return err + } + // Store the header too, signaling full block ownership + if err := WriteHeader(db, block.Header()); err != nil { + return err + } + return nil +} + +// [deprecated by eth/63] +// GetBlockByHashOld returns the old combined block corresponding to the hash +// or nil if not found. This method is only used by the upgrade mechanism to +// access the old combined block representation. It will be dropped after the +// network transitions to eth/63. +func GetBlockByHashOld(db common.Database, hash common.Hash) *types.Block { + data, _ := db.Get(append(blockHashPre, hash[:]...)) + if len(data) == 0 { + return nil + } + var block types.StorageBlock + if err := rlp.Decode(bytes.NewReader(data), &block); err != nil { + glog.V(logger.Error).Infof("invalid block RLP for hash %x: %v", hash, err) + return nil + } + return (*types.Block)(&block) +} diff --git a/core/types/block.go b/core/types/block.go index fd81db04cd..558b46e010 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -135,6 +135,7 @@ type Block struct { ReceivedAt time.Time } +// [deprecated by eth/63] // StorageBlock defines the RLP encoding of a Block stored in the // state database. The StorageBlock encoding contains fields that // would otherwise need to be recomputed. @@ -147,6 +148,7 @@ type extblock struct { Uncles []*Header } +// [deprecated by eth/63] // "storage" block encoding. used for database. type storageblock struct { Header *Header @@ -268,6 +270,7 @@ func (b *Block) EncodeRLP(w io.Writer) error { }) } +// [deprecated by eth/63] func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error { var sb storageblock if err := s.Decode(&sb); err != nil { @@ -277,6 +280,7 @@ func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error { return nil } +// [deprecated by eth/63] func (b *StorageBlock) EncodeRLP(w io.Writer) error { return rlp.Encode(w, storageblock{ Header: b.header, diff --git a/eth/backend.go b/eth/backend.go index ad2a2c1f94..7bafcbc83e 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -18,6 +18,7 @@ package eth import ( + "bytes" "crypto/ecdsa" "encoding/json" "fmt" @@ -267,11 +268,7 @@ func New(config *Config) (*Ethereum, error) { newdb = func(path string) (common.Database, error) { return ethdb.NewLDBDatabase(path, config.DatabaseCache) } } - // attempt to merge database together, upgrading from an old version - if err := mergeDatabases(config.DataDir, newdb); err != nil { - return nil, err - } - + // Open the chain database and perform any upgrades needed chainDb, err := newdb(filepath.Join(config.DataDir, "chaindata")) if err != nil { return nil, fmt.Errorf("blockchain db err: %v", err) @@ -279,6 +276,10 @@ func New(config *Config) (*Ethereum, error) { if db, ok := chainDb.(*ethdb.LDBDatabase); ok { db.Meter("eth/db/chaindata/") } + if err := upgradeChainDatabase(chainDb); err != nil { + return nil, err + } + dappDb, err := newdb(filepath.Join(config.DataDir, "dapp")) if err != nil { return nil, fmt.Errorf("dapp db err: %v", err) @@ -718,74 +719,55 @@ func saveBlockchainVersion(db common.Database, bcVersion int) { } } -// mergeDatabases when required merge old database layout to one single database -func mergeDatabases(datadir string, newdb func(path string) (common.Database, error)) error { - // Check if already upgraded - data := filepath.Join(datadir, "chaindata") - if _, err := os.Stat(data); !os.IsNotExist(err) { +// upgradeChainDatabase ensures that the chain database stores block split into +// separate header and body entries. +func upgradeChainDatabase(db common.Database) error { + // Short circuit if the head block is stored already as separate header and body + data, err := db.Get([]byte("LastBlock")) + if err != nil { return nil } - // make sure it's not just a clean path - chainPath := filepath.Join(datadir, "blockchain") - if _, err := os.Stat(chainPath); os.IsNotExist(err) { + head := common.BytesToHash(data) + + if block := core.GetBlockByHashOld(db, head); block == nil { return nil } - glog.Infoln("Database upgrade required. Upgrading...") + // At least some of the database is still the old format, upgrade (skip the head block!) + glog.V(logger.Info).Info("Old database detected, upgrading...") - database, err := newdb(data) - if err != nil { - return fmt.Errorf("creating data db err: %v", err) - } - defer database.Close() + if db, ok := db.(*ethdb.LDBDatabase); ok { + blockPrefix := []byte("block-hash-") + for it := db.NewIterator(); it.Next(); { + // Skip anything other than a combined block + if !bytes.HasPrefix(it.Key(), blockPrefix) { + continue + } + // Skip the head block (merge last to signal upgrade completion) + if bytes.HasSuffix(it.Key(), head.Bytes()) { + continue + } + // Load the block, split and serialize (order!) + block := core.GetBlockByHashOld(db, common.BytesToHash(bytes.TrimPrefix(it.Key(), blockPrefix))) - // Migrate blocks - chainDb, err := newdb(chainPath) - if err != nil { - return fmt.Errorf("state db err: %v", err) - } - defer chainDb.Close() - - if chain, ok := chainDb.(*ethdb.LDBDatabase); ok { - glog.Infoln("Merging blockchain database...") - it := chain.NewIterator() - for it.Next() { - database.Put(it.Key(), it.Value()) + if err := core.WriteBody(db, block); err != nil { + return err + } + if err := core.WriteHeader(db, block.Header()); err != nil { + return err + } + if err := db.Delete(it.Key()); err != nil { + return err + } } - it.Release() - } + // Lastly, upgrade the head block, disabling the upgrade mechanism + current := core.GetBlockByHashOld(db, head) - // Migrate state - stateDb, err := newdb(filepath.Join(datadir, "state")) - if err != nil { - return fmt.Errorf("state db err: %v", err) - } - defer stateDb.Close() - - if state, ok := stateDb.(*ethdb.LDBDatabase); ok { - glog.Infoln("Merging state database...") - it := state.NewIterator() - for it.Next() { - database.Put(it.Key(), it.Value()) + if err := core.WriteBody(db, current); err != nil { + return err } - it.Release() - } - - // Migrate transaction / receipts - extraDb, err := newdb(filepath.Join(datadir, "extra")) - if err != nil { - return fmt.Errorf("state db err: %v", err) - } - defer extraDb.Close() - - if extra, ok := extraDb.(*ethdb.LDBDatabase); ok { - glog.Infoln("Merging transaction database...") - - it := extra.NewIterator() - for it.Next() { - database.Put(it.Key(), it.Value()) + if err := core.WriteHeader(db, current.Header()); err != nil { + return err } - it.Release() } - return nil } From a06c4c964d92e6b89ed6a2f0275e06ddcaaf8e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 1 Sep 2015 14:29:54 +0300 Subject: [PATCH 2/3] core, eth: add/use separate header/body accessors --- core/chain_manager.go | 275 ++++++++++++++++++++++--------------- core/chain_manager_test.go | 5 +- core/chain_util.go | 56 ++++++-- core/genesis.go | 2 +- eth/handler.go | 43 +++--- eth/peer.go | 6 + eth/protocol.go | 16 +++ 7 files changed, 257 insertions(+), 146 deletions(-) diff --git a/core/chain_manager.go b/core/chain_manager.go index 8dccd04c55..745b270f7c 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -48,6 +48,8 @@ var ( ) const ( + headerCacheLimit = 256 + bodyCacheLimit = 256 blockCacheLimit = 256 maxFutureBlocks = 256 maxTimeFutureBlocks = 30 @@ -71,7 +73,10 @@ type ChainManager struct { lastBlockHash common.Hash currentGasLimit *big.Int - cache *lru.Cache // cache is the LRU caching + headerCache *lru.Cache // Cache for the most recent block headers + bodyCache *lru.Cache // Cache for the most recent block bodies + bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format + blockCache *lru.Cache // Cache for the most recent entire blocks futureBlocks *lru.Cache // future blocks are blocks added for later processing quit chan struct{} @@ -84,13 +89,22 @@ type ChainManager struct { } func NewChainManager(chainDb common.Database, pow pow.PoW, mux *event.TypeMux) (*ChainManager, error) { - cache, _ := lru.New(blockCacheLimit) + headerCache, _ := lru.New(headerCacheLimit) + bodyCache, _ := lru.New(bodyCacheLimit) + bodyRLPCache, _ := lru.New(bodyCacheLimit) + blockCache, _ := lru.New(blockCacheLimit) + futureBlocks, _ := lru.New(maxFutureBlocks) + bc := &ChainManager{ - chainDb: chainDb, - eventMux: mux, - quit: make(chan struct{}), - cache: cache, - pow: pow, + chainDb: chainDb, + eventMux: mux, + quit: make(chan struct{}), + headerCache: headerCache, + bodyCache: bodyCache, + bodyRLPCache: bodyRLPCache, + blockCache: blockCache, + futureBlocks: futureBlocks, + pow: pow, } bc.genesisBlock = bc.GetBlockByNumber(0) @@ -105,11 +119,9 @@ func NewChainManager(chainDb common.Database, pow pow.PoW, mux *event.TypeMux) ( } glog.V(logger.Info).Infoln("WARNING: Wrote default ethereum genesis block") } - 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 { if block := bc.GetBlock(hash); block != nil { @@ -123,14 +135,8 @@ func NewChainManager(chainDb common.Database, pow pow.PoW, mux *event.TypeMux) ( glog.V(logger.Error).Infoln("Chain reorg was successfull. Resuming normal operation") } } - // Take ownership of this particular state - - bc.futureBlocks, _ = lru.New(maxFutureBlocks) - bc.makeCache() - go bc.update() - return bc, nil } @@ -139,13 +145,15 @@ func (bc *ChainManager) SetHead(head *types.Block) { defer bc.mu.Unlock() for block := bc.currentBlock; block != nil && block.Hash() != head.Hash(); block = bc.GetBlock(block.ParentHash()) { - bc.removeBlock(block) + DeleteBlock(bc.chainDb, block.Hash()) } + bc.headerCache.Purge() + bc.bodyCache.Purge() + bc.bodyRLPCache.Purge() + bc.blockCache.Purge() + bc.futureBlocks.Purge() - bc.cache, _ = lru.New(blockCacheLimit) bc.currentBlock = head - bc.makeCache() - bc.setTotalDifficulty(head.Td) bc.insert(head) bc.setLastState() @@ -199,11 +207,9 @@ func (bc *ChainManager) recover() bool { if len(data) != 0 { block := bc.GetBlock(common.BytesToHash(data)) if block != nil { - err := bc.chainDb.Put([]byte("LastBlock"), block.Hash().Bytes()) - if err != nil { - glog.Fatalln("db write err:", err) + if err := WriteHead(bc.chainDb, block); err != nil { + glog.Fatalf("failed to write database head: %v", err) } - bc.currentBlock = block bc.lastBlockHash = block.Hash() return true @@ -213,14 +219,14 @@ func (bc *ChainManager) recover() bool { } func (bc *ChainManager) setLastState() error { - data, _ := bc.chainDb.Get([]byte("LastBlock")) - if len(data) != 0 { - block := bc.GetBlock(common.BytesToHash(data)) + head := GetHeadHash(bc.chainDb) + if head != (common.Hash{}) { + block := bc.GetBlock(head) if block != nil { bc.currentBlock = block bc.lastBlockHash = block.Hash() } else { - glog.Infof("LastBlock (%x) not found. Recovering...\n", data) + glog.Infof("LastBlock (%x) not found. Recovering...\n", head) if bc.recover() { glog.Infof("Recover successful") } else { @@ -240,63 +246,37 @@ func (bc *ChainManager) setLastState() error { return nil } -func (bc *ChainManager) makeCache() { - bc.cache, _ = lru.New(blockCacheLimit) - // load in last `blockCacheLimit` - 1 blocks. Last block is the current. - bc.cache.Add(bc.genesisBlock.Hash(), bc.genesisBlock) - for _, block := range bc.GetBlocksFromHash(bc.currentBlock.Hash(), blockCacheLimit) { - bc.cache.Add(block.Hash(), block) - } -} - +// Reset purges the entire blockchain, restoring it to its genesis state. func (bc *ChainManager) Reset() { + bc.ResetWithGenesisBlock(bc.genesisBlock) +} + +// ResetWithGenesisBlock purges the entire blockchain, restoring it to the +// specified genesis state. +func (bc *ChainManager) ResetWithGenesisBlock(genesis *types.Block) { bc.mu.Lock() defer bc.mu.Unlock() + // Dump the entire block chain and purge the caches for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.ParentHash()) { - bc.removeBlock(block) + DeleteBlock(bc.chainDb, block.Hash()) } + bc.headerCache.Purge() + bc.bodyCache.Purge() + bc.bodyRLPCache.Purge() + bc.blockCache.Purge() + bc.futureBlocks.Purge() - bc.cache, _ = lru.New(blockCacheLimit) + // Prepare the genesis block and reinitialize the chain + bc.genesisBlock = genesis + bc.genesisBlock.Td = genesis.Difficulty() - // Prepare the genesis block - err := WriteBlock(bc.chainDb, bc.genesisBlock) - if err != nil { - glog.Fatalln("db err:", err) + if err := WriteBlock(bc.chainDb, bc.genesisBlock); err != nil { + glog.Fatalf("failed to write genesis block: %v", err) } - bc.insert(bc.genesisBlock) bc.currentBlock = bc.genesisBlock - bc.makeCache() - - bc.setTotalDifficulty(common.Big("0")) -} - -func (bc *ChainManager) removeBlock(block *types.Block) { - bc.chainDb.Delete(append(blockHashPre, block.Hash().Bytes()...)) -} - -func (bc *ChainManager) ResetWithGenesisBlock(gb *types.Block) { - bc.mu.Lock() - defer bc.mu.Unlock() - - for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.ParentHash()) { - bc.removeBlock(block) - } - - // Prepare the genesis block - gb.Td = gb.Difficulty() - bc.genesisBlock = gb - - err := WriteBlock(bc.chainDb, bc.genesisBlock) - if err != nil { - glog.Fatalln("db err:", err) - } - - bc.insert(bc.genesisBlock) - bc.currentBlock = bc.genesisBlock - bc.makeCache() - bc.td = gb.Difficulty() + bc.setTotalDifficulty(genesis.Difficulty()) } // Export writes the active chain to the given writer. @@ -359,56 +339,130 @@ func (bc *ChainManager) Genesis() *types.Block { return bc.genesisBlock } -// Block fetching methods +// HasHeader checks if a block header is present in the database or not, caching +// it if present. +func (bc *ChainManager) HasHeader(hash common.Hash) bool { + return bc.GetHeader(hash) != nil +} + +// GetHeader retrieves a block header from the database by hash, caching it if +// found. +func (self *ChainManager) GetHeader(hash common.Hash) *types.Header { + // Short circuit if the header's already in the cache, retrieve otherwise + if header, ok := self.headerCache.Get(hash); ok { + return header.(*types.Header) + } + header := GetHeaderByHash(self.chainDb, hash) + if header == nil { + return nil + } + // Cache the found header for next time and return + self.headerCache.Add(header.Hash(), header) + return header +} + +// GetHeaderByNumber retrieves a block header from the database by number, +// caching it (associated with its hash) if found. +func (self *ChainManager) GetHeaderByNumber(number uint64) *types.Header { + hash := GetHashByNumber(self.chainDb, number) + if hash == (common.Hash{}) { + return nil + } + return self.GetHeader(hash) +} + +// GetBody retrieves a block body (transactions, uncles and total difficulty) +// from the database by hash, caching it if found. The resion for the peculiar +// pointer-to-slice return type is to differentiate between empty and inexistent +// bodies. +func (self *ChainManager) GetBody(hash common.Hash) (*[]*types.Transaction, *[]*types.Header) { + // Short circuit if the body's already in the cache, retrieve otherwise + if cached, ok := self.bodyCache.Get(hash); ok { + body := cached.(*storageBody) + return &body.Transactions, &body.Uncles + } + transactions, uncles, td := GetBodyByHash(self.chainDb, hash) + if td == nil { + return nil, nil + } + // Cache the found body for next time and return + self.bodyCache.Add(hash, &storageBody{ + Transactions: transactions, + Uncles: uncles, + }) + return &transactions, &uncles +} + +// GetBodyRLP retrieves a block body in RLP encoding from the database by hash, +// caching it if found. +func (self *ChainManager) GetBodyRLP(hash common.Hash) []byte { + // Short circuit if the body's already in the cache, retrieve otherwise + if cached, ok := self.bodyRLPCache.Get(hash); ok { + return cached.([]byte) + } + body, td := GetBodyRLPByHash(self.chainDb, hash) + if td == nil { + return nil + } + // Cache the found body for next time and return + self.bodyRLPCache.Add(hash, body) + return body +} + +// HasBlock checks if a block is fully present in the database or not, caching +// it if present. func (bc *ChainManager) HasBlock(hash common.Hash) bool { return bc.GetBlock(hash) != nil } -func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) (chain []common.Hash) { - block := self.GetBlock(hash) - if block == nil { - return - } - // XXX Could be optimised by using a different database which only holds hashes (i.e., linked list) - for i := uint64(0); i < max; i++ { - block = self.GetBlock(block.ParentHash()) - if block == nil { - break - } - - chain = append(chain, block.Hash()) - if block.Number().Cmp(common.Big0) <= 0 { - break - } - } - - return -} - +// GetBlock retrieves a block from the database by hash, caching it if found. func (self *ChainManager) GetBlock(hash common.Hash) *types.Block { - if block, ok := self.cache.Get(hash); ok { + // Short circuit if the block's already in the cache, retrieve otherwise + if block, ok := self.blockCache.Get(hash); ok { return block.(*types.Block) } - block := GetBlockByHash(self.chainDb, hash) if block == nil { return nil } - - // Add the block to the cache - self.cache.Add(hash, (*types.Block)(block)) - - return (*types.Block)(block) + // Cache the found block for next time and return + self.blockCache.Add(block.Hash(), block) + return block } -func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block { - self.mu.RLock() - defer self.mu.RUnlock() - - return self.getBlockByNumber(num) - +// GetBlockByNumber retrieves a block from the database by number, caching it +// (associated with its hash) if found. +func (self *ChainManager) GetBlockByNumber(number uint64) *types.Block { + hash := GetHashByNumber(self.chainDb, number) + if hash == (common.Hash{}) { + return nil + } + return self.GetBlock(hash) } +// GetBlockHashesFromHash retrieves a number of block hashes starting at a given +// hash, fetching towards the genesis block. +func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { + // Get the origin header from which to fetch + header := self.GetHeader(hash) + if header == nil { + return nil + } + // Iterate the headers until enough is collected or the genesis reached + chain := make([]common.Hash, 0, max) + for i := uint64(0); i < max; i++ { + if header = self.GetHeader(header.ParentHash); header == nil { + break + } + chain = append(chain, header.Hash()) + if header.Number.Cmp(common.Big0) <= 0 { + break + } + } + return chain +} + +// [deprecated by eth/62] // GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors. func (self *ChainManager) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) { for i := 0; i < n; i++ { @@ -422,11 +476,6 @@ func (self *ChainManager) GetBlocksFromHash(hash common.Hash, n int) (blocks []* return } -// non blocking version -func (self *ChainManager) getBlockByNumber(num uint64) *types.Block { - return GetBlockByNumber(self.chainDb, num) -} - func (self *ChainManager) GetUnclesInChain(block *types.Block, length int) (uncles []*types.Header) { for i := 0; block != nil && i < length; i++ { uncles = append(uncles, block.Uncles()...) diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go index 002dcbe446..97e7cacdc9 100644 --- a/core/chain_manager_test.go +++ b/core/chain_manager_test.go @@ -388,7 +388,10 @@ func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block func chm(genesis *types.Block, db common.Database) *ChainManager { var eventMux event.TypeMux bc := &ChainManager{chainDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: FakePow{}} - bc.cache, _ = lru.New(100) + bc.headerCache, _ = lru.New(100) + bc.bodyCache, _ = lru.New(100) + bc.bodyRLPCache, _ = lru.New(100) + bc.blockCache, _ = lru.New(100) bc.futureBlocks, _ = lru.New(100) bc.processor = bproc{} bc.ResetWithGenesisBlock(genesis) diff --git a/core/chain_util.go b/core/chain_util.go index 19ea243a45..c12bdda75d 100644 --- a/core/chain_util.go +++ b/core/chain_util.go @@ -29,6 +29,8 @@ import ( ) var ( + headKey = []byte("LastBlock") + headerHashPre = []byte("header-hash-") bodyHashPre = []byte("body-hash-") blockNumPre = []byte("block-num-") @@ -120,6 +122,24 @@ type storageBody struct { Uncles []*types.Header } +// GetHashByNumber retrieves a hash assigned to a canonical block number. +func GetHashByNumber(db common.Database, number uint64) common.Hash { + data, _ := db.Get(append(blockNumPre, big.NewInt(int64(number)).Bytes()...)) + if len(data) == 0 { + return common.Hash{} + } + return common.BytesToHash(data) +} + +// GetHeadHash retrieves the hash of the current canonical head block. +func GetHeadHash(db common.Database) common.Hash { + data, _ := db.Get(headKey) + if len(data) == 0 { + return common.Hash{} + } + return common.BytesToHash(data) +} + // GetHeaderRLPByHash retrieves a block header in its raw RLP database encoding, // or nil if the header's not found. func GetHeaderRLPByHash(db common.Database, hash common.Hash) []byte { @@ -202,24 +222,24 @@ func GetBlockByNumber(db common.Database, number uint64) *types.Block { return GetBlockByHash(db, common.BytesToHash(key)) } -// WriteCanonNumber writes the canonical hash for the given block -func WriteCanonNumber(db common.Database, block *types.Block) error { - key := append(blockNumPre, block.Number().Bytes()...) - err := db.Put(key, block.Hash().Bytes()) - if err != nil { +// WriteCanonNumber stores the canonical hash for the given block number. +func WriteCanonNumber(db common.Database, hash common.Hash, number uint64) error { + key := append(blockNumPre, big.NewInt(int64(number)).Bytes()...) + if err := db.Put(key, hash.Bytes()); err != nil { + glog.Fatalf("failed to store number to hash mapping into database: %v", err) return err } return nil } -// WriteHead force writes the current head +// WriteHead updates the head block of the chain database. func WriteHead(db common.Database, block *types.Block) error { - err := WriteCanonNumber(db, block) - if err != nil { + if err := WriteCanonNumber(db, block.Hash(), block.NumberU64()); err != nil { + glog.Fatalf("failed to store canonical number into database: %v", err) return err } - err = db.Put([]byte("LastBlock"), block.Hash().Bytes()) - if err != nil { + if err := db.Put(headKey, block.Hash().Bytes()); err != nil { + glog.Fatalf("failed to store last block into database: %v", err) return err } return nil @@ -272,6 +292,22 @@ func WriteBlock(db common.Database, block *types.Block) error { return nil } +// DeleteHeader removes all block header data associated with a hash. +func DeleteHeader(db common.Database, hash common.Hash) { + db.Delete(append(headerHashPre, hash.Bytes()...)) +} + +// DeleteBody removes all block body data associated with a hash. +func DeleteBody(db common.Database, hash common.Hash) { + db.Delete(append(bodyHashPre, hash.Bytes()...)) +} + +// DeleteBlock removes all block data associated with a hash. +func DeleteBlock(db common.Database, hash common.Hash) { + DeleteHeader(db, hash) + DeleteBody(db, hash) +} + // [deprecated by eth/63] // GetBlockByHashOld returns the old combined block corresponding to the hash // or nil if not found. This method is only used by the upgrade mechanism to diff --git a/core/genesis.go b/core/genesis.go index 7d4e03c990..6fbc671b06 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -86,7 +86,7 @@ func WriteGenesisBlock(chainDb common.Database, reader io.Reader) (*types.Block, if block := GetBlockByHash(chainDb, block.Hash()); block != nil { glog.V(logger.Info).Infoln("Genesis block already in chain. Writing canonical number") - err := WriteCanonNumber(chainDb, block) + err := WriteCanonNumber(chainDb, block.Hash(), block.NumberU64()) if err != nil { return nil, err } diff --git a/eth/handler.go b/eth/handler.go index f22afecb77..95f4e8ce21 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -345,33 +345,33 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if err := msg.Decode(&query); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } - // Gather blocks until the fetch or network limits is reached + // Gather headers until the fetch or network limits is reached var ( bytes common.StorageSize headers []*types.Header unknown bool ) for !unknown && len(headers) < int(query.Amount) && bytes < softResponseLimit && len(headers) < downloader.MaxHeaderFetch { - // Retrieve the next block satisfying the query - var origin *types.Block + // Retrieve the next header satisfying the query + var origin *types.Header if query.Origin.Hash != (common.Hash{}) { - origin = pm.chainman.GetBlock(query.Origin.Hash) + origin = pm.chainman.GetHeader(query.Origin.Hash) } else { - origin = pm.chainman.GetBlockByNumber(query.Origin.Number) + origin = pm.chainman.GetHeaderByNumber(query.Origin.Number) } if origin == nil { break } - headers = append(headers, origin.Header()) - bytes += origin.Size() + headers = append(headers, origin) + bytes += 500 // Approximate, should be good enough estimate - // Advance to the next block of the query + // Advance to the next header of the query switch { case query.Origin.Hash != (common.Hash{}) && query.Reverse: // Hash based traversal towards the genesis block for i := 0; i < int(query.Skip)+1; i++ { - if block := pm.chainman.GetBlock(query.Origin.Hash); block != nil { - query.Origin.Hash = block.ParentHash() + if header := pm.chainman.GetHeader(query.Origin.Hash); header != nil { + query.Origin.Hash = header.ParentHash } else { unknown = true break @@ -379,9 +379,9 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } case query.Origin.Hash != (common.Hash{}) && !query.Reverse: // Hash based traversal towards the leaf block - if block := pm.chainman.GetBlockByNumber(origin.NumberU64() + query.Skip + 1); block != nil { - if pm.chainman.GetBlockHashesFromHash(block.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash { - query.Origin.Hash = block.Hash() + if header := pm.chainman.GetHeaderByNumber(origin.Number.Uint64() + query.Skip + 1); header != nil { + if pm.chainman.GetBlockHashesFromHash(header.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash { + query.Origin.Hash = header.Hash() } else { unknown = true } @@ -452,23 +452,24 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { // Gather blocks until the fetch or network limits is reached var ( hash common.Hash - bytes common.StorageSize - bodies []*blockBody + bytes int + bodies []*blockBodyRLP ) for bytes < softResponseLimit && len(bodies) < downloader.MaxBlockFetch { - //Retrieve the hash of the next block + // Retrieve the hash of the next block if err := msgStream.Decode(&hash); err == rlp.EOL { break } else if err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } - // Retrieve the requested block, stopping if enough was found - if block := pm.chainman.GetBlock(hash); block != nil { - bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) - bytes += block.Size() + // Retrieve the requested block body, stopping if enough was found + if data := pm.chainman.GetBodyRLP(hash); len(data) != 0 { + body := blockBodyRLP(data) + bodies = append(bodies, &body) + bytes += len(body) } } - return p.SendBlockBodies(bodies) + return p.SendBlockBodiesRLP(bodies) case p.version >= eth63 && msg.Code == GetNodeDataMsg: // Decode the retrieval message diff --git a/eth/peer.go b/eth/peer.go index 8d7c488859..f1ddd97265 100644 --- a/eth/peer.go +++ b/eth/peer.go @@ -184,6 +184,12 @@ func (p *peer) SendBlockBodies(bodies []*blockBody) error { return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesData(bodies)) } +// SendBlockBodiesRLP sends a batch of block contents to the remote peer from +// an already RLP encoded format. +func (p *peer) SendBlockBodiesRLP(bodies []*blockBodyRLP) error { + return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesRLPData(bodies)) +} + // SendNodeData sends a batch of arbitrary internal data, corresponding to the // hashes requested. func (p *peer) SendNodeData(data [][]byte) error { diff --git a/eth/protocol.go b/eth/protocol.go index 49f096a3b2..24007bbb52 100644 --- a/eth/protocol.go +++ b/eth/protocol.go @@ -213,6 +213,22 @@ type blockBody struct { // blockBodiesData is the network packet for block content distribution. type blockBodiesData []*blockBody +// blockBodyRLP represents the RLP encoded data content of a single block. +type blockBodyRLP []byte + +// EncodeRLP is a specialized encoder for a block body to pass the already +// encoded body RLPs from the database on, without double encoding. +func (b *blockBodyRLP) EncodeRLP(w io.Writer) error { + if _, err := w.Write([]byte(*b)); err != nil { + return err + } + return nil +} + +// blockBodiesRLPData is the network packet for block content distribution +// based on original RLP formatting (i.e. skip the db-decode/proto-encode). +type blockBodiesRLPData []*blockBodyRLP + // nodeDataData is the network response packet for a node data retrieval. type nodeDataData []struct { Value []byte From 713d0a9a9db88df5bfc2f9ccbbdcd8e980fbe33c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Mon, 7 Sep 2015 14:35:34 +0300 Subject: [PATCH 3/3] core: make Header immutable, add RawHeader for building --- cmd/geth/main.go | 2 +- core/bench_test.go | 8 +- core/block_cache_test.go | 7 +- core/block_processor.go | 56 +++--- core/block_processor_test.go | 4 +- core/chain_makers.go | 14 +- core/chain_makers_test.go | 8 +- core/chain_manager.go | 26 ++- core/chain_manager_test.go | 4 +- core/chain_util.go | 43 ++++- core/genesis.go | 14 +- core/types/block.go | 255 ++++++------------------ core/types/header.go | 309 ++++++++++++++++++++++++++++++ core/vm_env.go | 4 +- eth/backend.go | 2 +- eth/downloader/downloader.go | 2 +- eth/downloader/downloader_test.go | 8 +- eth/downloader/queue.go | 14 +- eth/fetcher/fetcher.go | 12 +- eth/fetcher/fetcher_test.go | 8 +- eth/handler.go | 4 +- eth/handler_test.go | 16 +- miner/worker.go | 17 +- rpc/api/parsing.go | 28 +-- tests/block_test_util.go | 67 ++++--- 25 files changed, 581 insertions(+), 351 deletions(-) create mode 100644 core/types/header.go diff --git a/cmd/geth/main.go b/cmd/geth/main.go index aacb588feb..3c2f37a233 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -536,7 +536,7 @@ func blockRecovery(ctx *cli.Context) { glog.Fatalln("block not found. Recovery failed") } - err = core.WriteHead(blockDb, block) + err = core.WriteHeadBlockMeta(blockDb, block) if err != nil { glog.Fatalln("block write err", err) } diff --git a/core/bench_test.go b/core/bench_test.go index baae8a7a5e..cd36ee76a7 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -133,12 +133,12 @@ func genTxRing(naccounts int) func(int, *BlockGen) { // genUncles generates blocks with two uncle headers. func genUncles(i int, gen *BlockGen) { if i >= 6 { - b2 := gen.PrevBlock(i - 6).Header() + b2 := gen.PrevBlock(i - 6).Header().Raw() b2.Extra = []byte("foo") - gen.AddUncle(b2) - b3 := gen.PrevBlock(i - 6).Header() + gen.AddUncle(types.NewHeader(b2)) + b3 := gen.PrevBlock(i - 6).Header().Raw() b3.Extra = []byte("bar") - gen.AddUncle(b3) + gen.AddUncle(types.NewHeader(b3)) } } diff --git a/core/block_cache_test.go b/core/block_cache_test.go index ef826d5bda..5a08572785 100644 --- a/core/block_cache_test.go +++ b/core/block_cache_test.go @@ -27,8 +27,11 @@ import ( func newChain(size int) (chain []*types.Block) { var parentHash common.Hash for i := 0; i < size; i++ { - head := &types.Header{ParentHash: parentHash, Number: big.NewInt(int64(i))} - block := types.NewBlock(head, nil, nil, nil) + header := &types.RawHeader{ + ParentHash: parentHash, + Number: big.NewInt(int64(i)), + } + block := types.NewBlock(header, nil, nil, nil) chain = append(chain, block) parentHash = block.Hash() } diff --git a/core/block_processor.go b/core/block_processor.go index 1238fda7b9..9819126a8b 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -230,7 +230,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st // Validate the received block's bloom with the one derived from the generated receipts. // For valid blocks this should always validate to true. rbloom := types.CreateBloom(receipts) - if rbloom != header.Bloom { + if rbloom != header.Bloom() { err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom) return } @@ -238,21 +238,21 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st // The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]])) // can be used by light clients to make sure they've received the correct Txs txSha := types.DeriveSha(txs) - if txSha != header.TxHash { + if txSha != header.TxHash() { err = fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha) return } // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]])) receiptSha := types.DeriveSha(receipts) - if receiptSha != header.ReceiptHash { + if receiptSha != header.ReceiptHash() { err = fmt.Errorf("invalid receipt root hash. received=%x calculated=%x", header.ReceiptHash, receiptSha) return } // Verify UncleHash before running other uncle validations unclesSha := types.CalcUncleHash(uncles) - if unclesSha != header.UncleHash { + if unclesSha != header.UncleHash() { err = fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha) return } @@ -267,7 +267,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st // Commit state objects/accounts to a temporary trie (does not save) // used to calculate the state root. state.SyncObjects() - if header.Root != state.Root() { + if header.Root() != state.Root() { err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root()) return } @@ -291,16 +291,16 @@ func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*t reward := new(big.Int).Set(BlockReward) r := new(big.Int) for _, uncle := range uncles { - r.Add(uncle.Number, big8) - r.Sub(r, header.Number) + r.Add(uncle.Number(), big8) + r.Sub(r, header.Number()) r.Mul(r, BlockReward) r.Div(r, big8) - statedb.AddBalance(uncle.Coinbase, r) + statedb.AddBalance(uncle.Coinbase(), r) r.Div(BlockReward, big32) reward.Add(reward, r) } - statedb.AddBalance(header.Coinbase, reward) + statedb.AddBalance(header.Coinbase(), reward) } func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *types.Block) error { @@ -333,11 +333,11 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty return UncleError("uncle[%d](%x) is ancestor", i, hash[:4]) } - if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == parent.Hash() { - return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4]) + if uncleParent := uncle.ParentHash(); ancestors[uncleParent] == nil || uncleParent == parent.Hash() { + return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncleParent[:4]) } - if err := ValidateHeader(sm.Pow, uncle, ancestors[uncle.ParentHash], true, true); err != nil { + if err := ValidateHeader(sm.Pow, uncle, ancestors[uncle.ParentHash()], true, true); err != nil { return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err)) } } @@ -368,49 +368,49 @@ func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err erro // See YP section 4.3.4. "Block Header Validity" // Validates a block. Returns an error if the block is invalid. -func ValidateHeader(pow pow.PoW, block *types.Header, parent *types.Block, checkPow, uncle bool) error { - if big.NewInt(int64(len(block.Extra))).Cmp(params.MaximumExtraDataSize) == 1 { - return fmt.Errorf("Block extra data too long (%d)", len(block.Extra)) +func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Block, checkPow, uncle bool) error { + if big.NewInt(int64(len(header.Extra()))).Cmp(params.MaximumExtraDataSize) == 1 { + return fmt.Errorf("Header extra data too long (%d)", len(header.Extra())) } if uncle { - if block.Time.Cmp(common.MaxBig) == 1 { + if header.Time().Cmp(common.MaxBig) == 1 { return BlockTSTooBigErr } } else { - if block.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 { + if header.Time().Cmp(big.NewInt(time.Now().Unix())) == 1 { return BlockFutureErr } } - if block.Time.Cmp(parent.Time()) != 1 { + if header.Time().Cmp(parent.Time()) != 1 { return BlockEqualTSErr } - expd := CalcDifficulty(block.Time.Uint64(), parent.Time().Uint64(), parent.Number(), parent.Difficulty()) - if expd.Cmp(block.Difficulty) != 0 { - return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd) + expd := CalcDifficulty(header.Time().Uint64(), parent.Time().Uint64(), parent.Number(), parent.Difficulty()) + if expd.Cmp(header.Difficulty()) != 0 { + return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty(), expd) } var a, b *big.Int a = parent.GasLimit() - a = a.Sub(a, block.GasLimit) + a = a.Sub(a, header.GasLimit()) a.Abs(a) b = parent.GasLimit() b = b.Div(b, params.GasLimitBoundDivisor) - if !(a.Cmp(b) < 0) || (block.GasLimit.Cmp(params.MinGasLimit) == -1) { - return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b) + if !(a.Cmp(b) < 0) || (header.GasLimit().Cmp(params.MinGasLimit) == -1) { + return fmt.Errorf("GasLimit check failed for header %v (%v > %v)", header.GasLimit(), a, b) } num := parent.Number() - num.Sub(block.Number, num) + num.Sub(header.Number(), num) if num.Cmp(big.NewInt(1)) != 0 { return BlockNumberErr } if checkPow { - // Verify the nonce of the block. Return an error if it's not valid - if !pow.Verify(types.NewBlockWithHeader(block)) { - return ValidationError("Block's nonce is invalid (= %x)", block.Nonce) + // Verify the nonce of the header. Return an error if it's not valid + if !pow.Verify(types.NewBlockWithHeader(header)) { + return ValidationError("Block's nonce is invalid (= %x)", header.Nonce()) } } diff --git a/core/block_processor_test.go b/core/block_processor_test.go index e0b2d3313f..580129a247 100644 --- a/core/block_processor_test.go +++ b/core/block_processor_test.go @@ -48,13 +48,13 @@ func TestNumber(t *testing.T) { statedb := state.New(chain.Genesis().Root(), chain.chainDb) header := makeHeader(chain.Genesis(), statedb) header.Number = big.NewInt(3) - err := ValidateHeader(pow, header, chain.Genesis(), false, false) + err := ValidateHeader(pow, types.NewHeader(header), chain.Genesis(), false, false) if err != BlockNumberErr { t.Errorf("expected block number error, got %q", err) } header = makeHeader(chain.Genesis(), statedb) - err = ValidateHeader(pow, header, chain.Genesis(), false, false) + err = ValidateHeader(pow, types.NewHeader(header), chain.Genesis(), false, false) if err == BlockNumberErr { t.Errorf("didn't expect block number error") } diff --git a/core/chain_makers.go b/core/chain_makers.go index b009e0c28c..20d0511a96 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -49,7 +49,7 @@ type BlockGen struct { i int parent *types.Block chain []*types.Block - header *types.Header + header *types.RawHeader statedb *state.StateDB coinbase *state.StateObject @@ -89,12 +89,12 @@ func (b *BlockGen) AddTx(tx *types.Transaction) { if b.coinbase == nil { b.SetCoinbase(common.Address{}) } - _, gas, err := ApplyMessage(NewEnv(b.statedb, nil, tx, b.header), tx, b.coinbase) + _, gas, err := ApplyMessage(NewEnv(b.statedb, nil, tx, types.NewHeader(b.header)), tx, b.coinbase) if err != nil { panic(err) } b.statedb.SyncIntermediate() - b.header.GasUsed.Add(b.header.GasUsed, gas) + b.header.GasUsed = new(big.Int).Add(b.header.GasUsed, gas) receipt := types.NewReceipt(b.statedb.Root().Bytes(), b.header.GasUsed) logs := b.statedb.GetLogs(tx.Hash()) receipt.SetLogs(logs) @@ -145,12 +145,12 @@ func (b *BlockGen) PrevBlock(index int) *types.Block { func GenerateChain(parent *types.Block, db common.Database, n int, gen func(int, *BlockGen)) []*types.Block { statedb := state.New(parent.Root(), db) blocks := make(types.Blocks, n) - genblock := func(i int, h *types.Header) *types.Block { + genblock := func(i int, h *types.RawHeader) *types.Block { b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb} if gen != nil { gen(i, b) } - AccumulateRewards(statedb, h, b.uncles) + AccumulateRewards(statedb, types.NewHeader(h), b.uncles) statedb.SyncIntermediate() h.Root = statedb.Root() return types.NewBlock(h, b.txs, b.uncles, b.receipts) @@ -165,14 +165,14 @@ func GenerateChain(parent *types.Block, db common.Database, n int, gen func(int, return blocks } -func makeHeader(parent *types.Block, state *state.StateDB) *types.Header { +func makeHeader(parent *types.Block, state *state.StateDB) *types.RawHeader { var time *big.Int if parent.Time() == nil { time = big.NewInt(10) } else { time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds } - return &types.Header{ + return &types.RawHeader{ Root: state.Root(), ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index 1c868624df..31e3ff29a4 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -66,12 +66,12 @@ func ExampleGenerateChain() { gen.SetExtra([]byte("yeehaw")) case 3: // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). - b2 := gen.PrevBlock(1).Header() + b2 := gen.PrevBlock(1).Header().Raw() b2.Extra = []byte("foo") - gen.AddUncle(b2) - b3 := gen.PrevBlock(2).Header() + gen.AddUncle(types.NewHeader(b2)) + b3 := gen.PrevBlock(2).Header().Raw() b3.Extra = []byte("foo") - gen.AddUncle(b3) + gen.AddUncle(types.NewHeader(b3)) } }) diff --git a/core/chain_manager.go b/core/chain_manager.go index 745b270f7c..381d009f99 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -207,7 +207,7 @@ func (bc *ChainManager) recover() bool { if len(data) != 0 { block := bc.GetBlock(common.BytesToHash(data)) if block != nil { - if err := WriteHead(bc.chainDb, block); err != nil { + if err := WriteHeadBlockMeta(bc.chainDb, block); err != nil { glog.Fatalf("failed to write database head: %v", err) } bc.currentBlock = block @@ -219,7 +219,7 @@ func (bc *ChainManager) recover() bool { } func (bc *ChainManager) setLastState() error { - head := GetHeadHash(bc.chainDb) + head := GetHeadBlockHash(bc.chainDb) if head != (common.Hash{}) { block := bc.GetBlock(head) if block != nil { @@ -315,7 +315,7 @@ func (self *ChainManager) ExportN(w io.Writer, first uint64, last uint64) error // insert injects a block into the current chain block chain. Note, this function // assumes that the `mu` mutex is held! func (bc *ChainManager) insert(block *types.Block) { - err := WriteHead(bc.chainDb, block) + err := WriteHeadBlockMeta(bc.chainDb, block) if err != nil { glog.Fatal("db write fail:", err) } @@ -451,11 +451,11 @@ func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) [ // Iterate the headers until enough is collected or the genesis reached chain := make([]common.Hash, 0, max) for i := uint64(0); i < max; i++ { - if header = self.GetHeader(header.ParentHash); header == nil { + if header = self.GetHeader(header.ParentHash()); header == nil { break } chain = append(chain, header.Hash()) - if header.Number.Cmp(common.Big0) <= 0 { + if header.Number().Cmp(common.Big0) <= 0 { break } } @@ -531,6 +531,22 @@ const ( SideStatTy ) +/* +// WriteHeader inserts a potentially new header into the database, updating the +// head-header-hash index too if greater than the previous. +func (self *ChainManager) WriteHeader(db common.Database, header *types.Header) error { + self.wg.Add(1) + defer self.wg.Done() + + // Store the header into the database + if err := WriteHeader(self.chainDb, header); err != nil { + glog.Fatalf("failed to write header: %v", err) + return err + } + // If the header's higher than our previous head, update + if header. +}*/ + // WriteBlock writes the block to the chain (or pending queue) func (self *ChainManager) WriteBlock(block *types.Block, queued bool) (status writeStatus, err error) { self.wg.Add(1) diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go index 97e7cacdc9..8692bb97a2 100644 --- a/core/chain_manager_test.go +++ b/core/chain_manager_test.go @@ -369,7 +369,7 @@ func (bproc) Process(*types.Block) (state.Logs, types.Receipts, error) { return func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block { var chain []*types.Block for i, difficulty := range d { - header := &types.Header{ + header := &types.RawHeader{ Coinbase: common.Address{seed}, Number: big.NewInt(int64(i + 1)), Difficulty: big.NewInt(int64(difficulty)), @@ -379,7 +379,7 @@ func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block } else { header.ParentHash = chain[i-1].Hash() } - block := types.NewBlockWithHeader(header) + block := types.NewBlockWithRawHeader(header) chain = append(chain, block) } return chain diff --git a/core/chain_util.go b/core/chain_util.go index c12bdda75d..185e04a135 100644 --- a/core/chain_util.go +++ b/core/chain_util.go @@ -29,7 +29,8 @@ import ( ) var ( - headKey = []byte("LastBlock") + headHeaderKey = []byte("LastHeader") + headBlockKey = []byte("LastBlock") headerHashPre = []byte("header-hash-") bodyHashPre = []byte("body-hash-") @@ -131,9 +132,22 @@ func GetHashByNumber(db common.Database, number uint64) common.Hash { return common.BytesToHash(data) } -// GetHeadHash retrieves the hash of the current canonical head block. -func GetHeadHash(db common.Database) common.Hash { - data, _ := db.Get(headKey) +// GetHeadHeaderHash retrieves the hash of the current canonical head block's +// header. The different between this and GetHeadBlockHash is that whereas the +// last block hash is only updated upon a full block import, the last header +// hash is updated already at header import, allowing head tracking for the +// fast synchronization mechanism. +func GetHeadHeaderHash(db common.Database) common.Hash { + data, _ := db.Get(headHeaderKey) + if len(data) == 0 { + return common.Hash{} + } + return common.BytesToHash(data) +} + +// GetHeadBlockHash retrieves the hash of the current canonical head block. +func GetHeadBlockHash(db common.Database) common.Hash { + data, _ := db.Get(headBlockKey) if len(data) == 0 { return common.Hash{} } @@ -232,14 +246,23 @@ func WriteCanonNumber(db common.Database, hash common.Hash, number uint64) error return nil } -// WriteHead updates the head block of the chain database. -func WriteHead(db common.Database, block *types.Block) error { - if err := WriteCanonNumber(db, block.Hash(), block.NumberU64()); err != nil { - glog.Fatalf("failed to store canonical number into database: %v", err) +// WriteHeadBlockMeta stores the head header's hash. +func WriteHeadHeaderHash(db common.Database, header *types.Header) error { + if err := db.Put(headHeaderKey, header.Hash().Bytes()); err != nil { + glog.Fatalf("failed to store last header's hash into database: %v", err) return err } - if err := db.Put(headKey, block.Hash().Bytes()); err != nil { - glog.Fatalf("failed to store last block into database: %v", err) + return nil +} + +// WriteHeadBlockMeta stores the head block's metadata (number and hash). +func WriteHeadBlockMeta(db common.Database, block *types.Block) error { + if err := WriteCanonNumber(db, block.Hash(), block.NumberU64()); err != nil { + glog.Fatalf("failed to store last block's canonical number into database: %v", err) + return err + } + if err := db.Put(headBlockKey, block.Hash().Bytes()); err != nil { + glog.Fatalf("failed to store last block's hash into database: %v", err) return err } return nil diff --git a/core/genesis.go b/core/genesis.go index 6fbc671b06..5ba09e477b 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -71,7 +71,8 @@ func WriteGenesisBlock(chainDb common.Database, reader io.Reader) (*types.Block, statedb.SyncObjects() difficulty := common.String2Big(genesis.Difficulty) - block := types.NewBlock(&types.Header{ + + header := &types.RawHeader{ Nonce: types.EncodeNonce(common.String2Big(genesis.Nonce).Uint64()), Time: common.String2Big(genesis.Timestamp), ParentHash: common.HexToHash(genesis.ParentHash), @@ -81,7 +82,8 @@ func WriteGenesisBlock(chainDb common.Database, reader io.Reader) (*types.Block, MixDigest: common.HexToHash(genesis.Mixhash), Coinbase: common.HexToAddress(genesis.Coinbase), Root: statedb.Root(), - }, nil, nil, nil) + } + block := types.NewBlock(header, nil, nil, nil) block.Td = difficulty if block := GetBlockByHash(chainDb, block.Hash()); block != nil { @@ -99,7 +101,7 @@ func WriteGenesisBlock(chainDb common.Database, reader io.Reader) (*types.Block, if err != nil { return nil, err } - err = WriteHead(chainDb, block) + err = WriteHeadBlockMeta(chainDb, block) if err != nil { return nil, err } @@ -115,11 +117,13 @@ func GenesisBlockForTesting(db common.Database, addr common.Address, balance *bi obj.SetBalance(balance) statedb.SyncObjects() statedb.Sync() - block := types.NewBlock(&types.Header{ + + header := &types.RawHeader{ Difficulty: params.GenesisDifficulty, GasLimit: params.GenesisGasLimit, Root: statedb.Root(), - }, nil, nil, nil) + } + block := types.NewBlock(header, nil, nil, nil) block.Td = params.GenesisDifficulty return block } diff --git a/core/types/block.go b/core/types/block.go index 558b46e010..6dd3718820 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -18,9 +18,6 @@ package types import ( - "bytes" - "encoding/binary" - "encoding/json" "fmt" "io" "math/big" @@ -29,94 +26,11 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/rlp" ) -// A BlockNonce is a 64-bit hash which proves (combined with the -// mix-hash) that a suffcient amount of computation has been carried -// out on a block. -type BlockNonce [8]byte - -func EncodeNonce(i uint64) BlockNonce { - var n BlockNonce - binary.BigEndian.PutUint64(n[:], i) - return n -} - -func (n BlockNonce) Uint64() uint64 { - return binary.BigEndian.Uint64(n[:]) -} - -type Header struct { - ParentHash common.Hash // Hash to the previous block - UncleHash common.Hash // Uncles of this block - Coinbase common.Address // The coin base address - Root common.Hash // Block Trie state - TxHash common.Hash // Tx sha - ReceiptHash common.Hash // Receipt sha - Bloom Bloom // Bloom - Difficulty *big.Int // Difficulty for the current block - Number *big.Int // The block number - GasLimit *big.Int // Gas limit - GasUsed *big.Int // Gas used - Time *big.Int // Creation time - Extra []byte // Extra data - MixDigest common.Hash // for quick difficulty verification - Nonce BlockNonce -} - -func (h *Header) Hash() common.Hash { - return rlpHash(h) -} - -func (h *Header) HashNoNonce() common.Hash { - return rlpHash([]interface{}{ - h.ParentHash, - h.UncleHash, - h.Coinbase, - h.Root, - h.TxHash, - h.ReceiptHash, - h.Bloom, - h.Difficulty, - h.Number, - h.GasLimit, - h.GasUsed, - h.Time, - h.Extra, - }) -} - -func (h *Header) UnmarshalJSON(data []byte) error { - var ext struct { - ParentHash string - Coinbase string - Difficulty string - GasLimit string - Time *big.Int - Extra string - } - dec := json.NewDecoder(bytes.NewReader(data)) - if err := dec.Decode(&ext); err != nil { - return err - } - - h.ParentHash = common.HexToHash(ext.ParentHash) - h.Coinbase = common.HexToAddress(ext.Coinbase) - h.Difficulty = common.String2Big(ext.Difficulty) - h.Time = ext.Time - h.Extra = []byte(ext.Extra) - return nil -} - -func rlpHash(x interface{}) (h common.Hash) { - hw := sha3.NewKeccak256() - rlp.Encode(hw, x) - hw.Sum(h[:0]) - return h -} - +// Header is the main Ethereum block, containing all of the block data belonging +// to the consensus protocol, as well as a few added fields for the implementation. type Block struct { header *Header uncles []*Header @@ -124,7 +38,6 @@ type Block struct { receipts Receipts // caches - hash atomic.Value size atomic.Value // Td is used by package core to store the total difficulty @@ -169,69 +82,56 @@ var ( // The values of TxHash, UncleHash, ReceiptHash and Bloom in header // are ignored and set to values derived from the given txs, uncles // and receipts. -func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block { - b := &Block{header: copyHeader(header), Td: new(big.Int)} +func NewBlock(header *RawHeader, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block { + head := header.Copy() // TODO: panic if len(txs) != len(receipts) if len(txs) == 0 { - b.header.TxHash = emptyRootHash + head.TxHash = emptyRootHash } else { - b.header.TxHash = DeriveSha(Transactions(txs)) - b.transactions = make(Transactions, len(txs)) - copy(b.transactions, txs) + head.TxHash = DeriveSha(Transactions(txs)) } + txsCopy := make(Transactions, len(txs)) + copy(txsCopy, txs) if len(receipts) == 0 { - b.header.ReceiptHash = emptyRootHash + head.ReceiptHash = emptyRootHash } else { - b.header.ReceiptHash = DeriveSha(Receipts(receipts)) - b.header.Bloom = CreateBloom(receipts) - b.receipts = make([]*Receipt, len(receipts)) - copy(b.receipts, receipts) + head.ReceiptHash = DeriveSha(Receipts(receipts)) + head.Bloom = CreateBloom(receipts) } + receiptsCopy := make([]*Receipt, len(receipts)) + copy(receiptsCopy, receipts) if len(uncles) == 0 { - b.header.UncleHash = emptyUncleHash + head.UncleHash = emptyUncleHash } else { - b.header.UncleHash = CalcUncleHash(uncles) - b.uncles = make([]*Header, len(uncles)) - for i := range uncles { - b.uncles[i] = copyHeader(uncles[i]) - } + head.UncleHash = CalcUncleHash(uncles) + } + unclesCopy := make([]*Header, len(uncles)) + for i := range uncles { + unclesCopy[i] = uncles[i].Copy() + } + // Assemble and return an immutable block + return &Block{ + header: &Header{rawHeader: *head}, + transactions: txsCopy, + receipts: receiptsCopy, + uncles: unclesCopy, + Td: new(big.Int), } - - return b } -// NewBlockWithHeader creates a block with the given header data. The -// header data is copied, changes to header and to the field values -// will not affect the block. +// NewBlockWithRawHeader creates a block with the given header data. The header +// data is copied, changes to header and to the field values will not affect +// the block. +func NewBlockWithRawHeader(raw *RawHeader) *Block { + return &Block{header: NewHeader(raw)} +} + +// NewBlockWithHeader creates a block with the given immutable header. func NewBlockWithHeader(header *Header) *Block { - return &Block{header: copyHeader(header)} -} - -func copyHeader(h *Header) *Header { - cpy := *h - if cpy.Time = new(big.Int); h.Time != nil { - cpy.Time.Set(h.Time) - } - if cpy.Difficulty = new(big.Int); h.Difficulty != nil { - cpy.Difficulty.Set(h.Difficulty) - } - if cpy.Number = new(big.Int); h.Number != nil { - cpy.Number.Set(h.Number) - } - if cpy.GasLimit = new(big.Int); h.GasLimit != nil { - cpy.GasLimit.Set(h.GasLimit) - } - if cpy.GasUsed = new(big.Int); h.GasUsed != nil { - cpy.GasUsed.Set(h.GasUsed) - } - if len(h.Extra) > 0 { - cpy.Extra = make([]byte, len(h.Extra)) - copy(cpy.Extra, h.Extra) - } - return &cpy + return &Block{header: header} } func (b *Block) ValidateFields() error { @@ -304,29 +204,28 @@ func (b *Block) Transaction(hash common.Hash) *Transaction { return nil } -func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) } -func (b *Block) GasLimit() *big.Int { return new(big.Int).Set(b.header.GasLimit) } -func (b *Block) GasUsed() *big.Int { return new(big.Int).Set(b.header.GasUsed) } -func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) } -func (b *Block) Time() *big.Int { return new(big.Int).Set(b.header.Time) } +func (b *Block) Number() *big.Int { return b.header.Number() } +func (b *Block) GasLimit() *big.Int { return b.header.GasLimit() } +func (b *Block) GasUsed() *big.Int { return b.header.GasUsed() } +func (b *Block) Difficulty() *big.Int { return b.header.Difficulty() } +func (b *Block) Time() *big.Int { return b.header.Time() } -func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() } -func (b *Block) MixDigest() common.Hash { return b.header.MixDigest } -func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.header.Nonce[:]) } -func (b *Block) Bloom() Bloom { return b.header.Bloom } -func (b *Block) Coinbase() common.Address { return b.header.Coinbase } -func (b *Block) Root() common.Hash { return b.header.Root } -func (b *Block) ParentHash() common.Hash { return b.header.ParentHash } -func (b *Block) TxHash() common.Hash { return b.header.TxHash } -func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash } -func (b *Block) UncleHash() common.Hash { return b.header.UncleHash } -func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) } +func (b *Block) NumberU64() uint64 { return b.header.Number().Uint64() } +func (b *Block) MixDigest() common.Hash { return b.header.MixDigest() } +func (b *Block) Nonce() uint64 { return b.header.Nonce().Uint64() } +func (b *Block) Bloom() Bloom { return b.header.Bloom() } +func (b *Block) Coinbase() common.Address { return b.header.Coinbase() } +func (b *Block) Root() common.Hash { return b.header.Root() } +func (b *Block) ParentHash() common.Hash { return b.header.ParentHash() } +func (b *Block) TxHash() common.Hash { return b.header.TxHash() } +func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash() } +func (b *Block) UncleHash() common.Hash { return b.header.UncleHash() } +func (b *Block) Extra() []byte { return b.header.Extra() } -func (b *Block) Header() *Header { return copyHeader(b.header) } +func (b *Block) Header() *Header { return b.header } -func (b *Block) HashNoNonce() common.Hash { - return b.header.HashNoNonce() -} +func (b *Block) HashNoNonce() common.Hash { return b.header.HashNoNonce() } +func (b *Block) Hash() common.Hash { return b.header.Hash() } func (b *Block) Size() common.StorageSize { if size := b.size.Load(); size != nil { @@ -352,11 +251,11 @@ func CalcUncleHash(uncles []*Header) common.Hash { // WithMiningResult returns a new block with the data from b // where nonce and mix digest are set to the provided values. func (b *Block) WithMiningResult(nonce uint64, mixDigest common.Hash) *Block { - cpy := *b.header - binary.BigEndian.PutUint64(cpy.Nonce[:], nonce) - cpy.MixDigest = mixDigest + header := b.header.Raw() + header.Nonce = EncodeNonce(nonce) + header.MixDigest = mixDigest return &Block{ - header: &cpy, + header: &Header{rawHeader: *header}, transactions: b.transactions, receipts: b.receipts, uncles: b.uncles, @@ -367,28 +266,19 @@ func (b *Block) WithMiningResult(nonce uint64, mixDigest common.Hash) *Block { // WithBody returns a new block with the given transaction and uncle contents. func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block { block := &Block{ - header: copyHeader(b.header), + header: b.header.Copy(), transactions: make([]*Transaction, len(transactions)), uncles: make([]*Header, len(uncles)), } copy(block.transactions, transactions) for i := range uncles { - block.uncles[i] = copyHeader(uncles[i]) + block.uncles[i] = uncles[i].Copy() } return block } // Implement pow.Block -func (b *Block) Hash() common.Hash { - if hash := b.hash.Load(); hash != nil { - return hash.(common.Hash) - } - v := rlpHash(b.header) - b.hash.Store(v) - return v -} - func (b *Block) String() string { str := fmt.Sprintf(`Block(#%v): Size: %v TD: %v { MinerHash: %x @@ -402,27 +292,6 @@ Uncles: return str } -func (h *Header) String() string { - return fmt.Sprintf(`Header(%x): -[ - ParentHash: %x - UncleHash: %x - Coinbase: %x - Root: %x - TxSha %x - ReceiptSha: %x - Bloom: %x - Difficulty: %v - Number: %v - GasLimit: %v - GasUsed: %v - Time: %v - Extra: %s - MixDigest: %x - Nonce: %x -]`, h.Hash(), h.ParentHash, h.UncleHash, h.Coinbase, h.Root, h.TxHash, h.ReceiptHash, h.Bloom, h.Difficulty, h.Number, h.GasLimit, h.GasUsed, h.Time, h.Extra, h.MixDigest, h.Nonce) -} - type Blocks []*Block type BlockBy func(b1, b2 *Block) bool @@ -446,4 +315,4 @@ func (self blockSorter) Swap(i, j int) { } func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) } -func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 } +func Number(b1, b2 *Block) bool { return b1.header.Number().Cmp(b2.header.Number()) < 0 } diff --git a/core/types/header.go b/core/types/header.go new file mode 100644 index 0000000000..2dddeadd58 --- /dev/null +++ b/core/types/header.go @@ -0,0 +1,309 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains the block header type and related methods. + +package types + +import ( + "bytes" + "encoding/binary" + "encoding/json" + "fmt" + "io" + "math/big" + "sync/atomic" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/sha3" + "github.com/ethereum/go-ethereum/rlp" +) + +// A BlockNonce is a 64-bit hash which proves (combined with the +// mix-hash) that a suffcient amount of computation has been carried +// out on a block. +type BlockNonce [8]byte + +func EncodeNonce(i uint64) BlockNonce { + var n BlockNonce + binary.BigEndian.PutUint64(n[:], i) + return n +} + +func (n BlockNonce) Uint64() uint64 { + return binary.BigEndian.Uint64(n[:]) +} + +// RawHeader is the base consensus header used by the Ethereum system, which +// contains only the bare essential fields defined by the consensus protocol. +// +// This structure is mutable, and can be used to create an immutable header +// used throughout the codebase. Do not pass this structure around, only use +// to aid in new header construction or data query from an existing header. +type RawHeader struct { + ParentHash common.Hash // Hash of the previous block in the chain + UncleHash common.Hash // Hash of the uncles contained in this block + Coinbase common.Address // Owner address of the block (miner) + Root common.Hash // Merkle-Patricia state trie root hash + TxHash common.Hash // Hash of the transactions contained in this block + ReceiptHash common.Hash // Hash of the transactions receipts contained in this block + Bloom Bloom // Bloom filter for the event logs in the block + Difficulty *big.Int // Proof-of-work difficulty of this block + Number *big.Int // Index number of the block within the chain + GasLimit *big.Int // Maximum gas allowance for this block + GasUsed *big.Int // Amount of gas actually used in this block + Time *big.Int // Creation timestamp of this block + Extra []byte // Extra data inserted into the block by the miner + MixDigest common.Hash // Miner mix digest for quick difficulty verification + Nonce BlockNonce // Proof-of-work nonce of the block +} + +// hash calculates the nonce-inclusive hash of the header. As the raw header is +// mutable, this hasher cannot cache its expensive operation, hence it's private +// and users are required to use the immutable Header's Hash method. +func (h *RawHeader) hash() common.Hash { + return rlpHash(h) +} + +// hashNoNonce calculates the miner hash of the header (no mix digest or nonce). +// Similarly to the hash method, as the raw header is mutable, this hasher also +// cannot cache its expensive operation, hence it's private and users are asked +// to use the immutable Header's HashNoNonce method. +func (h *RawHeader) hashNoNonce() common.Hash { + return rlpHash([]interface{}{ + h.ParentHash, + h.UncleHash, + h.Coinbase, + h.Root, + h.TxHash, + h.ReceiptHash, + h.Bloom, + h.Difficulty, + h.Number, + h.GasLimit, + h.GasUsed, + h.Time, + h.Extra, + }) +} + +// Copy creates a deep copy of a raw header. +func (h *RawHeader) Copy() *RawHeader { + cpy := *h + if cpy.Difficulty = new(big.Int); h.Difficulty != nil { + cpy.Difficulty.Set(h.Difficulty) + } + if cpy.Number = new(big.Int); h.Number != nil { + cpy.Number.Set(h.Number) + } + if cpy.GasLimit = new(big.Int); h.GasLimit != nil { + cpy.GasLimit.Set(h.GasLimit) + } + if cpy.GasUsed = new(big.Int); h.GasUsed != nil { + cpy.GasUsed.Set(h.GasUsed) + } + if cpy.Time = new(big.Int); h.Time != nil { + cpy.Time.Set(h.Time) + } + if len(h.Extra) > 0 { + cpy.Extra = common.CopyBytes(h.Extra) + } + return &cpy +} + +// Header is the immutable Ethereum block header, containing all of the metadata +// related to a chain block, and some additional helper fields, caches. +type Header struct { + rawHeader RawHeader // Base header fields part of the consensus protocol + + hashNoNonce atomic.Value // Cached hash of the header without the nonce + hashWithNonce atomic.Value // Cached hash of the header with the nonce + rlpSize atomic.Value // Size of the header in RLP encoded format +} + +// NewHeader creates an immutable header from a mutable raw header. +func NewHeader(raw *RawHeader) *Header { + return &Header{ + rawHeader: *raw.Copy(), + } +} + +// Raw creates and returns a mutable deep copy of the raw consensus header. +func (h *Header) Raw() *RawHeader { + return h.rawHeader.Copy() +} + +// ParentHash retrieves the hash of this block's parent in the blockchain. +func (h *Header) ParentHash() common.Hash { return h.rawHeader.ParentHash } + +// UncleHash retrieves the hash of the uncle blocks contained within this block. +func (h *Header) UncleHash() common.Hash { return h.rawHeader.UncleHash } + +// Coinbase retrieves the account owning this block (i.e. miner). +func (h *Header) Coinbase() common.Address { return h.rawHeader.Coinbase } + +// Root retrieves the root hash of the Merkle-Patricia state trie formed. +func (h *Header) Root() common.Hash { return h.rawHeader.Root } + +// TxHash retrieves the hash of the transactions contained within this block. +func (h *Header) TxHash() common.Hash { return h.rawHeader.TxHash } + +// ReceiptHash retrieves the hash of the transaction receipts contained within. +func (h *Header) ReceiptHash() common.Hash { return h.rawHeader.ReceiptHash } + +// Bloom retrieves the bloom filter of the event logs contained within. +func (h *Header) Bloom() Bloom { return h.rawHeader.Bloom } + +// Difficulty retrieves the proof-of-work difficulty of this block. +func (h *Header) Difficulty() *big.Int { return new(big.Int).Set(h.rawHeader.Difficulty) } + +// Number retrieves the index/number of the block within the chain. +func (h *Header) Number() *big.Int { return new(big.Int).Set(h.rawHeader.Number) } + +// GasLimit retrieves the maximum gas allowance for this block. +func (h *Header) GasLimit() *big.Int { return new(big.Int).Set(h.rawHeader.GasLimit) } + +// GasUsed retrieves the amount of gas actually used in this block. +func (h *Header) GasUsed() *big.Int { return new(big.Int).Set(h.rawHeader.GasUsed) } + +// Time retrieves the creation timestamp of this block. +func (h *Header) Time() *big.Int { return new(big.Int).Set(h.rawHeader.Time) } + +// Extra retrieves the extra data inserted into the block by the miner. +func (h *Header) Extra() []byte { + return common.CopyBytes(h.rawHeader.Extra) +} + +// MixDigest retrieves the miner mix digest for quick difficulty verification. +func (h *Header) MixDigest() common.Hash { return h.rawHeader.MixDigest } + +// Nonce retrieves the proof-of-work nonce of the block. +func (h *Header) Nonce() BlockNonce { return h.rawHeader.Nonce } + +// RlpSize retrieves the RLP encoded size of the header, calculating and caching +// if it unknown. +func (h *Header) RlpSize() common.StorageSize { + if size := h.rlpSize.Load(); size != nil && size.(common.StorageSize).Int64() != 0 { + return size.(common.StorageSize) + } + counter := writeCounter(0) + rlp.Encode(&counter, h.rawHeader) + + h.rlpSize.Store(common.StorageSize(counter)) + return common.StorageSize(counter) +} + +// Hash retrieves the nonce-inclusive hash of the header, calculating and caching +// it if unknown. +func (h *Header) Hash() common.Hash { + if hash := h.hashWithNonce.Load(); hash != nil && hash.(common.Hash) != (common.Hash{}) { + return hash.(common.Hash) + } + hash := h.rawHeader.hash() + h.hashWithNonce.Store(hash) + return hash +} + +// HashNoNonce retrieves the miner hash of the header (no mix digest or nonce), +// calculating and caching it if unknown. +func (h *Header) HashNoNonce() common.Hash { + if hash := h.hashNoNonce.Load(); hash != nil && hash.(common.Hash) != (common.Hash{}) { + return hash.(common.Hash) + } + hash := h.rawHeader.hashNoNonce() + h.hashNoNonce.Store(hash) + return hash +} + +// DecodeRLP implements the rlp.Decoder interface, deserializing a raw header +// from an RLP Data stream. +func (h *Header) DecodeRLP(s *rlp.Stream) error { + // Reset any previously cached fields + h.hashNoNonce.Store(common.Hash{}) + h.hashWithNonce.Store(common.Hash{}) + h.rlpSize.Store(common.StorageSize(0)) + + // Fill in the real RLP encoded size of the header + _, size, _ := s.Kind() + h.rlpSize.Store(common.StorageSize(rlp.ListSize(size))) + + // Decode the RLP encoded raw header + return s.Decode(&h.rawHeader) +} + +// EncodeRLP implements the rlp.Encoder interface, serializing a raw header into +// and RLP data stream. +func (h *Header) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, h.rawHeader) +} + +func (h *Header) UnmarshalJSON(data []byte) error { + var ext struct { + ParentHash string + Coinbase string + Difficulty string + GasLimit string + Time *big.Int + Extra string + } + dec := json.NewDecoder(bytes.NewReader(data)) + if err := dec.Decode(&ext); err != nil { + return err + } + h.rawHeader.ParentHash = common.HexToHash(ext.ParentHash) + h.rawHeader.Coinbase = common.HexToAddress(ext.Coinbase) + h.rawHeader.Difficulty = common.String2Big(ext.Difficulty) + h.rawHeader.Time = ext.Time + h.rawHeader.Extra = []byte(ext.Extra) + + return nil +} + +// Copy creates a deep copy of a header. +func (h *Header) Copy() *Header { + cpy := new(Header) + cpy.rawHeader = *h.rawHeader.Copy() + return cpy +} + +// String implements the fmt.Stringer interface, formatting a header into a string. +func (h *Header) String() string { + return fmt.Sprintf(`Header(%x): +[ + ParentHash: %x + UncleHash: %x + Coinbase: %x + Root: %x + TxSha: %x + ReceiptSha: %x + Bloom: %x + Difficulty: %v + Number: %v + GasLimit: %v + GasUsed: %v + Time: %v + Extra: %s + MixDigest: %x + Nonce: %x +]`, h.Hash(), h.ParentHash(), h.UncleHash(), h.Coinbase(), h.Root(), h.TxHash(), h.ReceiptHash(), h.Bloom(), h.Difficulty(), h.Number(), h.GasLimit(), h.GasUsed(), h.Time(), h.Extra(), h.MixDigest(), h.Nonce()) +} + +func rlpHash(x interface{}) (h common.Hash) { + hw := sha3.NewKeccak256() + rlp.Encode(hw, x) + hw.Sum(h[:0]) + return h +} diff --git a/core/vm_env.go b/core/vm_env.go index a08f024fe7..fb373ccc23 100644 --- a/core/vm_env.go +++ b/core/vm_env.go @@ -27,7 +27,7 @@ import ( type VMEnv struct { state *state.StateDB - header *types.Header + header *types.RawHeader msg Message depth int chain *ChainManager @@ -40,7 +40,7 @@ func NewEnv(state *state.StateDB, chain *ChainManager, msg Message, header *type return &VMEnv{ chain: chain, state: state, - header: header, + header: header.Raw(), msg: msg, typ: vm.StdVmTy, } diff --git a/eth/backend.go b/eth/backend.go index 7bafcbc83e..d911ae6438 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -315,7 +315,7 @@ func New(config *Config) (*Ethereum, error) { // This is for testing only. if config.GenesisBlock != nil { core.WriteBlock(chainDb, config.GenesisBlock) - core.WriteHead(chainDb, config.GenesisBlock) + core.WriteHeadBlockMeta(chainDb, config.GenesisBlock) } if !config.SkipBcVersionCheck { diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 574f2ba15e..a593da0ed2 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -810,7 +810,7 @@ func (d *Downloader) findAncestor(p *peer) (uint64, error) { finished = true for i := len(headers) - 1; i >= 0; i-- { if d.hasBlock(headers[i].Hash()) { - number, hash = headers[i].Number.Uint64(), headers[i].Hash() + number, hash = headers[i].Number().Uint64(), headers[i].Hash() break } } diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 8d009b6717..0eccb1885e 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -58,7 +58,11 @@ func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common } // If the block number is a multiple of 5, add a bonus uncle to the block if i%5 == 0 { - block.AddUncle(&types.Header{ParentHash: block.PrevBlock(i - 1).Hash(), Number: big.NewInt(int64(i - 1))}) + uncle := &types.RawHeader{ + ParentHash: block.PrevBlock(i - 1).Hash(), + Number: big.NewInt(int64(i - 1)), + } + block.AddUncle(types.NewHeader(uncle)) } }) hashes := make([]common.Hash, n+1) @@ -696,7 +700,7 @@ func testBlockBodyAttackerDropping(t *testing.T, protocol int) { // Assemble a good or bad block, depending of the test raw := core.GenerateChain(genesis, testdb, 1, nil)[0] if tt.failure { - parent := types.NewBlock(&types.Header{}, nil, nil, nil) + parent := types.NewBlock(new(types.RawHeader), nil, nil, nil) raw = core.GenerateChain(parent, testdb, 1, nil)[0] } block := &Block{OriginPeer: id, RawBlock: raw} diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 7db78327b3..431dd59f6f 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -202,7 +202,7 @@ func (q *queue) Insert(headers []*types.Header) []*types.Header { // Queue the header for body retrieval inserts = append(inserts, header) q.headerPool[hash] = header - q.headerQueue.Push(header, -float32(header.Number.Uint64())) + q.headerQueue.Push(header, -float32(header.Number().Uint64())) } return inserts } @@ -340,7 +340,7 @@ func (q *queue) Reserve(p *peer, count int) (*fetchRequest, bool, error) { header := q.headerQueue.PopItem().(*types.Header) // If the header defines an empty block, deliver straight - if header.TxHash == types.DeriveSha(types.Transactions{}) && header.UncleHash == types.CalcUncleHash([]*types.Header{}) { + if header.TxHash() == types.DeriveSha(types.Transactions{}) && header.UncleHash() == types.CalcUncleHash([]*types.Header{}) { if err := q.enqueue("", types.NewBlockWithHeader(header)); err != nil { return nil, false, errInvalidChain } @@ -357,7 +357,7 @@ func (q *queue) Reserve(p *peer, count int) (*fetchRequest, bool, error) { } // Merge all the skipped headers back for _, header := range skip { - q.headerQueue.Push(header, -float32(header.Number.Uint64())) + q.headerQueue.Push(header, -float32(header.Number().Uint64())) } // Assemble and return the block download request if len(send) == 0 { @@ -382,7 +382,7 @@ func (q *queue) Cancel(request *fetchRequest) { q.hashQueue.Push(hash, float32(index)) } for _, header := range request.Headers { - q.headerQueue.Push(header, -float32(header.Number.Uint64())) + q.headerQueue.Push(header, -float32(header.Number().Uint64())) } delete(q.pendPool, request.Peer.id) } @@ -408,7 +408,7 @@ func (q *queue) Expire(timeout time.Duration) []string { q.hashQueue.Push(hash, float32(index)) } for _, header := range request.Headers { - q.headerQueue.Push(header, -float32(header.Number.Uint64())) + q.headerQueue.Push(header, -float32(header.Number().Uint64())) } peers = append(peers, id) } @@ -496,7 +496,7 @@ func (q *queue) Deliver(id string, txLists [][]*types.Transaction, uncleLists [] break } // Reconstruct the next block if contents match up - if types.DeriveSha(types.Transactions(txLists[i])) != header.TxHash || types.CalcUncleHash(uncleLists[i]) != header.UncleHash { + if types.DeriveSha(types.Transactions(txLists[i])) != header.TxHash() || types.CalcUncleHash(uncleLists[i]) != header.UncleHash() { errs = []error{errInvalidBody} break } @@ -513,7 +513,7 @@ func (q *queue) Deliver(id string, txLists [][]*types.Transaction, uncleLists [] // Return all failed or missing fetches to the queue for _, header := range request.Headers { if header != nil { - q.headerQueue.Push(header, -float32(header.Number.Uint64())) + q.headerQueue.Push(header, -float32(header.Number().Uint64())) } } // If none of the blocks were good, it's a stale delivery diff --git a/eth/fetcher/fetcher.go b/eth/fetcher/fetcher.go index b8ec1fc554..9d1c8a038e 100644 --- a/eth/fetcher/fetcher.go +++ b/eth/fetcher/fetcher.go @@ -537,8 +537,8 @@ func (f *Fetcher) loop() { // Filter fetcher-requested headers from other synchronisation algorithms if announce := f.fetching[hash]; announce != nil && f.fetched[hash] == nil && f.completing[hash] == nil && f.queued[hash] == nil { // If the delivered header does not match the promised number, drop the announcer - if header.Number.Uint64() != announce.number { - glog.V(logger.Detail).Infof("[eth/62] Peer %s: invalid block number for [%x…]: announced %d, provided %d", announce.origin, header.Hash().Bytes()[:4], announce.number, header.Number.Uint64()) + if header.Number().Uint64() != announce.number { + glog.V(logger.Detail).Infof("[eth/62] Peer %s: invalid block number for [%x…]: announced %d, provided %d", announce.origin, header.Hash().Bytes()[:4], announce.number, header.Number().Uint64()) f.dropPeer(announce.origin) f.forgetHash(hash) continue @@ -549,8 +549,8 @@ func (f *Fetcher) loop() { announce.time = task.time // If the block is empty (header only), short circuit into the final import queue - if header.TxHash == types.DeriveSha(types.Transactions{}) && header.UncleHash == types.CalcUncleHash([]*types.Header{}) { - glog.V(logger.Detail).Infof("[eth/62] Peer %s: block #%d [%x…] empty, skipping body retrieval", announce.origin, header.Number.Uint64(), header.Hash().Bytes()[:4]) + if header.TxHash() == types.DeriveSha(types.Transactions{}) && header.UncleHash() == types.CalcUncleHash([]*types.Header{}) { + glog.V(logger.Detail).Infof("[eth/62] Peer %s: block #%d [%x…] empty, skipping body retrieval", announce.origin, header.Number().Uint64(), header.Hash().Bytes()[:4]) block := types.NewBlockWithHeader(header) block.ReceivedAt = task.time @@ -562,7 +562,7 @@ func (f *Fetcher) loop() { // Otherwise add to the list of blocks needing completion incomplete = append(incomplete, announce) } else { - glog.V(logger.Detail).Infof("[eth/62] Peer %s: block #%d [%x…] already imported, discarding header", announce.origin, header.Number.Uint64(), header.Hash().Bytes()[:4]) + glog.V(logger.Detail).Infof("[eth/62] Peer %s: block #%d [%x…] already imported, discarding header", announce.origin, header.Number().Uint64(), header.Hash().Bytes()[:4]) f.forgetHash(hash) } } else { @@ -614,7 +614,7 @@ func (f *Fetcher) loop() { txnHash := types.DeriveSha(types.Transactions(task.transactions[i])) uncleHash := types.CalcUncleHash(task.uncles[i]) - if txnHash == announce.header.TxHash && uncleHash == announce.header.UncleHash { + if txnHash == announce.header.TxHash() && uncleHash == announce.header.UncleHash() { // Mark the body matched, reassemble if still unknown matched = true diff --git a/eth/fetcher/fetcher_test.go b/eth/fetcher/fetcher_test.go index 707d8d7583..fafd171750 100644 --- a/eth/fetcher/fetcher_test.go +++ b/eth/fetcher/fetcher_test.go @@ -37,7 +37,7 @@ var ( testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") testAddress = crypto.PubkeyToAddress(testKey.PublicKey) genesis = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000)) - unknownBlock = types.NewBlock(&types.Header{GasLimit: params.GenesisGasLimit}, nil, nil, nil) + unknownBlock = types.NewBlock(&types.RawHeader{GasLimit: params.GenesisGasLimit}, nil, nil, nil) ) // makeChain creates a chain of n blocks starting at and including parent. @@ -58,7 +58,11 @@ func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common } // If the block number is a multiple of 5, add a bonus uncle to the block if i%5 == 0 { - block.AddUncle(&types.Header{ParentHash: block.PrevBlock(i - 1).Hash(), Number: big.NewInt(int64(i - 1))}) + uncle := &types.RawHeader{ + ParentHash: block.PrevBlock(i - 1).Hash(), + Number: big.NewInt(int64(i - 1)), + } + block.AddUncle(types.NewHeader(uncle)) } }) hashes := make([]common.Hash, n+1) diff --git a/eth/handler.go b/eth/handler.go index 95f4e8ce21..8e862d9245 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -371,7 +371,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { // Hash based traversal towards the genesis block for i := 0; i < int(query.Skip)+1; i++ { if header := pm.chainman.GetHeader(query.Origin.Hash); header != nil { - query.Origin.Hash = header.ParentHash + query.Origin.Hash = header.ParentHash() } else { unknown = true break @@ -379,7 +379,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } case query.Origin.Hash != (common.Hash{}) && !query.Reverse: // Hash based traversal towards the leaf block - if header := pm.chainman.GetHeaderByNumber(origin.Number.Uint64() + query.Skip + 1); header != nil { + if header := pm.chainman.GetHeaderByNumber(origin.Number().Uint64() + query.Skip + 1); header != nil { if pm.chainman.GetBlockHashesFromHash(header.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash { query.Origin.Hash = header.Hash() } else { diff --git a/eth/handler_test.go b/eth/handler_test.go index 6400d4e789..64dc239523 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -401,12 +401,12 @@ func testGetNodeData(t *testing.T, protocol int) { block.SetExtra([]byte("yeehaw")) case 3: // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). - b2 := block.PrevBlock(1).Header() + b2 := block.PrevBlock(1).Header().Raw() b2.Extra = []byte("foo") - block.AddUncle(b2) - b3 := block.PrevBlock(2).Header() + block.AddUncle(types.NewHeader(b2)) + b3 := block.PrevBlock(2).Header().Raw() b3.Extra = []byte("foo") - block.AddUncle(b3) + block.AddUncle(types.NewHeader(b3)) } } // Assemble the test environment @@ -490,12 +490,12 @@ func testGetReceipt(t *testing.T, protocol int) { block.SetExtra([]byte("yeehaw")) case 3: // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). - b2 := block.PrevBlock(1).Header() + b2 := block.PrevBlock(1).Header().Raw() b2.Extra = []byte("foo") - block.AddUncle(b2) - b3 := block.PrevBlock(2).Header() + block.AddUncle(types.NewHeader(b2)) + b3 := block.PrevBlock(2).Header().Raw() b3.Extra = []byte("foo") - block.AddUncle(b3) + block.AddUncle(types.NewHeader(b3)) } } // Assemble the test environment diff --git a/miner/worker.go b/miner/worker.go index 16a16931d7..c593be698c 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -75,7 +75,7 @@ type Work struct { Block *types.Block // the new block - header *types.Header + header *types.RawHeader txs []*types.Transaction receipts []*types.Receipt @@ -346,7 +346,7 @@ func (self *worker) push(work *Work) { } // makeCurrent creates a new environment for the current cycle. -func (self *worker) makeCurrent(parent *types.Block, header *types.Header) { +func (self *worker) makeCurrent(parent *types.Block, header *types.RawHeader) { state := state.New(parent.Root(), self.eth.ChainDb()) work := &Work{ state: state, @@ -443,9 +443,9 @@ func (self *worker) commitNewWork() { glog.V(logger.Info).Infoln("We are too far in the future. Waiting for", wait) time.Sleep(wait) } - num := parent.Number() - header := &types.Header{ + + header := &types.RawHeader{ ParentHash: parent.Hash(), Number: num.Add(num, common.Big1), Difficulty: core.CalcDifficulty(uint64(tstamp), parent.Time().Uint64(), parent.Number(), parent.Difficulty()), @@ -455,7 +455,6 @@ func (self *worker) commitNewWork() { Extra: self.extra, Time: big.NewInt(tstamp), } - previous := self.current self.makeCurrent(parent, header) work := self.current @@ -526,7 +525,7 @@ func (self *worker) commitNewWork() { if atomic.LoadInt32(&self.mining) == 1 { // commit state root after all state transitions. - core.AccumulateRewards(work.state, header, uncles) + core.AccumulateRewards(work.state, types.NewHeader(header), uncles) work.state.SyncObjects() header.Root = work.state.Root() } @@ -549,8 +548,8 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error { if work.uncles.Has(hash) { return core.UncleError("Uncle not unique") } - if !work.ancestors.Has(uncle.ParentHash) { - return core.UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4])) + if parent := uncle.ParentHash(); !work.ancestors.Has(parent) { + return core.UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", parent[:4])) } if work.family.Has(hash) { return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", hash)) @@ -619,7 +618,7 @@ func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *b func (env *Work) commitTransaction(tx *types.Transaction, proc *core.BlockProcessor) error { snap := env.state.Copy() - receipt, _, err := proc.ApplyTransaction(env.coinbase, env.state, env.header, tx, env.header.GasUsed, true) + receipt, _, err := proc.ApplyTransaction(env.coinbase, env.state, types.NewHeader(env.header), tx, env.header.GasUsed, true) if err != nil { env.state.Set(snap) return err diff --git a/rpc/api/parsing.go b/rpc/api/parsing.go index 5858bc1361..f700862bbc 100644 --- a/rpc/api/parsing.go +++ b/rpc/api/parsing.go @@ -383,21 +383,21 @@ func NewUncleRes(h *types.Header) *UncleRes { } var v = new(UncleRes) - v.BlockNumber = newHexNum(h.Number) + v.BlockNumber = newHexNum(h.Number()) v.BlockHash = newHexData(h.Hash()) - v.ParentHash = newHexData(h.ParentHash) - v.Sha3Uncles = newHexData(h.UncleHash) - v.Nonce = newHexData(h.Nonce[:]) - v.LogsBloom = newHexData(h.Bloom) - v.TransactionRoot = newHexData(h.TxHash) - v.StateRoot = newHexData(h.Root) - v.Miner = newHexData(h.Coinbase) - v.Difficulty = newHexNum(h.Difficulty) - v.ExtraData = newHexData(h.Extra) - v.GasLimit = newHexNum(h.GasLimit) - v.GasUsed = newHexNum(h.GasUsed) - v.UnixTimestamp = newHexNum(h.Time) - v.ReceiptHash = newHexData(h.ReceiptHash) + v.ParentHash = newHexData(h.ParentHash()) + v.Sha3Uncles = newHexData(h.UncleHash()) + v.Nonce = newHexData(h.Nonce()) + v.LogsBloom = newHexData(h.Bloom()) + v.TransactionRoot = newHexData(h.TxHash()) + v.StateRoot = newHexData(h.Root()) + v.Miner = newHexData(h.Coinbase()) + v.Difficulty = newHexNum(h.Difficulty()) + v.ExtraData = newHexData(h.Extra()) + v.GasLimit = newHexNum(h.GasLimit()) + v.GasUsed = newHexNum(h.GasUsed()) + v.UnixTimestamp = newHexNum(h.Time()) + v.ReceiptHash = newHexData(h.ReceiptHash()) return v } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 2090afce71..764f550f1b 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -297,77 +297,77 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error { func (s *BlockTest) validateBlockHeader(h *btHeader, h2 *types.Header) error { expectedBloom := mustConvertBytes(h.Bloom) - if !bytes.Equal(expectedBloom, h2.Bloom.Bytes()) { - return fmt.Errorf("Bloom: expected: %v, decoded: %v", expectedBloom, h2.Bloom.Bytes()) + if !bytes.Equal(expectedBloom, h2.Bloom().Bytes()) { + return fmt.Errorf("Bloom: expected: %v, decoded: %v", expectedBloom, h2.Bloom().Bytes()) } expectedCoinbase := mustConvertBytes(h.Coinbase) - if !bytes.Equal(expectedCoinbase, h2.Coinbase.Bytes()) { - return fmt.Errorf("Coinbase: expected: %v, decoded: %v", expectedCoinbase, h2.Coinbase.Bytes()) + if !bytes.Equal(expectedCoinbase, h2.Coinbase().Bytes()) { + return fmt.Errorf("Coinbase: expected: %v, decoded: %v", expectedCoinbase, h2.Coinbase().Bytes()) } expectedMixHashBytes := mustConvertBytes(h.MixHash) - if !bytes.Equal(expectedMixHashBytes, h2.MixDigest.Bytes()) { - return fmt.Errorf("MixHash: expected: %v, decoded: %v", expectedMixHashBytes, h2.MixDigest.Bytes()) + if !bytes.Equal(expectedMixHashBytes, h2.MixDigest().Bytes()) { + return fmt.Errorf("MixHash: expected: %v, decoded: %v", expectedMixHashBytes, h2.MixDigest().Bytes()) } expectedNonce := mustConvertBytes(h.Nonce) - if !bytes.Equal(expectedNonce, h2.Nonce[:]) { - return fmt.Errorf("Nonce: expected: %v, decoded: %v", expectedNonce, h2.Nonce) + if nonce := h2.Nonce(); !bytes.Equal(expectedNonce, nonce[:]) { + return fmt.Errorf("Nonce: expected: %v, decoded: %v", expectedNonce, nonce) } expectedNumber := mustConvertBigInt(h.Number, 16) - if expectedNumber.Cmp(h2.Number) != 0 { - return fmt.Errorf("Number: expected: %v, decoded: %v", expectedNumber, h2.Number) + if expectedNumber.Cmp(h2.Number()) != 0 { + return fmt.Errorf("Number: expected: %v, decoded: %v", expectedNumber, h2.Number()) } expectedParentHash := mustConvertBytes(h.ParentHash) - if !bytes.Equal(expectedParentHash, h2.ParentHash.Bytes()) { - return fmt.Errorf("Parent hash: expected: %v, decoded: %v", expectedParentHash, h2.ParentHash.Bytes()) + if !bytes.Equal(expectedParentHash, h2.ParentHash().Bytes()) { + return fmt.Errorf("Parent hash: expected: %v, decoded: %v", expectedParentHash, h2.ParentHash().Bytes()) } expectedReceiptHash := mustConvertBytes(h.ReceiptTrie) - if !bytes.Equal(expectedReceiptHash, h2.ReceiptHash.Bytes()) { - return fmt.Errorf("Receipt hash: expected: %v, decoded: %v", expectedReceiptHash, h2.ReceiptHash.Bytes()) + if !bytes.Equal(expectedReceiptHash, h2.ReceiptHash().Bytes()) { + return fmt.Errorf("Receipt hash: expected: %v, decoded: %v", expectedReceiptHash, h2.ReceiptHash().Bytes()) } expectedTxHash := mustConvertBytes(h.TransactionsTrie) - if !bytes.Equal(expectedTxHash, h2.TxHash.Bytes()) { - return fmt.Errorf("Tx hash: expected: %v, decoded: %v", expectedTxHash, h2.TxHash.Bytes()) + if !bytes.Equal(expectedTxHash, h2.TxHash().Bytes()) { + return fmt.Errorf("Tx hash: expected: %v, decoded: %v", expectedTxHash, h2.TxHash().Bytes()) } expectedStateHash := mustConvertBytes(h.StateRoot) - if !bytes.Equal(expectedStateHash, h2.Root.Bytes()) { - return fmt.Errorf("State hash: expected: %v, decoded: %v", expectedStateHash, h2.Root.Bytes()) + if !bytes.Equal(expectedStateHash, h2.Root().Bytes()) { + return fmt.Errorf("State hash: expected: %v, decoded: %v", expectedStateHash, h2.Root().Bytes()) } expectedUncleHash := mustConvertBytes(h.UncleHash) - if !bytes.Equal(expectedUncleHash, h2.UncleHash.Bytes()) { - return fmt.Errorf("Uncle hash: expected: %v, decoded: %v", expectedUncleHash, h2.UncleHash.Bytes()) + if !bytes.Equal(expectedUncleHash, h2.UncleHash().Bytes()) { + return fmt.Errorf("Uncle hash: expected: %v, decoded: %v", expectedUncleHash, h2.UncleHash().Bytes()) } expectedExtraData := mustConvertBytes(h.ExtraData) - if !bytes.Equal(expectedExtraData, h2.Extra) { - return fmt.Errorf("Extra data: expected: %v, decoded: %v", expectedExtraData, h2.Extra) + if !bytes.Equal(expectedExtraData, h2.Extra()) { + return fmt.Errorf("Extra data: expected: %v, decoded: %v", expectedExtraData, h2.Extra()) } expectedDifficulty := mustConvertBigInt(h.Difficulty, 16) - if expectedDifficulty.Cmp(h2.Difficulty) != 0 { - return fmt.Errorf("Difficulty: expected: %v, decoded: %v", expectedDifficulty, h2.Difficulty) + if expectedDifficulty.Cmp(h2.Difficulty()) != 0 { + return fmt.Errorf("Difficulty: expected: %v, decoded: %v", expectedDifficulty, h2.Difficulty()) } expectedGasLimit := mustConvertBigInt(h.GasLimit, 16) - if expectedGasLimit.Cmp(h2.GasLimit) != 0 { - return fmt.Errorf("GasLimit: expected: %v, decoded: %v", expectedGasLimit, h2.GasLimit) + if expectedGasLimit.Cmp(h2.GasLimit()) != 0 { + return fmt.Errorf("GasLimit: expected: %v, decoded: %v", expectedGasLimit, h2.GasLimit()) } expectedGasUsed := mustConvertBigInt(h.GasUsed, 16) - if expectedGasUsed.Cmp(h2.GasUsed) != 0 { - return fmt.Errorf("GasUsed: expected: %v, decoded: %v", expectedGasUsed, h2.GasUsed) + if expectedGasUsed.Cmp(h2.GasUsed()) != 0 { + return fmt.Errorf("GasUsed: expected: %v, decoded: %v", expectedGasUsed, h2.GasUsed()) } expectedTimestamp := mustConvertBigInt(h.Timestamp, 16) - if expectedTimestamp.Cmp(h2.Time) != 0 { - return fmt.Errorf("Timestamp: expected: %v, decoded: %v", expectedTimestamp, h2.Time) + if expectedTimestamp.Cmp(h2.Time()) != 0 { + return fmt.Errorf("Timestamp: expected: %v, decoded: %v", expectedTimestamp, h2.Time()) } return nil @@ -440,14 +440,14 @@ func convertBlockTest(in *btJSON) (out *BlockTest, err error) { func mustConvertGenesis(testGenesis btHeader) *types.Block { hdr := mustConvertHeader(testGenesis) hdr.Number = big.NewInt(0) - b := types.NewBlockWithHeader(hdr) + b := types.NewBlockWithRawHeader(hdr) b.Td = new(big.Int) return b } -func mustConvertHeader(in btHeader) *types.Header { +func mustConvertHeader(in btHeader) *types.RawHeader { // hex decode these fields - header := &types.Header{ + return &types.RawHeader{ //SeedHash: mustConvertBytes(in.SeedHash), MixDigest: mustConvertHash(in.MixHash), Bloom: mustConvertBloom(in.Bloom), @@ -464,7 +464,6 @@ func mustConvertHeader(in btHeader) *types.Header { Time: mustConvertBigInt(in.Timestamp, 16), Nonce: types.EncodeNonce(mustConvertUint(in.Nonce, 16)), } - return header } func mustConvertBlock(testBlock btBlock) (*types.Block, error) {