Skip to content

Commit b879386

Browse files
committed
core, triedb/pathdb: integrate state snapshot inth pathdb
1 parent 1321a42 commit b879386

26 files changed

+2568
-206
lines changed

core/blockchain.go

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ func (c *CacheConfig) triedbConfig(isVerkle bool) *triedb.Config {
161161
if c.StateScheme == rawdb.PathScheme {
162162
config.PathDB = &pathdb.Config{
163163
StateHistory: c.StateHistory,
164-
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
164+
TrieCleanSize: c.TrieCleanLimit * 1024 * 1024,
165+
StateCleanSize: c.SnapshotLimit * 1024 * 1024,
165166
WriteBufferSize: c.TrieDirtyLimit * 1024 * 1024,
166167
}
167168
}
@@ -349,11 +350,14 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
349350
// Do nothing here until the state syncer picks it up.
350351
log.Info("Genesis state is missing, wait state sync")
351352
} else {
352-
// Head state is missing, before the state recovery, find out the
353-
// disk layer point of snapshot(if it's enabled). Make sure the
354-
// rewound point is lower than disk layer.
353+
// Head state is missing, before the state recovery, find out the disk
354+
// layer point of snapshot(if it's enabled). Make sure the rewound point
355+
// is lower than disk layer.
356+
//
357+
// Note it's unnecessary in path mode which always keep trie data and
358+
// state data consistent.
355359
var diskRoot common.Hash
356-
if bc.cacheConfig.SnapshotLimit > 0 {
360+
if bc.cacheConfig.SnapshotLimit > 0 && bc.cacheConfig.StateScheme == rawdb.HashScheme {
357361
diskRoot = rawdb.ReadSnapshotRoot(bc.db)
358362
}
359363
if diskRoot != (common.Hash{}) {
@@ -426,15 +430,39 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
426430
bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
427431
}
428432
}
433+
bc.setupSnapshot()
429434

435+
// Rewind the chain in case of an incompatible config upgrade.
436+
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
437+
log.Warn("Rewinding chain to upgrade configuration", "err", compat)
438+
if compat.RewindToTime > 0 {
439+
bc.SetHeadWithTimestamp(compat.RewindToTime)
440+
} else {
441+
bc.SetHead(compat.RewindToBlock)
442+
}
443+
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
444+
}
445+
446+
// Start tx indexer if it's enabled.
447+
if txLookupLimit != nil {
448+
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
449+
}
450+
return bc, nil
451+
}
452+
453+
func (bc *BlockChain) setupSnapshot() {
454+
// Short circuit if the chain is established with path scheme, as the
455+
// state snapshot has been integrated into path database natively.
456+
if bc.cacheConfig.StateScheme == rawdb.PathScheme {
457+
return
458+
}
430459
// Load any existing snapshot, regenerating it if loading failed
431460
if bc.cacheConfig.SnapshotLimit > 0 {
432461
// If the chain was rewound past the snapshot persistent layer (causing
433462
// a recovery block number to be persisted to disk), check if we're still
434463
// in recovery mode and in that case, don't invalidate the snapshot on a
435464
// head mismatch.
436465
var recover bool
437-
438466
head := bc.CurrentBlock()
439467
if layer := rawdb.ReadSnapshotRecoveryNumber(bc.db); layer != nil && *layer >= head.Number.Uint64() {
440468
log.Warn("Enabling snapshot recovery", "chainhead", head.Number, "diskbase", *layer)
@@ -451,23 +479,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
451479
// Re-initialize the state database with snapshot
452480
bc.statedb = state.NewDatabase(bc.triedb, bc.snaps)
453481
}
454-
455-
// Rewind the chain in case of an incompatible config upgrade.
456-
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
457-
log.Warn("Rewinding chain to upgrade configuration", "err", compat)
458-
if compat.RewindToTime > 0 {
459-
bc.SetHeadWithTimestamp(compat.RewindToTime)
460-
} else {
461-
bc.SetHead(compat.RewindToBlock)
462-
}
463-
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
464-
}
465-
466-
// Start tx indexer if it's enabled.
467-
if txLookupLimit != nil {
468-
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
469-
}
470-
return bc, nil
471482
}
472483

