Skip to content

Commit e14f9ee

Browse files
committed
core, triedb/pathdb: integrate state snapshot inth pathdb
1 parent 0eb2eee commit e14f9ee

26 files changed

+2578
-206
lines changed

core/blockchain.go

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,13 @@ func (c *CacheConfig) triedbConfig(isVerkle bool) *triedb.Config {
182182
}
183183
if c.StateScheme == rawdb.PathScheme {
184184
config.PathDB = &pathdb.Config{
185-
StateHistory: c.StateHistory,
186-
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
185+
StateHistory: c.StateHistory,
186+
TrieCleanSize: c.TrieCleanLimit * 1024 * 1024,
187+
StateCleanSize: c.SnapshotLimit * 1024 * 1024,
188+
189+
// TODO(rjl493456442): The write buffer represents the memory limit used
190+
// for flushing both trie data and state data to disk. The config name
191+
// should be updated to eliminate the confusion.
187192
WriteBufferSize: c.TrieDirtyLimit * 1024 * 1024,
188193
}
189194
}
@@ -379,11 +384,14 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
379384
// Do nothing here until the state syncer picks it up.
380385
log.Info("Genesis state is missing, wait state sync")
381386
} else {
382-
// Head state is missing, before the state recovery, find out the
383-
// disk layer point of snapshot(if it's enabled). Make sure the
384-
// rewound point is lower than disk layer.
387+
// Head state is missing, before the state recovery, find out the disk
388+
// layer point of snapshot(if it's enabled). Make sure the rewound point
389+
// is lower than disk layer.
390+
//
391+
// Note it's unnecessary in path mode which always keep trie data and
392+
// state data consistent.
385393
var diskRoot common.Hash
386-
if bc.cacheConfig.SnapshotLimit > 0 {
394+
if bc.cacheConfig.SnapshotLimit > 0 && bc.cacheConfig.StateScheme == rawdb.HashScheme {
387395
diskRoot = rawdb.ReadSnapshotRoot(bc.db)
388396
}
389397
if diskRoot != (common.Hash{}) {
@@ -456,15 +464,39 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
456464
bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
457465
}
458466
}
467+
bc.setupSnapshot()
468+
469+
// Rewind the chain in case of an incompatible config upgrade.
470+
if compatErr != nil {
471+
log.Warn("Rewinding chain to upgrade configuration", "err", compatErr)
472+
if compatErr.RewindToTime > 0 {
473+
bc.SetHeadWithTimestamp(compatErr.RewindToTime)
474+
} else {
475+
bc.SetHead(compatErr.RewindToBlock)
476+
}
477+
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
478+
}
479+
480+
// Start tx indexer if it's enabled.
481+
if txLookupLimit != nil {
482+
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
483+
}
484+
return bc, nil
485+
}
459486

