From a2411ef25059038c301819ef79ece95d9a57c4e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20IRMAK?= Date: Fri, 30 May 2025 16:00:07 +0300 Subject: [PATCH] core/state: reverse dependency relation between state and snapshot snapshot package depends on encoding/gob, which we want to avoid in our tinygo builds. This allows state package to be compiled in isolation. --- core/state/database.go | 9 +- core/state/pruner/pruner.go | 3 +- core/state/snapshot.go | 114 +++++++++++++++++++++++++ core/state/snapshot/conversion.go | 9 +- core/state/snapshot/iterator.go | 50 ++--------- core/state/snapshot/iterator_binary.go | 17 ++-- core/state/snapshot/iterator_fast.go | 15 ++-- core/state/snapshot/iterator_test.go | 37 ++++---- core/state/snapshot/snapshot.go | 36 ++------ core/state/statedb.go | 3 +- core/state/statedb_fuzz_test.go | 2 +- 11 files changed, 177 insertions(+), 118 deletions(-) create mode 100644 core/state/snapshot.go diff --git a/core/state/database.go b/core/state/database.go index aec841f59b..cab75bbb4f 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -22,7 +22,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" @@ -61,7 +60,7 @@ type Database interface { TrieDB() *triedb.Database // Snapshot returns the underlying state snapshot. - Snapshot() *snapshot.Tree + Snapshot() SnapshotTree } // Trie is a Ethereum Merkle Patricia trie. @@ -147,14 +146,14 @@ type Trie interface { type CachingDB struct { disk ethdb.KeyValueStore triedb *triedb.Database - snap *snapshot.Tree + snap SnapshotTree codeCache *lru.SizeConstrainedCache[common.Hash, []byte] codeSizeCache *lru.Cache[common.Hash, int] pointCache *utils.PointCache } // NewDatabase creates a state database with the provided data sources. -func NewDatabase(triedb *triedb.Database, snap *snapshot.Tree) *CachingDB { +func NewDatabase(triedb *triedb.Database, snap SnapshotTree) *CachingDB { return &CachingDB{ disk: triedb.Disk(), triedb: triedb, @@ -272,7 +271,7 @@ func (db *CachingDB) PointCache() *utils.PointCache { } // Snapshot returns the underlying state snapshot. -func (db *CachingDB) Snapshot() *snapshot.Tree { +func (db *CachingDB) Snapshot() SnapshotTree { return db.snap } diff --git a/core/state/pruner/pruner.go b/core/state/pruner/pruner.go index 46558a6fce..d70b4fe87c 100644 --- a/core/state/pruner/pruner.go +++ b/core/state/pruner/pruner.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" @@ -251,7 +252,7 @@ func (p *Pruner) Prune(root common.Hash) error { // target. The reason for picking it is: // - in most of the normal cases, the related state is available // - the probability of this layer being reorg is very low - var layers []snapshot.Snapshot + var layers []state.Snapshot if root == (common.Hash{}) { // Retrieve all snapshot layers from the current HEAD. // In theory there are 128 difflayers + 1 disk layer present, diff --git a/core/state/snapshot.go b/core/state/snapshot.go new file mode 100644 index 0000000000..e40fae558e --- /dev/null +++ b/core/state/snapshot.go @@ -0,0 +1,114 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package state provides a caching layer atop the Ethereum state trie. +package state + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Snapshot represents the functionality supported by a snapshot storage layer. +type Snapshot interface { + // Root returns the root hash for which this snapshot was made. + Root() common.Hash + + // Account directly retrieves the account associated with a particular hash in + // the snapshot slim data format. + Account(hash common.Hash) (*types.SlimAccount, error) + + // AccountRLP directly retrieves the account RLP associated with a particular + // hash in the snapshot slim data format. + AccountRLP(hash common.Hash) ([]byte, error) + + // Storage directly retrieves the storage data associated with a particular hash, + // within a particular account. + Storage(accountHash, storageHash common.Hash) ([]byte, error) +} + +// Tree is an Ethereum state snapshot tree. It consists of one persistent base +// layer backed by a key-value store, on top of which arbitrarily many in-memory +// diff layers are topped. The memory diffs can form a tree with branching, but +// the disk layer is singleton and common to all. If a reorg goes deeper than the +// disk layer, everything needs to be deleted. +// +// The goal of a state snapshot is twofold: to allow direct access to account and +// storage data to avoid expensive multi-level trie lookups; and to allow sorted, +// cheap iteration of the account/storage tries for sync aid. +type SnapshotTree interface { + // Snapshot retrieves a snapshot belonging to the given block root, or nil if no + // snapshot is maintained for that block. + Snapshot(blockRoot common.Hash) Snapshot + + // Update adds a new snapshot into the tree, if that can be linked to an existing + // old parent. It is disallowed to insert a disk layer (the origin of all). + Update(blockRoot common.Hash, parentRoot common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error + + // Cap traverses downwards the snapshot tree from a head block hash until the + // number of allowed layers are crossed. All layers beyond the permitted number + // are flattened downwards. + Cap(root common.Hash, layers int) error + + // StorageIterator creates a new storage iterator for the specified root hash and + // account. The iterator will be move to the specific start position. + StorageIterator(root common.Hash, account common.Hash, seek common.Hash) (StorageIterator, error) + + // AccountIterator creates a new account iterator for the specified root hash and + // seeks to a starting account hash. + AccountIterator(root common.Hash, seek common.Hash) (AccountIterator, error) +} + +// Iterator is an iterator to step over all the accounts or the specific +// storage in a snapshot which may or may not be composed of multiple layers. +type Iterator interface { + // Next steps the iterator forward one element, returning false if exhausted, + // or an error if iteration failed for some reason (e.g. root being iterated + // becomes stale and garbage collected). + Next() bool + + // Error returns any failure that occurred during iteration, which might have + // caused a premature iteration exit (e.g. snapshot stack becoming stale). + Error() error + + // Hash returns the hash of the account or storage slot the iterator is + // currently at. + Hash() common.Hash + + // Release releases associated resources. Release should always succeed and + // can be called multiple times without causing error. + Release() +} + +// AccountIterator is an iterator to step over all the accounts in a snapshot, +// which may or may not be composed of multiple layers. +type AccountIterator interface { + Iterator + + // Account returns the RLP encoded slim account the iterator is currently at. + // An error will be returned if the iterator becomes invalid + Account() []byte +} + +// StorageIterator is an iterator to step over the specific storage in a snapshot, +// which may or may not be composed of multiple layers. +type StorageIterator interface { + Iterator + + // Slot returns the storage slot the iterator is currently at. An error will + // be returned if the iterator becomes invalid + Slot() []byte +} diff --git a/core/state/snapshot/conversion.go b/core/state/snapshot/conversion.go index 4b0774f2ae..f568bb57fc 100644 --- a/core/state/snapshot/conversion.go +++ b/core/state/snapshot/conversion.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" @@ -233,7 +234,7 @@ func runReport(stats *generateStats, stop chan bool) { // generateTrieRoot generates the trie hash based on the snapshot iterator. // It can be used for generating account trie, storage trie or even the // whole state which connects the accounts and the corresponding storages. -func generateTrieRoot(db ethdb.KeyValueWriter, scheme string, it Iterator, account common.Hash, generatorFn trieGeneratorFn, leafCallback leafCallbackFn, stats *generateStats, report bool) (common.Hash, error) { +func generateTrieRoot(db ethdb.KeyValueWriter, scheme string, it state.Iterator, account common.Hash, generatorFn trieGeneratorFn, leafCallback leafCallbackFn, stats *generateStats, report bool) (common.Hash, error) { var ( in = make(chan trieKV) // chan to pass leaves out = make(chan common.Hash, 1) // chan to collect result @@ -290,7 +291,7 @@ func generateTrieRoot(db ethdb.KeyValueWriter, scheme string, it Iterator, accou fullData []byte ) if leafCallback == nil { - fullData, err = types.FullAccountRLP(it.(AccountIterator).Account()) + fullData, err = types.FullAccountRLP(it.(state.AccountIterator).Account()) if err != nil { return stop(err) } @@ -302,7 +303,7 @@ func generateTrieRoot(db ethdb.KeyValueWriter, scheme string, it Iterator, accou return stop(err) } // Fetch the next account and process it concurrently - account, err := types.FullAccount(it.(AccountIterator).Account()) + account, err := types.FullAccount(it.(state.AccountIterator).Account()) if err != nil { return stop(err) } @@ -325,7 +326,7 @@ func generateTrieRoot(db ethdb.KeyValueWriter, scheme string, it Iterator, accou } leaf = trieKV{it.Hash(), fullData} } else { - leaf = trieKV{it.Hash(), common.CopyBytes(it.(StorageIterator).Slot())} + leaf = trieKV{it.Hash(), common.CopyBytes(it.(state.StorageIterator).Slot())} } in <- leaf diff --git a/core/state/snapshot/iterator.go b/core/state/snapshot/iterator.go index cd9c225844..e1bfed7beb 100644 --- a/core/state/snapshot/iterator.go +++ b/core/state/snapshot/iterator.go @@ -23,50 +23,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/ethdb" ) -// Iterator is an iterator to step over all the accounts or the specific -// storage in a snapshot which may or may not be composed of multiple layers. -type Iterator interface { - // Next steps the iterator forward one element, returning false if exhausted, - // or an error if iteration failed for some reason (e.g. root being iterated - // becomes stale and garbage collected). - Next() bool - - // Error returns any failure that occurred during iteration, which might have - // caused a premature iteration exit (e.g. snapshot stack becoming stale). - Error() error - - // Hash returns the hash of the account or storage slot the iterator is - // currently at. - Hash() common.Hash - - // Release releases associated resources. Release should always succeed and - // can be called multiple times without causing error. - Release() -} - -// AccountIterator is an iterator to step over all the accounts in a snapshot, -// which may or may not be composed of multiple layers. -type AccountIterator interface { - Iterator - - // Account returns the RLP encoded slim account the iterator is currently at. - // An error will be returned if the iterator becomes invalid - Account() []byte -} - -// StorageIterator is an iterator to step over the specific storage in a snapshot, -// which may or may not be composed of multiple layers. -type StorageIterator interface { - Iterator - - // Slot returns the storage slot the iterator is currently at. An error will - // be returned if the iterator becomes invalid - Slot() []byte -} - // diffAccountIterator is an account iterator that steps over the accounts (both // live and deleted) contained within a single diff layer. Higher order iterators // will use the deleted accounts to skip deeper iterators. @@ -83,7 +43,7 @@ type diffAccountIterator struct { } // AccountIterator creates an account iterator over a single diff layer. -func (dl *diffLayer) AccountIterator(seek common.Hash) AccountIterator { +func (dl *diffLayer) AccountIterator(seek common.Hash) state.AccountIterator { // Seek out the requested starting account hashes := dl.AccountList() index := sort.Search(len(hashes), func(i int) bool { @@ -164,7 +124,7 @@ type diskAccountIterator struct { } // AccountIterator creates an account iterator over a disk layer. -func (dl *diskLayer) AccountIterator(seek common.Hash) AccountIterator { +func (dl *diskLayer) AccountIterator(seek common.Hash) state.AccountIterator { pos := common.TrimRightZeroes(seek[:]) return &diskAccountIterator{ layer: dl, @@ -244,7 +204,7 @@ type diffStorageIterator struct { // "destructed" returned. If it's true then it means the whole storage is // destructed in this layer(maybe recreated too), don't bother deeper layer // for storage retrieval. -func (dl *diffLayer) StorageIterator(account common.Hash, seek common.Hash) StorageIterator { +func (dl *diffLayer) StorageIterator(account common.Hash, seek common.Hash) state.StorageIterator { // Create the storage for this account even it's marked // as destructed. The iterator is for the new one which // just has the same address as the deleted one. @@ -336,7 +296,7 @@ type diskStorageIterator struct { // If the whole storage is destructed, then all entries in the disk // layer are deleted already. So the "destructed" flag returned here // is always false. -func (dl *diskLayer) StorageIterator(account common.Hash, seek common.Hash) StorageIterator { +func (dl *diskLayer) StorageIterator(account common.Hash, seek common.Hash) state.StorageIterator { pos := common.TrimRightZeroes(seek[:]) return &diskStorageIterator{ layer: dl, diff --git a/core/state/snapshot/iterator_binary.go b/core/state/snapshot/iterator_binary.go index 254c4b860f..e71753c493 100644 --- a/core/state/snapshot/iterator_binary.go +++ b/core/state/snapshot/iterator_binary.go @@ -20,14 +20,15 @@ import ( "bytes" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" ) // binaryIterator is a simplistic iterator to step over the accounts or storage // in a snapshot, which may or may not be composed of multiple layers. Performance // wise this iterator is slow, it's meant for cross validating the fast one, type binaryIterator struct { - a Iterator - b Iterator + a state.Iterator + b state.Iterator aDone bool bDone bool accountIterator bool @@ -39,7 +40,7 @@ type binaryIterator struct { // initBinaryAccountIterator creates a simplistic iterator to step over all the // accounts in a slow, but easily verifiable way. Note this function is used for // initialization, use `newBinaryAccountIterator` as the API. -func (dl *diffLayer) initBinaryAccountIterator(seek common.Hash) Iterator { +func (dl *diffLayer) initBinaryAccountIterator(seek common.Hash) state.Iterator { parent, ok := dl.parent.(*diffLayer) if !ok { l := &binaryIterator{ @@ -64,7 +65,7 @@ func (dl *diffLayer) initBinaryAccountIterator(seek common.Hash) Iterator { // initBinaryStorageIterator creates a simplistic iterator to step over all the // storage slots in a slow, but easily verifiable way. Note this function is used // for initialization, use `newBinaryStorageIterator` as the API. -func (dl *diffLayer) initBinaryStorageIterator(account, seek common.Hash) Iterator { +func (dl *diffLayer) initBinaryStorageIterator(account, seek common.Hash) state.Iterator { parent, ok := dl.parent.(*diffLayer) if !ok { l := &binaryIterator{ @@ -192,14 +193,14 @@ func (it *binaryIterator) Release() { // newBinaryAccountIterator creates a simplistic account iterator to step over // all the accounts in a slow, but easily verifiable way. -func (dl *diffLayer) newBinaryAccountIterator(seek common.Hash) AccountIterator { +func (dl *diffLayer) newBinaryAccountIterator(seek common.Hash) state.AccountIterator { iter := dl.initBinaryAccountIterator(seek) - return iter.(AccountIterator) + return iter.(state.AccountIterator) } // newBinaryStorageIterator creates a simplistic account iterator to step over // all the storage slots in a slow, but easily verifiable way. -func (dl *diffLayer) newBinaryStorageIterator(account, seek common.Hash) StorageIterator { +func (dl *diffLayer) newBinaryStorageIterator(account, seek common.Hash) state.StorageIterator { iter := dl.initBinaryStorageIterator(account, seek) - return iter.(StorageIterator) + return iter.(state.StorageIterator) } diff --git a/core/state/snapshot/iterator_fast.go b/core/state/snapshot/iterator_fast.go index 61fc434178..ed5cd71832 100644 --- a/core/state/snapshot/iterator_fast.go +++ b/core/state/snapshot/iterator_fast.go @@ -24,13 +24,14 @@ import ( "sort" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" ) // weightedIterator is an iterator with an assigned weight. It is used to prioritise // which account or storage slot is the correct one if multiple iterators find the // same one (modified in multiple consecutive blocks). type weightedIterator struct { - it Iterator + it state.Iterator priority int } @@ -162,9 +163,9 @@ func (fi *fastIterator) Next() bool { // do the sorting already fi.initiated = true if fi.account { - fi.curAccount = fi.iterators[0].it.(AccountIterator).Account() + fi.curAccount = fi.iterators[0].it.(state.AccountIterator).Account() } else { - fi.curSlot = fi.iterators[0].it.(StorageIterator).Slot() + fi.curSlot = fi.iterators[0].it.(state.StorageIterator).Slot() } if innerErr := fi.iterators[0].it.Error(); innerErr != nil { fi.fail = innerErr @@ -188,9 +189,9 @@ func (fi *fastIterator) Next() bool { return false // exhausted } if fi.account { - fi.curAccount = fi.iterators[0].it.(AccountIterator).Account() + fi.curAccount = fi.iterators[0].it.(state.AccountIterator).Account() } else { - fi.curSlot = fi.iterators[0].it.(StorageIterator).Slot() + fi.curSlot = fi.iterators[0].it.(state.StorageIterator).Slot() } if innerErr := fi.iterators[0].it.Error(); innerErr != nil { fi.fail = innerErr @@ -319,13 +320,13 @@ func (fi *fastIterator) Debug() { // newFastAccountIterator creates a new hierarchical account iterator with one // element per diff layer. The returned combo iterator can be used to walk over // the entire snapshot diff stack simultaneously. -func newFastAccountIterator(tree *Tree, root common.Hash, seek common.Hash) (AccountIterator, error) { +func newFastAccountIterator(tree *Tree, root common.Hash, seek common.Hash) (state.AccountIterator, error) { return newFastIterator(tree, root, common.Hash{}, seek, true) } // newFastStorageIterator creates a new hierarchical storage iterator with one // element per diff layer. The returned combo iterator can be used to walk over // the entire snapshot diff stack simultaneously. -func newFastStorageIterator(tree *Tree, root common.Hash, account common.Hash, seek common.Hash) (StorageIterator, error) { +func newFastStorageIterator(tree *Tree, root common.Hash, account common.Hash, seek common.Hash) (state.StorageIterator, error) { return newFastIterator(tree, root, account, seek, false) } diff --git a/core/state/snapshot/iterator_test.go b/core/state/snapshot/iterator_test.go index 2e882f484e..ce489fd98a 100644 --- a/core/state/snapshot/iterator_test.go +++ b/core/state/snapshot/iterator_test.go @@ -27,6 +27,7 @@ import ( "github.com/VictoriaMetrics/fastcache" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" ) // TestAccountIteratorBasics tests some simple single-layer(diff and disk) iteration @@ -179,7 +180,7 @@ const ( verifyStorage ) -func verifyIterator(t *testing.T, expCount int, it Iterator, verify verifyContent) { +func verifyIterator(t *testing.T, expCount int, it state.Iterator, verify verifyContent) { t.Helper() var ( @@ -192,9 +193,9 @@ func verifyIterator(t *testing.T, expCount int, it Iterator, verify verifyConten t.Errorf("wrong order: %x >= %x", last, hash) } count++ - if verify == verifyAccount && len(it.(AccountIterator).Account()) == 0 { + if verify == verifyAccount && len(it.(state.AccountIterator).Account()) == 0 { t.Errorf("iterator returned nil-value for hash %x", hash) - } else if verify == verifyStorage && len(it.(StorageIterator).Slot()) == 0 { + } else if verify == verifyStorage && len(it.(state.StorageIterator).Slot()) == 0 { t.Errorf("iterator returned nil-value for hash %x", hash) } last = hash @@ -555,19 +556,19 @@ func TestAccountIteratorLargeTraversal(t *testing.T) { // - continues iterating func TestAccountIteratorFlattening(t *testing.T) { t.Run("fast", func(t *testing.T) { - testAccountIteratorFlattening(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + testAccountIteratorFlattening(t, func(snaps *Tree, root, seek common.Hash) state.AccountIterator { it, _ := snaps.AccountIterator(root, seek) return it }) }) t.Run("binary", func(t *testing.T) { - testAccountIteratorFlattening(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + testAccountIteratorFlattening(t, func(snaps *Tree, root, seek common.Hash) state.AccountIterator { return snaps.layers[root].(*diffLayer).newBinaryAccountIterator(seek) }) }) } -func testAccountIteratorFlattening(t *testing.T, newIterator func(snaps *Tree, root, seek common.Hash) AccountIterator) { +func testAccountIteratorFlattening(t *testing.T, newIterator func(snaps *Tree, root, seek common.Hash) state.AccountIterator) { // Create an empty base layer and a snapshot tree out of it base := &diskLayer{ diskdb: rawdb.NewMemoryDatabase(), @@ -601,20 +602,20 @@ func testAccountIteratorFlattening(t *testing.T, newIterator func(snaps *Tree, r func TestAccountIteratorSeek(t *testing.T) { t.Run("fast", func(t *testing.T) { - testAccountIteratorSeek(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + testAccountIteratorSeek(t, func(snaps *Tree, root, seek common.Hash) state.AccountIterator { it, _ := snaps.AccountIterator(root, seek) return it }) }) t.Run("binary", func(t *testing.T) { - testAccountIteratorSeek(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + testAccountIteratorSeek(t, func(snaps *Tree, root, seek common.Hash) state.AccountIterator { it := snaps.layers[root].(*diffLayer).newBinaryAccountIterator(seek) return it }) }) } -func testAccountIteratorSeek(t *testing.T, newIterator func(snaps *Tree, root, seek common.Hash) AccountIterator) { +func testAccountIteratorSeek(t *testing.T, newIterator func(snaps *Tree, root, seek common.Hash) state.AccountIterator) { // Create a snapshot stack with some initial data base := &diskLayer{ diskdb: rawdb.NewMemoryDatabase(), @@ -679,19 +680,19 @@ func testAccountIteratorSeek(t *testing.T, newIterator func(snaps *Tree, root, s func TestStorageIteratorSeek(t *testing.T) { t.Run("fast", func(t *testing.T) { - testStorageIteratorSeek(t, func(snaps *Tree, root, account, seek common.Hash) StorageIterator { + testStorageIteratorSeek(t, func(snaps *Tree, root, account, seek common.Hash) state.StorageIterator { it, _ := snaps.StorageIterator(root, account, seek) return it }) }) t.Run("binary", func(t *testing.T) { - testStorageIteratorSeek(t, func(snaps *Tree, root, account, seek common.Hash) StorageIterator { + testStorageIteratorSeek(t, func(snaps *Tree, root, account, seek common.Hash) state.StorageIterator { return snaps.layers[root].(*diffLayer).newBinaryStorageIterator(account, seek) }) }) } -func testStorageIteratorSeek(t *testing.T, newIterator func(snaps *Tree, root, account, seek common.Hash) StorageIterator) { +func testStorageIteratorSeek(t *testing.T, newIterator func(snaps *Tree, root, account, seek common.Hash) state.StorageIterator) { // Create a snapshot stack with some initial data base := &diskLayer{ diskdb: rawdb.NewMemoryDatabase(), @@ -756,19 +757,19 @@ func testStorageIteratorSeek(t *testing.T, newIterator func(snaps *Tree, root, a // should not output any accounts or nil-values for those cases. func TestAccountIteratorDeletions(t *testing.T) { t.Run("fast", func(t *testing.T) { - testAccountIteratorDeletions(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + testAccountIteratorDeletions(t, func(snaps *Tree, root, seek common.Hash) state.AccountIterator { it, _ := snaps.AccountIterator(root, seek) return it }) }) t.Run("binary", func(t *testing.T) { - testAccountIteratorDeletions(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + testAccountIteratorDeletions(t, func(snaps *Tree, root, seek common.Hash) state.AccountIterator { return snaps.layers[root].(*diffLayer).newBinaryAccountIterator(seek) }) }) } -func testAccountIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, root, seek common.Hash) AccountIterator) { +func testAccountIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, root, seek common.Hash) state.AccountIterator) { // Create an empty base layer and a snapshot tree out of it base := &diskLayer{ diskdb: rawdb.NewMemoryDatabase(), @@ -812,19 +813,19 @@ func testAccountIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, ro func TestStorageIteratorDeletions(t *testing.T) { t.Run("fast", func(t *testing.T) { - testStorageIteratorDeletions(t, func(snaps *Tree, root, account, seek common.Hash) StorageIterator { + testStorageIteratorDeletions(t, func(snaps *Tree, root, account, seek common.Hash) state.StorageIterator { it, _ := snaps.StorageIterator(root, account, seek) return it }) }) t.Run("binary", func(t *testing.T) { - testStorageIteratorDeletions(t, func(snaps *Tree, root, account, seek common.Hash) StorageIterator { + testStorageIteratorDeletions(t, func(snaps *Tree, root, account, seek common.Hash) state.StorageIterator { return snaps.layers[root].(*diffLayer).newBinaryStorageIterator(account, seek) }) }) } -func testStorageIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, root, account, seek common.Hash) StorageIterator) { +func testStorageIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, root, account, seek common.Hash) state.StorageIterator) { // Create an empty base layer and a snapshot tree out of it base := &diskLayer{ diskdb: rawdb.NewMemoryDatabase(), diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index f0f6296433..16a94aa603 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -25,7 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" @@ -96,28 +96,10 @@ var ( errSnapshotCycle = errors.New("snapshot cycle") ) -// Snapshot represents the functionality supported by a snapshot storage layer. -type Snapshot interface { - // Root returns the root hash for which this snapshot was made. - Root() common.Hash - - // Account directly retrieves the account associated with a particular hash in - // the snapshot slim data format. - Account(hash common.Hash) (*types.SlimAccount, error) - - // AccountRLP directly retrieves the account RLP associated with a particular - // hash in the snapshot slim data format. - AccountRLP(hash common.Hash) ([]byte, error) - - // Storage directly retrieves the storage data associated with a particular hash, - // within a particular account. - Storage(accountHash, storageHash common.Hash) ([]byte, error) -} - // snapshot is the internal version of the snapshot data layer that supports some // additional methods compared to the public API. type snapshot interface { - Snapshot + state.Snapshot // Parent returns the subsequent layer of a snapshot, or nil if the base was // reached. @@ -142,10 +124,10 @@ type snapshot interface { Stale() bool // AccountIterator creates an account iterator over an arbitrary layer. - AccountIterator(seek common.Hash) AccountIterator + AccountIterator(seek common.Hash) state.AccountIterator // StorageIterator creates a storage iterator over an arbitrary layer. - StorageIterator(account common.Hash, seek common.Hash) StorageIterator + StorageIterator(account common.Hash, seek common.Hash) state.StorageIterator } // Config includes the configurations for snapshots. @@ -293,7 +275,7 @@ func (t *Tree) Disable() { // Snapshot retrieves a snapshot belonging to the given block root, or nil if no // snapshot is maintained for that block. -func (t *Tree) Snapshot(blockRoot common.Hash) Snapshot { +func (t *Tree) Snapshot(blockRoot common.Hash) state.Snapshot { t.lock.RLock() defer t.lock.RUnlock() @@ -303,7 +285,7 @@ func (t *Tree) Snapshot(blockRoot common.Hash) Snapshot { // Snapshots returns all visited layers from the topmost layer with specific // root and traverses downward. The layer amount is limited by the given number. // If nodisk is set, then disk layer is excluded. -func (t *Tree) Snapshots(root common.Hash, limits int, nodisk bool) []Snapshot { +func (t *Tree) Snapshots(root common.Hash, limits int, nodisk bool) []state.Snapshot { t.lock.RLock() defer t.lock.RUnlock() @@ -314,7 +296,7 @@ func (t *Tree) Snapshots(root common.Hash, limits int, nodisk bool) []Snapshot { if layer == nil { return nil } - var ret []Snapshot + var ret []state.Snapshot for { if _, isdisk := layer.(*diskLayer); isdisk && nodisk { break @@ -728,7 +710,7 @@ func (t *Tree) Rebuild(root common.Hash) { // AccountIterator creates a new account iterator for the specified root hash and // seeks to a starting account hash. -func (t *Tree) AccountIterator(root common.Hash, seek common.Hash) (AccountIterator, error) { +func (t *Tree) AccountIterator(root common.Hash, seek common.Hash) (state.AccountIterator, error) { ok, err := t.generating() if err != nil { return nil, err @@ -741,7 +723,7 @@ func (t *Tree) AccountIterator(root common.Hash, seek common.Hash) (AccountItera // StorageIterator creates a new storage iterator for the specified root hash and // account. The iterator will be move to the specific start position. -func (t *Tree) StorageIterator(root common.Hash, account common.Hash, seek common.Hash) (StorageIterator, error) { +func (t *Tree) StorageIterator(root common.Hash, account common.Hash, seek common.Hash) (state.StorageIterator, error) { ok, err := t.generating() if err != nil { return nil, err diff --git a/core/state/statedb.go b/core/state/statedb.go index 51453055c3..d3bbf7805a 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" @@ -943,7 +942,7 @@ func (s *StateDB) clearJournalAndRefund() { // of a specific account. It leverages the associated state snapshot for fast // storage iteration and constructs trie node deletion markers by creating // stack trie with iterated slots. -func (s *StateDB) fastDeleteStorage(snaps *snapshot.Tree, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) { +func (s *StateDB) fastDeleteStorage(snaps SnapshotTree, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) { iter, err := snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{}) if err != nil { return nil, nil, nil, err diff --git a/core/state/statedb_fuzz_test.go b/core/state/statedb_fuzz_test.go index 7dada63d45..186e9a6c85 100644 --- a/core/state/statedb_fuzz_test.go +++ b/core/state/statedb_fuzz_test.go @@ -209,7 +209,7 @@ func (test *stateTest) run() bool { if i != 0 { root = roots[len(roots)-1] } - state, err := New(root, NewDatabase(tdb, snaps)) + state, err := New(root, NewDatabase(tdb, nil)) if err != nil { panic(err) }