473484
// 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"
@@ -175,7 +176,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
175176
}
176177
}
177178
// If snap sync is requested but snapshots are disabled, fail loudly
178-
if h.snapSync.Load() && config.Chain.Snapshots() == nil {
179+
if h.snapSync.Load() && (config.Chain.Snapshots() == nil && config.Chain.TrieDB().Scheme() == rawdb.HashScheme) {
179180
return nil, errors.New("snap sync not supported with snapshots disabled")
180181
}
181182
// Construct the downloader (long sync)

eth/protocols/snap/handler.go

Lines changed: 34 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,14 @@ 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+
it, err = chain.Snapshots().AccountIterator(req.Root, req.Origin)
290+
} else {
291+
it, err = chain.TrieDB().AccountIterator(req.Root, req.Origin)
292+
}
283293
if err != nil {
284294
return nil, nil
285295
}
@@ -359,7 +369,17 @@ func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesP
359369
limit, req.Limit = common.BytesToHash(req.Limit), nil
360370
}
361371
// Retrieve the requested state and bail out if non existent
362-
it, err := chain.Snapshots().StorageIterator(req.Root, account, origin)
372+
var (
373+
err error
374+
it snapshot.StorageIterator
375+
)
376+
// Temporary solution: using the snapshot interface for both cases.
377+
// This can be removed once the hash scheme is deprecated.
378+
if chain.TrieDB().Scheme() == rawdb.HashScheme {
379+
it, err = chain.Snapshots().StorageIterator(req.Root, account, origin)
380+
} else {
381+
it, err = chain.TrieDB().StorageIterator(req.Root, account, origin)
382+
}
363383
if err != nil {
364384
return nil, nil
365385
}
@@ -479,8 +499,15 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket, s
479499
// We don't have the requested state available, bail out
480500
return nil, nil
481501
}
482-
// The 'snap' might be nil, in which case we cannot serve storage slots.
483-
snap := chain.Snapshots().Snapshot(req.Root)
502+
// The 'reader' might be nil, in which case we cannot serve storage slots
503+
// via snapshot.
504+
var reader database.StateReader
505+
if chain.Snapshots() != nil {
506+
reader = chain.Snapshots().Snapshot(req.Root)
507+
}
508+
if reader == nil {
509+
reader, _ = triedb.StateReader(req.Root)
510+
}
484511
// Retrieve trie nodes until the packet size limit is reached
485512
var (
486513
nodes [][]byte
@@ -505,8 +532,9 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket, s
505532

506533
default:
507534
var stRoot common.Hash
535+
508536
// Storage slots requested, open the storage trie and retrieve from there
509-
if snap == nil {
537+
if reader == nil {
510538
// We don't have the requested state snapshotted yet (or it is stale),
511539
// but can look up the account via the trie instead.
512540
account, err := accTrie.GetAccountByHash(common.BytesToHash(pathset[0]))
@@ -516,7 +544,7 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket, s
516544
}
517545
stRoot = account.Root
518546
} else {
519-
account, err := snap.Account(common.BytesToHash(pathset[0]))
547+
account, err := reader.Account(common.BytesToHash(pathset[0]))
520548
loads++ // always account database reads, even for failures
521549
if err != nil || account == nil {
522550
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
}

tests/block_test_util.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,10 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t
187187
}
188188
// Cross-check the snapshot-to-hash against the trie hash
189189
if snapshotter {
190-
if err := chain.Snapshots().Verify(chain.CurrentBlock().Root); err != nil {
191-
return err
190+
if chain.Snapshots() != nil {
191+
if err := chain.Snapshots().Verify(chain.CurrentBlock().Root); err != nil {
192+
return err
193+
}
192194
}
193195
}
194196
return t.validateImportedHeaders(chain, validBlocks)

trie/database_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ import (
2525
"github.com/ethereum/go-ethereum/triedb/database"
2626
)
2727

28-
// testReader implements database.Reader interface, providing function to
28+
// testReader implements database.NodeReader interface, providing function to
2929
// access trie nodes.
3030
type testReader struct {
3131
db ethdb.Database
3232
scheme string
3333
nodes []*trienode.MergedNodeSet // sorted from new to old
3434
}
3535

36-
// Node implements database.Reader interface, retrieving trie node with
36+
// Node implements database.NodeReader interface, retrieving trie node with
3737
// all available cached layers.
3838
func (r *testReader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) {
3939
// Check the node presence with the cached layer, from latest to oldest.
@@ -54,7 +54,7 @@ func (r *testReader) Node(owner common.Hash, path []byte, hash common.Hash) ([]b
5454
return rawdb.ReadTrieNode(r.db, owner, path, hash, r.scheme), nil
5555
}
5656

57-
// testDb implements database.Database interface, using for testing purpose.
57+
// testDb implements database.NodeDatabase interface, using for testing purpose.
5858
type testDb struct {
5959
disk ethdb.Database
6060
root common.Hash

0 commit comments

Comments
 (0)