@@ -34,6 +34,7 @@ import (
34
34
"github.com/ethereum/go-ethereum/consensus"
35
35
"github.com/ethereum/go-ethereum/core/rawdb"
36
36
"github.com/ethereum/go-ethereum/core/state"
37
+ "github.com/ethereum/go-ethereum/core/state/snapshot"
37
38
"github.com/ethereum/go-ethereum/core/types"
38
39
"github.com/ethereum/go-ethereum/core/vm"
39
40
"github.com/ethereum/go-ethereum/ethdb"
61
62
storageUpdateTimer = metrics .NewRegisteredTimer ("chain/storage/updates" , nil )
62
63
storageCommitTimer = metrics .NewRegisteredTimer ("chain/storage/commits" , nil )
63
64
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
+
64
69
blockInsertTimer = metrics .NewRegisteredTimer ("chain/inserts" , nil )
65
70
blockValidationTimer = metrics .NewRegisteredTimer ("chain/validation" , nil )
66
71
blockExecutionTimer = metrics .NewRegisteredTimer ("chain/execution" , nil )
@@ -115,6 +120,9 @@ type CacheConfig struct {
115
120
TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk
116
121
TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node)
117
122
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
118
126
}
119
127
120
128
// BlockChain represents the canonical chain given a database with a genesis
@@ -136,6 +144,7 @@ type BlockChain struct {
136
144
cacheConfig * CacheConfig // Cache configuration for pruning
137
145
138
146
db ethdb.Database // Low level persistent database to store final content in
147
+ snaps * snapshot.Tree // Snapshot tree for fast trie leaf access
139
148
triegc * prque.Prque // Priority queue mapping block numbers to tries to gc
140
149
gcproc time.Duration // Accumulates canonical block processing for trie dumping
141
150
@@ -188,6 +197,8 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
188
197
TrieCleanLimit : 256 ,
189
198
TrieDirtyLimit : 256 ,
190
199
TrieTimeLimit : 5 * time .Minute ,
200
+ SnapshotLimit : 256 ,
201
+ SnapshotWait : true ,
191
202
}
192
203
}
193
204
bodyCache , _ := lru .New (bodyCacheLimit )
@@ -293,6 +304,10 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
293
304
}
294
305
}
295
306
}
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
+ }
296
311
// Take ownership of this particular state
297
312
go bc .update ()
298
313
return bc , nil
@@ -339,7 +354,7 @@ func (bc *BlockChain) loadLastState() error {
339
354
return bc .Reset ()
340
355
}
341
356
// 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 {
343
358
// Dangling block without a state associated, init from scratch
344
359
log .Warn ("Head state missing, repairing chain" , "number" , currentBlock .Number (), "hash" , currentBlock .Hash ())
345
360
if err := bc .repair (& currentBlock ); err != nil {
@@ -401,7 +416,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
401
416
if newHeadBlock == nil {
402
417
newHeadBlock = bc .genesisBlock
403
418
} 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 {
405
420
// Rewound state missing, rolled back to before pivot, reset to genesis
406
421
newHeadBlock = bc .genesisBlock
407
422
}
@@ -486,6 +501,10 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
486
501
headBlockGauge .Update (int64 (block .NumberU64 ()))
487
502
bc .chainmu .Unlock ()
488
503
504
+ // Destroy any existing state snapshot and regenerate it in the background
505
+ if bc .snaps != nil {
506
+ bc .snaps .Rebuild (block .Root ())
507
+ }
489
508
log .Info ("Committed new head block" , "number" , block .Number (), "hash" , hash )
490
509
return nil
491
510
}
@@ -524,7 +543,7 @@ func (bc *BlockChain) State() (*state.StateDB, error) {
524
543
525
544
// StateAt returns a new mutable state based on a particular point in time.
526
545
func (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 )
528
547
}
529
548
530
549
// StateCache returns the caching database underpinning the blockchain instance.
@@ -576,7 +595,7 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
576
595
func (bc * BlockChain ) repair (head * * types.Block ) error {
577
596
for {
578
597
// 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 {
580
599
log .Info ("Rewound blockchain to past state" , "number" , (* head ).Number (), "hash" , (* head ).Hash ())
581
600
return nil
582
601
}
@@ -839,6 +858,14 @@ func (bc *BlockChain) Stop() {
839
858
840
859
bc .wg .Wait ()
841
860
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
+ }
842
869
// Ensure the state of a recent block is also stored to disk before exiting.
843
870
// We're writing three different states to catch different restart scenarios:
844
871
// - HEAD: So we don't need to reprocess any blocks in the general case
@@ -857,6 +884,12 @@ func (bc *BlockChain) Stop() {
857
884
}
858
885
}
859
886
}
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
+ }
860
893
for ! bc .triegc .Empty () {
861
894
triedb .Dereference (bc .triegc .PopItem ().(common.Hash ))
862
895
}
@@ -1647,7 +1680,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
1647
1680
if parent == nil {
1648
1681
parent = bc .GetHeader (block .ParentHash (), block .NumberU64 ()- 1 )
1649
1682
}
1650
- statedb , err := state .New (parent .Root , bc .stateCache )
1683
+ statedb , err := state .New (parent .Root , bc .stateCache , bc . snaps )
1651
1684
if err != nil {
1652
1685
return it .index , err
1653
1686
}
@@ -1656,9 +1689,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
1656
1689
var followupInterrupt uint32
1657
1690
if ! bc .cacheConfig .TrieCleanNoPrefetch {
1658
1691
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 )
1660
1693
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 )
1662
1695
1663
1696
blockPrefetchExecuteTimer .Update (time .Since (start ))
1664
1697
if atomic .LoadUint32 (interrupt ) == 1 {
@@ -1676,14 +1709,16 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
1676
1709
return it .index , err
1677
1710
}
1678
1711
// 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
1683
1718
1684
1719
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
1687
1722
1688
1723
blockExecutionTimer .Update (time .Since (substart ) - trieproc - triehash )
1689
1724
@@ -1712,10 +1747,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
1712
1747
atomic .StoreUint32 (& followupInterrupt , 1 )
1713
1748
1714
1749
// 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
1717
1753
1718
- blockWriteTimer .Update (time .Since (substart ) - statedb .AccountCommits - statedb .StorageCommits )
1754
+ blockWriteTimer .Update (time .Since (substart ) - statedb .AccountCommits - statedb .StorageCommits - statedb . SnapshotCommits )
1719
1755
blockInsertTimer .UpdateSince (start )
1720
1756
1721
1757
switch status {
0 commit comments