diff options
Diffstat (limited to 'core/blockchain.go')
-rw-r--r-- | core/blockchain.go | 210 |
1 files changed, 170 insertions, 40 deletions
diff --git a/core/blockchain.go b/core/blockchain.go index 6b42ea97e..b68e7d3ae 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" @@ -67,9 +68,10 @@ type BlockChain struct { chainmu sync.RWMutex tsmu sync.RWMutex - checkpoint int // checkpoint counts towards the new checkpoint - currentHeader *types.Header // Current head of the header chain (may be above the block chain!) - currentBlock *types.Block // Current head of the block chain + checkpoint int // checkpoint counts towards the new checkpoint + currentHeader *types.Header // Current head of the header chain (may be above the block chain!) + currentBlock *types.Block // Current head of the block chain + currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!) headerCache *lru.Cache // Cache for the most recent block headers bodyCache *lru.Cache // Cache for the most recent block bodies @@ -160,12 +162,21 @@ func (self *BlockChain) loadLastState() error { self.currentHeader = header } } + // Restore the last known head fast block + self.currentFastBlock = self.currentBlock + if head := GetHeadFastBlockHash(self.chainDb); head != (common.Hash{}) { + if block := self.GetBlock(head); block != nil { + self.currentFastBlock = block + } + } // Issue a status log and return headerTd := self.GetTd(self.currentHeader.Hash()) blockTd := self.GetTd(self.currentBlock.Hash()) + fastTd := self.GetTd(self.currentFastBlock.Hash()) - glog.V(logger.Info).Infof("Last header: #%d [%x…] TD=%v", self.currentHeader.Number, self.currentHeader.Hash(), headerTd) - glog.V(logger.Info).Infof("Last block: #%d [%x…] TD=%v", self.currentBlock.Number(), self.currentBlock.Hash(), blockTd) + glog.V(logger.Info).Infof("Last header: #%d [%x…] TD=%v", self.currentHeader.Number, self.currentHeader.Hash().Bytes()[:4], headerTd) + glog.V(logger.Info).Infof("Fast block: #%d [%x…] TD=%v", self.currentFastBlock.Number(), self.currentFastBlock.Hash().Bytes()[:4], fastTd) + glog.V(logger.Info).Infof("Last block: #%d [%x…] TD=%v", self.currentBlock.Number(), self.currentBlock.Hash().Bytes()[:4], blockTd) return nil } @@ -178,23 +189,48 @@ func (bc *BlockChain) SetHead(head uint64) { bc.mu.Lock() defer bc.mu.Unlock() - // Delete everything from the current header head (is above block head) - for i := bc.currentHeader.Number.Uint64(); i > head; i-- { - if hash := GetCanonicalHash(bc.chainDb, i); hash != (common.Hash{}) { - DeleteCanonicalHash(bc.chainDb, i) - DeleteHeader(bc.chainDb, hash) - DeleteBody(bc.chainDb, hash) - DeleteTd(bc.chainDb, hash) + // Figure out the highest known canonical assignment + height := uint64(0) + if bc.currentHeader != nil { + if hh := bc.currentHeader.Number.Uint64(); hh > height { + height = hh } } - bc.currentHeader = GetHeader(bc.chainDb, GetCanonicalHash(bc.chainDb, head)) - - // Rewind the block chain until a whole block is found - for bc.GetBlockByNumber(head) == nil { - head-- + if bc.currentBlock != nil { + if bh := bc.currentBlock.NumberU64(); bh > height { + height = bh + } } - bc.currentBlock = bc.GetBlockByNumber(head) + if bc.currentFastBlock != nil { + if fbh := bc.currentFastBlock.NumberU64(); fbh > height { + height = fbh + } + } + // Gather all the hashes that need deletion + drop := make(map[common.Hash]struct{}) + for bc.currentHeader != nil && bc.currentHeader.Number.Uint64() > head { + drop[bc.currentHeader.Hash()] = struct{}{} + bc.currentHeader = bc.GetHeader(bc.currentHeader.ParentHash) + } + for bc.currentBlock != nil && bc.currentBlock.NumberU64() > head { + drop[bc.currentBlock.Hash()] = struct{}{} + bc.currentBlock = bc.GetBlock(bc.currentBlock.ParentHash()) + } + for bc.currentFastBlock != nil && bc.currentFastBlock.NumberU64() > head { + drop[bc.currentFastBlock.Hash()] = struct{}{} + bc.currentFastBlock = bc.GetBlock(bc.currentFastBlock.ParentHash()) + } + // Roll back the canonical chain numbering + for i := height; i > head; i-- { + DeleteCanonicalHash(bc.chainDb, i) + } + // Delete everything found by the above rewind + for hash, _ := range drop { + DeleteHeader(bc.chainDb, hash) + DeleteBody(bc.chainDb, hash) + DeleteTd(bc.chainDb, hash) + } // Clear out any stale content from the caches bc.headerCache.Purge() bc.bodyCache.Purge() @@ -203,6 +239,9 @@ func (bc *BlockChain) SetHead(head uint64) { bc.futureBlocks.Purge() // Update all computed fields to the new head + if bc.currentBlock == nil { + bc.currentBlock = bc.genesisBlock + } bc.insert(bc.currentBlock) bc.loadLastState() } @@ -222,8 +261,7 @@ func (self *BlockChain) LastBlockHash() common.Hash { } // CurrentHeader retrieves the current head header of the canonical chain. The -// header is retrieved from the chain manager's internal cache, involving no -// database operations. +// header is retrieved from the chain manager's internal cache. func (self *BlockChain) CurrentHeader() *types.Header { self.mu.RLock() defer self.mu.RUnlock() @@ -232,8 +270,7 @@ func (self *BlockChain) CurrentHeader() *types.Header { } // CurrentBlock retrieves the current head block of the canonical chain. The -// block is retrieved from the chain manager's internal cache, involving no -// database operations. +// block is retrieved from the chain manager's internal cache. func (self *BlockChain) CurrentBlock() *types.Block { self.mu.RLock() defer self.mu.RUnlock() @@ -241,6 +278,15 @@ func (self *BlockChain) CurrentBlock() *types.Block { return self.currentBlock } +// CurrentFastBlock retrieves the current fast-sync head block of the canonical +// chain. The block is retrieved from the chain manager's internal cache. +func (self *BlockChain) CurrentFastBlock() *types.Block { + self.mu.RLock() + defer self.mu.RUnlock() + + return self.currentFastBlock +} + func (self *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) { self.mu.RLock() defer self.mu.RUnlock() @@ -264,22 +310,12 @@ func (bc *BlockChain) Reset() { // ResetWithGenesisBlock purges the entire blockchain, restoring it to the // specified genesis state. func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) { + // Dump the entire block chain and purge the caches + bc.SetHead(0) + 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()) { - DeleteBlock(bc.chainDb, block.Hash()) - } - for header := bc.currentHeader; header != nil; header = bc.GetHeader(header.ParentHash) { - DeleteBlock(bc.chainDb, header.Hash()) - } - bc.headerCache.Purge() - bc.bodyCache.Purge() - bc.bodyRLPCache.Purge() - bc.blockCache.Purge() - bc.futureBlocks.Purge() - // Prepare the genesis block and reinitialize the chain if err := WriteTd(bc.chainDb, genesis.Hash(), genesis.Difficulty()); err != nil { glog.Fatalf("failed to write genesis block TD: %v", err) @@ -291,6 +327,7 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) { bc.insert(bc.genesisBlock) bc.currentBlock = bc.genesisBlock bc.currentHeader = bc.genesisBlock.Header() + bc.currentFastBlock = bc.genesisBlock } // Export writes the active chain to the given writer. @@ -328,8 +365,8 @@ func (self *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { // insert injects a new head block into the current block chain. This method // assumes that the block is indeed a true head. It will also reset the head -// header to this very same block to prevent the headers from diverging on a -// different header chain. +// header and the head fast sync block to this very same block to prevent them +// from diverging on a different header chain. // // Note, this function assumes that the `mu` mutex is held! func (bc *BlockChain) insert(block *types.Block) { @@ -343,9 +380,13 @@ func (bc *BlockChain) insert(block *types.Block) { if err := WriteHeadHeaderHash(bc.chainDb, block.Hash()); err != nil { glog.Fatalf("failed to insert head header hash: %v", err) } + if err := WriteHeadFastBlockHash(bc.chainDb, block.Hash()); err != nil { + glog.Fatalf("failed to insert head fast block hash: %v", err) + } // Update the internal state with the head block bc.currentBlock = block bc.currentHeader = block.Header() + bc.currentFastBlock = block } // Accessors @@ -634,7 +675,7 @@ func (self *BlockChain) InsertHeaderChain(chain []*types.Header, verify bool) (i for i, header := range chain { // Short circuit insertion if shutting down if atomic.LoadInt32(&self.procInterrupt) == 1 { - glog.V(logger.Debug).Infoln("Premature abort during header chain processing") + glog.V(logger.Debug).Infoln("premature abort during header chain processing") break } hash := header.Hash() @@ -653,7 +694,7 @@ func (self *BlockChain) InsertHeaderChain(chain []*types.Header, verify bool) (i } } if BadHashes[hash] { - glog.V(logger.Error).Infof("Bad header %d [%x…], known bad hash", header.Number, hash) + glog.V(logger.Error).Infof("bad header %d [%x…], known bad hash", header.Number, hash) return i, BadHashError(hash) } // Write the header to the chain and get the status @@ -674,6 +715,95 @@ func (self *BlockChain) InsertHeaderChain(chain []*types.Header, verify bool) (i return 0, nil } +// InsertReceiptChain attempts to complete an already existing header chain with +// transaction and receipt data. +func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) { + self.wg.Add(1) + defer self.wg.Done() + + // Make sure only one thread manipulates the chain at once + self.chainmu.Lock() + defer self.chainmu.Unlock() + + // Collect some import statistics to report on + stats := struct{ processed, ignored int }{} + start := time.Now() + + // Iterate over the blocks and receipts, inserting any new ones + for i := 0; i < len(blockChain) && i < len(receiptChain); i++ { + block, receipts := blockChain[i], receiptChain[i] + + // Short circuit insertion if shutting down + if atomic.LoadInt32(&self.procInterrupt) == 1 { + glog.V(logger.Debug).Infoln("premature abort during receipt chain processing") + break + } + // Short circuit if the owner header is unknown + if !self.HasHeader(block.Hash()) { + glog.V(logger.Debug).Infof("containing header #%d [%x…] unknown", block.Number(), block.Hash().Bytes()[:4]) + return i, fmt.Errorf("containing header #%d [%x…] unknown", block.Number(), block.Hash().Bytes()[:4]) + } + // Skip if the entire data is already known + if self.HasBlock(block.Hash()) { + stats.ignored++ + continue + } + // Compute all the non-consensus fields of the receipts + transactions, logIndex := block.Transactions(), uint(0) + for j := 0; j < len(receipts); j++ { + // The transaction hash can be retrieved from the transaction itself + receipts[j].TxHash = transactions[j].Hash() + + // The contract address can be derived from the transaction itself + if MessageCreatesContract(transactions[j]) { + from, _ := transactions[j].From() + receipts[j].ContractAddress = crypto.CreateAddress(from, transactions[j].Nonce()) + } + // The used gas can be calculated based on previous receipts + if j == 0 { + receipts[j].GasUsed = new(big.Int).Set(receipts[j].CumulativeGasUsed) + } else { + receipts[j].GasUsed = new(big.Int).Sub(receipts[j].CumulativeGasUsed, receipts[j-1].CumulativeGasUsed) + } + // The derived log fields can simply be set from the block and transaction + for k := 0; k < len(receipts[j].Logs); k++ { + receipts[j].Logs[k].BlockNumber = block.NumberU64() + receipts[j].Logs[k].BlockHash = block.Hash() + receipts[j].Logs[k].TxHash = receipts[j].TxHash + receipts[j].Logs[k].TxIndex = uint(j) + receipts[j].Logs[k].Index = logIndex + logIndex++ + } + } + // Write all the data out into the database + if err := WriteBody(self.chainDb, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil { + glog.Fatalf("failed to write block body: %v", err) + return i, err + } + if err := PutBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil { + glog.Fatalf("failed to write block receipts: %v", err) + return i, err + } + // Update the head fast sync block if better + self.mu.Lock() + if self.GetTd(self.currentFastBlock.Hash()).Cmp(self.GetTd(block.Hash())) < 0 { + if err := WriteHeadFastBlockHash(self.chainDb, block.Hash()); err != nil { + glog.Fatalf("failed to update head fast block hash: %v", err) + } + self.currentFastBlock = block + } + self.mu.Unlock() + + stats.processed++ + } + // Report some public statistics so the user has a clue what's going on + first, last := blockChain[0], blockChain[len(blockChain)-1] + glog.V(logger.Info).Infof("imported %d receipt(s) (%d ignored) in %v. #%d [%x… / %x…]", stats.processed, stats.ignored, + time.Since(start), last.Number(), first.Hash().Bytes()[:4], last.Hash().Bytes()[:4]) + + return 0, nil +} + // WriteBlock writes the block to the chain. func (self *BlockChain) WriteBlock(block *types.Block) (status writeStatus, err error) { self.wg.Add(1) @@ -799,7 +929,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { return i, err } - if err := PutBlockReceipts(self.chainDb, block, receipts); err != nil { + if err := PutBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil { glog.V(logger.Warn).Infoln("error writing block receipts:", err) } |