From c77047a67f8d9b4bba4ba362e95af3c4d8e3061a Mon Sep 17 00:00:00 2001 From: Gary Rong Date: Wed, 24 Jan 2024 15:03:37 +0800 Subject: [PATCH] all: add dbconfig package --- cmd/evm/internal/t8ntool/execution.go | 7 +- cmd/evm/runner.go | 9 +- cmd/geth/snapshot.go | 2 +- cmd/utils/flags.go | 17 ++- cmd/utils/history_test.go | 3 +- core/blockchain.go | 3 + core/blockchain_sethead_test.go | 11 +- core/chain_makers.go | 5 +- core/chain_makers_test.go | 5 +- core/genesis.go | 14 +-- core/genesis_test.go | 17 +-- core/headerchain_test.go | 3 +- core/state/database.go | 3 +- core/state/pruner/pruner.go | 9 +- core/state/snapshot/generate.go | 3 +- core/state/snapshot/generate_test.go | 13 +-- core/state/state_test.go | 10 +- core/state/statedb.go | 6 +- core/state/statedb_fuzz_test.go | 8 +- core/state/statedb_test.go | 24 ++-- core/state/sync_test.go | 41 ++++--- core/types/hashing_test.go | 9 +- eth/api_debug_test.go | 13 ++- eth/downloader/testchain_test.go | 3 +- eth/filters/filter_test.go | 5 +- eth/gasprice/gasprice_test.go | 5 +- eth/protocols/snap/sync_test.go | 12 +- eth/state_accessor.go | 5 +- eth/tracers/api_test.go | 1 + internal/ethapi/api_test.go | 1 + .../utils.go => internal/testrand/rand.go | 28 ++--- miner/miner_test.go | 3 +- tests/block_test_util.go | 17 ++- tests/fuzzers/rangeproof/rangeproof-fuzzer.go | 3 +- tests/state_test_util.go | 13 ++- trie/committer.go | 8 +- trie/database_test.go | 6 +- trie/iterator_test.go | 2 +- trie/stacktrie_test.go | 8 +- trie/sync_test.go | 16 +-- trie/trie.go | 2 +- trie/trie_reader.go | 24 ++-- trie/verkle.go | 2 +- triedb/config.go | 61 +++++++++++ triedb/database.go | 66 +++-------- triedb/database/database.go | 20 ++-- triedb/dbconfig/config.go | 62 +++++++++++ triedb/hashdb/database.go | 75 +++++++------ triedb/pathdb/config.go | 63 +++++++++++ triedb/pathdb/database.go | 64 +++-------- triedb/pathdb/database_test.go | 82 +++++++++----- triedb/pathdb/difflayer.go | 8 +- triedb/pathdb/difflayer_test.go | 18 +-- triedb/pathdb/disklayer.go | 19 +++- triedb/pathdb/history.go | 4 +- triedb/pathdb/history_test.go | 18 +-- triedb/pathdb/journal.go | 4 +- triedb/pathdb/layertree.go | 4 +- triedb/pathdb/{testutils.go => testhasher.go} | 20 ++-- .../state.go => triedb/state/trie.go | 103 ++++-------------- triedb/state/types.go | 59 ++++++++++ 61 files changed, 678 insertions(+), 471 deletions(-) rename trie/testutil/utils.go => internal/testrand/rand.go (61%) create mode 100644 triedb/config.go create mode 100644 triedb/dbconfig/config.go create mode 100644 triedb/pathdb/config.go rename triedb/pathdb/{testutils.go => testhasher.go} (86%) rename trie/triestate/state.go => triedb/state/trie.go (63%) create mode 100644 triedb/state/types.go diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index cb975054c1..c29f0bcfcc 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -36,7 +36,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" - "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) @@ -356,7 +356,10 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, } func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB { - sdb := state.NewDatabaseWithConfig(db, &triedb.Config{Preimages: true}) + config := dbconfig.HashDefaults + config.Preimages = true + + sdb := state.NewDatabaseWithConfig(db, &config) statedb, _ := state.New(types.EmptyRootHash, sdb, nil) for addr, a := range accounts { statedb.SetCode(addr, a.Code) diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index b8e8b542b7..e9a408c357 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -39,7 +39,7 @@ import ( "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/triedb" - "github.com/ethereum/go-ethereum/triedb/hashdb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" "github.com/urfave/cli/v2" ) @@ -148,10 +148,9 @@ func runCmd(ctx *cli.Context) error { } db := rawdb.NewMemoryDatabase() - triedb := triedb.NewDatabase(db, &triedb.Config{ - Preimages: preimages, - HashDB: hashdb.Defaults, - }) + config := dbconfig.HashDefaults + config.Preimages = preimages + triedb := triedb.NewDatabase(db, &config) defer triedb.Close() genesis := genesisConfig.MustCommit(db, triedb) sdb := state.NewDatabaseWithNodeDB(db, triedb) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 192c850868..85379a5a13 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -428,7 +428,7 @@ func traverseRawState(ctx *cli.Context) error { log.Error("Failed to open iterator", "root", root, "err", err) return err } - reader, err := triedb.Reader(root) + reader, err := triedb.NodeReader(root) if err != nil { log.Error("State is non-existent", "root", root) return nil diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index b38f33b8dd..7c3145bebe 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -70,6 +70,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/pathdb" @@ -2193,6 +2194,7 @@ func MakeConsolePreloads(ctx *cli.Context) []string { } // MakeTrieDatabase constructs a trie database based on the configured scheme. +// Don't forget to call db.Close afterwards to prevent memory leak. func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *triedb.Database { config := &triedb.Config{ Preimages: preimage, @@ -2206,13 +2208,20 @@ func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, read // Read-only mode is not implemented in hash mode, // ignore the parameter silently. TODO(rjl493456442) // please config it if read mode is implemented. - config.HashDB = hashdb.Defaults + config.HashDB = &hashdb.Config{ + CleanCacheSize: 16 * 1024 * 1024, + ChildResolver: trie.MerkleResolver, + } return triedb.NewDatabase(disk, config) } + config.PathDB = &pathdb.Config{ + StateHistory: params.FullImmutabilityThreshold, + CleanCacheSize: pathdb.DefaultCleanSize, + DirtyCacheSize: pathdb.DefaultBufferSize, + TrieOpener: trie.NewMerkleOpener, + } if readOnly { - config.PathDB = pathdb.ReadOnly - } else { - config.PathDB = pathdb.Defaults + config.PathDB.ReadOnly = true } return triedb.NewDatabase(disk, config) } diff --git a/cmd/utils/history_test.go b/cmd/utils/history_test.go index 9b7f1797d8..1beffda622 100644 --- a/cmd/utils/history_test.go +++ b/cmd/utils/history_test.go @@ -37,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" ) var ( @@ -171,7 +172,7 @@ func TestHistoryImportAndExport(t *testing.T) { db2.Close() }) - genesis.MustCommit(db2, triedb.NewDatabase(db, triedb.HashDefaults)) + genesis.MustCommit(db2, triedb.NewDatabase(db, &dbconfig.HashDefaults)) imported, err := core.NewBlockChain(db2, nil, genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil) if err != nil { t.Fatalf("unable to initialize chain: %v", err) diff --git a/core/blockchain.go b/core/blockchain.go index 9bd7fdcd95..bf4810be03 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -47,6 +47,7 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/pathdb" @@ -151,6 +152,7 @@ func (c *CacheConfig) triedbConfig() *triedb.Config { if c.StateScheme == rawdb.HashScheme { config.HashDB = &hashdb.Config{ CleanCacheSize: c.TrieCleanLimit * 1024 * 1024, + ChildResolver: trie.MerkleResolver, } } if c.StateScheme == rawdb.PathScheme { @@ -158,6 +160,7 @@ func (c *CacheConfig) triedbConfig() *triedb.Config { StateHistory: c.StateHistory, CleanCacheSize: c.TrieCleanLimit * 1024 * 1024, DirtyCacheSize: c.TrieDirtyLimit * 1024 * 1024, + TrieOpener: trie.NewMerkleOpener, } } return config diff --git a/core/blockchain_sethead_test.go b/core/blockchain_sethead_test.go index b96ee12c99..8fc0a1ddbc 100644 --- a/core/blockchain_sethead_test.go +++ b/core/blockchain_sethead_test.go @@ -35,8 +35,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/triedb" - "github.com/ethereum/go-ethereum/triedb/hashdb" - "github.com/ethereum/go-ethereum/triedb/pathdb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" ) // rewindTest is a test case for chain rollback upon user request. @@ -2033,13 +2032,13 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme } // Reopen the trie database without persisting in-memory dirty nodes. chain.triedb.Close() - dbconfig := &triedb.Config{} + var dbconf triedb.Config if scheme == rawdb.PathScheme { - dbconfig.PathDB = pathdb.Defaults + dbconf = dbconfig.PathDefaults } else { - dbconfig.HashDB = hashdb.Defaults + dbconf = dbconfig.HashDefaults } - chain.triedb = triedb.NewDatabase(chain.db, dbconfig) + chain.triedb = triedb.NewDatabase(chain.db, &dbconf) chain.stateCache = state.NewDatabaseWithNodeDB(chain.db, chain.triedb) // Force run a freeze cycle diff --git a/core/chain_makers.go b/core/chain_makers.go index 733030fd1c..cf3d0374bc 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" "github.com/holiman/uint256" ) @@ -362,7 +363,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse } // Forcibly use hash-based state scheme for retaining all nodes in disk. - triedb := triedb.NewDatabase(db, triedb.HashDefaults) + triedb := triedb.NewDatabase(db, &dbconfig.HashDefaults) defer triedb.Close() for i := 0; i < n; i++ { @@ -407,7 +408,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse // then generate chain on top. func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts) { db := rawdb.NewMemoryDatabase() - triedb := triedb.NewDatabase(db, triedb.HashDefaults) + triedb := triedb.NewDatabase(db, &dbconfig.HashDefaults) defer triedb.Close() _, err := genesis.Commit(db, triedb) if err != nil { diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index b46b898afb..746cbf59c9 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" ) func TestGeneratePOSChain(t *testing.T) { @@ -81,7 +82,7 @@ func TestGeneratePOSChain(t *testing.T) { Storage: storage, Code: common.Hex2Bytes("600154600354"), } - genesis := gspec.MustCommit(gendb, triedb.NewDatabase(gendb, triedb.HashDefaults)) + genesis := gspec.MustCommit(gendb, triedb.NewDatabase(gendb, &dbconfig.HashDefaults)) genchain, genreceipts := GenerateChain(gspec.Config, genesis, beacon.NewFaker(), gendb, 4, func(i int, gen *BlockGen) { gen.SetParentBeaconRoot(common.Hash{byte(i + 1)}) @@ -204,7 +205,7 @@ func ExampleGenerateChain() { Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}}, } - genesis := gspec.MustCommit(genDb, triedb.NewDatabase(genDb, triedb.HashDefaults)) + genesis := gspec.MustCommit(genDb, triedb.NewDatabase(genDb, &dbconfig.HashDefaults)) // This call generates a chain of 5 blocks. The function runs for // each block and adds different features to gen based on the diff --git a/core/genesis.go b/core/genesis.go index 3f1fde8dfc..683dad741a 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -37,7 +37,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" - "github.com/ethereum/go-ethereum/triedb/pathdb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" "github.com/holiman/uint256" ) @@ -117,16 +117,16 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) { // If a genesis-time verkle trie is requested, create a trie config // with the verkle trie enabled so that the tree can be initialized // as such. - var config *triedb.Config + var config triedb.Config if isVerkle { - config = &triedb.Config{ - PathDB: pathdb.Defaults, - IsVerkle: true, - } + config = dbconfig.PathDefaults + config.IsVerkle = true + } else { + config = dbconfig.HashDefaults } // Create an ephemeral in-memory database for computing hash, // all the derived states will be discarded to not pollute disk. - db := state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), config) + db := state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &config) statedb, err := state.New(types.EmptyRootHash, db, nil) if err != nil { return common.Hash{}, err diff --git a/core/genesis_test.go b/core/genesis_test.go index 61be0bd252..3db53815a7 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -32,14 +32,14 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/triedb" - "github.com/ethereum/go-ethereum/triedb/pathdb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" ) func TestInvalidCliqueConfig(t *testing.T) { block := DefaultGoerliGenesisBlock() block.ExtraData = []byte{} db := rawdb.NewMemoryDatabase() - if _, err := block.Commit(db, triedb.NewDatabase(db, nil)); err == nil { + if _, err := block.Commit(db, triedb.NewDatabase(db, &dbconfig.HashDefaults)); err == nil { t.Fatal("Expected error on invalid clique config") } } @@ -189,7 +189,7 @@ func TestGenesisHashes(t *testing.T) { } { // Test via MustCommit db := rawdb.NewMemoryDatabase() - if have := c.genesis.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults)).Hash(); have != c.want { + if have := c.genesis.MustCommit(db, triedb.NewDatabase(db, &dbconfig.HashDefaults)).Hash(); have != c.want { t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex()) } // Test via ToBlock @@ -207,7 +207,7 @@ func TestGenesis_Commit(t *testing.T) { } db := rawdb.NewMemoryDatabase() - genesisBlock := genesis.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults)) + genesisBlock := genesis.MustCommit(db, triedb.NewDatabase(db, &dbconfig.HashDefaults)) if genesis.Difficulty != nil { t.Fatalf("assumption wrong") @@ -259,9 +259,9 @@ func TestReadWriteGenesisAlloc(t *testing.T) { func newDbConfig(scheme string) *triedb.Config { if scheme == rawdb.HashScheme { - return triedb.HashDefaults + return &dbconfig.HashDefaults } - return &triedb.Config{PathDB: pathdb.Defaults} + return &dbconfig.PathDefaults } func TestVerkleGenesisCommit(t *testing.T) { @@ -311,7 +311,10 @@ func TestVerkleGenesisCommit(t *testing.T) { } db := rawdb.NewMemoryDatabase() - triedb := triedb.NewDatabase(db, &triedb.Config{IsVerkle: true, PathDB: pathdb.Defaults}) + + config := dbconfig.PathDefaults + config.IsVerkle = true + triedb := triedb.NewDatabase(db, &config) block := genesis.MustCommit(db, triedb) if !bytes.Equal(block.Root().Bytes(), expected) { t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got) diff --git a/core/headerchain_test.go b/core/headerchain_test.go index 25d9bfffcb..e342287d6c 100644 --- a/core/headerchain_test.go +++ b/core/headerchain_test.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" ) func verifyUnbrokenCanonchain(hc *HeaderChain) error { @@ -73,7 +74,7 @@ func TestHeaderInsertion(t *testing.T) { db = rawdb.NewMemoryDatabase() gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges} ) - gspec.Commit(db, triedb.NewDatabase(db, nil)) + gspec.Commit(db, triedb.NewDatabase(db, &dbconfig.HashDefaults)) hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false }) if err != nil { t.Fatal(err) diff --git a/core/state/database.go b/core/state/database.go index 7520923eef..7758d52590 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/utils" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" ) const ( @@ -145,7 +146,7 @@ type Trie interface { // concurrent use, but does not retain any recent trie nodes in memory. To keep some // historical state in memory, use the NewDatabaseWithConfig constructor. func NewDatabase(db ethdb.Database) Database { - return NewDatabaseWithConfig(db, nil) + return NewDatabaseWithConfig(db, &dbconfig.HashDefaults) } // NewDatabaseWithConfig creates a backing store for state. The returned database diff --git a/core/state/pruner/pruner.go b/core/state/pruner/pruner.go index 59c580daca..740c81c060 100644 --- a/core/state/pruner/pruner.go +++ b/core/state/pruner/pruner.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" ) const ( @@ -87,7 +88,7 @@ func NewPruner(db ethdb.Database, config Config) (*Pruner, error) { return nil, errors.New("failed to load head block") } // Offline pruning is only supported in legacy hash based scheme. - triedb := triedb.NewDatabase(db, triedb.HashDefaults) + triedb := triedb.NewDatabase(db, &dbconfig.HashDefaults) snapconfig := snapshot.Config{ CacheSize: 256, @@ -367,7 +368,7 @@ func RecoverPruning(datadir string, db ethdb.Database) error { AsyncBuild: false, } // Offline pruning is only supported in legacy hash based scheme. - triedb := triedb.NewDatabase(db, triedb.HashDefaults) + triedb := triedb.NewDatabase(db, &dbconfig.HashDefaults) snaptree, err := snapshot.New(snapconfig, db, triedb, headBlock.Root()) if err != nil { return err // The relevant snapshot(s) might not exist @@ -410,7 +411,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error { if genesis == nil { return errors.New("missing genesis block") } - t, err := trie.NewStateTrie(trie.StateTrieID(genesis.Root()), triedb.NewDatabase(db, triedb.HashDefaults)) + t, err := trie.NewStateTrie(trie.StateTrieID(genesis.Root()), triedb.NewDatabase(db, &dbconfig.HashDefaults)) if err != nil { return err } @@ -434,7 +435,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error { } if acc.Root != types.EmptyRootHash { id := trie.StorageTrieID(genesis.Root(), common.BytesToHash(accIter.LeafKey()), acc.Root) - storageTrie, err := trie.NewStateTrie(id, triedb.NewDatabase(db, triedb.HashDefaults)) + storageTrie, err := trie.NewStateTrie(id, triedb.NewDatabase(db, &dbconfig.HashDefaults)) if err != nil { return err } diff --git a/core/state/snapshot/generate.go b/core/state/snapshot/generate.go index 8de4b134d3..a3305f4f81 100644 --- a/core/state/snapshot/generate.go +++ b/core/state/snapshot/generate.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" ) var ( @@ -354,7 +355,7 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi var resolver trie.NodeResolver if len(result.keys) > 0 { mdb := rawdb.NewMemoryDatabase() - tdb := triedb.NewDatabase(mdb, triedb.HashDefaults) + tdb := triedb.NewDatabase(mdb, &dbconfig.HashDefaults) defer tdb.Close() snapTrie := trie.NewEmpty(tdb) for i, key := range result.keys { diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go index da93ebc875..25670e6665 100644 --- a/core/state/snapshot/generate_test.go +++ b/core/state/snapshot/generate_test.go @@ -31,8 +31,7 @@ import ( "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/triedb" - "github.com/ethereum/go-ethereum/triedb/hashdb" - "github.com/ethereum/go-ethereum/triedb/pathdb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) @@ -162,14 +161,14 @@ type testHelper struct { } func newHelper(scheme string) *testHelper { - diskdb := rawdb.NewMemoryDatabase() - config := &triedb.Config{} + var config triedb.Config if scheme == rawdb.PathScheme { - config.PathDB = &pathdb.Config{} // disable caching + config = dbconfig.PathDefaults } else { - config.HashDB = &hashdb.Config{} // disable caching + config = dbconfig.HashDefaults } - triedb := triedb.NewDatabase(diskdb, config) + diskdb := rawdb.NewMemoryDatabase() + triedb := triedb.NewDatabase(diskdb, &config) accTrie, _ := trie.NewStateTrie(trie.StateTrieID(types.EmptyRootHash), triedb) return &testHelper{ diskdb: diskdb, diff --git a/core/state/state_test.go b/core/state/state_test.go index 9be610f962..2cea04b339 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -26,7 +26,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" "github.com/holiman/uint256" ) @@ -43,7 +43,9 @@ func newStateEnv() *stateEnv { func TestDump(t *testing.T) { db := rawdb.NewMemoryDatabase() - tdb := NewDatabaseWithConfig(db, &triedb.Config{Preimages: true}) + config := dbconfig.HashDefaults + config.Preimages = true + tdb := NewDatabaseWithConfig(db, &config) sdb, _ := New(types.EmptyRootHash, tdb, nil) s := &stateEnv{db: db, state: sdb} @@ -100,7 +102,9 @@ func TestDump(t *testing.T) { func TestIterativeDump(t *testing.T) { db := rawdb.NewMemoryDatabase() - tdb := NewDatabaseWithConfig(db, &triedb.Config{Preimages: true}) + config := dbconfig.HashDefaults + config.Preimages = true + tdb := NewDatabaseWithConfig(db, &config) sdb, _ := New(types.EmptyRootHash, tdb, nil) s := &stateEnv{db: db, state: sdb} diff --git a/core/state/statedb.go b/core/state/statedb.go index f90b30f399..07211b87f5 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -31,7 +31,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/triedb/state" "github.com/holiman/uint256" ) @@ -130,7 +130,7 @@ type StateDB struct { StorageDeleted int // Testing hooks - onCommit func(states *triestate.Set) // Hook invoked when commit is performed + onCommit func(states *state.Origin) // Hook invoked when commit is performed } // New creates a new state from a given trie. @@ -1239,7 +1239,7 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er } if root != origin { start = time.Now() - set := triestate.New(s.accountsOrigin, s.storagesOrigin) + set := state.NewOrigin(s.accountsOrigin, s.storagesOrigin) if err := s.db.TrieDB().Update(root, origin, block, nodes, set); err != nil { return common.Hash{}, err } diff --git a/core/state/statedb_fuzz_test.go b/core/state/statedb_fuzz_test.go index b416bcf1f3..a91385cf46 100644 --- a/core/state/statedb_fuzz_test.go +++ b/core/state/statedb_fuzz_test.go @@ -35,9 +35,9 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" - "github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/triedb" - "github.com/ethereum/go-ethereum/triedb/pathdb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" + "github.com/ethereum/go-ethereum/triedb/state" "github.com/holiman/uint256" ) @@ -177,12 +177,12 @@ func (test *stateTest) run() bool { roots []common.Hash accountList []map[common.Address][]byte storageList []map[common.Address]map[common.Hash][]byte - onCommit = func(states *triestate.Set) { + onCommit = func(states *state.Origin) { accountList = append(accountList, copySet(states.Accounts)) storageList = append(storageList, copy2DSet(states.Storages)) } disk = rawdb.NewMemoryDatabase() - tdb = triedb.NewDatabase(disk, &triedb.Config{PathDB: pathdb.Defaults}) + tdb = triedb.NewDatabase(disk, &dbconfig.PathDefaults) sdb = NewDatabaseWithNodeDB(disk, tdb) byzantium = rand.Intn(2) == 0 ) diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 3649b0ac58..e78693e9fc 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -38,8 +38,7 @@ import ( "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/triedb" - "github.com/ethereum/go-ethereum/triedb/hashdb" - "github.com/ethereum/go-ethereum/triedb/pathdb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" "github.com/holiman/uint256" ) @@ -49,7 +48,7 @@ func TestUpdateLeaks(t *testing.T) { // Create an empty state database var ( db = rawdb.NewMemoryDatabase() - tdb = triedb.NewDatabase(db, nil) + tdb = triedb.NewDatabase(db, &dbconfig.HashDefaults) ) state, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(db, tdb), nil) @@ -85,8 +84,8 @@ func TestIntermediateLeaks(t *testing.T) { // Create two state databases, one transitioning to the final state, the other final from the beginning transDb := rawdb.NewMemoryDatabase() finalDb := rawdb.NewMemoryDatabase() - transNdb := triedb.NewDatabase(transDb, nil) - finalNdb := triedb.NewDatabase(finalDb, nil) + transNdb := triedb.NewDatabase(transDb, &dbconfig.HashDefaults) + finalNdb := triedb.NewDatabase(finalDb, &dbconfig.HashDefaults) transState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(transDb, transNdb), nil) finalState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(finalDb, finalNdb), nil) @@ -803,14 +802,9 @@ func testMissingTrieNodes(t *testing.T, scheme string) { memDb = rawdb.NewMemoryDatabase() ) if scheme == rawdb.PathScheme { - tdb = triedb.NewDatabase(memDb, &triedb.Config{PathDB: &pathdb.Config{ - CleanCacheSize: 0, - DirtyCacheSize: 0, - }}) // disable caching + tdb = triedb.NewDatabase(memDb, &dbconfig.PathDefaults) // disable caching } else { - tdb = triedb.NewDatabase(memDb, &triedb.Config{HashDB: &hashdb.Config{ - CleanCacheSize: 0, - }}) // disable caching + tdb = triedb.NewDatabase(memDb, &dbconfig.HashDefaults) // cache is disabled by default } db := NewDatabaseWithNodeDB(memDb, tdb) @@ -1033,7 +1027,7 @@ func TestFlushOrderDataLoss(t *testing.T) { // Create a state trie with many accounts and slots var ( memdb = rawdb.NewMemoryDatabase() - triedb = triedb.NewDatabase(memdb, nil) + triedb = triedb.NewDatabase(memdb, &dbconfig.HashDefaults) statedb = NewDatabaseWithNodeDB(memdb, triedb) state, _ = New(types.EmptyRootHash, statedb, nil) ) @@ -1105,7 +1099,7 @@ func TestStateDBTransientStorage(t *testing.T) { func TestResetObject(t *testing.T) { var ( disk = rawdb.NewMemoryDatabase() - tdb = triedb.NewDatabase(disk, nil) + tdb = triedb.NewDatabase(disk, &dbconfig.HashDefaults) db = NewDatabaseWithNodeDB(disk, tdb) snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash) state, _ = New(types.EmptyRootHash, db, snaps) @@ -1139,7 +1133,7 @@ func TestResetObject(t *testing.T) { func TestDeleteStorage(t *testing.T) { var ( disk = rawdb.NewMemoryDatabase() - tdb = triedb.NewDatabase(disk, nil) + tdb = triedb.NewDatabase(disk, &dbconfig.HashDefaults) db = NewDatabaseWithNodeDB(disk, tdb) snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash) state, _ = New(types.EmptyRootHash, db, snaps) diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 052c166578..46e4a2cef5 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -28,8 +28,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" - "github.com/ethereum/go-ethereum/triedb/hashdb" - "github.com/ethereum/go-ethereum/triedb/pathdb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" "github.com/holiman/uint256" ) @@ -44,14 +43,16 @@ type testAccount struct { // makeTestState create a sample test state to test node-wise reconstruction. func makeTestState(scheme string) (ethdb.Database, Database, *triedb.Database, common.Hash, []*testAccount) { // Create an empty state - config := &triedb.Config{Preimages: true} + var config triedb.Config if scheme == rawdb.PathScheme { - config.PathDB = pathdb.Defaults + config = dbconfig.PathDefaults } else { - config.HashDB = hashdb.Defaults + config = dbconfig.HashDefaults } + config.Preimages = true + db := rawdb.NewMemoryDatabase() - nodeDb := triedb.NewDatabase(db, config) + nodeDb := triedb.NewDatabase(db, &config) sdb := NewDatabaseWithNodeDB(db, nodeDb) state, _ := New(types.EmptyRootHash, sdb, nil) @@ -90,7 +91,9 @@ func makeTestState(scheme string) (ethdb.Database, Database, *triedb.Database, c func checkStateAccounts(t *testing.T, db ethdb.Database, scheme string, root common.Hash, accounts []*testAccount) { var config triedb.Config if scheme == rawdb.PathScheme { - config.PathDB = pathdb.Defaults + config = dbconfig.PathDefaults + } else { + config = dbconfig.HashDefaults } // Check root availability and state contents state, err := New(root, NewDatabaseWithConfig(db, &config), nil) @@ -115,11 +118,15 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, scheme string, root com // checkStateConsistency checks that all data of a state root is present. func checkStateConsistency(db ethdb.Database, scheme string, root common.Hash) error { - config := &triedb.Config{Preimages: true} + var config triedb.Config if scheme == rawdb.PathScheme { - config.PathDB = pathdb.Defaults + config = dbconfig.PathDefaults + } else { + config = dbconfig.HashDefaults } - state, err := New(root, NewDatabaseWithConfig(db, config), nil) + config.Preimages = true + + state, err := New(root, NewDatabaseWithConfig(db, &config), nil) if err != nil { return err } @@ -131,8 +138,8 @@ func checkStateConsistency(db ethdb.Database, scheme string, root common.Hash) e // Tests that an empty state is not scheduled for syncing. func TestEmptyStateSync(t *testing.T) { - dbA := triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil) - dbB := triedb.NewDatabase(rawdb.NewMemoryDatabase(), &triedb.Config{PathDB: pathdb.Defaults}) + dbA := triedb.NewDatabase(rawdb.NewMemoryDatabase(), &dbconfig.HashDefaults) + dbB := triedb.NewDatabase(rawdb.NewMemoryDatabase(), &dbconfig.PathDefaults) sync := NewStateSync(types.EmptyRootHash, rawdb.NewMemoryDatabase(), nil, dbA.Scheme()) if paths, nodes, codes := sync.Missing(1); len(paths) != 0 || len(nodes) != 0 || len(codes) != 0 { @@ -206,7 +213,7 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool, s for i := 0; i < len(codes); i++ { codeElements = append(codeElements, stateElement{code: codes[i]}) } - reader, err := ndb.Reader(srcRoot) + reader, err := ndb.NodeReader(srcRoot) if err != nil { t.Fatalf("state is not existent, %#x", srcRoot) } @@ -325,7 +332,7 @@ func testIterativeDelayedStateSync(t *testing.T, scheme string) { for i := 0; i < len(codes); i++ { codeElements = append(codeElements, stateElement{code: codes[i]}) } - reader, err := ndb.Reader(srcRoot) + reader, err := ndb.NodeReader(srcRoot) if err != nil { t.Fatalf("state is not existent, %#x", srcRoot) } @@ -429,7 +436,7 @@ func testIterativeRandomStateSync(t *testing.T, count int, scheme string) { for _, hash := range codes { codeQueue[hash] = struct{}{} } - reader, err := ndb.Reader(srcRoot) + reader, err := ndb.NodeReader(srcRoot) if err != nil { t.Fatalf("state is not existent, %#x", srcRoot) } @@ -522,7 +529,7 @@ func testIterativeRandomDelayedStateSync(t *testing.T, scheme string) { for _, hash := range codes { codeQueue[hash] = struct{}{} } - reader, err := ndb.Reader(srcRoot) + reader, err := ndb.NodeReader(srcRoot) if err != nil { t.Fatalf("state is not existent, %#x", srcRoot) } @@ -627,7 +634,7 @@ func testIncompleteStateSync(t *testing.T, scheme string) { addedPaths []string addedHashes []common.Hash ) - reader, err := ndb.Reader(srcRoot) + reader, err := ndb.NodeReader(srcRoot) if err != nil { t.Fatalf("state is not available %x", srcRoot) } diff --git a/core/types/hashing_test.go b/core/types/hashing_test.go index a6949414f3..edb41e58e9 100644 --- a/core/types/hashing_test.go +++ b/core/types/hashing_test.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" ) func TestDeriveSha(t *testing.T) { @@ -40,7 +41,7 @@ func TestDeriveSha(t *testing.T) { t.Fatal(err) } for len(txs) < 1000 { - exp := types.DeriveSha(txs, trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil))) + exp := types.DeriveSha(txs, trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), &dbconfig.HashDefaults))) got := types.DeriveSha(txs, trie.NewStackTrie(nil)) if !bytes.Equal(got[:], exp[:]) { t.Fatalf("%d txs: got %x exp %x", len(txs), got, exp) @@ -87,7 +88,7 @@ func BenchmarkDeriveSha200(b *testing.B) { b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { - exp = types.DeriveSha(txs, trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil))) + exp = types.DeriveSha(txs, trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), &dbconfig.HashDefaults))) } }) @@ -108,7 +109,7 @@ func TestFuzzDeriveSha(t *testing.T) { rndSeed := mrand.Int() for i := 0; i < 10; i++ { seed := rndSeed + i - exp := types.DeriveSha(newDummy(i), trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil))) + exp := types.DeriveSha(newDummy(i), trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), &dbconfig.HashDefaults))) got := types.DeriveSha(newDummy(i), trie.NewStackTrie(nil)) if !bytes.Equal(got[:], exp[:]) { printList(newDummy(seed)) @@ -136,7 +137,7 @@ func TestDerivableList(t *testing.T) { }, } for i, tc := range tcs[1:] { - exp := types.DeriveSha(flatList(tc), trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil))) + exp := types.DeriveSha(flatList(tc), trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), &dbconfig.HashDefaults))) got := types.DeriveSha(flatList(tc), trie.NewStackTrie(nil)) if !bytes.Equal(got[:], exp[:]) { t.Fatalf("case %d: got %x exp %x", i, got, exp) diff --git a/eth/api_debug_test.go b/eth/api_debug_test.go index 671e935beb..6d118422f7 100644 --- a/eth/api_debug_test.go +++ b/eth/api_debug_test.go @@ -29,7 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" "github.com/holiman/uint256" "golang.org/x/exp/slices" ) @@ -62,13 +62,15 @@ func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, st func TestAccountRange(t *testing.T) { t.Parallel() + config := dbconfig.HashDefaults + config.Preimages = true + var ( - statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &triedb.Config{Preimages: true}) + statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &config) sdb, _ = state.New(types.EmptyRootHash, statedb, nil) addrs = [AccountRangeMaxResults * 2]common.Address{} m = map[common.Address]bool{} ) - for i := range addrs { hash := common.HexToHash(fmt.Sprintf("%x", i)) addr := common.BytesToAddress(crypto.Keccak256Hash(hash.Bytes()).Bytes()) @@ -158,9 +160,12 @@ func TestEmptyAccountRange(t *testing.T) { func TestStorageRangeAt(t *testing.T) { t.Parallel() + config := dbconfig.HashDefaults + config.Preimages = true + // Create a state where account 0x010000... has a few storage entries. var ( - db = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &triedb.Config{Preimages: true}) + db = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &config) sdb, _ = state.New(types.EmptyRootHash, db, nil) addr = common.Address{0x01} keys = []common.Hash{ // hashes of Keys of storage diff --git a/eth/downloader/testchain_test.go b/eth/downloader/testchain_test.go index 46f3febd8b..67cc80da92 100644 --- a/eth/downloader/testchain_test.go +++ b/eth/downloader/testchain_test.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" ) // Test chain parameters. @@ -44,7 +45,7 @@ var ( Alloc: types.GenesisAlloc{testAddress: {Balance: big.NewInt(1000000000000000)}}, BaseFee: big.NewInt(params.InitialBaseFee), } - testGenesis = testGspec.MustCommit(testDB, triedb.NewDatabase(testDB, triedb.HashDefaults)) + testGenesis = testGspec.MustCommit(testDB, triedb.NewDatabase(testDB, &dbconfig.HashDefaults)) ) // The common prefix of all test chains: diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index 48aaa584db..9fcff03626 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" ) func makeReceipt(addr common.Address) *types.Receipt { @@ -86,7 +87,7 @@ func BenchmarkFilters(b *testing.B) { // The test txs are not properly signed, can't simply create a chain // and then import blocks. TODO(rjl493456442) try to get rid of the // manual database writes. - gspec.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults)) + gspec.MustCommit(db, triedb.NewDatabase(db, &dbconfig.HashDefaults)) for i, block := range chain { rawdb.WriteBlock(db, block) @@ -181,7 +182,7 @@ func TestFilters(t *testing.T) { // Hack: GenerateChainWithGenesis creates a new db. // Commit the genesis manually and use GenerateChain. - _, err = gspec.Commit(db, triedb.NewDatabase(db, nil)) + _, err = gspec.Commit(db, triedb.NewDatabase(db, &dbconfig.HashDefaults)) if err != nil { t.Fatal(err) } diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index 1d2e02cde6..0995b5d1b6 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -166,7 +166,10 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke b.AddTx(types.MustSignNewTx(key, signer, txdata)) }) // Construct testing chain - chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), &core.CacheConfig{TrieCleanNoPrefetch: true}, gspec, nil, engine, vm.Config{}, nil, nil) + chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), &core.CacheConfig{ + TrieCleanNoPrefetch: true, + StateScheme: rawdb.HashScheme, + }, gspec, nil, engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to create local chain, %v", err) } diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go index cea83aa2bc..cd855b3b14 100644 --- a/eth/protocols/snap/sync_test.go +++ b/eth/protocols/snap/sync_test.go @@ -32,13 +32,13 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/internal/testrand" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" - "github.com/ethereum/go-ethereum/trie/testutil" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/triedb" - "github.com/ethereum/go-ethereum/triedb/pathdb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" "github.com/holiman/uint256" "golang.org/x/crypto/sha3" "golang.org/x/exp/slices" @@ -1816,8 +1816,8 @@ func makeUnevenStorageTrie(owner common.Hash, slots int, db *triedb.Database) (c break } for j := 0; j < slots/3; j++ { - key := append([]byte{byte(n)}, testutil.RandBytes(31)...) - val, _ := rlp.EncodeToBytes(testutil.RandBytes(32)) + key := append([]byte{byte(n)}, testrand.Bytes(31)...) + val, _ := rlp.EncodeToBytes(testrand.Bytes(32)) elem := &kv{key, val} tr.MustUpdate(elem.k, elem.v) @@ -1970,7 +1970,7 @@ func TestSlotEstimation(t *testing.T) { func newDbConfig(scheme string) *triedb.Config { if scheme == rawdb.HashScheme { - return &triedb.Config{} + return &dbconfig.HashDefaults } - return &triedb.Config{PathDB: pathdb.Defaults} + return &dbconfig.PathDefaults } diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 526361a2b8..1d618d733d 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" ) // noopReleaser is returned in case there is no operation expected @@ -68,7 +69,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u // the internal junks created by tracing will be persisted into the disk. // TODO(rjl493456442), clean cache is disabled to prevent memory leak, // please re-enable it for better performance. - database = state.NewDatabaseWithConfig(eth.chainDb, triedb.HashDefaults) + database = state.NewDatabaseWithConfig(eth.chainDb, &dbconfig.HashDefaults) if statedb, err = state.New(block.Root(), database, nil); err == nil { log.Info("Found disk backend for state trie", "root", block.Root(), "number", block.Number()) return statedb, noopReleaser, nil @@ -85,7 +86,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u // the internal junks created by tracing will be persisted into the disk. // TODO(rjl493456442), clean cache is disabled to prevent memory leak, // please re-enable it for better performance. - tdb = triedb.NewDatabase(eth.chainDb, triedb.HashDefaults) + tdb = triedb.NewDatabase(eth.chainDb, &dbconfig.HashDefaults) database = state.NewDatabaseWithNodeDB(eth.chainDb, tdb) // If we didn't check the live database, do check state over ephemeral database, diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index d8e4b9a4ef..270d1d02ba 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -78,6 +78,7 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i i TrieDirtyLimit: 256, TrieTimeLimit: 5 * time.Minute, SnapshotLimit: 0, + StateScheme: rawdb.HashScheme, TrieDirtyDisabled: true, // Archive mode } chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, gspec, nil, backend.engine, vm.Config{}, nil, nil) diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 3f69f86144..59b5bdb4c3 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -441,6 +441,7 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, engine consensus.E TrieTimeLimit: 5 * time.Minute, SnapshotLimit: 0, TrieDirtyDisabled: true, // Archive mode + StateScheme: rawdb.HashScheme, } ) accman, acc := newTestAccountManager(t) diff --git a/trie/testutil/utils.go b/internal/testrand/rand.go similarity index 61% rename from trie/testutil/utils.go rename to internal/testrand/rand.go index a75d0431b0..690993de05 100644 --- a/trie/testutil/utils.go +++ b/internal/testrand/rand.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package testutil +package testrand import ( crand "crypto/rand" @@ -22,11 +22,9 @@ import ( mrand "math/rand" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/trie/trienode" ) -// Prng is a pseudo random number generator seeded by strong randomness. +// prng is a pseudo random number generator seeded by strong randomness. // The randomness is printed on startup in order to make failures reproducible. var prng = initRand() @@ -37,25 +35,19 @@ func initRand() *mrand.Rand { return rnd } -// RandBytes generates a random byte slice with specified length. -func RandBytes(n int) []byte { +// Bytes generates a random byte slice with specified length. +func Bytes(n int) []byte { r := make([]byte, n) prng.Read(r) return r } -// RandomHash generates a random blob of data and returns it as a hash. -func RandomHash() common.Hash { - return common.BytesToHash(RandBytes(common.HashLength)) +// Hash generates a random hash. +func Hash() common.Hash { + return common.BytesToHash(Bytes(common.HashLength)) } -// RandomAddress generates a random blob of data and returns it as an address. -func RandomAddress() common.Address { - return common.BytesToAddress(RandBytes(common.AddressLength)) -} - -// RandomNode generates a random node. -func RandomNode() *trienode.Node { - val := RandBytes(100) - return trienode.New(crypto.Keccak256Hash(val), val) +// Address generates a random address. +func Address() common.Address { + return common.BytesToAddress(Bytes(common.AddressLength)) } diff --git a/miner/miner_test.go b/miner/miner_test.go index 7c39564240..9c70710939 100644 --- a/miner/miner_test.go +++ b/miner/miner_test.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" ) type mockBackend struct { @@ -143,7 +144,7 @@ func createMiner(t *testing.T) *Miner { } // Create chainConfig chainDB := rawdb.NewMemoryDatabase() - triedb := triedb.NewDatabase(chainDB, nil) + triedb := triedb.NewDatabase(chainDB, &dbconfig.HashDefaults) genesis := minerTestGenesisBlock(15, 11_500_000, common.HexToAddress("12345")) chainConfig, _, err := core.SetupGenesisBlock(chainDB, triedb, genesis) if err != nil { diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 53d733f1c4..bfc4b8df63 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -40,8 +40,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/triedb" - "github.com/ethereum/go-ethereum/triedb/hashdb" - "github.com/ethereum/go-ethereum/triedb/pathdb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" ) // A BlockTest checks handling of entire blocks. @@ -116,19 +115,19 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, tracer vm.EVMLogger, po } // import pre accounts & construct test genesis block & state root var ( - db = rawdb.NewMemoryDatabase() - tconf = &triedb.Config{ - Preimages: true, - } + db = rawdb.NewMemoryDatabase() + dbconf triedb.Config ) if scheme == rawdb.PathScheme { - tconf.PathDB = pathdb.Defaults + dbconf = dbconfig.PathDefaults } else { - tconf.HashDB = hashdb.Defaults + dbconf = dbconfig.HashDefaults } + dbconf.Preimages = true + // Commit genesis state gspec := t.genesis(config) - triedb := triedb.NewDatabase(db, tconf) + triedb := triedb.NewDatabase(db, &dbconf) gblock, err := gspec.Commit(db, triedb) if err != nil { return err diff --git a/tests/fuzzers/rangeproof/rangeproof-fuzzer.go b/tests/fuzzers/rangeproof/rangeproof-fuzzer.go index dcafebb265..b8c6f86b1a 100644 --- a/tests/fuzzers/rangeproof/rangeproof-fuzzer.go +++ b/tests/fuzzers/rangeproof/rangeproof-fuzzer.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" "golang.org/x/exp/slices" ) @@ -57,7 +58,7 @@ func (f *fuzzer) readInt() uint64 { } func (f *fuzzer) randomTrie(n int) (*trie.Trie, map[string]*kv) { - trie := trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), &dbconfig.HashDefaults)) vals := make(map[string]*kv) size := f.readInt() // Fill it with some fluff diff --git a/tests/state_test_util.go b/tests/state_test_util.go index c916d26d41..4f8b71369b 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -40,8 +40,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/triedb" - "github.com/ethereum/go-ethereum/triedb/hashdb" - "github.com/ethereum/go-ethereum/triedb/pathdb" + "github.com/ethereum/go-ethereum/triedb/dbconfig" "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) @@ -444,13 +443,15 @@ type StateTestState struct { // MakePreState creates a state containing the given allocation. func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, snapshotter bool, scheme string) StateTestState { - tconf := &triedb.Config{Preimages: true} + var dbconf triedb.Config if scheme == rawdb.HashScheme { - tconf.HashDB = hashdb.Defaults + dbconf = dbconfig.HashDefaults } else { - tconf.PathDB = pathdb.Defaults + dbconf = dbconfig.PathDefaults } - triedb := triedb.NewDatabase(db, tconf) + dbconf.Preimages = true + + triedb := triedb.NewDatabase(db, &dbconf) sdb := state.NewDatabaseWithNodeDB(db, triedb) statedb, _ := state.New(types.EmptyRootHash, sdb, nil) for addr, a := range accounts { diff --git a/trie/committer.go b/trie/committer.go index 4e2f7b8bd6..3b252bf7bb 100644 --- a/trie/committer.go +++ b/trie/committer.go @@ -154,12 +154,8 @@ func (c *committer) store(path []byte, n node) node { return hash } -// MerkleResolver the children resolver in merkle-patricia-tree. -type MerkleResolver struct{} - -// ForEach implements childResolver, decodes the provided node and -// traverses the children inside. -func (resolver MerkleResolver) ForEach(node []byte, onChild func(common.Hash)) { +// MerkleResolver decodes the provided node and traverses the children inside. +func MerkleResolver(node []byte, onChild func(common.Hash)) { forGatherChildren(mustDecodeNodeUnsafe(nil, node), onChild) } diff --git a/trie/database_test.go b/trie/database_test.go index aed508b368..837c209fdb 100644 --- a/trie/database_test.go +++ b/trie/database_test.go @@ -25,7 +25,7 @@ import ( "github.com/ethereum/go-ethereum/triedb/database" ) -// testReader implements database.Reader interface, providing function to +// testReader implements database.NodeReader interface, providing function to // access trie nodes. type testReader struct { db ethdb.Database @@ -33,7 +33,7 @@ type testReader struct { nodes []*trienode.MergedNodeSet // sorted from new to old } -// Node implements database.Reader interface, retrieving trie node with +// Node implements database.NodeReader interface, retrieving trie node with // all available cached layers. func (r *testReader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) { // Check the node presence with the cached layer, from latest to oldest. @@ -73,7 +73,7 @@ func newTestDatabase(diskdb ethdb.Database, scheme string) *testDb { } } -func (db *testDb) Reader(stateRoot common.Hash) (database.Reader, error) { +func (db *testDb) NodeReader(stateRoot common.Hash) (database.NodeReader, error) { nodes, _ := db.dirties(stateRoot, true) return &testReader{db: db.disk, scheme: db.scheme, nodes: nodes}, nil } diff --git a/trie/iterator_test.go b/trie/iterator_test.go index 41e83f6cb6..cced13a68a 100644 --- a/trie/iterator_test.go +++ b/trie/iterator_test.go @@ -146,7 +146,7 @@ func testNodeIteratorCoverage(t *testing.T, scheme string) { } } // Cross check the hashes and the database itself - reader, err := nodeDb.Reader(trie.Hash()) + reader, err := nodeDb.NodeReader(trie.Hash()) if err != nil { t.Fatalf("state is not available %x", trie.Hash()) } diff --git a/trie/stacktrie_test.go b/trie/stacktrie_test.go index 3a0e1cb260..203ebd99a9 100644 --- a/trie/stacktrie_test.go +++ b/trie/stacktrie_test.go @@ -25,7 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/trie/testutil" + "github.com/ethereum/go-ethereum/internal/testrand" "github.com/stretchr/testify/assert" "golang.org/x/exp/slices" ) @@ -431,12 +431,12 @@ func TestPartialStackTrie(t *testing.T) { for i := 0; i < n; i++ { var val []byte if rand.Intn(3) == 0 { - val = testutil.RandBytes(3) + val = testrand.Bytes(3) } else { - val = testutil.RandBytes(32) + val = testrand.Bytes(32) } entries = append(entries, &kv{ - k: testutil.RandBytes(32), + k: testrand.Bytes(32), v: val, }) } diff --git a/trie/sync_test.go b/trie/sync_test.go index 7bc68c041f..e0a31fca6f 100644 --- a/trie/sync_test.go +++ b/trie/sync_test.go @@ -182,7 +182,7 @@ func testIterativeSync(t *testing.T, count int, bypath bool, scheme string) { syncPath: NewSyncPath([]byte(paths[i])), }) } - reader, err := srcDb.Reader(srcTrie.Hash()) + reader, err := srcDb.NodeReader(srcTrie.Hash()) if err != nil { t.Fatalf("State is not available %x", srcTrie.Hash()) } @@ -257,7 +257,7 @@ func testIterativeDelayedSync(t *testing.T, scheme string) { syncPath: NewSyncPath([]byte(paths[i])), }) } - reader, err := srcDb.Reader(srcTrie.Hash()) + reader, err := srcDb.NodeReader(srcTrie.Hash()) if err != nil { t.Fatalf("State is not available %x", srcTrie.Hash()) } @@ -326,7 +326,7 @@ func testIterativeRandomSync(t *testing.T, count int, scheme string) { syncPath: NewSyncPath([]byte(paths[i])), } } - reader, err := srcDb.Reader(srcTrie.Hash()) + reader, err := srcDb.NodeReader(srcTrie.Hash()) if err != nil { t.Fatalf("State is not available %x", srcTrie.Hash()) } @@ -393,7 +393,7 @@ func testIterativeRandomDelayedSync(t *testing.T, scheme string) { syncPath: NewSyncPath([]byte(path)), } } - reader, err := srcDb.Reader(srcTrie.Hash()) + reader, err := srcDb.NodeReader(srcTrie.Hash()) if err != nil { t.Fatalf("State is not available %x", srcTrie.Hash()) } @@ -465,7 +465,7 @@ func testDuplicateAvoidanceSync(t *testing.T, scheme string) { syncPath: NewSyncPath([]byte(paths[i])), }) } - reader, err := srcDb.Reader(srcTrie.Hash()) + reader, err := srcDb.NodeReader(srcTrie.Hash()) if err != nil { t.Fatalf("State is not available %x", srcTrie.Hash()) } @@ -541,7 +541,7 @@ func testIncompleteSync(t *testing.T, scheme string) { syncPath: NewSyncPath([]byte(paths[i])), }) } - reader, err := srcDb.Reader(srcTrie.Hash()) + reader, err := srcDb.NodeReader(srcTrie.Hash()) if err != nil { t.Fatalf("State is not available %x", srcTrie.Hash()) } @@ -633,7 +633,7 @@ func testSyncOrdering(t *testing.T, scheme string) { }) reqs = append(reqs, NewSyncPath([]byte(paths[i]))) } - reader, err := srcDb.Reader(srcTrie.Hash()) + reader, err := srcDb.NodeReader(srcTrie.Hash()) if err != nil { t.Fatalf("State is not available %x", srcTrie.Hash()) } @@ -703,7 +703,7 @@ func syncWithHookWriter(t *testing.T, root common.Hash, db ethdb.Database, srcDb syncPath: NewSyncPath([]byte(paths[i])), }) } - reader, err := srcDb.Reader(root) + reader, err := srcDb.NodeReader(root) if err != nil { t.Fatalf("State is not available %x", root) } diff --git a/trie/trie.go b/trie/trie.go index 12764e18d1..f61bc85909 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -80,7 +80,7 @@ func (t *Trie) Copy() *Trie { // zero hash or the sha3 hash of an empty string, then trie is initially // empty, otherwise, the root node must be present in database or returns // a MissingNodeError if not. -func New(id *ID, db database.Database) (*Trie, error) { +func New(id *ID, db database.NodeDatabase) (*Trie, error) { reader, err := newTrieReader(id.StateRoot, id.Owner, db) if err != nil { return nil, err diff --git a/trie/trie_reader.go b/trie/trie_reader.go index 42bc4316fe..993e85d997 100644 --- a/trie/trie_reader.go +++ b/trie/trie_reader.go @@ -20,27 +20,27 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/triedb/database" + "github.com/ethereum/go-ethereum/triedb/state" ) // trieReader is a wrapper of the underlying node reader. It's not safe // for concurrent usage. type trieReader struct { owner common.Hash - reader database.Reader + reader database.NodeReader banned map[string]struct{} // Marker to prevent node from being accessed, for tests } // newTrieReader initializes the trie reader with the given node reader. -func newTrieReader(stateRoot, owner common.Hash, db database.Database) (*trieReader, error) { +func newTrieReader(stateRoot, owner common.Hash, db database.NodeDatabase) (*trieReader, error) { if stateRoot == (common.Hash{}) || stateRoot == types.EmptyRootHash { if stateRoot == (common.Hash{}) { log.Error("Zero state root hash!") } return &trieReader{owner: owner}, nil } - reader, err := db.Reader(stateRoot) + reader, err := db.NodeReader(stateRoot) if err != nil { return nil, &MissingNodeError{Owner: owner, NodeHash: stateRoot, err: err} } @@ -73,22 +73,22 @@ func (r *trieReader) node(path []byte, hash common.Hash) ([]byte, error) { return blob, nil } -// MerkleLoader implements triestate.TrieLoader for constructing tries. -type MerkleLoader struct { - db database.Database +// MerkleOpener implements state.TrieOpener for constructing tries. +type MerkleOpener struct { + db database.NodeDatabase } -// NewMerkleLoader creates the merkle trie loader. -func NewMerkleLoader(db database.Database) *MerkleLoader { - return &MerkleLoader{db: db} +// NewMerkleOpener creates the merkle trie opener. +func NewMerkleOpener(db database.NodeDatabase) state.TrieOpener { + return &MerkleOpener{db: db} } // OpenTrie opens the main account trie. -func (l *MerkleLoader) OpenTrie(root common.Hash) (triestate.Trie, error) { +func (l *MerkleOpener) OpenTrie(root common.Hash) (state.Trie, error) { return New(TrieID(root), l.db) } // OpenStorageTrie opens the storage trie of an account. -func (l *MerkleLoader) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (triestate.Trie, error) { +func (l *MerkleOpener) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (state.Trie, error) { return New(StorageTrieID(stateRoot, addrHash, root), l.db) } diff --git a/trie/verkle.go b/trie/verkle.go index 01d813d9ec..239e4e5e36 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -45,7 +45,7 @@ type VerkleTrie struct { } // NewVerkleTrie constructs a verkle tree based on the specified root hash. -func NewVerkleTrie(root common.Hash, db database.Database, cache *utils.PointCache) (*VerkleTrie, error) { +func NewVerkleTrie(root common.Hash, db database.NodeDatabase, cache *utils.PointCache) (*VerkleTrie, error) { reader, err := newTrieReader(root, common.Hash{}, db) if err != nil { return nil, err diff --git a/triedb/config.go b/triedb/config.go new file mode 100644 index 0000000000..ea664e8923 --- /dev/null +++ b/triedb/config.go @@ -0,0 +1,61 @@ +// Copyright 2024 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 triedb + +import ( + "errors" + + "github.com/ethereum/go-ethereum/triedb/hashdb" + "github.com/ethereum/go-ethereum/triedb/pathdb" +) + +// Config defines all options for configuring database. +type Config struct { + Preimages bool // Flag whether the preimage of node key is recorded + IsVerkle bool // Flag whether the db is holding a verkle tree + HashDB *hashdb.Config // Configs for hash-based scheme + PathDB *pathdb.Config // Configs for experimental path-based scheme +} + +// sanitize validates the provided config. +func (config *Config) sanitize() error { + if config == nil { + return errors.New("config is nil") + } + if config.HashDB != nil && config.PathDB != nil { + return errors.New("both 'hash' and 'path' mode are configured") + } + if config.HashDB == nil && config.PathDB == nil { + return errors.New("neither 'hash' nor 'path' mode is configured") + } + return nil +} + +// Copy returns a deep copied config object. +func (config *Config) Copy() *Config { + cpy := &Config{ + Preimages: config.Preimages, + IsVerkle: config.IsVerkle, + } + if config.HashDB != nil { + cpy.HashDB = config.HashDB.Copy() + } + if config.PathDB != nil { + cpy.PathDB = config.PathDB.Copy() + } + return cpy +} diff --git a/triedb/database.go b/triedb/database.go index 939a21f147..f53ef6d9cb 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -22,29 +22,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/triedb/database" "github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/pathdb" + "github.com/ethereum/go-ethereum/triedb/state" ) -// Config defines all necessary options for database. -type Config struct { - Preimages bool // Flag whether the preimage of node key is recorded - IsVerkle bool // Flag whether the db is holding a verkle tree - HashDB *hashdb.Config // Configs for hash-based scheme - PathDB *pathdb.Config // Configs for experimental path-based scheme -} - -// HashDefaults represents a config for using hash-based scheme with -// default settings. -var HashDefaults = &Config{ - Preimages: false, - HashDB: hashdb.Defaults, -} - // backend defines the methods needed to access/update trie nodes in different // state scheme. type backend interface { @@ -62,13 +46,17 @@ type backend interface { // and dirty disk layer nodes, so both are merged into the second return. Size() (common.StorageSize, common.StorageSize) + // NodeReader returns a node reader associated with the specific state. + // An error will be returned if the specified state is not available. + NodeReader(root common.Hash) (database.NodeReader, error) + // Update performs a state transition by committing dirty nodes contained // in the given set in order to update state from the specified parent to // the specified root. // // The passed in maps(nodes, states) will be retained to avoid copying // everything. Therefore, these maps must not be changed afterwards. - Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error + Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *state.Origin) error // Commit writes all relevant trie nodes belonging to the specified state // to disk. Report specifies whether logs will be displayed in info level. @@ -91,9 +79,8 @@ type Database struct { // NewDatabase initializes the trie database with default settings, note // the legacy hash-based scheme is used by default. func NewDatabase(diskdb ethdb.Database, config *Config) *Database { - // Sanitize the config and use the default one if it's not specified. - if config == nil { - config = HashDefaults + if err := config.sanitize(); err != nil { + log.Crit("Database config is invalid", "error", err) } var preimages *preimageStore if config.Preimages { @@ -104,34 +91,18 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database { diskdb: diskdb, preimages: preimages, } - if config.HashDB != nil && config.PathDB != nil { - log.Crit("Both 'hash' and 'path' mode are configured") - } if config.PathDB != nil { db.backend = pathdb.New(diskdb, config.PathDB) } else { - var resolver hashdb.ChildResolver - if config.IsVerkle { - // TODO define verkle resolver - log.Crit("Verkle node resolver is not defined") - } else { - resolver = trie.MerkleResolver{} - } - db.backend = hashdb.New(diskdb, config.HashDB, resolver) + db.backend = hashdb.New(diskdb, config.HashDB) } return db } -// Reader returns a reader for accessing all trie nodes with provided state root. -// An error will be returned if the requested state is not available. -func (db *Database) Reader(blockRoot common.Hash) (database.Reader, error) { - switch b := db.backend.(type) { - case *hashdb.Database: - return b.Reader(blockRoot) - case *pathdb.Database: - return b.Reader(blockRoot) - } - return nil, errors.New("unknown backend") +// NodeReader returns a reader for accessing all trie nodes with provided state +// root. An error will be returned if the requested state is not available. +func (db *Database) NodeReader(blockRoot common.Hash) (database.NodeReader, error) { + return db.backend.NodeReader(blockRoot) } // Update performs a state transition by committing dirty nodes contained in the @@ -141,7 +112,7 @@ func (db *Database) Reader(blockRoot common.Hash) (database.Reader, error) { // // The passed in maps(nodes, states) will be retained to avoid copying everything. // Therefore, these maps must not be changed afterwards. -func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error { +func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *state.Origin) error { if db.preimages != nil { db.preimages.commit(false) } @@ -265,14 +236,7 @@ func (db *Database) Recover(target common.Hash) error { if !ok { return errors.New("not supported") } - var loader triestate.TrieLoader - if db.config.IsVerkle { - // TODO define verkle loader - log.Crit("Verkle loader is not defined") - } else { - loader = trie.NewMerkleLoader(db) - } - return pdb.Recover(target, loader) + return pdb.Recover(target) } // Recoverable returns the indicator if the specified state is enabled to be diff --git a/triedb/database/database.go b/triedb/database/database.go index 18a8f454e2..f1a7e6523e 100644 --- a/triedb/database/database.go +++ b/triedb/database/database.go @@ -16,18 +16,23 @@ package database -import ( - "github.com/ethereum/go-ethereum/common" -) +import "github.com/ethereum/go-ethereum/common" -// Reader wraps the Node method of a backing trie reader. -type Reader interface { +// NodeReader wraps the Node method of a backing trie reader. +type NodeReader interface { // Node retrieves the trie node blob with the provided trie identifier, // node path and the corresponding node hash. No error will be returned // if the node is not found. Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) } +// NodeDatabase warps the methods of a backing trie store. +type NodeDatabase interface { + // NodeReader returns a node reader associated with the specific state. + // An error will be returned if the specified state is not available. + NodeReader(stateRoot common.Hash) (NodeReader, error) +} + // PreimageStore wraps the methods of a backing store for reading and writing // trie node preimages. type PreimageStore interface { @@ -41,8 +46,5 @@ type PreimageStore interface { // Database wraps the methods of a backing trie store. type Database interface { PreimageStore - - // Reader returns a node reader associated with the specific state. - // An error will be returned if the specified state is not available. - Reader(stateRoot common.Hash) (Reader, error) + NodeDatabase } diff --git a/triedb/dbconfig/config.go b/triedb/dbconfig/config.go new file mode 100644 index 0000000000..13e5bae8f5 --- /dev/null +++ b/triedb/dbconfig/config.go @@ -0,0 +1,62 @@ +// Copyright 2024 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 dbconfig + +import ( + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie" + "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/hashdb" + "github.com/ethereum/go-ethereum/triedb/pathdb" +) + +// HashDefaults represents a configuration for using a hash-based scheme with +// default settings. The default configuration is assumed immutable, so please +// deep-copy the configuration if any mutation is expected. +var HashDefaults = triedb.Config{ + Preimages: false, + IsVerkle: false, + HashDB: &hashdb.Config{ + // Explicitly set clean cache size to zero as default to avoid + // creating fastcache, otherwise database must be closed when + // it's no longer needed to prevent memory leak. + CleanCacheSize: 0, + + // Merkle trie resolver is used as the default node resolver. + ChildResolver: trie.MerkleResolver, + }, +} + +// PathDefaults represents a configuration for using a path-based scheme with +// default settings. The default configuration is assumed immutable, so please +// deep-copy the configuration if any mutation is expected. +var PathDefaults = triedb.Config{ + Preimages: false, + IsVerkle: false, + PathDB: &pathdb.Config{ + StateHistory: params.FullImmutabilityThreshold, + + // Explicitly set clean cache size to zero as default to avoid + // creating fastcache, otherwise database must be closed when + // it's no longer needed to prevent memory leak. + CleanCacheSize: 0, + DirtyCacheSize: pathdb.DefaultBufferSize, + + // Merkle trie loader is used as the default trie loader. + TrieOpener: trie.NewMerkleOpener, + }, +} diff --git a/triedb/hashdb/database.go b/triedb/hashdb/database.go index e45ccdba32..f437159818 100644 --- a/triedb/hashdb/database.go +++ b/triedb/hashdb/database.go @@ -32,7 +32,8 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/triedb/database" + "github.com/ethereum/go-ethereum/triedb/state" ) var ( @@ -59,32 +60,42 @@ var ( memcacheCommitBytesMeter = metrics.NewRegisteredMeter("hashdb/memcache/commit/bytes", nil) ) -// ChildResolver defines the required method to decode the provided -// trie node and iterate the children on top. -type ChildResolver interface { - ForEach(node []byte, onChild func(common.Hash)) -} - // Config contains the settings for database. type Config struct { - CleanCacheSize int // Maximum memory allowance (in bytes) for caching clean nodes + // CleanCacheSize specifies the maximum memory allowance (in bytes) + // for caching clean nodes. + CleanCacheSize int + + // ChildResolver defines the method to decode the provided trie node + // and iterate the hash children on top. + ChildResolver func(node []byte, onChild func(common.Hash)) } -// Defaults is the default setting for database if it's not specified. -// Notably, clean cache is disabled explicitly, -var Defaults = &Config{ - // Explicitly set clean cache size to 0 to avoid creating fastcache, - // otherwise database must be closed when it's no longer needed to - // prevent memory leak. - CleanCacheSize: 0, +// sanitize validates the provided config. +func (config *Config) sanitize() error { + if config == nil { + return errors.New("hashdb config is nil") + } + if config.ChildResolver == nil { + return errors.New("node resolver is not configured") + } + return nil +} + +// Copy returns a deep copied config object. +func (config *Config) Copy() *Config { + return &Config{ + CleanCacheSize: config.CleanCacheSize, + ChildResolver: config.ChildResolver, + } } // Database is an intermediate write layer between the trie data structures and // the disk database. The aim is to accumulate trie writes in-memory and only // periodically flush a couple tries to disk, garbage collecting the remainder. type Database struct { - diskdb ethdb.Database // Persistent storage for matured trie nodes - resolver ChildResolver // The handler to resolve children of nodes + diskdb ethdb.Database // Persistent storage for matured trie nodes + config *Config // configuration used in hash db cleans *fastcache.Cache // GC friendly memory cache of clean node RLPs dirties map[common.Hash]*cachedNode // Data and references relationships of dirty trie nodes @@ -123,27 +134,27 @@ var cachedNodeSize = int(reflect.TypeOf(cachedNode{}).Size()) // forChildren invokes the callback for all the tracked children of this node, // both the implicit ones from inside the node as well as the explicit ones // from outside the node. -func (n *cachedNode) forChildren(resolver ChildResolver, onChild func(hash common.Hash)) { +func (n *cachedNode) forChildren(resolver func(node []byte, onChild func(common.Hash)), onChild func(hash common.Hash)) { for child := range n.external { onChild(child) } - resolver.ForEach(n.node, onChild) + resolver(n.node, onChild) } // New initializes the hash-based node database. -func New(diskdb ethdb.Database, config *Config, resolver ChildResolver) *Database { - if config == nil { - config = Defaults +func New(diskdb ethdb.Database, config *Config) *Database { + if err := config.sanitize(); err != nil { + log.Crit("Hash database config is invalid", "error", err) } var cleans *fastcache.Cache if config.CleanCacheSize > 0 { cleans = fastcache.New(config.CleanCacheSize) } return &Database{ - diskdb: diskdb, - resolver: resolver, - cleans: cleans, - dirties: make(map[common.Hash]*cachedNode), + diskdb: diskdb, + config: config, + cleans: cleans, + dirties: make(map[common.Hash]*cachedNode), } } @@ -162,7 +173,7 @@ func (db *Database) insert(hash common.Hash, node []byte) { node: node, flushPrev: db.newest, } - entry.forChildren(db.resolver, func(child common.Hash) { + entry.forChildren(db.config.ChildResolver, func(child common.Hash) { if c := db.dirties[child]; c != nil { c.parents++ } @@ -315,7 +326,7 @@ func (db *Database) dereference(hash common.Hash) { db.dirties[node.flushNext].flushPrev = node.flushPrev } // Dereference all children and delete the node - node.forChildren(db.resolver, func(child common.Hash) { + node.forChildren(db.config.ChildResolver, func(child common.Hash) { db.dereference(child) }) delete(db.dirties, hash) @@ -464,7 +475,7 @@ func (db *Database) commit(hash common.Hash, batch ethdb.Batch, uncacher *cleane var err error // Dereference all children and delete the node - node.forChildren(db.resolver, func(child common.Hash) { + node.forChildren(db.config.ChildResolver, func(child common.Hash) { if err == nil { err = db.commit(child, batch, uncacher) } @@ -548,7 +559,7 @@ func (db *Database) Initialized(genesisRoot common.Hash) bool { // Update inserts the dirty nodes in provided nodeset into database and link the // account trie with multiple storage tries if necessary. -func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error { +func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *state.Origin) error { // Ensure the parent state is present and signal a warning if not. if parent != types.EmptyRootHash { if blob, _ := db.node(parent); len(blob) == 0 { @@ -629,9 +640,9 @@ func (db *Database) Scheme() string { return rawdb.HashScheme } -// Reader retrieves a node reader belonging to the given state root. +// NodeReader retrieves a node reader belonging to the given state root. // An error will be returned if the requested state is not available. -func (db *Database) Reader(root common.Hash) (*reader, error) { +func (db *Database) NodeReader(root common.Hash) (database.NodeReader, error) { if _, err := db.node(root); err != nil { return nil, fmt.Errorf("state %#x is not available, %v", root, err) } diff --git a/triedb/pathdb/config.go b/triedb/pathdb/config.go new file mode 100644 index 0000000000..dc4e811f01 --- /dev/null +++ b/triedb/pathdb/config.go @@ -0,0 +1,63 @@ +// Copyright 2024 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 pathdb + +import ( + "errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/triedb/database" + "github.com/ethereum/go-ethereum/triedb/state" +) + +// Config contains the settings for database. +type Config struct { + StateHistory uint64 // Number of recent blocks to maintain state history for + CleanCacheSize int // Maximum memory allowance (in bytes) for caching clean nodes + DirtyCacheSize int // Maximum memory allowance (in bytes) for caching dirty nodes + ReadOnly bool // Flag whether the database is opened in read only mode. + TrieOpener func(db database.NodeDatabase) state.TrieOpener // Function to create trie loader for trie state transition +} + +// sanitize checks the provided user configurations and changes anything that's +// unreasonable or unworkable. +func (c *Config) sanitize() (*Config, error) { + if c == nil { + return nil, errors.New("pathdb config is nil") + } + if c.TrieOpener == nil { + return nil, errors.New("trie opener is not configured") + } + conf := *c + if conf.DirtyCacheSize > maxBufferSize { + log.Warn("Sanitizing invalid node buffer size", "provided", common.StorageSize(conf.DirtyCacheSize), "updated", common.StorageSize(maxBufferSize)) + conf.DirtyCacheSize = maxBufferSize + } + return &conf, nil +} + +// Copy returns a deep copied config object. +func (c *Config) Copy() *Config { + return &Config{ + StateHistory: c.StateHistory, + CleanCacheSize: c.CleanCacheSize, + DirtyCacheSize: c.DirtyCacheSize, + ReadOnly: c.ReadOnly, + TrieOpener: c.TrieOpener, + } +} diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index b1e01abac4..c3d1b7e710 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -28,17 +28,14 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/triedb/database" + "github.com/ethereum/go-ethereum/triedb/state" ) const ( - // maxDiffLayers is the maximum diff layers allowed in the layer tree. - maxDiffLayers = 128 - - // defaultCleanSize is the default memory allowance of clean cache. - defaultCleanSize = 16 * 1024 * 1024 + // DefaultCleanSize is the default memory allowance of clean cache. + DefaultCleanSize = 16 * 1024 * 1024 // maxBufferSize is the maximum memory allowance of node buffer. // Too large nodebuffer will cause the system to pause for a long @@ -54,6 +51,9 @@ const ( DefaultBufferSize = 64 * 1024 * 1024 ) +// maxDiffLayers is the maximum diff layers allowed in the layer tree. +var maxDiffLayers = 128 + // layer is the interface implemented by all state layers which includes some // public methods and some additional methods for internal usage. type layer interface { @@ -76,7 +76,7 @@ type layer interface { // the provided dirty trie nodes along with the state change set. // // Note, the maps are retained by the method to avoid copying everything. - update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer + update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *state.Origin) *diffLayer // journal commits an entire diff hierarchy to disk into a single journal entry. // This is meant to be used during shutdown to persist the layer without @@ -84,35 +84,6 @@ type layer interface { journal(w io.Writer) error } -// Config contains the settings for database. -type Config struct { - StateHistory uint64 // Number of recent blocks to maintain state history for - CleanCacheSize int // Maximum memory allowance (in bytes) for caching clean nodes - DirtyCacheSize int // Maximum memory allowance (in bytes) for caching dirty nodes - ReadOnly bool // Flag whether the database is opened in read only mode. -} - -// sanitize checks the provided user configurations and changes anything that's -// unreasonable or unworkable. -func (c *Config) sanitize() *Config { - conf := *c - if conf.DirtyCacheSize > maxBufferSize { - log.Warn("Sanitizing invalid node buffer size", "provided", common.StorageSize(conf.DirtyCacheSize), "updated", common.StorageSize(maxBufferSize)) - conf.DirtyCacheSize = maxBufferSize - } - return &conf -} - -// Defaults contains default settings for Ethereum mainnet. -var Defaults = &Config{ - StateHistory: params.FullImmutabilityThreshold, - CleanCacheSize: defaultCleanSize, - DirtyCacheSize: DefaultBufferSize, -} - -// ReadOnly is the config in order to open database in read only mode. -var ReadOnly = &Config{ReadOnly: true} - // Database is a multiple-layered structure for maintaining in-memory trie nodes. // It consists of one persistent base layer backed by a key-value store, on top // of which arbitrarily many in-memory diff layers are stacked. The memory diffs @@ -135,6 +106,7 @@ type Database struct { diskdb ethdb.Database // Persistent storage for matured trie nodes tree *layerTree // The group for all known layers freezer *rawdb.ResettableFreezer // Freezer for storing trie histories, nil possible in tests + trieOpener state.TrieOpener // Trie loader to open trie for state recovering lock sync.RWMutex // Lock to prevent mutations from happening at the same time } @@ -142,11 +114,10 @@ type Database struct { // store (with a number of memory layers from a journal). If the journal is not // matched with the base persistent layer, all the recorded diff layers are discarded. func New(diskdb ethdb.Database, config *Config) *Database { - if config == nil { - config = Defaults + config, err := config.sanitize() + if err != nil { + log.Crit("Path database config is invalid", "err", err) } - config = config.sanitize() - db := &Database{ readOnly: config.ReadOnly, bufferSize: config.DirtyCacheSize, @@ -156,6 +127,7 @@ func New(diskdb ethdb.Database, config *Config) *Database { // Construct the layer tree by resolving the in-disk singleton state // and in-memory layer journal. db.tree = newLayerTree(db.loadLayers()) + db.trieOpener = db.config.TrieOpener(db) // Open the freezer for state history if the passed database contains an // ancient store. Otherwise, all the relevant functionalities are disabled. @@ -206,8 +178,8 @@ func New(diskdb ethdb.Database, config *Config) *Database { return db } -// Reader retrieves a layer belonging to the given state root. -func (db *Database) Reader(root common.Hash) (layer, error) { +// NodeReader retrieves a layer belonging to the given state root. +func (db *Database) NodeReader(root common.Hash) (database.NodeReader, error) { l := db.tree.get(root) if l == nil { return nil, fmt.Errorf("state %#x is not available", root) @@ -222,7 +194,7 @@ func (db *Database) Reader(root common.Hash) (layer, error) { // // The passed in maps(nodes, states) will be retained to avoid copying everything. // Therefore, these maps must not be changed afterwards. -func (db *Database) Update(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error { +func (db *Database) Update(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *state.Origin) error { // Hold the lock to prevent concurrent mutations. db.lock.Lock() defer db.lock.Unlock() @@ -331,7 +303,7 @@ func (db *Database) Enable(root common.Hash) error { // Recover rollbacks the database to a specified historical point. // The state is supported as the rollback destination only if it's // canonical state and the corresponding trie histories are existent. -func (db *Database) Recover(root common.Hash, loader triestate.TrieLoader) error { +func (db *Database) Recover(root common.Hash) error { db.lock.Lock() defer db.lock.Unlock() @@ -357,7 +329,7 @@ func (db *Database) Recover(root common.Hash, loader triestate.TrieLoader) error if err != nil { return err } - dl, err = dl.revert(h, loader) + dl, err = dl.revert(h) if err != nil { return err } diff --git a/triedb/pathdb/database_test.go b/triedb/pathdb/database_test.go index 2e7e1bef05..4103749f7f 100644 --- a/triedb/pathdb/database_test.go +++ b/triedb/pathdb/database_test.go @@ -27,10 +27,11 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/internal/testrand" "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/trie/testutil" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/triedb/database" + "github.com/ethereum/go-ethereum/triedb/state" "github.com/holiman/uint256" ) @@ -54,7 +55,7 @@ func generateAccount(storageRoot common.Hash) types.StateAccount { return types.StateAccount{ Nonce: uint64(rand.Intn(100)), Balance: uint256.NewInt(rand.Uint64()), - CodeHash: testutil.RandBytes(32), + CodeHash: testrand.Bytes(32), Root: storageRoot, } } @@ -98,22 +99,27 @@ type tester struct { func newTester(t *testing.T, historyLimit uint64) *tester { var ( - disk, _ = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false) - db = New(disk, &Config{ + snapAccounts = make(map[common.Hash]map[common.Hash][]byte) + snapStorages = make(map[common.Hash]map[common.Hash]map[common.Hash][]byte) + disk, _ = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false) + db = New(disk, &Config{ StateHistory: historyLimit, - CleanCacheSize: 256 * 1024, - DirtyCacheSize: 256 * 1024, + CleanCacheSize: 16 * 1024, + DirtyCacheSize: 16 * 1024, + TrieOpener: func(db database.NodeDatabase) state.TrieOpener { + return newHashOpener(snapAccounts, snapStorages) + }, }) obj = &tester{ db: db, preimages: make(map[common.Hash]common.Address), accounts: make(map[common.Hash][]byte), storages: make(map[common.Hash]map[common.Hash][]byte), - snapAccounts: make(map[common.Hash]map[common.Hash][]byte), - snapStorages: make(map[common.Hash]map[common.Hash]map[common.Hash][]byte), + snapAccounts: snapAccounts, + snapStorages: snapStorages, } ) - for i := 0; i < 2*128; i++ { + for i := 0; i < 16; i++ { var parent = types.EmptyRootHash if len(obj.roots) != 0 { parent = obj.roots[len(obj.roots)-1] @@ -146,8 +152,8 @@ func (t *tester) generateStorage(ctx *genctx, addr common.Address) common.Hash { origin = make(map[common.Hash][]byte) ) for i := 0; i < 10; i++ { - v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testutil.RandBytes(32))) - hash := testutil.RandomHash() + v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testrand.Bytes(32))) + hash := testrand.Hash() storage[hash] = v origin[hash] = nil @@ -175,8 +181,8 @@ func (t *tester) mutateStorage(ctx *genctx, addr common.Address, root common.Has } } for i := 0; i < 3; i++ { - v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testutil.RandBytes(32))) - hash := testutil.RandomHash() + v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testrand.Bytes(32))) + hash := testrand.Hash() storage[hash] = v origin[hash] = nil @@ -209,7 +215,7 @@ func (t *tester) clearStorage(ctx *genctx, addr common.Address, root common.Hash return root } -func (t *tester) generate(parent common.Hash) (common.Hash, *trienode.MergedNodeSet, *triestate.Set) { +func (t *tester) generate(parent common.Hash) (common.Hash, *trienode.MergedNodeSet, *state.Origin) { var ( ctx = newCtx() dirties = make(map[common.Hash]struct{}) @@ -218,7 +224,7 @@ func (t *tester) generate(parent common.Hash) (common.Hash, *trienode.MergedNode switch rand.Intn(opLen) { case createAccountOp: // account creation - addr := testutil.RandomAddress() + addr := testrand.Address() addrHash := crypto.Keccak256Hash(addr.Bytes()) if _, ok := t.accounts[addrHash]; ok { continue @@ -299,7 +305,7 @@ func (t *tester) generate(parent common.Hash) (common.Hash, *trienode.MergedNode } } } - return root, ctx.nodes, triestate.New(ctx.accountOrigin, ctx.storageOrigin) + return root, ctx.nodes, state.NewOrigin(ctx.accountOrigin, ctx.storageOrigin) } // lastRoot returns the latest root hash, or empty if nothing is cached. @@ -311,7 +317,7 @@ func (t *tester) lastHash() common.Hash { } func (t *tester) verifyState(root common.Hash) error { - reader, err := t.db.Reader(root) + reader, err := t.db.NodeReader(root) if err != nil { return err } @@ -379,6 +385,10 @@ func (t *tester) bottomIndex() int { } func TestDatabaseRollback(t *testing.T) { + maxDiffLayers = 4 + defer func() { + maxDiffLayers = 128 + }() // Verify state histories tester := newTester(t, 0) defer tester.release() @@ -388,13 +398,11 @@ func TestDatabaseRollback(t *testing.T) { } // Revert database from top to bottom for i := tester.bottomIndex(); i >= 0; i-- { - root := tester.roots[i] parent := types.EmptyRootHash if i > 0 { parent = tester.roots[i-1] } - loader := newHashLoader(tester.snapAccounts[root], tester.snapStorages[root]) - if err := tester.db.Recover(parent, loader); err != nil { + if err := tester.db.Recover(parent); err != nil { t.Fatalf("Failed to revert db, err: %v", err) } if i > 0 { @@ -409,6 +417,10 @@ func TestDatabaseRollback(t *testing.T) { } func TestDatabaseRecoverable(t *testing.T) { + maxDiffLayers = 4 + defer func() { + maxDiffLayers = 128 + }() var ( tester = newTester(t, 0) index = tester.bottomIndex() @@ -448,6 +460,10 @@ func TestDatabaseRecoverable(t *testing.T) { } func TestDisable(t *testing.T) { + maxDiffLayers = 4 + defer func() { + maxDiffLayers = 128 + }() tester := newTester(t, 0) defer tester.release() @@ -459,7 +475,7 @@ func TestDisable(t *testing.T) { t.Fatalf("Invalid activation should be rejected") } if err := tester.db.Enable(stored); err != nil { - t.Fatal("Failed to activate database") + t.Fatalf("Failed to activate database, %v", err) } // Ensure journal is deleted from disk @@ -484,6 +500,10 @@ func TestDisable(t *testing.T) { } func TestCommit(t *testing.T) { + maxDiffLayers = 4 + defer func() { + maxDiffLayers = 128 + }() tester := newTester(t, 0) defer tester.release() @@ -508,6 +528,10 @@ func TestCommit(t *testing.T) { } func TestJournal(t *testing.T) { + maxDiffLayers = 4 + defer func() { + maxDiffLayers = 128 + }() tester := newTester(t, 0) defer tester.release() @@ -515,7 +539,7 @@ func TestJournal(t *testing.T) { t.Errorf("Failed to journal, err: %v", err) } tester.db.Close() - tester.db = New(tester.db.diskdb, nil) + tester.db = New(tester.db.diskdb, tester.db.config) // Verify states including disk layer and all diff on top. for i := 0; i < len(tester.roots); i++ { @@ -532,6 +556,10 @@ func TestJournal(t *testing.T) { } func TestCorruptedJournal(t *testing.T) { + maxDiffLayers = 4 + defer func() { + maxDiffLayers = 128 + }() tester := newTester(t, 0) defer tester.release() @@ -547,7 +575,7 @@ func TestCorruptedJournal(t *testing.T) { rawdb.WriteTrieJournal(tester.db.diskdb, blob) // Verify states, all not-yet-written states should be discarded - tester.db = New(tester.db.diskdb, nil) + tester.db = New(tester.db.diskdb, tester.db.config) for i := 0; i < len(tester.roots); i++ { if tester.roots[i] == root { if err := tester.verifyState(root); err != nil { @@ -574,11 +602,15 @@ func TestCorruptedJournal(t *testing.T) { // truncating the tail histories. This ensures that the ID of the persistent state // always falls within the range of [oldest-history-id, latest-history-id]. func TestTailTruncateHistory(t *testing.T) { + maxDiffLayers = 4 + defer func() { + maxDiffLayers = 128 + }() tester := newTester(t, 10) defer tester.release() tester.db.Close() - tester.db = New(tester.db.diskdb, &Config{StateHistory: 10}) + tester.db = New(tester.db.diskdb, &Config{StateHistory: 10, TrieOpener: func(db database.NodeDatabase) state.TrieOpener { return nil }}) head, err := tester.db.freezer.Ancients() if err != nil { diff --git a/triedb/pathdb/difflayer.go b/triedb/pathdb/difflayer.go index 10567715d2..b17af4751f 100644 --- a/triedb/pathdb/difflayer.go +++ b/triedb/pathdb/difflayer.go @@ -23,7 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/triedb/state" ) // diffLayer represents a collection of modifications made to the in-memory tries @@ -37,7 +37,7 @@ type diffLayer struct { id uint64 // Corresponding state id block uint64 // Associated block number nodes map[common.Hash]map[string]*trienode.Node // Cached trie nodes indexed by owner and path - states *triestate.Set // Associated state change set for building history + states *state.Origin // Associated state change set for building history memory uint64 // Approximate guess as to how much memory we use parent layer // Parent layer modified by this one, never nil, **can be changed** @@ -45,7 +45,7 @@ type diffLayer struct { } // newDiffLayer creates a new diff layer on top of an existing layer. -func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer { +func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *state.Origin) *diffLayer { var ( size int64 count int @@ -138,7 +138,7 @@ func (dl *diffLayer) Node(owner common.Hash, path []byte, hash common.Hash) ([]b // update implements the layer interface, creating a new layer on top of the // existing layer tree with the specified data items. -func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer { +func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *state.Origin) *diffLayer { return newDiffLayer(dl, root, id, block, nodes, states) } diff --git a/triedb/pathdb/difflayer_test.go b/triedb/pathdb/difflayer_test.go index 9b5907c3c5..75890b8a83 100644 --- a/triedb/pathdb/difflayer_test.go +++ b/triedb/pathdb/difflayer_test.go @@ -22,7 +22,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/trie/testutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/internal/testrand" "github.com/ethereum/go-ethereum/trie/trienode" ) @@ -66,8 +67,9 @@ func benchmarkSearch(b *testing.B, depth int, total int) { nodes[common.Hash{}] = make(map[string]*trienode.Node) for i := 0; i < 3000; i++ { var ( - path = testutil.RandBytes(32) - node = testutil.RandomNode() + path = testrand.Bytes(32) + blob = testrand.Bytes(100) + node = trienode.New(crypto.Keccak256Hash(blob), blob) ) nodes[common.Hash{}][string(path)] = trienode.New(node.Hash, node.Blob) if npath == nil && depth == index { @@ -112,8 +114,9 @@ func BenchmarkPersist(b *testing.B) { nodes[common.Hash{}] = make(map[string]*trienode.Node) for i := 0; i < 3000; i++ { var ( - path = testutil.RandBytes(32) - node = testutil.RandomNode() + path = testrand.Bytes(32) + blob = testrand.Bytes(100) + node = trienode.New(crypto.Keccak256Hash(blob), blob) ) nodes[common.Hash{}][string(path)] = trienode.New(node.Hash, node.Blob) } @@ -149,8 +152,9 @@ func BenchmarkJournal(b *testing.B) { nodes[common.Hash{}] = make(map[string]*trienode.Node) for i := 0; i < 3000; i++ { var ( - path = testutil.RandBytes(32) - node = testutil.RandomNode() + path = testrand.Bytes(32) + blob = testrand.Bytes(100) + node = trienode.New(crypto.Keccak256Hash(blob), blob) ) nodes[common.Hash{}][string(path)] = trienode.New(node.Hash, node.Blob) } diff --git a/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go index 777e4ec8a7..cbe33596e1 100644 --- a/triedb/pathdb/disklayer.go +++ b/triedb/pathdb/disklayer.go @@ -26,7 +26,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/triedb/state" "golang.org/x/crypto/sha3" ) @@ -160,7 +160,7 @@ func (dl *diskLayer) Node(owner common.Hash, path []byte, hash common.Hash) ([]b // update implements the layer interface, returning a new diff layer on top // with the given state set. -func (dl *diskLayer) update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer { +func (dl *diskLayer) update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *state.Origin) *diffLayer { return newDiffLayer(dl, root, id, block, nodes, states) } @@ -234,17 +234,28 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) { } // revert applies the given state history and return a reverted disk layer. -func (dl *diskLayer) revert(h *history, loader triestate.TrieLoader) (*diskLayer, error) { +func (dl *diskLayer) revert(h *history) (*diskLayer, error) { if h.meta.root != dl.rootHash() { return nil, errUnexpectedHistory } if dl.id == 0 { return nil, fmt.Errorf("%w: zero state id", errStateUnrecoverable) } + var ( + hasher = crypto.NewKeccakState() + accounts = make(map[common.Hash][]byte) + storages = make(map[common.Hash]map[common.Hash][]byte) + ) + for addr, blob := range h.accounts { + accounts[crypto.HashData(hasher, addr.Bytes())] = blob + } + for addr, storage := range h.storages { + storages[crypto.HashData(hasher, addr.Bytes())] = storage + } // Apply the reverse state changes upon the current state. This must // be done before holding the lock in order to access state in "this" // layer. - nodes, err := triestate.Apply(h.meta.parent, h.meta.root, h.accounts, h.storages, loader) + nodes, err := state.Apply(h.meta.parent, h.meta.root, accounts, storages, dl.db.trieOpener) if err != nil { return nil, err } diff --git a/triedb/pathdb/history.go b/triedb/pathdb/history.go index 68fb4809f0..2dff3c9a75 100644 --- a/triedb/pathdb/history.go +++ b/triedb/pathdb/history.go @@ -27,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/triedb/state" "golang.org/x/exp/slices" ) @@ -242,7 +242,7 @@ type history struct { } // newHistory constructs the state history object with provided state change set. -func newHistory(root common.Hash, parent common.Hash, block uint64, states *triestate.Set) *history { +func newHistory(root common.Hash, parent common.Hash, block uint64, states *state.Origin) *history { var ( accountList []common.Address storageList = make(map[common.Address][]common.Hash) diff --git a/triedb/pathdb/history_test.go b/triedb/pathdb/history_test.go index ab0d44777d..2cc57ed928 100644 --- a/triedb/pathdb/history_test.go +++ b/triedb/pathdb/history_test.go @@ -26,32 +26,32 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/internal/testrand" "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/trie/testutil" - "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/triedb/state" ) // randomStateSet generates a random state change set. -func randomStateSet(n int) *triestate.Set { +func randomStateSet(n int) *state.Origin { var ( accounts = make(map[common.Address][]byte) storages = make(map[common.Address]map[common.Hash][]byte) ) for i := 0; i < n; i++ { - addr := testutil.RandomAddress() + addr := testrand.Address() storages[addr] = make(map[common.Hash][]byte) for j := 0; j < 3; j++ { - v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testutil.RandBytes(32))) - storages[addr][testutil.RandomHash()] = v + v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testrand.Bytes(32))) + storages[addr][testrand.Hash()] = v } account := generateAccount(types.EmptyRootHash) accounts[addr] = types.SlimAccountRLP(account) } - return triestate.New(accounts, storages) + return state.NewOrigin(accounts, storages) } func makeHistory() *history { - return newHistory(testutil.RandomHash(), types.EmptyRootHash, 0, randomStateSet(3)) + return newHistory(testrand.Hash(), types.EmptyRootHash, 0, randomStateSet(3)) } func makeHistories(n int) []*history { @@ -60,7 +60,7 @@ func makeHistories(n int) []*history { result []*history ) for i := 0; i < n; i++ { - root := testutil.RandomHash() + root := testrand.Hash() h := newHistory(root, parent, uint64(i), randomStateSet(3)) parent = root result = append(result, h) diff --git a/triedb/pathdb/journal.go b/triedb/pathdb/journal.go index 3a0b7ebae2..2d1d49fbc0 100644 --- a/triedb/pathdb/journal.go +++ b/triedb/pathdb/journal.go @@ -30,7 +30,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/triedb/state" ) var ( @@ -239,7 +239,7 @@ func (db *Database) loadDiffLayer(parent layer, r *rlp.Stream) (layer, error) { } storages[entry.Account] = set } - return db.loadDiffLayer(newDiffLayer(parent, root, parent.stateID()+1, block, nodes, triestate.New(accounts, storages)), r) + return db.loadDiffLayer(newDiffLayer(parent, root, parent.stateID()+1, block, nodes, state.NewOrigin(accounts, storages)), r) } // journal implements the layer interface, marshaling the un-flushed trie nodes diff --git a/triedb/pathdb/layertree.go b/triedb/pathdb/layertree.go index d314779910..63b5afbdb0 100644 --- a/triedb/pathdb/layertree.go +++ b/triedb/pathdb/layertree.go @@ -24,7 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/triedb/state" ) // layerTree is a group of state layers identified by the state root. @@ -86,7 +86,7 @@ func (tree *layerTree) len() int { } // add inserts a new layer into the tree if it can be linked to an existing old parent. -func (tree *layerTree) add(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error { +func (tree *layerTree) add(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *state.Origin) error { // Reject noop updates to avoid self-loops. This is a special case that can // happen for clique networks and proof-of-stake networks where empty blocks // don't modify the state (0 block subsidy). diff --git a/triedb/pathdb/testutils.go b/triedb/pathdb/testhasher.go similarity index 86% rename from triedb/pathdb/testutils.go rename to triedb/pathdb/testhasher.go index d6fdacb421..b162eb47d4 100644 --- a/triedb/pathdb/testutils.go +++ b/triedb/pathdb/testhasher.go @@ -24,7 +24,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/triedb/state" "golang.org/x/exp/slices" ) @@ -133,24 +133,24 @@ func hash(states map[common.Hash][]byte) (common.Hash, []byte) { return crypto.Keccak256Hash(input), input } -type hashLoader struct { - accounts map[common.Hash][]byte - storages map[common.Hash]map[common.Hash][]byte +type hashOpener struct { + accounts map[common.Hash]map[common.Hash][]byte + storages map[common.Hash]map[common.Hash]map[common.Hash][]byte } -func newHashLoader(accounts map[common.Hash][]byte, storages map[common.Hash]map[common.Hash][]byte) *hashLoader { - return &hashLoader{ +func newHashOpener(accounts map[common.Hash]map[common.Hash][]byte, storages map[common.Hash]map[common.Hash]map[common.Hash][]byte) *hashOpener { + return &hashOpener{ accounts: accounts, storages: storages, } } // OpenTrie opens the main account trie. -func (l *hashLoader) OpenTrie(root common.Hash) (triestate.Trie, error) { - return newTestHasher(common.Hash{}, root, l.accounts) +func (l *hashOpener) OpenTrie(root common.Hash) (state.Trie, error) { + return newTestHasher(common.Hash{}, root, l.accounts[root]) } // OpenStorageTrie opens the storage trie of an account. -func (l *hashLoader) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (triestate.Trie, error) { - return newTestHasher(addrHash, root, l.storages[addrHash]) +func (l *hashOpener) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (state.Trie, error) { + return newTestHasher(addrHash, root, l.storages[stateRoot][addrHash]) } diff --git a/trie/triestate/state.go b/triedb/state/trie.go similarity index 63% rename from trie/triestate/state.go rename to triedb/state/trie.go index aa4d32f852..56b0defa29 100644 --- a/trie/triestate/state.go +++ b/triedb/state/trie.go @@ -14,19 +14,16 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see -package triestate +package state import ( "errors" "fmt" - "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie/trienode" - "golang.org/x/crypto/sha3" ) // Trie is an Ethereum state trie, can be implemented by Ethereum Merkle Patricia @@ -46,8 +43,8 @@ type Trie interface { Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) } -// TrieLoader wraps functions to load tries. -type TrieLoader interface { +// TrieOpener wraps functions to load tries. +type TrieOpener interface { // OpenTrie opens the main account trie. OpenTrie(root common.Hash) (Trie, error) @@ -55,55 +52,20 @@ type TrieLoader interface { OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (Trie, error) } -// Set represents a collection of mutated states during a state transition. -// The value refers to the original content of state before the transition -// is made. Nil means that the state was not present previously. -type Set struct { - Accounts map[common.Address][]byte // Mutated account set, nil means the account was not present - Storages map[common.Address]map[common.Hash][]byte // Mutated storage set, nil means the slot was not present - size common.StorageSize // Approximate size of set -} - -// New constructs the state set with provided data. -func New(accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte) *Set { - return &Set{ - Accounts: accounts, - Storages: storages, - } -} - -// Size returns the approximate memory size occupied by the set. -func (s *Set) Size() common.StorageSize { - if s.size != 0 { - return s.size - } - for _, account := range s.Accounts { - s.size += common.StorageSize(common.AddressLength + len(account)) - } - for _, slots := range s.Storages { - for _, val := range slots { - s.size += common.StorageSize(common.HashLength + len(val)) - } - s.size += common.StorageSize(common.AddressLength) - } - return s.size -} - // context wraps all fields for executing state diffs. type context struct { prevRoot common.Hash postRoot common.Hash - accounts map[common.Address][]byte - storages map[common.Address]map[common.Hash][]byte + accounts map[common.Hash][]byte + storages map[common.Hash]map[common.Hash][]byte accountTrie Trie nodes *trienode.MergedNodeSet } -// Apply traverses the provided state diffs, apply them in the associated -// post-state and return the generated dirty trie nodes. The state can be -// loaded via the provided trie loader. -func Apply(prevRoot common.Hash, postRoot common.Hash, accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte, loader TrieLoader) (map[common.Hash]map[string]*trienode.Node, error) { - tr, err := loader.OpenTrie(postRoot) +// Apply traverses the provided state diffs, applying them in the associated +// post-state and return the produced trie changes. +func Apply(prevRoot common.Hash, postRoot common.Hash, accounts map[common.Hash][]byte, storages map[common.Hash]map[common.Hash][]byte, opener TrieOpener) (map[common.Hash]map[string]*trienode.Node, error) { + tr, err := opener.OpenTrie(postRoot) if err != nil { return nil, err } @@ -115,12 +77,12 @@ func Apply(prevRoot common.Hash, postRoot common.Hash, accounts map[common.Addre accountTrie: tr, nodes: trienode.NewMergedNodeSet(), } - for addr, account := range accounts { + for addrHash, account := range accounts { var err error if len(account) == 0 { - err = deleteAccount(ctx, loader, addr) + err = deleteAccount(ctx, opener, addrHash) } else { - err = updateAccount(ctx, loader, addr) + err = updateAccount(ctx, opener, addrHash) } if err != nil { return nil, fmt.Errorf("failed to revert state, err: %w", err) @@ -142,14 +104,10 @@ func Apply(prevRoot common.Hash, postRoot common.Hash, accounts map[common.Addre // updateAccount the account was present in prev-state, and may or may not // existent in post-state. Apply the reverse diff and verify if the storage // root matches the one in prev-state account. -func updateAccount(ctx *context, loader TrieLoader, addr common.Address) error { +func updateAccount(ctx *context, opener TrieOpener, addrHash common.Hash) error { // The account was present in prev-state, decode it from the // 'slim-rlp' format bytes. - h := newHasher() - defer h.release() - - addrHash := h.hash(addr.Bytes()) - prev, err := types.FullAccount(ctx.accounts[addr]) + prev, err := types.FullAccount(ctx.accounts[addrHash]) if err != nil { return err } @@ -166,11 +124,11 @@ func updateAccount(ctx *context, loader TrieLoader, addr common.Address) error { } } // Apply all storage changes into the post-state storage trie. - st, err := loader.OpenStorageTrie(ctx.postRoot, addrHash, post.Root) + st, err := opener.OpenStorageTrie(ctx.postRoot, addrHash, post.Root) if err != nil { return err } - for key, val := range ctx.storages[addr] { + for key, val := range ctx.storages[addrHash] { var err error if len(val) == 0 { err = st.Delete(key.Bytes()) @@ -206,12 +164,8 @@ func updateAccount(ctx *context, loader TrieLoader, addr common.Address) error { // deleteAccount the account was not present in prev-state, and is expected // to be existent in post-state. Apply the reverse diff and verify if the // account and storage is wiped out correctly. -func deleteAccount(ctx *context, loader TrieLoader, addr common.Address) error { +func deleteAccount(ctx *context, opener TrieOpener, addrHash common.Hash) error { // The account must be existent in post-state, load the account. - h := newHasher() - defer h.release() - - addrHash := h.hash(addr.Bytes()) blob, err := ctx.accountTrie.Get(addrHash.Bytes()) if err != nil { return err @@ -223,11 +177,11 @@ func deleteAccount(ctx *context, loader TrieLoader, addr common.Address) error { if err := rlp.DecodeBytes(blob, &post); err != nil { return err } - st, err := loader.OpenStorageTrie(ctx.postRoot, addrHash, post.Root) + st, err := opener.OpenStorageTrie(ctx.postRoot, addrHash, post.Root) if err != nil { return err } - for key, val := range ctx.storages[addr] { + for key, val := range ctx.storages[addrHash] { if len(val) != 0 { return errors.New("expect storage deletion") } @@ -252,22 +206,3 @@ func deleteAccount(ctx *context, loader TrieLoader, addr common.Address) error { // Delete the post-state account from the main trie. return ctx.accountTrie.Delete(addrHash.Bytes()) } - -// hasher is used to compute the sha256 hash of the provided data. -type hasher struct{ sha crypto.KeccakState } - -var hasherPool = sync.Pool{ - New: func() interface{} { return &hasher{sha: sha3.NewLegacyKeccak256().(crypto.KeccakState)} }, -} - -func newHasher() *hasher { - return hasherPool.Get().(*hasher) -} - -func (h *hasher) hash(data []byte) common.Hash { - return crypto.HashData(h.sha, data) -} - -func (h *hasher) release() { - hasherPool.Put(h) -} diff --git a/triedb/state/types.go b/triedb/state/types.go new file mode 100644 index 0000000000..820eb17110 --- /dev/null +++ b/triedb/state/types.go @@ -0,0 +1,59 @@ +// Copyright 2024 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 + +import "github.com/ethereum/go-ethereum/common" + +// Origin represents the prev-state for a state transition. +type Origin struct { + // Accounts represents the account data before the state transition, keyed + // by the account address. The nil value means the account was not present + // before. + Accounts map[common.Address][]byte + + // Storages represents the storage data before the state transition, keyed + // by the account address and slot key hash. The nil value means the slot + // was not present. + Storages map[common.Address]map[common.Hash][]byte + + size common.StorageSize // Approximate size of set +} + +// NewOrigin constructs the state set with provided data. +func NewOrigin(accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte) *Origin { + return &Origin{ + Accounts: accounts, + Storages: storages, + } +} + +// Size returns the approximate memory size occupied by the set. +func (s *Origin) Size() common.StorageSize { + if s.size != 0 { + return s.size + } + for _, account := range s.Accounts { + s.size += common.StorageSize(common.AddressLength + len(account)) + } + for _, slots := range s.Storages { + for _, val := range slots { + s.size += common.StorageSize(common.HashLength + len(val)) + } + s.size += common.StorageSize(common.AddressLength) + } + return s.size +}