aboutsummaryrefslogtreecommitdiffstats
path: root/core/blockchain.go
diff options
context:
space:
mode:
Diffstat (limited to 'core/blockchain.go')
-rw-r--r--core/blockchain.go105
1 files changed, 81 insertions, 24 deletions
diff --git a/core/blockchain.go b/core/blockchain.go
index 4ac2c3a44..651c67c5d 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -93,7 +93,10 @@ const (
// - Version 6
// The following incompatible database changes were added:
// * Transaction lookup information stores the corresponding block number instead of block hash
- BlockChainVersion uint64 = 6
+ // - Version 7
+ // The following incompatible database changes were added:
+ // * Use freezer as the ancient database to maintain all ancient data
+ BlockChainVersion uint64 = 7
)
// CacheConfig contains the configuration values for the trie caching/pruning
@@ -215,10 +218,35 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
if bc.genesisBlock == nil {
return nil, ErrNoGenesis
}
+ // Initialize the chain with ancient data if it isn't empty.
+ if bc.empty() {
+ if frozen, err := bc.db.Ancients(); err == nil && frozen > 0 {
+ for i := uint64(0); i < frozen; i++ {
+ // Inject hash<->number mapping.
+ hash := rawdb.ReadCanonicalHash(bc.db, i)
+ if hash == (common.Hash{}) {
+ return nil, errors.New("broken ancient database")
+ }
+ rawdb.WriteHeaderNumber(bc.db, hash, i)
+
+ // Inject txlookup indexes.
+ block := rawdb.ReadBlock(bc.db, hash, i)
+ if block == nil {
+ return nil, errors.New("broken ancient database")
+ }
+ rawdb.WriteTxLookupEntries(bc.db, block)
+ }
+ hash := rawdb.ReadCanonicalHash(bc.db, frozen-1)
+ rawdb.WriteHeadHeaderHash(bc.db, hash)
+ rawdb.WriteHeadFastBlockHash(bc.db, hash)
+
+ log.Info("Initialized chain with ancients", "number", frozen-1, "hash", hash)
+ }
+ }
if err := bc.loadLastState(); err != nil {
return nil, err
}
- if frozen, err := bc.db.Ancients(); err == nil && frozen >= 1 {
+ if frozen, err := bc.db.Ancients(); err == nil && frozen > 0 {
var (
needRewind bool
low uint64
@@ -278,6 +306,20 @@ func (bc *BlockChain) GetVMConfig() *vm.Config {
return &bc.vmConfig
}
+// empty returns an indicator whether the blockchain is empty.
+// Note, it's a special case that we connect a non-empty ancient
+// database with an empty node, so that we can plugin the ancient
+// into node seamlessly.
+func (bc *BlockChain) empty() bool {
+ genesis := bc.genesisBlock.Hash()
+ for _, hash := range []common.Hash{rawdb.ReadHeadBlockHash(bc.db), rawdb.ReadHeadHeaderHash(bc.db), rawdb.ReadHeadFastBlockHash(bc.db)} {
+ if hash != genesis {
+ return false
+ }
+ }
+ return true
+}
+
// loadLastState loads the last known chain state from the database. This method
// assumes that the chain manager mutex is held.
func (bc *BlockChain) loadLastState() error {
@@ -383,7 +425,9 @@ func (bc *BlockChain) SetHead(head uint64) error {
if num+1 <= frozen {
// Truncate all relative data(header, total difficulty, body, receipt
// and canonical hash) from ancient store.
- bc.db.TruncateAncients(num + 1)
+ if err := bc.db.TruncateAncients(num + 1); err != nil {
+ log.Crit("Failed to truncate ancient data", "number", num, "err", err)
+ }
// Remove the hash <-> number mapping from the active store.
rawdb.DeleteHeaderNumber(db, hash)
@@ -948,6 +992,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
}
}
}()
+ var deleted types.Blocks
for i, block := range blockChain {
// Short circuit insertion if shutting down or processing failed
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
@@ -961,16 +1006,38 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
if !bc.HasHeader(block.Hash(), block.NumberU64()) {
return i, fmt.Errorf("containing header #%d [%x…] unknown", block.Number(), block.Hash().Bytes()[:4])
}
- // Compute all the non-consensus fields of the receipts
- if err := receiptChain[i].DeriveFields(bc.chainConfig, block.Hash(), block.NumberU64(), block.Transactions()); err != nil {
- return i, fmt.Errorf("failed to derive receipts data: %v", err)
+ var (
+ start = time.Now()
+ logged = time.Now()
+ count int
+ )
+ // Migrate all ancient blocks. This can happen if someone upgrades from Geth
+ // 1.8.x to 1.9.x mid-fast-sync. Perhaps we can get rid of this path in the
+ // long term.
+ for {
+ // We can ignore the error here since light client won't hit this code path.
+ frozen, _ := bc.db.Ancients()
+ if frozen >= block.NumberU64() {
+ break
+ }
+ h := rawdb.ReadCanonicalHash(bc.db, frozen)
+ b := rawdb.ReadBlock(bc.db, h, frozen)
+ size += rawdb.WriteAncientBlock(bc.db, b, rawdb.ReadReceipts(bc.db, h, frozen, bc.chainConfig), rawdb.ReadTd(bc.db, h, frozen))
+ count += 1
+
+ // Always keep genesis block in active database.
+ if b.NumberU64() != 0 {
+ deleted = append(deleted, b)
+ }
+ if time.Since(logged) > 8*time.Second {
+ log.Info("Migrating ancient blocks", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
+ logged = time.Now()
+ }
}
- // Initialize freezer with genesis block first
- if frozen, err := bc.db.Ancients(); err == nil && frozen == 0 && block.NumberU64() == 1 {
- genesisBlock := rawdb.ReadBlock(bc.db, rawdb.ReadCanonicalHash(bc.db, 0), 0)
- size += rawdb.WriteAncientBlock(bc.db, genesisBlock, nil, genesisBlock.Difficulty())
+ if count > 0 {
+ log.Info("Migrated ancient blocks", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
}
- // Flush data into ancient store.
+ // Flush data into ancient database.
size += rawdb.WriteAncientBlock(bc.db, block, receiptChain[i], bc.GetTd(block.Hash(), block.NumberU64()))
rawdb.WriteTxLookupEntries(batch, block)
@@ -992,15 +1059,8 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
}
previous = nil // disable rollback explicitly
- // Remove the ancient data from the active store
- cleanGenesis := len(blockChain) > 0 && blockChain[0].NumberU64() == 1
- if cleanGenesis {
- // Migrate genesis block to ancient store too.
- rawdb.DeleteBlockWithoutNumber(batch, rawdb.ReadCanonicalHash(bc.db, 0), 0)
- rawdb.DeleteCanonicalHash(batch, 0)
- }
// Wipe out canonical block data.
- for _, block := range blockChain {
+ for _, block := range append(deleted, blockChain...) {
rawdb.DeleteBlockWithoutNumber(batch, block.Hash(), block.NumberU64())
rawdb.DeleteCanonicalHash(batch, block.NumberU64())
}
@@ -1008,8 +1068,9 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
return 0, err
}
batch.Reset()
+
// Wipe out side chain too.
- for _, block := range blockChain {
+ for _, block := range append(deleted, blockChain...) {
for _, hash := range rawdb.ReadAllHashes(bc.db, block.NumberU64()) {
rawdb.DeleteBlock(batch, hash, block.NumberU64())
}
@@ -1035,10 +1096,6 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
stats.ignored++
continue
}
- // Compute all the non-consensus fields of the receipts
- if err := receiptChain[i].DeriveFields(bc.chainConfig, block.Hash(), block.NumberU64(), block.Transactions()); err != nil {
- return i, fmt.Errorf("failed to derive receipts data: %v", err)
- }
// Write all the data out into the database
rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body())
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])