|
|
|
@ -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.mu.Lock() |
|
|
|
|
defer bc.mu.Unlock() |
|
|
|
|
|
|
|
|
|
for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.ParentHash()) { |
|
|
|
|
bc.removeBlock(block) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bc.cache, _ = lru.New(blockCacheLimit) |
|
|
|
|
|
|
|
|
|
// Prepare the genesis block
|
|
|
|
|
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.setTotalDifficulty(common.Big("0")) |
|
|
|
|
bc.ResetWithGenesisBlock(bc.genesisBlock) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (bc *ChainManager) removeBlock(block *types.Block) { |
|
|
|
|
bc.chainDb.Delete(append(blockHashPre, block.Hash().Bytes()...)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (bc *ChainManager) ResetWithGenesisBlock(gb *types.Block) { |
|
|
|
|
// 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() |
|
|
|
|
|
|
|
|
|
// Prepare the genesis block
|
|
|
|
|
gb.Td = gb.Difficulty() |
|
|
|
|
bc.genesisBlock = gb |
|
|
|
|
// Prepare the genesis block and reinitialize the chain
|
|
|
|
|
bc.genesisBlock = genesis |
|
|
|
|
bc.genesisBlock.Td = genesis.Difficulty() |
|
|
|
|
|
|
|
|
|
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.td = gb.Difficulty() |
|
|
|
|
bc.setTotalDifficulty(genesis.Difficulty()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Export writes the active chain to the given writer.
|
|
|
|
@ -359,61 +339,130 @@ func (bc *ChainManager) Genesis() *types.Block { |
|
|
|
|
return bc.genesisBlock |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Block fetching methods
|
|
|
|
|
func (bc *ChainManager) HasBlock(hash common.Hash) bool { |
|
|
|
|
if bc.cache.Contains(hash) { |
|
|
|
|
return true |
|
|
|
|
// 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 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
data, _ := bc.chainDb.Get(append(blockHashPre, hash[:]...)) |
|
|
|
|
return len(data) != 0 |
|
|
|
|
// 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) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) (chain []common.Hash) { |
|
|
|
|
block := self.GetBlock(hash) |
|
|
|
|
if block == nil { |
|
|
|
|
return |
|
|
|
|
// 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 |
|
|
|
|
} |
|
|
|
|
// 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 |
|
|
|
|
} |
|
|
|
|
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 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
chain = append(chain, block.Hash()) |
|
|
|
|
if block.Number().Cmp(common.Big0) <= 0 { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
// 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 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return |
|
|
|
|
// 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 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 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++ { |
|
|
|
@ -427,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()...) |
|
|
|
|