487+
func (bc *BlockChain) setupSnapshot() {
488+
// Short circuit if the chain is established with path scheme, as the
489+
// state snapshot has been integrated into path database natively.
490+
if bc.cacheConfig.StateScheme == rawdb.PathScheme {
491+
return
492+
}
460493
// Load any existing snapshot, regenerating it if loading failed
461494
if bc.cacheConfig.SnapshotLimit > 0 {
462495
// If the chain was rewound past the snapshot persistent layer (causing
463496
// a recovery block number to be persisted to disk), check if we're still
464497
// in recovery mode and in that case, don't invalidate the snapshot on a
465498
// head mismatch.
466499
var recover bool
467-
468500
head := bc.CurrentBlock()
469501
if layer := rawdb.ReadSnapshotRecoveryNumber(bc.db); layer != nil && *layer >= head.Number.Uint64() {
470502
log.Warn("Enabling snapshot recovery", "chainhead", head.Number, "diskbase", *layer)
@@ -481,22 +513,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
481513
// Re-initialize the state database with snapshot
482514
bc.statedb = state.NewDatabase(bc.triedb, bc.snaps)
483515
}
484-
485-
// Rewind the chain in case of an incompatible config upgrade.
486-
if compatErr != nil {
487-
log.Warn("Rewinding chain to upgrade configuration", "err", compatErr)
488-
if compatErr.RewindToTime > 0 {
489-
bc.SetHeadWithTimestamp(compatErr.RewindToTime)
490-
} else {
491-
bc.SetHead(compatErr.RewindToBlock)
492-
}
493-
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
494-
}
495-
// Start tx indexer if it's enabled.
496-
if txLookupLimit != nil {
497-
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
498-
}
499-
return bc, nil
500516
}
501517

502518
// empty returns an indicator whether the blockchain is empty.

core/blockchain_repair_test.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1791,7 +1791,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
17911791
}
17921792
)
17931793
defer engine.Close()
1794-
if snapshots {
1794+
if snapshots && scheme == rawdb.HashScheme {
17951795
config.SnapshotLimit = 256
17961796
config.SnapshotWait = true
17971797
}
@@ -1820,7 +1820,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
18201820
if err := chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false); err != nil {
18211821
t.Fatalf("Failed to flush trie state: %v", err)
18221822
}
1823-
if snapshots {
1823+
if snapshots && scheme == rawdb.HashScheme {
18241824
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
18251825
t.Fatalf("Failed to flatten snapshots: %v", err)
18261826
}
@@ -1952,8 +1952,10 @@ func testIssue23496(t *testing.T, scheme string) {
19521952
if _, err := chain.InsertChain(blocks[1:2]); err != nil {
19531953
t.Fatalf("Failed to import canonical chain start: %v", err)
19541954
}
1955-
if err := chain.snaps.Cap(blocks[1].Root(), 0); err != nil {
1956-
t.Fatalf("Failed to flatten snapshots: %v", err)
1955+
if scheme == rawdb.HashScheme {
1956+
if err := chain.snaps.Cap(blocks[1].Root(), 0); err != nil {
1957+
t.Fatalf("Failed to flatten snapshots: %v", err)
1958+
}
19571959
}
19581960

19591961
// Insert block B3 and commit the state into disk
@@ -1997,15 +1999,21 @@ func testIssue23496(t *testing.T, scheme string) {
19971999
}
19982000
expHead := uint64(1)
19992001
if scheme == rawdb.PathScheme {
2000-
expHead = uint64(2)
2002+
expHead = uint64(3)
20012003
}
20022004
if head := chain.CurrentBlock(); head.Number.Uint64() != expHead {
20032005
t.Errorf("Head block mismatch: have %d, want %d", head.Number, expHead)
20042006
}
2005-
2006-
// Reinsert B2-B4
2007-
if _, err := chain.InsertChain(blocks[1:]); err != nil {
2008-
t.Fatalf("Failed to import canonical chain tail: %v", err)
2007+
if scheme == rawdb.PathScheme {
2008+
// Reinsert B3-B4
2009+
if _, err := chain.InsertChain(blocks[2:]); err != nil {
2010+
t.Fatalf("Failed to import canonical chain tail: %v", err)
2011+
}
2012+
} else {
2013+
// Reinsert B2-B4
2014+
if _, err := chain.InsertChain(blocks[1:]); err != nil {
2015+
t.Fatalf("Failed to import canonical chain tail: %v", err)
2016+
}
20092017
}
20102018
if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) {
20112019
t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4)
@@ -2016,7 +2024,9 @@ func testIssue23496(t *testing.T, scheme string) {
20162024
if head := chain.CurrentBlock(); head.Number.Uint64() != uint64(4) {
20172025
t.Errorf("Head block mismatch: have %d, want %d", head.Number, uint64(4))
20182026
}
2019-
if layer := chain.Snapshots().Snapshot(blocks[2].Root()); layer == nil {
2020-
t.Error("Failed to regenerate the snapshot of known state")
2027+
if scheme == rawdb.HashScheme {
2028+
if layer := chain.Snapshots().Snapshot(blocks[2].Root()); layer == nil {
2029+
t.Error("Failed to regenerate the snapshot of known state")
2030+
}
20212031
}
20222032
}

core/blockchain_sethead_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2023,7 +2023,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
20232023
}
20242024
if tt.commitBlock > 0 {
20252025
chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false)
2026-
if snapshots {
2026+
if snapshots && scheme == rawdb.HashScheme {
20272027
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
20282028
t.Fatalf("Failed to flatten snapshots: %v", err)
20292029
}

core/blockchain_snapshot_test.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
105105
if basic.commitBlock > 0 && basic.commitBlock == point {
106106
chain.TrieDB().Commit(blocks[point-1].Root(), false)
107107
}
108-
if basic.snapshotBlock > 0 && basic.snapshotBlock == point {
108+
if basic.snapshotBlock > 0 && basic.snapshotBlock == point && basic.scheme == rawdb.HashScheme {
109109
// Flushing the entire snap tree into the disk, the
110110
// relevant (a) snapshot root and (b) snapshot generator
111111
// will be persisted atomically.
@@ -149,13 +149,17 @@ func (basic *snapshotTestBasic) verify(t *testing.T, chain *BlockChain, blocks [
149149
block := chain.GetBlockByNumber(basic.expSnapshotBottom)
150150
if block == nil {
151151
t.Errorf("The corresponding block[%d] of snapshot disk layer is missing", basic.expSnapshotBottom)
152-
} else if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) {
153-
t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot())
152+
} else if basic.scheme == rawdb.HashScheme {
153+
if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) {
154+
t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot())
155+
}
154156
}
155157

156158
// Check the snapshot, ensure it's integrated
157-
if err := chain.snaps.Verify(block.Root()); err != nil {
158-
t.Errorf("The disk layer is not integrated %v", err)
159+
if basic.scheme == rawdb.HashScheme {
160+
if err := chain.snaps.Verify(block.Root()); err != nil {
161+
t.Errorf("The disk layer is not integrated %v", err)
162+
}
159163
}
160164
}
161165

@@ -570,7 +574,7 @@ func TestHighCommitCrashWithNewSnapshot(t *testing.T) {
570574
for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
571575
expHead := uint64(0)
572576
if scheme == rawdb.PathScheme {
573-
expHead = uint64(4)
577+
expHead = uint64(6)
574578
}
575579
test := &crashSnapshotTest{
576580
snapshotTestBasic{

core/state/database.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,9 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
186186
readers = append(readers, newFlatReader(snap))
187187
}
188188
} else {
189-
// If standalone state snapshot is not available, try to construct
190-
// the state reader with database.
189+
// If standalone state snapshot is not available (path scheme
190+
// or the state snapshot is explicitly disabled in hash mode),
191+
// try to construct the state reader with database.
191192
reader, err := db.triedb.StateReader(stateRoot)
192193
if err == nil {
193194
readers = append(readers, newFlatReader(reader)) // state reader is optional

core/state/snapshot/generate_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,9 @@ func newHelper(scheme string) *testHelper {
166166
diskdb := rawdb.NewMemoryDatabase()
167167
config := &triedb.Config{}
168168
if scheme == rawdb.PathScheme {
169-
config.PathDB = &pathdb.Config{} // disable caching
169+
config.PathDB = &pathdb.Config{
170+
SnapshotNoBuild: true,
171+
} // disable caching
170172
} else {
171173
config.HashDB = &hashdb.Config{} // disable caching
172174
}

core/state/statedb_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -979,7 +979,8 @@ func testMissingTrieNodes(t *testing.T, scheme string) {
979979
)
980980
if scheme == rawdb.PathScheme {
981981
tdb = triedb.NewDatabase(memDb, &triedb.Config{PathDB: &pathdb.Config{
982-
CleanCacheSize: 0,
982+
TrieCleanSize: 0,
983+
StateCleanSize: 0,
983984
WriteBufferSize: 0,
984985
}}) // disable caching
985986
} else {

eth/handler.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/ethereum/go-ethereum/common"
2828
"github.com/ethereum/go-ethereum/core"
2929
"github.com/ethereum/go-ethereum/core/forkid"
30+
"github.com/ethereum/go-ethereum/core/rawdb"
3031
"github.com/ethereum/go-ethereum/core/txpool"
3132
"github.com/ethereum/go-ethereum/core/types"
3233
"github.com/ethereum/go-ethereum/crypto"
@@ -183,7 +184,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
183184
}
184185
}
185186
// If snap sync is requested but snapshots are disabled, fail loudly
186-
if h.snapSync.Load() && config.Chain.Snapshots() == nil {
187+
if h.snapSync.Load() && (config.Chain.Snapshots() == nil && config.Chain.TrieDB().Scheme() == rawdb.HashScheme) {
187188
return nil, errors.New("snap sync not supported with snapshots disabled")
188189
}
189190
// Construct the downloader (long sync)

eth/protocols/snap/handler.go

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import (
2323

2424
"github.com/ethereum/go-ethereum/common"
2525
"github.com/ethereum/go-ethereum/core"
26+
"github.com/ethereum/go-ethereum/core/rawdb"
27+
"github.com/ethereum/go-ethereum/core/state/snapshot"
2628
"github.com/ethereum/go-ethereum/core/types"
2729
"github.com/ethereum/go-ethereum/log"
2830
"github.com/ethereum/go-ethereum/metrics"
@@ -31,6 +33,7 @@ import (
3133
"github.com/ethereum/go-ethereum/p2p/enr"
3234
"github.com/ethereum/go-ethereum/trie"
3335
"github.com/ethereum/go-ethereum/trie/trienode"
36+
"github.com/ethereum/go-ethereum/triedb/database"
3437
)
3538

3639
const (
@@ -279,7 +282,16 @@ func ServiceGetAccountRangeQuery(chain *core.BlockChain, req *GetAccountRangePac
279282
if err != nil {
280283
return nil, nil
281284
}
282-
it, err := chain.Snapshots().AccountIterator(req.Root, req.Origin)
285+
// Temporary solution: using the snapshot interface for both cases.
286+
// This can be removed once the hash scheme is deprecated.
287+
var it snapshot.AccountIterator
288+
if chain.TrieDB().Scheme() == rawdb.HashScheme {
289+
// The snapshot is assumed to be available in hash mode if
290+
// the SNAP protocol is enabled.
291+
it, err = chain.Snapshots().AccountIterator(req.Root, req.Origin)
292+
} else {
293+
it, err = chain.TrieDB().AccountIterator(req.Root, req.Origin)
294+
}
283295
if err != nil {
284296
return nil, nil
285297
}
@@ -359,7 +371,19 @@ func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesP
359371
limit, req.Limit = common.BytesToHash(req.Limit), nil
360372
}
361373
// Retrieve the requested state and bail out if non existent
362-
it, err := chain.Snapshots().StorageIterator(req.Root, account, origin)
374+
var (
375+
err error
376+
it snapshot.StorageIterator
377+
)
378+
// Temporary solution: using the snapshot interface for both cases.
379+
// This can be removed once the hash scheme is deprecated.
380+
if chain.TrieDB().Scheme() == rawdb.HashScheme {
381+
// The snapshot is assumed to be available in hash mode if
382+
// the SNAP protocol is enabled.
383+
it, err = chain.Snapshots().StorageIterator(req.Root, account, origin)
384+
} else {
385+
it, err = chain.TrieDB().StorageIterator(req.Root, account, origin)
386+
}
363387
if err != nil {
364388
return nil, nil
365389
}
@@ -479,8 +503,15 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket, s
479503
// We don't have the requested state available, bail out
480504
return nil, nil
481505
}
482-
// The 'snap' might be nil, in which case we cannot serve storage slots.
483-
snap := chain.Snapshots().Snapshot(req.Root)
506+
// The 'reader' might be nil, in which case we cannot serve storage slots
507+
// via snapshot.
508+
var reader database.StateReader
509+
if chain.Snapshots() != nil {
510+
reader = chain.Snapshots().Snapshot(req.Root)
511+
}
512+
if reader == nil {
513+
reader, _ = triedb.StateReader(req.Root)
514+
}
484515
// Retrieve trie nodes until the packet size limit is reached
485516
var (
486517
nodes [][]byte
@@ -505,8 +536,9 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket, s
505536

506537
default:
507538
var stRoot common.Hash
539+
508540
// Storage slots requested, open the storage trie and retrieve from there
509-
if snap == nil {
541+
if reader == nil {
510542
// We don't have the requested state snapshotted yet (or it is stale),
511543
// but can look up the account via the trie instead.
512544
account, err := accTrie.GetAccountByHash(common.BytesToHash(pathset[0]))
@@ -516,7 +548,7 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket, s
516548
}
517549
stRoot = account.Root
518550
} else {
519-
account, err := snap.Account(common.BytesToHash(pathset[0]))
551+
account, err := reader.Account(common.BytesToHash(pathset[0]))
520552
loads++ // always account database reads, even for failures
521553
if err != nil || account == nil {
522554
break

eth/protocols/snap/sync_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1962,5 +1962,5 @@ func newDbConfig(scheme string) *triedb.Config {
19621962
if scheme == rawdb.HashScheme {
19631963
return &triedb.Config{}
19641964
}
1965-
return &triedb.Config{PathDB: pathdb.Defaults}
1965+
return &triedb.Config{PathDB: &pathdb.Config{SnapshotNoBuild: true}}
19661966
}

0 commit comments

Comments
 (0)