@@ -34,6 +34,7 @@ import (
3434 "github.com/ethereum/go-ethereum/consensus"
3535 "github.com/ethereum/go-ethereum/core/rawdb"
3636 "github.com/ethereum/go-ethereum/core/state"
37+ "github.com/ethereum/go-ethereum/core/state/snapshot"
3738 "github.com/ethereum/go-ethereum/core/types"
3839 "github.com/ethereum/go-ethereum/core/vm"
3940 "github.com/ethereum/go-ethereum/ethdb"
6162 storageUpdateTimer = metrics .NewRegisteredTimer ("chain/storage/updates" , nil )
6263 storageCommitTimer = metrics .NewRegisteredTimer ("chain/storage/commits" , nil )
6364
65+ snapshotAccountReadTimer = metrics .NewRegisteredTimer ("chain/snapshot/account/reads" , nil )
66+ snapshotStorageReadTimer = metrics .NewRegisteredTimer ("chain/snapshot/storage/reads" , nil )
67+ snapshotCommitTimer = metrics .NewRegisteredTimer ("chain/snapshot/commits" , nil )
68+
6469 blockInsertTimer = metrics .NewRegisteredTimer ("chain/inserts" , nil )
6570 blockValidationTimer = metrics .NewRegisteredTimer ("chain/validation" , nil )
6671 blockExecutionTimer = metrics .NewRegisteredTimer ("chain/execution" , nil )
@@ -115,6 +120,9 @@ type CacheConfig struct {
115120 TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk
116121 TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node)
117122 TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
123+ SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory
124+
125+ SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
118126}
119127
120128// BlockChain represents the canonical chain given a database with a genesis
@@ -136,6 +144,7 @@ type BlockChain struct {
136144 cacheConfig * CacheConfig // Cache configuration for pruning
137145
138146 db ethdb.Database // Low level persistent database to store final content in
147+ snaps * snapshot.Tree // Snapshot tree for fast trie leaf access
139148 triegc * prque.Prque // Priority queue mapping block numbers to tries to gc
140149 gcproc time.Duration // Accumulates canonical block processing for trie dumping
141150
@@ -188,6 +197,8 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
188197 TrieCleanLimit : 256 ,
189198 TrieDirtyLimit : 256 ,
190199 TrieTimeLimit : 5 * time .Minute ,
200+ SnapshotLimit : 256 ,
201+ SnapshotWait : true ,
191202 }
192203 }
193204 bodyCache , _ := lru .New (bodyCacheLimit )
@@ -293,6 +304,10 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
293304 }
294305 }
295306 }
307+ // Load any existing snapshot, regenerating it if loading failed
308+ if bc .cacheConfig .SnapshotLimit > 0 {
309+ bc .snaps = snapshot .New (bc .db , bc .stateCache .TrieDB (), bc .cacheConfig .SnapshotLimit , bc .CurrentBlock ().Root (), ! bc .cacheConfig .SnapshotWait )
310+ }
296311 // Take ownership of this particular state
297312 go bc .update ()
298313 return bc , nil
@@ -339,7 +354,7 @@ func (bc *BlockChain) loadLastState() error {
339354 return bc .Reset ()
340355 }
341356 // Make sure the state associated with the block is available
342- if _ , err := state .New (currentBlock .Root (), bc .stateCache ); err != nil {
357+ if _ , err := state .New (currentBlock .Root (), bc .stateCache , bc . snaps ); err != nil {
343358 // Dangling block without a state associated, init from scratch
344359 log .Warn ("Head state missing, repairing chain" , "number" , currentBlock .Number (), "hash" , currentBlock .Hash ())
345360 if err := bc .repair (& currentBlock ); err != nil {
@@ -401,7 +416,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
401416 if newHeadBlock == nil {
402417 newHeadBlock = bc .genesisBlock
403418 } else {
404- if _ , err := state .New (newHeadBlock .Root (), bc .stateCache ); err != nil {
419+ if _ , err := state .New (newHeadBlock .Root (), bc .stateCache , bc . snaps ); err != nil {
405420 // Rewound state missing, rolled back to before pivot, reset to genesis
406421 newHeadBlock = bc .genesisBlock
407422 }
@@ -486,6 +501,10 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
486501 headBlockGauge .Update (int64 (block .NumberU64 ()))
487502 bc .chainmu .Unlock ()
488503
504+ // Destroy any existing state snapshot and regenerate it in the background
505+ if bc .snaps != nil {
506+ bc .snaps .Rebuild (block .Root ())
507+ }
489508 log .Info ("Committed new head block" , "number" , block .Number (), "hash" , hash )
490509 return nil
491510}
@@ -524,7 +543,7 @@ func (bc *BlockChain) State() (*state.StateDB, error) {
524543
525544// StateAt returns a new mutable state based on a particular point in time.
526545func (bc * BlockChain ) StateAt (root common.Hash ) (* state.StateDB , error ) {
527- return state .New (root , bc .stateCache )
546+ return state .New (root , bc .stateCache , bc . snaps )
528547}
529548
530549// StateCache returns the caching database underpinning the blockchain instance.
@@ -576,7 +595,7 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
576595func (bc * BlockChain ) repair (head * * types.Block ) error {
577596 for {
578597 // Abort if we've rewound to a head block that does have associated state
579- if _ , err := state .New ((* head ).Root (), bc .stateCache ); err == nil {
598+ if _ , err := state .New ((* head ).Root (), bc .stateCache , bc . snaps ); err == nil {
580599 log .Info ("Rewound blockchain to past state" , "number" , (* head ).Number (), "hash" , (* head ).Hash ())
581600 return nil
582601 }
@@ -839,6 +858,14 @@ func (bc *BlockChain) Stop() {
839858
840859 bc .wg .Wait ()
841860
861+ // Ensure that the entirety of the state snapshot is journalled to disk.
862+ var snapBase common.Hash
863+ if bc .snaps != nil {
864+ var err error
865+ if snapBase , err = bc .snaps .Journal (bc .CurrentBlock ().Root ()); err != nil {
866+ log .Error ("Failed to journal state snapshot" , "err" , err )
867+ }
868+ }
842869 // Ensure the state of a recent block is also stored to disk before exiting.
843870 // We're writing three different states to catch different restart scenarios:
844871 // - HEAD: So we don't need to reprocess any blocks in the general case
@@ -857,6 +884,12 @@ func (bc *BlockChain) Stop() {
857884 }
858885 }
859886 }
887+ if snapBase != (common.Hash {}) {
888+ log .Info ("Writing snapshot state to disk" , "root" , snapBase )
889+ if err := triedb .Commit (snapBase , true ); err != nil {
890+ log .Error ("Failed to commit recent state trie" , "err" , err )
891+ }
892+ }
860893 for ! bc .triegc .Empty () {
861894 triedb .Dereference (bc .triegc .PopItem ().(common.Hash ))
862895 }
@@ -1647,7 +1680,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
16471680 if parent == nil {
16481681 parent = bc .GetHeader (block .ParentHash (), block .NumberU64 ()- 1 )
16491682 }
1650- statedb , err := state .New (parent .Root , bc .stateCache )
1683+ statedb , err := state .New (parent .Root , bc .stateCache , bc . snaps )
16511684 if err != nil {
16521685 return it .index , err
16531686 }
@@ -1656,9 +1689,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
16561689 var followupInterrupt uint32
16571690 if ! bc .cacheConfig .TrieCleanNoPrefetch {
16581691 if followup , err := it .peek (); followup != nil && err == nil {
1659- throwaway , _ := state .New (parent .Root , bc .stateCache )
1692+ throwaway , _ := state .New (parent .Root , bc .stateCache , bc . snaps )
16601693 go func (start time.Time , followup * types.Block , throwaway * state.StateDB , interrupt * uint32 ) {
1661- bc .prefetcher .Prefetch (followup , throwaway , bc .vmConfig , interrupt )
1694+ bc .prefetcher .Prefetch (followup , throwaway , bc .vmConfig , & followupInterrupt )
16621695
16631696 blockPrefetchExecuteTimer .Update (time .Since (start ))
16641697 if atomic .LoadUint32 (interrupt ) == 1 {
@@ -1676,14 +1709,16 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
16761709 return it .index , err
16771710 }
16781711 // Update the metrics touched during block processing
1679- accountReadTimer .Update (statedb .AccountReads ) // Account reads are complete, we can mark them
1680- storageReadTimer .Update (statedb .StorageReads ) // Storage reads are complete, we can mark them
1681- accountUpdateTimer .Update (statedb .AccountUpdates ) // Account updates are complete, we can mark them
1682- storageUpdateTimer .Update (statedb .StorageUpdates ) // Storage updates are complete, we can mark them
1712+ accountReadTimer .Update (statedb .AccountReads ) // Account reads are complete, we can mark them
1713+ storageReadTimer .Update (statedb .StorageReads ) // Storage reads are complete, we can mark them
1714+ accountUpdateTimer .Update (statedb .AccountUpdates ) // Account updates are complete, we can mark them
1715+ storageUpdateTimer .Update (statedb .StorageUpdates ) // Storage updates are complete, we can mark them
1716+ snapshotAccountReadTimer .Update (statedb .SnapshotAccountReads ) // Account reads are complete, we can mark them
1717+ snapshotStorageReadTimer .Update (statedb .SnapshotStorageReads ) // Storage reads are complete, we can mark them
16831718
16841719 triehash := statedb .AccountHashes + statedb .StorageHashes // Save to not double count in validation
1685- trieproc := statedb .AccountReads + statedb .AccountUpdates
1686- trieproc += statedb .StorageReads + statedb .StorageUpdates
1720+ trieproc := statedb .SnapshotAccountReads + statedb . AccountReads + statedb .AccountUpdates
1721+ trieproc += statedb .SnapshotStorageReads + statedb . StorageReads + statedb .StorageUpdates
16871722
16881723 blockExecutionTimer .Update (time .Since (substart ) - trieproc - triehash )
16891724
@@ -1712,10 +1747,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
17121747 atomic .StoreUint32 (& followupInterrupt , 1 )
17131748
17141749 // Update the metrics touched during block commit
1715- accountCommitTimer .Update (statedb .AccountCommits ) // Account commits are complete, we can mark them
1716- storageCommitTimer .Update (statedb .StorageCommits ) // Storage commits are complete, we can mark them
1750+ accountCommitTimer .Update (statedb .AccountCommits ) // Account commits are complete, we can mark them
1751+ storageCommitTimer .Update (statedb .StorageCommits ) // Storage commits are complete, we can mark them
1752+ snapshotCommitTimer .Update (statedb .SnapshotCommits ) // Snapshot commits are complete, we can mark them
17171753
1718- blockWriteTimer .Update (time .Since (substart ) - statedb .AccountCommits - statedb .StorageCommits )
1754+ blockWriteTimer .Update (time .Since (substart ) - statedb .AccountCommits - statedb .StorageCommits - statedb . SnapshotCommits )
17191755 blockInsertTimer .UpdateSince (start )
17201756
17211757 switch status {
0 commit comments