diff options
Diffstat (limited to 'core/chain_manager.go')
-rw-r--r-- | core/chain_manager.go | 150 |
1 files changed, 85 insertions, 65 deletions
diff --git a/core/chain_manager.go b/core/chain_manager.go index 745b270f7..407945f8e 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/pow" + "github.com/ethereum/go-ethereum/rlp" "github.com/hashicorp/golang-lru" ) @@ -48,8 +49,9 @@ var ( ) const ( - headerCacheLimit = 256 + headerCacheLimit = 512 bodyCacheLimit = 256 + tdCacheLimit = 1024 blockCacheLimit = 256 maxFutureBlocks = 256 maxTimeFutureBlocks = 30 @@ -70,12 +72,12 @@ type ChainManager struct { checkpoint int // checkpoint counts towards the new checkpoint td *big.Int currentBlock *types.Block - lastBlockHash common.Hash currentGasLimit *big.Int 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 + tdCache *lru.Cache // Cache for the most recent block total difficulties blockCache *lru.Cache // Cache for the most recent entire blocks futureBlocks *lru.Cache // future blocks are blocks added for later processing @@ -92,6 +94,7 @@ func NewChainManager(chainDb common.Database, pow pow.PoW, mux *event.TypeMux) ( headerCache, _ := lru.New(headerCacheLimit) bodyCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit) + tdCache, _ := lru.New(tdCacheLimit) blockCache, _ := lru.New(blockCacheLimit) futureBlocks, _ := lru.New(maxFutureBlocks) @@ -102,6 +105,7 @@ func NewChainManager(chainDb common.Database, pow pow.PoW, mux *event.TypeMux) ( headerCache: headerCache, bodyCache: bodyCache, bodyRLPCache: bodyRLPCache, + tdCache: tdCache, blockCache: blockCache, futureBlocks: futureBlocks, pow: pow, @@ -154,7 +158,7 @@ func (bc *ChainManager) SetHead(head *types.Block) { bc.futureBlocks.Purge() bc.currentBlock = head - bc.setTotalDifficulty(head.Td) + bc.setTotalDifficulty(bc.GetTd(head.Hash())) bc.insert(head) bc.setLastState() } @@ -177,7 +181,7 @@ func (self *ChainManager) LastBlockHash() common.Hash { self.mu.RLock() defer self.mu.RUnlock() - return self.lastBlockHash + return self.currentBlock.Hash() } func (self *ChainManager) CurrentBlock() *types.Block { @@ -207,11 +211,13 @@ 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 { - glog.Fatalf("failed to write database head: %v", err) + if err := WriteCanonicalHash(bc.chainDb, block.Hash(), block.NumberU64()); err != nil { + glog.Fatalf("failed to write database head number: %v", err) + } + if err := WriteHeadBlockHash(bc.chainDb, block.Hash()); err != nil { + glog.Fatalf("failed to write database head hash: %v", err) } bc.currentBlock = block - bc.lastBlockHash = block.Hash() return true } } @@ -219,12 +225,11 @@ 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 { bc.currentBlock = block - bc.lastBlockHash = block.Hash() } else { glog.Infof("LastBlock (%x) not found. Recovering...\n", head) if bc.recover() { @@ -236,7 +241,7 @@ func (bc *ChainManager) setLastState() error { } else { bc.Reset() } - bc.td = bc.currentBlock.Td + bc.td = bc.GetTd(bc.currentBlock.Hash()) bc.currentGasLimit = CalcGasLimit(bc.currentBlock) if glog.V(logger.Info) { @@ -268,10 +273,10 @@ func (bc *ChainManager) ResetWithGenesisBlock(genesis *types.Block) { bc.futureBlocks.Purge() // Prepare the genesis block and reinitialize the chain - bc.genesisBlock = genesis - bc.genesisBlock.Td = genesis.Difficulty() - - if err := WriteBlock(bc.chainDb, bc.genesisBlock); err != nil { + if err := WriteTd(bc.chainDb, genesis.Hash(), genesis.Difficulty()); err != nil { + glog.Fatalf("failed to write genesis block TD: %v", err) + } + if err := WriteBlock(bc.chainDb, genesis); err != nil { glog.Fatalf("failed to write genesis block: %v", err) } bc.insert(bc.genesisBlock) @@ -315,23 +320,23 @@ 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) - if err != nil { - glog.Fatal("db write fail:", err) + // Add the block to the canonical chain number scheme and mark as the head + if err := WriteCanonicalHash(bc.chainDb, block.Hash(), block.NumberU64()); err != nil { + glog.Fatalf("failed to insert block number: %v", err) } - + if err := WriteHeadBlockHash(bc.chainDb, block.Hash()); err != nil { + glog.Fatalf("failed to insert block number: %v", err) + } + // Add a new restore point if we reached some limit bc.checkpoint++ if bc.checkpoint > checkpointLimit { - err = bc.chainDb.Put([]byte("checkpoint"), block.Hash().Bytes()) - if err != nil { - glog.Fatal("db write fail:", err) + if err := bc.chainDb.Put([]byte("checkpoint"), block.Hash().Bytes()); err != nil { + glog.Fatalf("failed to create checkpoint: %v", err) } - bc.checkpoint = 0 } - + // Update the internal internal state with the head block bc.currentBlock = block - bc.lastBlockHash = block.Hash() } // Accessors @@ -352,7 +357,7 @@ func (self *ChainManager) GetHeader(hash common.Hash) *types.Header { if header, ok := self.headerCache.Get(hash); ok { return header.(*types.Header) } - header := GetHeaderByHash(self.chainDb, hash) + header := GetHeader(self.chainDb, hash) if header == nil { return nil } @@ -364,44 +369,39 @@ func (self *ChainManager) GetHeader(hash common.Hash) *types.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) + hash := GetCanonicalHash(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) { +// GetBody retrieves a block body (transactions and uncles) from the database by +// hash, caching it if found. +func (self *ChainManager) GetBody(hash common.Hash) *types.Body { // 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 + body := cached.(*types.Body) + return body } - transactions, uncles, td := GetBodyByHash(self.chainDb, hash) - if td == nil { - return nil, nil + body := GetBody(self.chainDb, hash) + if body == nil { + return nil } // Cache the found body for next time and return - self.bodyCache.Add(hash, &storageBody{ - Transactions: transactions, - Uncles: uncles, - }) - return &transactions, &uncles + self.bodyCache.Add(hash, body) + return body } // 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 { +func (self *ChainManager) GetBodyRLP(hash common.Hash) rlp.RawValue { // Short circuit if the body's already in the cache, retrieve otherwise if cached, ok := self.bodyRLPCache.Get(hash); ok { - return cached.([]byte) + return cached.(rlp.RawValue) } - body, td := GetBodyRLPByHash(self.chainDb, hash) - if td == nil { + body := GetBodyRLP(self.chainDb, hash) + if len(body) == 0 { return nil } // Cache the found body for next time and return @@ -409,6 +409,22 @@ func (self *ChainManager) GetBodyRLP(hash common.Hash) []byte { return body } +// GetTd retrieves a block's total difficulty in the canonical chain from the +// database by hash, caching it if found. +func (self *ChainManager) GetTd(hash common.Hash) *big.Int { + // Short circuit if the td's already in the cache, retrieve otherwise + if cached, ok := self.tdCache.Get(hash); ok { + return cached.(*big.Int) + } + td := GetTd(self.chainDb, hash) + if td == nil { + return nil + } + // Cache the found body for next time and return + self.tdCache.Add(hash, td) + return td +} + // 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 { @@ -421,7 +437,7 @@ func (self *ChainManager) GetBlock(hash common.Hash) *types.Block { if block, ok := self.blockCache.Get(hash); ok { return block.(*types.Block) } - block := GetBlockByHash(self.chainDb, hash) + block := GetBlock(self.chainDb, hash) if block == nil { return nil } @@ -433,7 +449,7 @@ func (self *ChainManager) GetBlock(hash common.Hash) *types.Block { // 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) + hash := GetCanonicalHash(self.chainDb, number) if hash == (common.Hash{}) { return nil } @@ -455,7 +471,7 @@ func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) [ break } chain = append(chain, header.Hash()) - if header.Number.Cmp(common.Big0) <= 0 { + if header.Number.Cmp(common.Big0) == 0 { break } } @@ -531,15 +547,25 @@ const ( SideStatTy ) -// WriteBlock writes the block to the chain (or pending queue) -func (self *ChainManager) WriteBlock(block *types.Block, queued bool) (status writeStatus, err error) { +// WriteBlock writes the block to the chain. +func (self *ChainManager) WriteBlock(block *types.Block) (status writeStatus, err error) { self.wg.Add(1) defer self.wg.Done() + // Calculate the total difficulty of the block + ptd := self.GetTd(block.ParentHash()) + if ptd == nil { + return NonStatTy, ParentError(block.ParentHash()) + } + td := new(big.Int).Add(block.Difficulty(), ptd) + + self.mu.RLock() cblock := self.currentBlock + self.mu.RUnlock() + // Compare the TD of the last known block in the canonical chain to make sure it's greater. // At this point it's possible that a different chain (fork) becomes the new canonical chain. - if block.Td.Cmp(self.Td()) > 0 { + if td.Cmp(self.Td()) > 0 { // chain fork if block.ParentHash() != cblock.Hash() { // during split we merge two different chains and create the new canonical chain @@ -547,12 +573,10 @@ func (self *ChainManager) WriteBlock(block *types.Block, queued bool) (status wr if err != nil { return NonStatTy, err } - status = SplitStatTy } - self.mu.Lock() - self.setTotalDifficulty(block.Td) + self.setTotalDifficulty(td) self.insert(block) self.mu.Unlock() @@ -561,9 +585,11 @@ func (self *ChainManager) WriteBlock(block *types.Block, queued bool) (status wr status = SideStatTy } - err = WriteBlock(self.chainDb, block) - if err != nil { - glog.Fatalln("db err:", err) + if err := WriteTd(self.chainDb, block.Hash(), td); err != nil { + glog.Fatalf("failed to write block total difficulty: %v", err) + } + if err := WriteBlock(self.chainDb, block); err != nil { + glog.Fatalf("filed to write block contents: %v", err) } // Delete from future blocks self.futureBlocks.Remove(block.Hash()) @@ -622,11 +648,6 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { blockErr(block, err) return i, err } - - // Setting block.Td regardless of error (known for example) prevents errors down the line - // in the protocol handler - block.Td = new(big.Int).Set(CalcTD(block, self.GetBlock(block.ParentHash()))) - // Call in to the block processor and check for errors. It's likely that if one block fails // all others will fail too (unless a known block is returned). logs, receipts, err := self.processor.Process(block) @@ -666,7 +687,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { txcount += len(block.Transactions()) // write the block to the chain and get the status - status, err := self.WriteBlock(block, true) + status, err := self.WriteBlock(block) if err != nil { return i, err } @@ -799,12 +820,11 @@ out: case ChainEvent: // We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long // and in most cases isn't even necessary. - if self.lastBlockHash == event.Hash { + if self.currentBlock.Hash() == event.Hash { self.currentGasLimit = CalcGasLimit(event.Block) self.eventMux.Post(ChainHeadEvent{event.Block}) } } - self.eventMux.Post(event) } } |