all: add dbconfig package

This commit is contained in:
Gary Rong 2024-01-24 15:03:37 +08:00
parent ebf9e11af2
commit c77047a67f
61 changed files with 678 additions and 471 deletions

View file

@ -36,7 +36,7 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb/dbconfig"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"golang.org/x/crypto/sha3" "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 { 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) statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
for addr, a := range accounts { for addr, a := range accounts {
statedb.SetCode(addr, a.Code) statedb.SetCode(addr, a.Code)

View file

@ -39,7 +39,7 @@ import (
"github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/triedb" "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" "github.com/urfave/cli/v2"
) )
@ -148,10 +148,9 @@ func runCmd(ctx *cli.Context) error {
} }
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
triedb := triedb.NewDatabase(db, &triedb.Config{ config := dbconfig.HashDefaults
Preimages: preimages, config.Preimages = preimages
HashDB: hashdb.Defaults, triedb := triedb.NewDatabase(db, &config)
})
defer triedb.Close() defer triedb.Close()
genesis := genesisConfig.MustCommit(db, triedb) genesis := genesisConfig.MustCommit(db, triedb)
sdb := state.NewDatabaseWithNodeDB(db, triedb) sdb := state.NewDatabaseWithNodeDB(db, triedb)

View file

@ -428,7 +428,7 @@ func traverseRawState(ctx *cli.Context) error {
log.Error("Failed to open iterator", "root", root, "err", err) log.Error("Failed to open iterator", "root", root, "err", err)
return err return err
} }
reader, err := triedb.Reader(root) reader, err := triedb.NodeReader(root)
if err != nil { if err != nil {
log.Error("State is non-existent", "root", root) log.Error("State is non-existent", "root", root)
return nil return nil

View file

@ -70,6 +70,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/p2p/netutil"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/hashdb"
"github.com/ethereum/go-ethereum/triedb/pathdb" "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. // 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 { func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *triedb.Database {
config := &triedb.Config{ config := &triedb.Config{
Preimages: preimage, 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, // Read-only mode is not implemented in hash mode,
// ignore the parameter silently. TODO(rjl493456442) // ignore the parameter silently. TODO(rjl493456442)
// please config it if read mode is implemented. // 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) return triedb.NewDatabase(disk, config)
} }
config.PathDB = &pathdb.Config{
StateHistory: params.FullImmutabilityThreshold,
CleanCacheSize: pathdb.DefaultCleanSize,
DirtyCacheSize: pathdb.DefaultBufferSize,
TrieOpener: trie.NewMerkleOpener,
}
if readOnly { if readOnly {
config.PathDB = pathdb.ReadOnly config.PathDB.ReadOnly = true
} else {
config.PathDB = pathdb.Defaults
} }
return triedb.NewDatabase(disk, config) return triedb.NewDatabase(disk, config)
} }

View file

@ -37,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/dbconfig"
) )
var ( var (
@ -171,7 +172,7 @@ func TestHistoryImportAndExport(t *testing.T) {
db2.Close() 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) imported, err := core.NewBlockChain(db2, nil, genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
if err != nil { if err != nil {
t.Fatalf("unable to initialize chain: %v", err) t.Fatalf("unable to initialize chain: %v", err)

View file

@ -47,6 +47,7 @@ import (
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/hashdb"
"github.com/ethereum/go-ethereum/triedb/pathdb" "github.com/ethereum/go-ethereum/triedb/pathdb"
@ -151,6 +152,7 @@ func (c *CacheConfig) triedbConfig() *triedb.Config {
if c.StateScheme == rawdb.HashScheme { if c.StateScheme == rawdb.HashScheme {
config.HashDB = &hashdb.Config{ config.HashDB = &hashdb.Config{
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024, CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
ChildResolver: trie.MerkleResolver,
} }
} }
if c.StateScheme == rawdb.PathScheme { if c.StateScheme == rawdb.PathScheme {
@ -158,6 +160,7 @@ func (c *CacheConfig) triedbConfig() *triedb.Config {
StateHistory: c.StateHistory, StateHistory: c.StateHistory,
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024, CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
DirtyCacheSize: c.TrieDirtyLimit * 1024 * 1024, DirtyCacheSize: c.TrieDirtyLimit * 1024 * 1024,
TrieOpener: trie.NewMerkleOpener,
} }
} }
return config return config

View file

@ -35,8 +35,7 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/dbconfig"
"github.com/ethereum/go-ethereum/triedb/pathdb"
) )
// rewindTest is a test case for chain rollback upon user request. // 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. // Reopen the trie database without persisting in-memory dirty nodes.
chain.triedb.Close() chain.triedb.Close()
dbconfig := &triedb.Config{} var dbconf triedb.Config
if scheme == rawdb.PathScheme { if scheme == rawdb.PathScheme {
dbconfig.PathDB = pathdb.Defaults dbconf = dbconfig.PathDefaults
} else { } 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) chain.stateCache = state.NewDatabaseWithNodeDB(chain.db, chain.triedb)
// Force run a freeze cycle // Force run a freeze cycle

View file

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/dbconfig"
"github.com/holiman/uint256" "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. // 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() defer triedb.Close()
for i := 0; i < n; i++ { 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. // then generate chain on top.
func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts) { func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts) {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
triedb := triedb.NewDatabase(db, triedb.HashDefaults) triedb := triedb.NewDatabase(db, &dbconfig.HashDefaults)
defer triedb.Close() defer triedb.Close()
_, err := genesis.Commit(db, triedb) _, err := genesis.Commit(db, triedb)
if err != nil { if err != nil {

View file

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/dbconfig"
) )
func TestGeneratePOSChain(t *testing.T) { func TestGeneratePOSChain(t *testing.T) {
@ -81,7 +82,7 @@ func TestGeneratePOSChain(t *testing.T) {
Storage: storage, Storage: storage,
Code: common.Hex2Bytes("600154600354"), 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) { genchain, genreceipts := GenerateChain(gspec.Config, genesis, beacon.NewFaker(), gendb, 4, func(i int, gen *BlockGen) {
gen.SetParentBeaconRoot(common.Hash{byte(i + 1)}) gen.SetParentBeaconRoot(common.Hash{byte(i + 1)})
@ -204,7 +205,7 @@ func ExampleGenerateChain() {
Config: &params.ChainConfig{HomesteadBlock: new(big.Int)}, Config: &params.ChainConfig{HomesteadBlock: new(big.Int)},
Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}}, 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 // This call generates a chain of 5 blocks. The function runs for
// each block and adds different features to gen based on the // each block and adds different features to gen based on the

View file

@ -37,7 +37,7 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/pathdb" "github.com/ethereum/go-ethereum/triedb/dbconfig"
"github.com/holiman/uint256" "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 // If a genesis-time verkle trie is requested, create a trie config
// with the verkle trie enabled so that the tree can be initialized // with the verkle trie enabled so that the tree can be initialized
// as such. // as such.
var config *triedb.Config var config triedb.Config
if isVerkle { if isVerkle {
config = &triedb.Config{ config = dbconfig.PathDefaults
PathDB: pathdb.Defaults, config.IsVerkle = true
IsVerkle: true, } else {
} config = dbconfig.HashDefaults
} }
// Create an ephemeral in-memory database for computing hash, // Create an ephemeral in-memory database for computing hash,
// all the derived states will be discarded to not pollute disk. // 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) statedb, err := state.New(types.EmptyRootHash, db, nil)
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err

View file

@ -32,14 +32,14 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/triedb" "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) { func TestInvalidCliqueConfig(t *testing.T) {
block := DefaultGoerliGenesisBlock() block := DefaultGoerliGenesisBlock()
block.ExtraData = []byte{} block.ExtraData = []byte{}
db := rawdb.NewMemoryDatabase() 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") t.Fatal("Expected error on invalid clique config")
} }
} }
@ -189,7 +189,7 @@ func TestGenesisHashes(t *testing.T) {
} { } {
// Test via MustCommit // Test via MustCommit
db := rawdb.NewMemoryDatabase() 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()) t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex())
} }
// Test via ToBlock // Test via ToBlock
@ -207,7 +207,7 @@ func TestGenesis_Commit(t *testing.T) {
} }
db := rawdb.NewMemoryDatabase() 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 { if genesis.Difficulty != nil {
t.Fatalf("assumption wrong") t.Fatalf("assumption wrong")
@ -259,9 +259,9 @@ func TestReadWriteGenesisAlloc(t *testing.T) {
func newDbConfig(scheme string) *triedb.Config { func newDbConfig(scheme string) *triedb.Config {
if scheme == rawdb.HashScheme { if scheme == rawdb.HashScheme {
return triedb.HashDefaults return &dbconfig.HashDefaults
} }
return &triedb.Config{PathDB: pathdb.Defaults} return &dbconfig.PathDefaults
} }
func TestVerkleGenesisCommit(t *testing.T) { func TestVerkleGenesisCommit(t *testing.T) {
@ -311,7 +311,10 @@ func TestVerkleGenesisCommit(t *testing.T) {
} }
db := rawdb.NewMemoryDatabase() 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) block := genesis.MustCommit(db, triedb)
if !bytes.Equal(block.Root().Bytes(), expected) { if !bytes.Equal(block.Root().Bytes(), expected) {
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got) t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)

View file

@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/dbconfig"
) )
func verifyUnbrokenCanonchain(hc *HeaderChain) error { func verifyUnbrokenCanonchain(hc *HeaderChain) error {
@ -73,7 +74,7 @@ func TestHeaderInsertion(t *testing.T) {
db = rawdb.NewMemoryDatabase() db = rawdb.NewMemoryDatabase()
gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges} 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 }) hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View file

@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/trie/utils" "github.com/ethereum/go-ethereum/trie/utils"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/dbconfig"
) )
const ( const (
@ -145,7 +146,7 @@ type Trie interface {
// concurrent use, but does not retain any recent trie nodes in memory. To keep some // concurrent use, but does not retain any recent trie nodes in memory. To keep some
// historical state in memory, use the NewDatabaseWithConfig constructor. // historical state in memory, use the NewDatabaseWithConfig constructor.
func NewDatabase(db ethdb.Database) Database { 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 // NewDatabaseWithConfig creates a backing store for state. The returned database

View file

@ -36,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/dbconfig"
) )
const ( const (
@ -87,7 +88,7 @@ func NewPruner(db ethdb.Database, config Config) (*Pruner, error) {
return nil, errors.New("failed to load head block") return nil, errors.New("failed to load head block")
} }
// Offline pruning is only supported in legacy hash based scheme. // 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{ snapconfig := snapshot.Config{
CacheSize: 256, CacheSize: 256,
@ -367,7 +368,7 @@ func RecoverPruning(datadir string, db ethdb.Database) error {
AsyncBuild: false, AsyncBuild: false,
} }
// Offline pruning is only supported in legacy hash based scheme. // 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()) snaptree, err := snapshot.New(snapconfig, db, triedb, headBlock.Root())
if err != nil { if err != nil {
return err // The relevant snapshot(s) might not exist return err // The relevant snapshot(s) might not exist
@ -410,7 +411,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error {
if genesis == nil { if genesis == nil {
return errors.New("missing genesis block") 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 { if err != nil {
return err return err
} }
@ -434,7 +435,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error {
} }
if acc.Root != types.EmptyRootHash { if acc.Root != types.EmptyRootHash {
id := trie.StorageTrieID(genesis.Root(), common.BytesToHash(accIter.LeafKey()), acc.Root) 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 { if err != nil {
return err return err
} }

View file

@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/dbconfig"
) )
var ( var (
@ -354,7 +355,7 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi
var resolver trie.NodeResolver var resolver trie.NodeResolver
if len(result.keys) > 0 { if len(result.keys) > 0 {
mdb := rawdb.NewMemoryDatabase() mdb := rawdb.NewMemoryDatabase()
tdb := triedb.NewDatabase(mdb, triedb.HashDefaults) tdb := triedb.NewDatabase(mdb, &dbconfig.HashDefaults)
defer tdb.Close() defer tdb.Close()
snapTrie := trie.NewEmpty(tdb) snapTrie := trie.NewEmpty(tdb)
for i, key := range result.keys { for i, key := range result.keys {

View file

@ -31,8 +31,7 @@ import (
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/dbconfig"
"github.com/ethereum/go-ethereum/triedb/pathdb"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
@ -162,14 +161,14 @@ type testHelper struct {
} }
func newHelper(scheme string) *testHelper { func newHelper(scheme string) *testHelper {
diskdb := rawdb.NewMemoryDatabase() var config triedb.Config
config := &triedb.Config{}
if scheme == rawdb.PathScheme { if scheme == rawdb.PathScheme {
config.PathDB = &pathdb.Config{} // disable caching config = dbconfig.PathDefaults
} else { } 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) accTrie, _ := trie.NewStateTrie(trie.StateTrieID(types.EmptyRootHash), triedb)
return &testHelper{ return &testHelper{
diskdb: diskdb, diskdb: diskdb,

View file

@ -26,7 +26,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb/dbconfig"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -43,7 +43,9 @@ func newStateEnv() *stateEnv {
func TestDump(t *testing.T) { func TestDump(t *testing.T) {
db := rawdb.NewMemoryDatabase() 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) sdb, _ := New(types.EmptyRootHash, tdb, nil)
s := &stateEnv{db: db, state: sdb} s := &stateEnv{db: db, state: sdb}
@ -100,7 +102,9 @@ func TestDump(t *testing.T) {
func TestIterativeDump(t *testing.T) { func TestIterativeDump(t *testing.T) {
db := rawdb.NewMemoryDatabase() 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) sdb, _ := New(types.EmptyRootHash, tdb, nil)
s := &stateEnv{db: db, state: sdb} s := &stateEnv{db: db, state: sdb}

View file

@ -31,7 +31,7 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode" "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" "github.com/holiman/uint256"
) )
@ -130,7 +130,7 @@ type StateDB struct {
StorageDeleted int StorageDeleted int
// Testing hooks // 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. // 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 { if root != origin {
start = time.Now() 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 { if err := s.db.TrieDB().Update(root, origin, block, nodes, set); err != nil {
return common.Hash{}, err return common.Hash{}, err
} }

View file

@ -35,9 +35,9 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "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"
"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" "github.com/holiman/uint256"
) )
@ -177,12 +177,12 @@ func (test *stateTest) run() bool {
roots []common.Hash roots []common.Hash
accountList []map[common.Address][]byte accountList []map[common.Address][]byte
storageList []map[common.Address]map[common.Hash][]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)) accountList = append(accountList, copySet(states.Accounts))
storageList = append(storageList, copy2DSet(states.Storages)) storageList = append(storageList, copy2DSet(states.Storages))
} }
disk = rawdb.NewMemoryDatabase() disk = rawdb.NewMemoryDatabase()
tdb = triedb.NewDatabase(disk, &triedb.Config{PathDB: pathdb.Defaults}) tdb = triedb.NewDatabase(disk, &dbconfig.PathDefaults)
sdb = NewDatabaseWithNodeDB(disk, tdb) sdb = NewDatabaseWithNodeDB(disk, tdb)
byzantium = rand.Intn(2) == 0 byzantium = rand.Intn(2) == 0
) )

View file

@ -38,8 +38,7 @@ import (
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/dbconfig"
"github.com/ethereum/go-ethereum/triedb/pathdb"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -49,7 +48,7 @@ func TestUpdateLeaks(t *testing.T) {
// Create an empty state database // Create an empty state database
var ( var (
db = rawdb.NewMemoryDatabase() db = rawdb.NewMemoryDatabase()
tdb = triedb.NewDatabase(db, nil) tdb = triedb.NewDatabase(db, &dbconfig.HashDefaults)
) )
state, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(db, tdb), nil) 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 // Create two state databases, one transitioning to the final state, the other final from the beginning
transDb := rawdb.NewMemoryDatabase() transDb := rawdb.NewMemoryDatabase()
finalDb := rawdb.NewMemoryDatabase() finalDb := rawdb.NewMemoryDatabase()
transNdb := triedb.NewDatabase(transDb, nil) transNdb := triedb.NewDatabase(transDb, &dbconfig.HashDefaults)
finalNdb := triedb.NewDatabase(finalDb, nil) finalNdb := triedb.NewDatabase(finalDb, &dbconfig.HashDefaults)
transState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(transDb, transNdb), nil) transState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(transDb, transNdb), nil)
finalState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(finalDb, finalNdb), nil) finalState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(finalDb, finalNdb), nil)
@ -803,14 +802,9 @@ func testMissingTrieNodes(t *testing.T, scheme string) {
memDb = rawdb.NewMemoryDatabase() memDb = rawdb.NewMemoryDatabase()
) )
if scheme == rawdb.PathScheme { if scheme == rawdb.PathScheme {
tdb = triedb.NewDatabase(memDb, &triedb.Config{PathDB: &pathdb.Config{ tdb = triedb.NewDatabase(memDb, &dbconfig.PathDefaults) // disable caching
CleanCacheSize: 0,
DirtyCacheSize: 0,
}}) // disable caching
} else { } else {
tdb = triedb.NewDatabase(memDb, &triedb.Config{HashDB: &hashdb.Config{ tdb = triedb.NewDatabase(memDb, &dbconfig.HashDefaults) // cache is disabled by default
CleanCacheSize: 0,
}}) // disable caching
} }
db := NewDatabaseWithNodeDB(memDb, tdb) db := NewDatabaseWithNodeDB(memDb, tdb)
@ -1033,7 +1027,7 @@ func TestFlushOrderDataLoss(t *testing.T) {
// Create a state trie with many accounts and slots // Create a state trie with many accounts and slots
var ( var (
memdb = rawdb.NewMemoryDatabase() memdb = rawdb.NewMemoryDatabase()
triedb = triedb.NewDatabase(memdb, nil) triedb = triedb.NewDatabase(memdb, &dbconfig.HashDefaults)
statedb = NewDatabaseWithNodeDB(memdb, triedb) statedb = NewDatabaseWithNodeDB(memdb, triedb)
state, _ = New(types.EmptyRootHash, statedb, nil) state, _ = New(types.EmptyRootHash, statedb, nil)
) )
@ -1105,7 +1099,7 @@ func TestStateDBTransientStorage(t *testing.T) {
func TestResetObject(t *testing.T) { func TestResetObject(t *testing.T) {
var ( var (
disk = rawdb.NewMemoryDatabase() disk = rawdb.NewMemoryDatabase()
tdb = triedb.NewDatabase(disk, nil) tdb = triedb.NewDatabase(disk, &dbconfig.HashDefaults)
db = NewDatabaseWithNodeDB(disk, tdb) db = NewDatabaseWithNodeDB(disk, tdb)
snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash) snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash)
state, _ = New(types.EmptyRootHash, db, snaps) state, _ = New(types.EmptyRootHash, db, snaps)
@ -1139,7 +1133,7 @@ func TestResetObject(t *testing.T) {
func TestDeleteStorage(t *testing.T) { func TestDeleteStorage(t *testing.T) {
var ( var (
disk = rawdb.NewMemoryDatabase() disk = rawdb.NewMemoryDatabase()
tdb = triedb.NewDatabase(disk, nil) tdb = triedb.NewDatabase(disk, &dbconfig.HashDefaults)
db = NewDatabaseWithNodeDB(disk, tdb) db = NewDatabaseWithNodeDB(disk, tdb)
snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash) snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash)
state, _ = New(types.EmptyRootHash, db, snaps) state, _ = New(types.EmptyRootHash, db, snaps)

View file

@ -28,8 +28,7 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/dbconfig"
"github.com/ethereum/go-ethereum/triedb/pathdb"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -44,14 +43,16 @@ type testAccount struct {
// makeTestState create a sample test state to test node-wise reconstruction. // makeTestState create a sample test state to test node-wise reconstruction.
func makeTestState(scheme string) (ethdb.Database, Database, *triedb.Database, common.Hash, []*testAccount) { func makeTestState(scheme string) (ethdb.Database, Database, *triedb.Database, common.Hash, []*testAccount) {
// Create an empty state // Create an empty state
config := &triedb.Config{Preimages: true} var config triedb.Config
if scheme == rawdb.PathScheme { if scheme == rawdb.PathScheme {
config.PathDB = pathdb.Defaults config = dbconfig.PathDefaults
} else { } else {
config.HashDB = hashdb.Defaults config = dbconfig.HashDefaults
} }
config.Preimages = true
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
nodeDb := triedb.NewDatabase(db, config) nodeDb := triedb.NewDatabase(db, &config)
sdb := NewDatabaseWithNodeDB(db, nodeDb) sdb := NewDatabaseWithNodeDB(db, nodeDb)
state, _ := New(types.EmptyRootHash, sdb, nil) 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) { func checkStateAccounts(t *testing.T, db ethdb.Database, scheme string, root common.Hash, accounts []*testAccount) {
var config triedb.Config var config triedb.Config
if scheme == rawdb.PathScheme { if scheme == rawdb.PathScheme {
config.PathDB = pathdb.Defaults config = dbconfig.PathDefaults
} else {
config = dbconfig.HashDefaults
} }
// Check root availability and state contents // Check root availability and state contents
state, err := New(root, NewDatabaseWithConfig(db, &config), nil) 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. // checkStateConsistency checks that all data of a state root is present.
func checkStateConsistency(db ethdb.Database, scheme string, root common.Hash) error { func checkStateConsistency(db ethdb.Database, scheme string, root common.Hash) error {
config := &triedb.Config{Preimages: true} var config triedb.Config
if scheme == rawdb.PathScheme { 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 { if err != nil {
return err 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. // Tests that an empty state is not scheduled for syncing.
func TestEmptyStateSync(t *testing.T) { func TestEmptyStateSync(t *testing.T) {
dbA := triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil) dbA := triedb.NewDatabase(rawdb.NewMemoryDatabase(), &dbconfig.HashDefaults)
dbB := triedb.NewDatabase(rawdb.NewMemoryDatabase(), &triedb.Config{PathDB: pathdb.Defaults}) dbB := triedb.NewDatabase(rawdb.NewMemoryDatabase(), &dbconfig.PathDefaults)
sync := NewStateSync(types.EmptyRootHash, rawdb.NewMemoryDatabase(), nil, dbA.Scheme()) 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 { 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++ { for i := 0; i < len(codes); i++ {
codeElements = append(codeElements, stateElement{code: codes[i]}) codeElements = append(codeElements, stateElement{code: codes[i]})
} }
reader, err := ndb.Reader(srcRoot) reader, err := ndb.NodeReader(srcRoot)
if err != nil { if err != nil {
t.Fatalf("state is not existent, %#x", srcRoot) 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++ { for i := 0; i < len(codes); i++ {
codeElements = append(codeElements, stateElement{code: codes[i]}) codeElements = append(codeElements, stateElement{code: codes[i]})
} }
reader, err := ndb.Reader(srcRoot) reader, err := ndb.NodeReader(srcRoot)
if err != nil { if err != nil {
t.Fatalf("state is not existent, %#x", srcRoot) 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 { for _, hash := range codes {
codeQueue[hash] = struct{}{} codeQueue[hash] = struct{}{}
} }
reader, err := ndb.Reader(srcRoot) reader, err := ndb.NodeReader(srcRoot)
if err != nil { if err != nil {
t.Fatalf("state is not existent, %#x", srcRoot) t.Fatalf("state is not existent, %#x", srcRoot)
} }
@ -522,7 +529,7 @@ func testIterativeRandomDelayedStateSync(t *testing.T, scheme string) {
for _, hash := range codes { for _, hash := range codes {
codeQueue[hash] = struct{}{} codeQueue[hash] = struct{}{}
} }
reader, err := ndb.Reader(srcRoot) reader, err := ndb.NodeReader(srcRoot)
if err != nil { if err != nil {
t.Fatalf("state is not existent, %#x", srcRoot) t.Fatalf("state is not existent, %#x", srcRoot)
} }
@ -627,7 +634,7 @@ func testIncompleteStateSync(t *testing.T, scheme string) {
addedPaths []string addedPaths []string
addedHashes []common.Hash addedHashes []common.Hash
) )
reader, err := ndb.Reader(srcRoot) reader, err := ndb.NodeReader(srcRoot)
if err != nil { if err != nil {
t.Fatalf("state is not available %x", srcRoot) t.Fatalf("state is not available %x", srcRoot)
} }

View file

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/dbconfig"
) )
func TestDeriveSha(t *testing.T) { func TestDeriveSha(t *testing.T) {
@ -40,7 +41,7 @@ func TestDeriveSha(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
for len(txs) < 1000 { 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)) got := types.DeriveSha(txs, trie.NewStackTrie(nil))
if !bytes.Equal(got[:], exp[:]) { if !bytes.Equal(got[:], exp[:]) {
t.Fatalf("%d txs: got %x exp %x", len(txs), 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.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { 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() rndSeed := mrand.Int()
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
seed := rndSeed + 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)) got := types.DeriveSha(newDummy(i), trie.NewStackTrie(nil))
if !bytes.Equal(got[:], exp[:]) { if !bytes.Equal(got[:], exp[:]) {
printList(newDummy(seed)) printList(newDummy(seed))
@ -136,7 +137,7 @@ func TestDerivableList(t *testing.T) {
}, },
} }
for i, tc := range tcs[1:] { 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)) got := types.DeriveSha(flatList(tc), trie.NewStackTrie(nil))
if !bytes.Equal(got[:], exp[:]) { if !bytes.Equal(got[:], exp[:]) {
t.Fatalf("case %d: got %x exp %x", i, got, exp) t.Fatalf("case %d: got %x exp %x", i, got, exp)

View file

@ -29,7 +29,7 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb/dbconfig"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"golang.org/x/exp/slices" "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) { func TestAccountRange(t *testing.T) {
t.Parallel() t.Parallel()
config := dbconfig.HashDefaults
config.Preimages = true
var ( var (
statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &triedb.Config{Preimages: true}) statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &config)
sdb, _ = state.New(types.EmptyRootHash, statedb, nil) sdb, _ = state.New(types.EmptyRootHash, statedb, nil)
addrs = [AccountRangeMaxResults * 2]common.Address{} addrs = [AccountRangeMaxResults * 2]common.Address{}
m = map[common.Address]bool{} m = map[common.Address]bool{}
) )
for i := range addrs { for i := range addrs {
hash := common.HexToHash(fmt.Sprintf("%x", i)) hash := common.HexToHash(fmt.Sprintf("%x", i))
addr := common.BytesToAddress(crypto.Keccak256Hash(hash.Bytes()).Bytes()) addr := common.BytesToAddress(crypto.Keccak256Hash(hash.Bytes()).Bytes())
@ -158,9 +160,12 @@ func TestEmptyAccountRange(t *testing.T) {
func TestStorageRangeAt(t *testing.T) { func TestStorageRangeAt(t *testing.T) {
t.Parallel() t.Parallel()
config := dbconfig.HashDefaults
config.Preimages = true
// Create a state where account 0x010000... has a few storage entries. // Create a state where account 0x010000... has a few storage entries.
var ( var (
db = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &triedb.Config{Preimages: true}) db = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &config)
sdb, _ = state.New(types.EmptyRootHash, db, nil) sdb, _ = state.New(types.EmptyRootHash, db, nil)
addr = common.Address{0x01} addr = common.Address{0x01}
keys = []common.Hash{ // hashes of Keys of storage keys = []common.Hash{ // hashes of Keys of storage

View file

@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/dbconfig"
) )
// Test chain parameters. // Test chain parameters.
@ -44,7 +45,7 @@ var (
Alloc: types.GenesisAlloc{testAddress: {Balance: big.NewInt(1000000000000000)}}, Alloc: types.GenesisAlloc{testAddress: {Balance: big.NewInt(1000000000000000)}},
BaseFee: big.NewInt(params.InitialBaseFee), 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: // The common prefix of all test chains:

View file

@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/dbconfig"
) )
func makeReceipt(addr common.Address) *types.Receipt { 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 // 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 // and then import blocks. TODO(rjl493456442) try to get rid of the
// manual database writes. // manual database writes.
gspec.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults)) gspec.MustCommit(db, triedb.NewDatabase(db, &dbconfig.HashDefaults))
for i, block := range chain { for i, block := range chain {
rawdb.WriteBlock(db, block) rawdb.WriteBlock(db, block)
@ -181,7 +182,7 @@ func TestFilters(t *testing.T) {
// Hack: GenerateChainWithGenesis creates a new db. // Hack: GenerateChainWithGenesis creates a new db.
// Commit the genesis manually and use GenerateChain. // 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -166,7 +166,10 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
b.AddTx(types.MustSignNewTx(key, signer, txdata)) b.AddTx(types.MustSignNewTx(key, signer, txdata))
}) })
// Construct testing chain // 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 { if err != nil {
t.Fatalf("Failed to create local chain, %v", err) t.Fatalf("Failed to create local chain, %v", err)
} }

View file

@ -32,13 +32,13 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/testrand"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "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/trie/trienode"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/pathdb" "github.com/ethereum/go-ethereum/triedb/dbconfig"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
@ -1816,8 +1816,8 @@ func makeUnevenStorageTrie(owner common.Hash, slots int, db *triedb.Database) (c
break break
} }
for j := 0; j < slots/3; j++ { for j := 0; j < slots/3; j++ {
key := append([]byte{byte(n)}, testutil.RandBytes(31)...) key := append([]byte{byte(n)}, testrand.Bytes(31)...)
val, _ := rlp.EncodeToBytes(testutil.RandBytes(32)) val, _ := rlp.EncodeToBytes(testrand.Bytes(32))
elem := &kv{key, val} elem := &kv{key, val}
tr.MustUpdate(elem.k, elem.v) tr.MustUpdate(elem.k, elem.v)
@ -1970,7 +1970,7 @@ func TestSlotEstimation(t *testing.T) {
func newDbConfig(scheme string) *triedb.Config { func newDbConfig(scheme string) *triedb.Config {
if scheme == rawdb.HashScheme { if scheme == rawdb.HashScheme {
return &triedb.Config{} return &dbconfig.HashDefaults
} }
return &triedb.Config{PathDB: pathdb.Defaults} return &dbconfig.PathDefaults
} }

View file

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/dbconfig"
) )
// noopReleaser is returned in case there is no operation expected // 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. // the internal junks created by tracing will be persisted into the disk.
// TODO(rjl493456442), clean cache is disabled to prevent memory leak, // TODO(rjl493456442), clean cache is disabled to prevent memory leak,
// please re-enable it for better performance. // 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 { 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()) log.Info("Found disk backend for state trie", "root", block.Root(), "number", block.Number())
return statedb, noopReleaser, nil 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. // the internal junks created by tracing will be persisted into the disk.
// TODO(rjl493456442), clean cache is disabled to prevent memory leak, // TODO(rjl493456442), clean cache is disabled to prevent memory leak,
// please re-enable it for better performance. // 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) database = state.NewDatabaseWithNodeDB(eth.chainDb, tdb)
// If we didn't check the live database, do check state over ephemeral database, // If we didn't check the live database, do check state over ephemeral database,

View file

@ -78,6 +78,7 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i i
TrieDirtyLimit: 256, TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute, TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 0, SnapshotLimit: 0,
StateScheme: rawdb.HashScheme,
TrieDirtyDisabled: true, // Archive mode TrieDirtyDisabled: true, // Archive mode
} }
chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, gspec, nil, backend.engine, vm.Config{}, nil, nil) chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, gspec, nil, backend.engine, vm.Config{}, nil, nil)

View file

@ -441,6 +441,7 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, engine consensus.E
TrieTimeLimit: 5 * time.Minute, TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 0, SnapshotLimit: 0,
TrieDirtyDisabled: true, // Archive mode TrieDirtyDisabled: true, // Archive mode
StateScheme: rawdb.HashScheme,
} }
) )
accman, acc := newTestAccountManager(t) accman, acc := newTestAccountManager(t)

View file

@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package testutil package testrand
import ( import (
crand "crypto/rand" crand "crypto/rand"
@ -22,11 +22,9 @@ import (
mrand "math/rand" mrand "math/rand"
"github.com/ethereum/go-ethereum/common" "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. // The randomness is printed on startup in order to make failures reproducible.
var prng = initRand() var prng = initRand()
@ -37,25 +35,19 @@ func initRand() *mrand.Rand {
return rnd return rnd
} }
// RandBytes generates a random byte slice with specified length. // Bytes generates a random byte slice with specified length.
func RandBytes(n int) []byte { func Bytes(n int) []byte {
r := make([]byte, n) r := make([]byte, n)
prng.Read(r) prng.Read(r)
return r return r
} }
// RandomHash generates a random blob of data and returns it as a hash. // Hash generates a random hash.
func RandomHash() common.Hash { func Hash() common.Hash {
return common.BytesToHash(RandBytes(common.HashLength)) return common.BytesToHash(Bytes(common.HashLength))
} }
// RandomAddress generates a random blob of data and returns it as an address. // Address generates a random address.
func RandomAddress() common.Address { func Address() common.Address {
return common.BytesToAddress(RandBytes(common.AddressLength)) return common.BytesToAddress(Bytes(common.AddressLength))
}
// RandomNode generates a random node.
func RandomNode() *trienode.Node {
val := RandBytes(100)
return trienode.New(crypto.Keccak256Hash(val), val)
} }

View file

@ -36,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/dbconfig"
) )
type mockBackend struct { type mockBackend struct {
@ -143,7 +144,7 @@ func createMiner(t *testing.T) *Miner {
} }
// Create chainConfig // Create chainConfig
chainDB := rawdb.NewMemoryDatabase() chainDB := rawdb.NewMemoryDatabase()
triedb := triedb.NewDatabase(chainDB, nil) triedb := triedb.NewDatabase(chainDB, &dbconfig.HashDefaults)
genesis := minerTestGenesisBlock(15, 11_500_000, common.HexToAddress("12345")) genesis := minerTestGenesisBlock(15, 11_500_000, common.HexToAddress("12345"))
chainConfig, _, err := core.SetupGenesisBlock(chainDB, triedb, genesis) chainConfig, _, err := core.SetupGenesisBlock(chainDB, triedb, genesis)
if err != nil { if err != nil {

View file

@ -40,8 +40,7 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/dbconfig"
"github.com/ethereum/go-ethereum/triedb/pathdb"
) )
// A BlockTest checks handling of entire blocks. // 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 // import pre accounts & construct test genesis block & state root
var ( var (
db = rawdb.NewMemoryDatabase() db = rawdb.NewMemoryDatabase()
tconf = &triedb.Config{ dbconf triedb.Config
Preimages: true,
}
) )
if scheme == rawdb.PathScheme { if scheme == rawdb.PathScheme {
tconf.PathDB = pathdb.Defaults dbconf = dbconfig.PathDefaults
} else { } else {
tconf.HashDB = hashdb.Defaults dbconf = dbconfig.HashDefaults
} }
dbconf.Preimages = true
// Commit genesis state // Commit genesis state
gspec := t.genesis(config) gspec := t.genesis(config)
triedb := triedb.NewDatabase(db, tconf) triedb := triedb.NewDatabase(db, &dbconf)
gblock, err := gspec.Commit(db, triedb) gblock, err := gspec.Commit(db, triedb)
if err != nil { if err != nil {
return err return err

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/dbconfig"
"golang.org/x/exp/slices" "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) { 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) vals := make(map[string]*kv)
size := f.readInt() size := f.readInt()
// Fill it with some fluff // Fill it with some fluff

View file

@ -40,8 +40,7 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/dbconfig"
"github.com/ethereum/go-ethereum/triedb/pathdb"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
@ -444,13 +443,15 @@ type StateTestState struct {
// MakePreState creates a state containing the given allocation. // MakePreState creates a state containing the given allocation.
func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, snapshotter bool, scheme string) StateTestState { 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 { if scheme == rawdb.HashScheme {
tconf.HashDB = hashdb.Defaults dbconf = dbconfig.HashDefaults
} else { } 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) sdb := state.NewDatabaseWithNodeDB(db, triedb)
statedb, _ := state.New(types.EmptyRootHash, sdb, nil) statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
for addr, a := range accounts { for addr, a := range accounts {

View file

@ -154,12 +154,8 @@ func (c *committer) store(path []byte, n node) node {
return hash return hash
} }
// MerkleResolver the children resolver in merkle-patricia-tree. // MerkleResolver decodes the provided node and traverses the children inside.
type MerkleResolver struct{} func MerkleResolver(node []byte, onChild func(common.Hash)) {
// ForEach implements childResolver, decodes the provided node and
// traverses the children inside.
func (resolver MerkleResolver) ForEach(node []byte, onChild func(common.Hash)) {
forGatherChildren(mustDecodeNodeUnsafe(nil, node), onChild) forGatherChildren(mustDecodeNodeUnsafe(nil, node), onChild)
} }

View file

@ -25,7 +25,7 @@ import (
"github.com/ethereum/go-ethereum/triedb/database" "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. // access trie nodes.
type testReader struct { type testReader struct {
db ethdb.Database db ethdb.Database
@ -33,7 +33,7 @@ type testReader struct {
nodes []*trienode.MergedNodeSet // sorted from new to old 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. // all available cached layers.
func (r *testReader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) { 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. // 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) nodes, _ := db.dirties(stateRoot, true)
return &testReader{db: db.disk, scheme: db.scheme, nodes: nodes}, nil return &testReader{db: db.disk, scheme: db.scheme, nodes: nodes}, nil
} }

View file

@ -146,7 +146,7 @@ func testNodeIteratorCoverage(t *testing.T, scheme string) {
} }
} }
// Cross check the hashes and the database itself // Cross check the hashes and the database itself
reader, err := nodeDb.Reader(trie.Hash()) reader, err := nodeDb.NodeReader(trie.Hash())
if err != nil { if err != nil {
t.Fatalf("state is not available %x", trie.Hash()) t.Fatalf("state is not available %x", trie.Hash())
} }

View file

@ -25,7 +25,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/crypto" "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" "github.com/stretchr/testify/assert"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
) )
@ -431,12 +431,12 @@ func TestPartialStackTrie(t *testing.T) {
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
var val []byte var val []byte
if rand.Intn(3) == 0 { if rand.Intn(3) == 0 {
val = testutil.RandBytes(3) val = testrand.Bytes(3)
} else { } else {
val = testutil.RandBytes(32) val = testrand.Bytes(32)
} }
entries = append(entries, &kv{ entries = append(entries, &kv{
k: testutil.RandBytes(32), k: testrand.Bytes(32),
v: val, v: val,
}) })
} }

View file

@ -182,7 +182,7 @@ func testIterativeSync(t *testing.T, count int, bypath bool, scheme string) {
syncPath: NewSyncPath([]byte(paths[i])), syncPath: NewSyncPath([]byte(paths[i])),
}) })
} }
reader, err := srcDb.Reader(srcTrie.Hash()) reader, err := srcDb.NodeReader(srcTrie.Hash())
if err != nil { if err != nil {
t.Fatalf("State is not available %x", srcTrie.Hash()) 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])), syncPath: NewSyncPath([]byte(paths[i])),
}) })
} }
reader, err := srcDb.Reader(srcTrie.Hash()) reader, err := srcDb.NodeReader(srcTrie.Hash())
if err != nil { if err != nil {
t.Fatalf("State is not available %x", srcTrie.Hash()) 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])), syncPath: NewSyncPath([]byte(paths[i])),
} }
} }
reader, err := srcDb.Reader(srcTrie.Hash()) reader, err := srcDb.NodeReader(srcTrie.Hash())
if err != nil { if err != nil {
t.Fatalf("State is not available %x", srcTrie.Hash()) t.Fatalf("State is not available %x", srcTrie.Hash())
} }
@ -393,7 +393,7 @@ func testIterativeRandomDelayedSync(t *testing.T, scheme string) {
syncPath: NewSyncPath([]byte(path)), syncPath: NewSyncPath([]byte(path)),
} }
} }
reader, err := srcDb.Reader(srcTrie.Hash()) reader, err := srcDb.NodeReader(srcTrie.Hash())
if err != nil { if err != nil {
t.Fatalf("State is not available %x", srcTrie.Hash()) 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])), syncPath: NewSyncPath([]byte(paths[i])),
}) })
} }
reader, err := srcDb.Reader(srcTrie.Hash()) reader, err := srcDb.NodeReader(srcTrie.Hash())
if err != nil { if err != nil {
t.Fatalf("State is not available %x", srcTrie.Hash()) 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])), syncPath: NewSyncPath([]byte(paths[i])),
}) })
} }
reader, err := srcDb.Reader(srcTrie.Hash()) reader, err := srcDb.NodeReader(srcTrie.Hash())
if err != nil { if err != nil {
t.Fatalf("State is not available %x", srcTrie.Hash()) 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]))) reqs = append(reqs, NewSyncPath([]byte(paths[i])))
} }
reader, err := srcDb.Reader(srcTrie.Hash()) reader, err := srcDb.NodeReader(srcTrie.Hash())
if err != nil { if err != nil {
t.Fatalf("State is not available %x", srcTrie.Hash()) 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])), syncPath: NewSyncPath([]byte(paths[i])),
}) })
} }
reader, err := srcDb.Reader(root) reader, err := srcDb.NodeReader(root)
if err != nil { if err != nil {
t.Fatalf("State is not available %x", root) t.Fatalf("State is not available %x", root)
} }

View file

@ -80,7 +80,7 @@ func (t *Trie) Copy() *Trie {
// zero hash or the sha3 hash of an empty string, then trie is initially // 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 // empty, otherwise, the root node must be present in database or returns
// a MissingNodeError if not. // 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) reader, err := newTrieReader(id.StateRoot, id.Owner, db)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -20,27 +20,27 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "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/database"
"github.com/ethereum/go-ethereum/triedb/state"
) )
// trieReader is a wrapper of the underlying node reader. It's not safe // trieReader is a wrapper of the underlying node reader. It's not safe
// for concurrent usage. // for concurrent usage.
type trieReader struct { type trieReader struct {
owner common.Hash owner common.Hash
reader database.Reader reader database.NodeReader
banned map[string]struct{} // Marker to prevent node from being accessed, for tests banned map[string]struct{} // Marker to prevent node from being accessed, for tests
} }
// newTrieReader initializes the trie reader with the given node reader. // 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{}) || stateRoot == types.EmptyRootHash {
if stateRoot == (common.Hash{}) { if stateRoot == (common.Hash{}) {
log.Error("Zero state root hash!") log.Error("Zero state root hash!")
} }
return &trieReader{owner: owner}, nil return &trieReader{owner: owner}, nil
} }
reader, err := db.Reader(stateRoot) reader, err := db.NodeReader(stateRoot)
if err != nil { if err != nil {
return nil, &MissingNodeError{Owner: owner, NodeHash: stateRoot, err: err} 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 return blob, nil
} }
// MerkleLoader implements triestate.TrieLoader for constructing tries. // MerkleOpener implements state.TrieOpener for constructing tries.
type MerkleLoader struct { type MerkleOpener struct {
db database.Database db database.NodeDatabase
} }
// NewMerkleLoader creates the merkle trie loader. // NewMerkleOpener creates the merkle trie opener.
func NewMerkleLoader(db database.Database) *MerkleLoader { func NewMerkleOpener(db database.NodeDatabase) state.TrieOpener {
return &MerkleLoader{db: db} return &MerkleOpener{db: db}
} }
// OpenTrie opens the main account trie. // 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) return New(TrieID(root), l.db)
} }
// OpenStorageTrie opens the storage trie of an account. // 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) return New(StorageTrieID(stateRoot, addrHash, root), l.db)
} }

View file

@ -45,7 +45,7 @@ type VerkleTrie struct {
} }
// NewVerkleTrie constructs a verkle tree based on the specified root hash. // 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) reader, err := newTrieReader(root, common.Hash{}, db)
if err != nil { if err != nil {
return nil, err return nil, err

61
triedb/config.go Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
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
}

View file

@ -22,29 +22,13 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "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/trienode"
"github.com/ethereum/go-ethereum/trie/triestate"
"github.com/ethereum/go-ethereum/triedb/database" "github.com/ethereum/go-ethereum/triedb/database"
"github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/hashdb"
"github.com/ethereum/go-ethereum/triedb/pathdb" "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 // backend defines the methods needed to access/update trie nodes in different
// state scheme. // state scheme.
type backend interface { type backend interface {
@ -62,13 +46,17 @@ type backend interface {
// and dirty disk layer nodes, so both are merged into the second return. // and dirty disk layer nodes, so both are merged into the second return.
Size() (common.StorageSize, common.StorageSize) 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 // Update performs a state transition by committing dirty nodes contained
// in the given set in order to update state from the specified parent to // in the given set in order to update state from the specified parent to
// the specified root. // the specified root.
// //
// The passed in maps(nodes, states) will be retained to avoid copying // The passed in maps(nodes, states) will be retained to avoid copying
// everything. Therefore, these maps must not be changed afterwards. // 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 // Commit writes all relevant trie nodes belonging to the specified state
// to disk. Report specifies whether logs will be displayed in info level. // 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 // NewDatabase initializes the trie database with default settings, note
// the legacy hash-based scheme is used by default. // the legacy hash-based scheme is used by default.
func NewDatabase(diskdb ethdb.Database, config *Config) *Database { func NewDatabase(diskdb ethdb.Database, config *Config) *Database {
// Sanitize the config and use the default one if it's not specified. if err := config.sanitize(); err != nil {
if config == nil { log.Crit("Database config is invalid", "error", err)
config = HashDefaults
} }
var preimages *preimageStore var preimages *preimageStore
if config.Preimages { if config.Preimages {
@ -104,34 +91,18 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database {
diskdb: diskdb, diskdb: diskdb,
preimages: preimages, preimages: preimages,
} }
if config.HashDB != nil && config.PathDB != nil {
log.Crit("Both 'hash' and 'path' mode are configured")
}
if config.PathDB != nil { if config.PathDB != nil {
db.backend = pathdb.New(diskdb, config.PathDB) db.backend = pathdb.New(diskdb, config.PathDB)
} else { } else {
var resolver hashdb.ChildResolver db.backend = hashdb.New(diskdb, config.HashDB)
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)
} }
return db return db
} }
// Reader returns a reader for accessing all trie nodes with provided state root. // NodeReader returns a reader for accessing all trie nodes with provided state
// An error will be returned if the requested state is not available. // root. An error will be returned if the requested state is not available.
func (db *Database) Reader(blockRoot common.Hash) (database.Reader, error) { func (db *Database) NodeReader(blockRoot common.Hash) (database.NodeReader, error) {
switch b := db.backend.(type) { return db.backend.NodeReader(blockRoot)
case *hashdb.Database:
return b.Reader(blockRoot)
case *pathdb.Database:
return b.Reader(blockRoot)
}
return nil, errors.New("unknown backend")
} }
// Update performs a state transition by committing dirty nodes contained in the // 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. // The passed in maps(nodes, states) will be retained to avoid copying everything.
// Therefore, these maps must not be changed afterwards. // 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 { if db.preimages != nil {
db.preimages.commit(false) db.preimages.commit(false)
} }
@ -265,14 +236,7 @@ func (db *Database) Recover(target common.Hash) error {
if !ok { if !ok {
return errors.New("not supported") return errors.New("not supported")
} }
var loader triestate.TrieLoader return pdb.Recover(target)
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)
} }
// Recoverable returns the indicator if the specified state is enabled to be // Recoverable returns the indicator if the specified state is enabled to be

View file

@ -16,18 +16,23 @@
package database package database
import ( import "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
)
// Reader wraps the Node method of a backing trie reader. // NodeReader wraps the Node method of a backing trie reader.
type Reader interface { type NodeReader interface {
// Node retrieves the trie node blob with the provided trie identifier, // Node retrieves the trie node blob with the provided trie identifier,
// node path and the corresponding node hash. No error will be returned // node path and the corresponding node hash. No error will be returned
// if the node is not found. // if the node is not found.
Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) 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 // PreimageStore wraps the methods of a backing store for reading and writing
// trie node preimages. // trie node preimages.
type PreimageStore interface { type PreimageStore interface {
@ -41,8 +46,5 @@ type PreimageStore interface {
// Database wraps the methods of a backing trie store. // Database wraps the methods of a backing trie store.
type Database interface { type Database interface {
PreimageStore PreimageStore
NodeDatabase
// 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)
} }

62
triedb/dbconfig/config.go Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
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,
},
}

View file

@ -32,7 +32,8 @@ import (
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie/trienode" "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 ( var (
@ -59,32 +60,42 @@ var (
memcacheCommitBytesMeter = metrics.NewRegisteredMeter("hashdb/memcache/commit/bytes", nil) 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. // Config contains the settings for database.
type Config struct { 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. // sanitize validates the provided config.
// Notably, clean cache is disabled explicitly, func (config *Config) sanitize() error {
var Defaults = &Config{ if config == nil {
// Explicitly set clean cache size to 0 to avoid creating fastcache, return errors.New("hashdb config is nil")
// otherwise database must be closed when it's no longer needed to }
// prevent memory leak. if config.ChildResolver == nil {
CleanCacheSize: 0, 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 // 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 // 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. // periodically flush a couple tries to disk, garbage collecting the remainder.
type Database struct { type Database struct {
diskdb ethdb.Database // Persistent storage for matured trie nodes diskdb ethdb.Database // Persistent storage for matured trie nodes
resolver ChildResolver // The handler to resolve children of nodes config *Config // configuration used in hash db
cleans *fastcache.Cache // GC friendly memory cache of clean node RLPs cleans *fastcache.Cache // GC friendly memory cache of clean node RLPs
dirties map[common.Hash]*cachedNode // Data and references relationships of dirty trie nodes 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, // 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 // both the implicit ones from inside the node as well as the explicit ones
// from outside the node. // 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 { for child := range n.external {
onChild(child) onChild(child)
} }
resolver.ForEach(n.node, onChild) resolver(n.node, onChild)
} }
// New initializes the hash-based node database. // New initializes the hash-based node database.
func New(diskdb ethdb.Database, config *Config, resolver ChildResolver) *Database { func New(diskdb ethdb.Database, config *Config) *Database {
if config == nil { if err := config.sanitize(); err != nil {
config = Defaults log.Crit("Hash database config is invalid", "error", err)
} }
var cleans *fastcache.Cache var cleans *fastcache.Cache
if config.CleanCacheSize > 0 { if config.CleanCacheSize > 0 {
cleans = fastcache.New(config.CleanCacheSize) cleans = fastcache.New(config.CleanCacheSize)
} }
return &Database{ return &Database{
diskdb: diskdb, diskdb: diskdb,
resolver: resolver, config: config,
cleans: cleans, cleans: cleans,
dirties: make(map[common.Hash]*cachedNode), dirties: make(map[common.Hash]*cachedNode),
} }
} }
@ -162,7 +173,7 @@ func (db *Database) insert(hash common.Hash, node []byte) {
node: node, node: node,
flushPrev: db.newest, 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 { if c := db.dirties[child]; c != nil {
c.parents++ c.parents++
} }
@ -315,7 +326,7 @@ func (db *Database) dereference(hash common.Hash) {
db.dirties[node.flushNext].flushPrev = node.flushPrev db.dirties[node.flushNext].flushPrev = node.flushPrev
} }
// Dereference all children and delete the node // 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) db.dereference(child)
}) })
delete(db.dirties, hash) delete(db.dirties, hash)
@ -464,7 +475,7 @@ func (db *Database) commit(hash common.Hash, batch ethdb.Batch, uncacher *cleane
var err error var err error
// Dereference all children and delete the node // 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 { if err == nil {
err = db.commit(child, batch, uncacher) 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 // Update inserts the dirty nodes in provided nodeset into database and link the
// account trie with multiple storage tries if necessary. // 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. // Ensure the parent state is present and signal a warning if not.
if parent != types.EmptyRootHash { if parent != types.EmptyRootHash {
if blob, _ := db.node(parent); len(blob) == 0 { if blob, _ := db.node(parent); len(blob) == 0 {
@ -629,9 +640,9 @@ func (db *Database) Scheme() string {
return rawdb.HashScheme 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. // 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 { if _, err := db.node(root); err != nil {
return nil, fmt.Errorf("state %#x is not available, %v", root, err) return nil, fmt.Errorf("state %#x is not available, %v", root, err)
} }

63
triedb/pathdb/config.go Normal file
View file

@ -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 <http://www.gnu.org/licenses/>
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,
}
}

View file

@ -28,17 +28,14 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "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/trienode"
"github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/triedb/database"
"github.com/ethereum/go-ethereum/triedb/state"
) )
const ( const (
// maxDiffLayers is the maximum diff layers allowed in the layer tree. // DefaultCleanSize is the default memory allowance of clean cache.
maxDiffLayers = 128 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. // maxBufferSize is the maximum memory allowance of node buffer.
// Too large nodebuffer will cause the system to pause for a long // Too large nodebuffer will cause the system to pause for a long
@ -54,6 +51,9 @@ const (
DefaultBufferSize = 64 * 1024 * 1024 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 // layer is the interface implemented by all state layers which includes some
// public methods and some additional methods for internal usage. // public methods and some additional methods for internal usage.
type layer interface { type layer interface {
@ -76,7 +76,7 @@ type layer interface {
// the provided dirty trie nodes along with the state change set. // the provided dirty trie nodes along with the state change set.
// //
// Note, the maps are retained by the method to avoid copying everything. // 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. // 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 // 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 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. // 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 // 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 // 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 diskdb ethdb.Database // Persistent storage for matured trie nodes
tree *layerTree // The group for all known layers tree *layerTree // The group for all known layers
freezer *rawdb.ResettableFreezer // Freezer for storing trie histories, nil possible in tests 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 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 // 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. // matched with the base persistent layer, all the recorded diff layers are discarded.
func New(diskdb ethdb.Database, config *Config) *Database { func New(diskdb ethdb.Database, config *Config) *Database {
if config == nil { config, err := config.sanitize()
config = Defaults if err != nil {
log.Crit("Path database config is invalid", "err", err)
} }
config = config.sanitize()
db := &Database{ db := &Database{
readOnly: config.ReadOnly, readOnly: config.ReadOnly,
bufferSize: config.DirtyCacheSize, 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 // Construct the layer tree by resolving the in-disk singleton state
// and in-memory layer journal. // and in-memory layer journal.
db.tree = newLayerTree(db.loadLayers()) db.tree = newLayerTree(db.loadLayers())
db.trieOpener = db.config.TrieOpener(db)
// Open the freezer for state history if the passed database contains an // Open the freezer for state history if the passed database contains an
// ancient store. Otherwise, all the relevant functionalities are disabled. // ancient store. Otherwise, all the relevant functionalities are disabled.
@ -206,8 +178,8 @@ func New(diskdb ethdb.Database, config *Config) *Database {
return db return db
} }
// Reader retrieves a layer belonging to the given state root. // NodeReader retrieves a layer belonging to the given state root.
func (db *Database) Reader(root common.Hash) (layer, error) { func (db *Database) NodeReader(root common.Hash) (database.NodeReader, error) {
l := db.tree.get(root) l := db.tree.get(root)
if l == nil { if l == nil {
return nil, fmt.Errorf("state %#x is not available", root) 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. // The passed in maps(nodes, states) will be retained to avoid copying everything.
// Therefore, these maps must not be changed afterwards. // 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. // Hold the lock to prevent concurrent mutations.
db.lock.Lock() db.lock.Lock()
defer db.lock.Unlock() 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. // Recover rollbacks the database to a specified historical point.
// The state is supported as the rollback destination only if it's // The state is supported as the rollback destination only if it's
// canonical state and the corresponding trie histories are existent. // 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() db.lock.Lock()
defer db.lock.Unlock() defer db.lock.Unlock()
@ -357,7 +329,7 @@ func (db *Database) Recover(root common.Hash, loader triestate.TrieLoader) error
if err != nil { if err != nil {
return err return err
} }
dl, err = dl.revert(h, loader) dl, err = dl.revert(h)
if err != nil { if err != nil {
return err return err
} }

View file

@ -27,10 +27,11 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/internal/testrand"
"github.com/ethereum/go-ethereum/rlp" "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/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" "github.com/holiman/uint256"
) )
@ -54,7 +55,7 @@ func generateAccount(storageRoot common.Hash) types.StateAccount {
return types.StateAccount{ return types.StateAccount{
Nonce: uint64(rand.Intn(100)), Nonce: uint64(rand.Intn(100)),
Balance: uint256.NewInt(rand.Uint64()), Balance: uint256.NewInt(rand.Uint64()),
CodeHash: testutil.RandBytes(32), CodeHash: testrand.Bytes(32),
Root: storageRoot, Root: storageRoot,
} }
} }
@ -98,22 +99,27 @@ type tester struct {
func newTester(t *testing.T, historyLimit uint64) *tester { func newTester(t *testing.T, historyLimit uint64) *tester {
var ( var (
disk, _ = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false) snapAccounts = make(map[common.Hash]map[common.Hash][]byte)
db = New(disk, &Config{ 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, StateHistory: historyLimit,
CleanCacheSize: 256 * 1024, CleanCacheSize: 16 * 1024,
DirtyCacheSize: 256 * 1024, DirtyCacheSize: 16 * 1024,
TrieOpener: func(db database.NodeDatabase) state.TrieOpener {
return newHashOpener(snapAccounts, snapStorages)
},
}) })
obj = &tester{ obj = &tester{
db: db, db: db,
preimages: make(map[common.Hash]common.Address), preimages: make(map[common.Hash]common.Address),
accounts: make(map[common.Hash][]byte), accounts: make(map[common.Hash][]byte),
storages: make(map[common.Hash]map[common.Hash][]byte), storages: make(map[common.Hash]map[common.Hash][]byte),
snapAccounts: make(map[common.Hash]map[common.Hash][]byte), snapAccounts: snapAccounts,
snapStorages: make(map[common.Hash]map[common.Hash]map[common.Hash][]byte), snapStorages: snapStorages,
} }
) )
for i := 0; i < 2*128; i++ { for i := 0; i < 16; i++ {
var parent = types.EmptyRootHash var parent = types.EmptyRootHash
if len(obj.roots) != 0 { if len(obj.roots) != 0 {
parent = obj.roots[len(obj.roots)-1] 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) origin = make(map[common.Hash][]byte)
) )
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testutil.RandBytes(32))) v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testrand.Bytes(32)))
hash := testutil.RandomHash() hash := testrand.Hash()
storage[hash] = v storage[hash] = v
origin[hash] = nil 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++ { for i := 0; i < 3; i++ {
v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testutil.RandBytes(32))) v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testrand.Bytes(32)))
hash := testutil.RandomHash() hash := testrand.Hash()
storage[hash] = v storage[hash] = v
origin[hash] = nil origin[hash] = nil
@ -209,7 +215,7 @@ func (t *tester) clearStorage(ctx *genctx, addr common.Address, root common.Hash
return root 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 ( var (
ctx = newCtx() ctx = newCtx()
dirties = make(map[common.Hash]struct{}) 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) { switch rand.Intn(opLen) {
case createAccountOp: case createAccountOp:
// account creation // account creation
addr := testutil.RandomAddress() addr := testrand.Address()
addrHash := crypto.Keccak256Hash(addr.Bytes()) addrHash := crypto.Keccak256Hash(addr.Bytes())
if _, ok := t.accounts[addrHash]; ok { if _, ok := t.accounts[addrHash]; ok {
continue 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. // 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 { func (t *tester) verifyState(root common.Hash) error {
reader, err := t.db.Reader(root) reader, err := t.db.NodeReader(root)
if err != nil { if err != nil {
return err return err
} }
@ -379,6 +385,10 @@ func (t *tester) bottomIndex() int {
} }
func TestDatabaseRollback(t *testing.T) { func TestDatabaseRollback(t *testing.T) {
maxDiffLayers = 4
defer func() {
maxDiffLayers = 128
}()
// Verify state histories // Verify state histories
tester := newTester(t, 0) tester := newTester(t, 0)
defer tester.release() defer tester.release()
@ -388,13 +398,11 @@ func TestDatabaseRollback(t *testing.T) {
} }
// Revert database from top to bottom // Revert database from top to bottom
for i := tester.bottomIndex(); i >= 0; i-- { for i := tester.bottomIndex(); i >= 0; i-- {
root := tester.roots[i]
parent := types.EmptyRootHash parent := types.EmptyRootHash
if i > 0 { if i > 0 {
parent = tester.roots[i-1] parent = tester.roots[i-1]
} }
loader := newHashLoader(tester.snapAccounts[root], tester.snapStorages[root]) if err := tester.db.Recover(parent); err != nil {
if err := tester.db.Recover(parent, loader); err != nil {
t.Fatalf("Failed to revert db, err: %v", err) t.Fatalf("Failed to revert db, err: %v", err)
} }
if i > 0 { if i > 0 {
@ -409,6 +417,10 @@ func TestDatabaseRollback(t *testing.T) {
} }
func TestDatabaseRecoverable(t *testing.T) { func TestDatabaseRecoverable(t *testing.T) {
maxDiffLayers = 4
defer func() {
maxDiffLayers = 128
}()
var ( var (
tester = newTester(t, 0) tester = newTester(t, 0)
index = tester.bottomIndex() index = tester.bottomIndex()
@ -448,6 +460,10 @@ func TestDatabaseRecoverable(t *testing.T) {
} }
func TestDisable(t *testing.T) { func TestDisable(t *testing.T) {
maxDiffLayers = 4
defer func() {
maxDiffLayers = 128
}()
tester := newTester(t, 0) tester := newTester(t, 0)
defer tester.release() defer tester.release()
@ -459,7 +475,7 @@ func TestDisable(t *testing.T) {
t.Fatalf("Invalid activation should be rejected") t.Fatalf("Invalid activation should be rejected")
} }
if err := tester.db.Enable(stored); err != nil { 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 // Ensure journal is deleted from disk
@ -484,6 +500,10 @@ func TestDisable(t *testing.T) {
} }
func TestCommit(t *testing.T) { func TestCommit(t *testing.T) {
maxDiffLayers = 4
defer func() {
maxDiffLayers = 128
}()
tester := newTester(t, 0) tester := newTester(t, 0)
defer tester.release() defer tester.release()
@ -508,6 +528,10 @@ func TestCommit(t *testing.T) {
} }
func TestJournal(t *testing.T) { func TestJournal(t *testing.T) {
maxDiffLayers = 4
defer func() {
maxDiffLayers = 128
}()
tester := newTester(t, 0) tester := newTester(t, 0)
defer tester.release() defer tester.release()
@ -515,7 +539,7 @@ func TestJournal(t *testing.T) {
t.Errorf("Failed to journal, err: %v", err) t.Errorf("Failed to journal, err: %v", err)
} }
tester.db.Close() 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. // Verify states including disk layer and all diff on top.
for i := 0; i < len(tester.roots); i++ { for i := 0; i < len(tester.roots); i++ {
@ -532,6 +556,10 @@ func TestJournal(t *testing.T) {
} }
func TestCorruptedJournal(t *testing.T) { func TestCorruptedJournal(t *testing.T) {
maxDiffLayers = 4
defer func() {
maxDiffLayers = 128
}()
tester := newTester(t, 0) tester := newTester(t, 0)
defer tester.release() defer tester.release()
@ -547,7 +575,7 @@ func TestCorruptedJournal(t *testing.T) {
rawdb.WriteTrieJournal(tester.db.diskdb, blob) rawdb.WriteTrieJournal(tester.db.diskdb, blob)
// Verify states, all not-yet-written states should be discarded // 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++ { for i := 0; i < len(tester.roots); i++ {
if tester.roots[i] == root { if tester.roots[i] == root {
if err := tester.verifyState(root); err != nil { 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 // 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]. // always falls within the range of [oldest-history-id, latest-history-id].
func TestTailTruncateHistory(t *testing.T) { func TestTailTruncateHistory(t *testing.T) {
maxDiffLayers = 4
defer func() {
maxDiffLayers = 128
}()
tester := newTester(t, 10) tester := newTester(t, 10)
defer tester.release() defer tester.release()
tester.db.Close() 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() head, err := tester.db.freezer.Ancients()
if err != nil { if err != nil {

View file

@ -23,7 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie/trienode" "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 // diffLayer represents a collection of modifications made to the in-memory tries
@ -37,7 +37,7 @@ type diffLayer struct {
id uint64 // Corresponding state id id uint64 // Corresponding state id
block uint64 // Associated block number block uint64 // Associated block number
nodes map[common.Hash]map[string]*trienode.Node // Cached trie nodes indexed by owner and path 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 memory uint64 // Approximate guess as to how much memory we use
parent layer // Parent layer modified by this one, never nil, **can be changed** 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. // 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 ( var (
size int64 size int64
count int 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 // update implements the layer interface, creating a new layer on top of the
// existing layer tree with the specified data items. // 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) return newDiffLayer(dl, root, id, block, nodes, states)
} }

View file

@ -22,7 +22,8 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb" "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" "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) nodes[common.Hash{}] = make(map[string]*trienode.Node)
for i := 0; i < 3000; i++ { for i := 0; i < 3000; i++ {
var ( var (
path = testutil.RandBytes(32) path = testrand.Bytes(32)
node = testutil.RandomNode() blob = testrand.Bytes(100)
node = trienode.New(crypto.Keccak256Hash(blob), blob)
) )
nodes[common.Hash{}][string(path)] = trienode.New(node.Hash, node.Blob) nodes[common.Hash{}][string(path)] = trienode.New(node.Hash, node.Blob)
if npath == nil && depth == index { if npath == nil && depth == index {
@ -112,8 +114,9 @@ func BenchmarkPersist(b *testing.B) {
nodes[common.Hash{}] = make(map[string]*trienode.Node) nodes[common.Hash{}] = make(map[string]*trienode.Node)
for i := 0; i < 3000; i++ { for i := 0; i < 3000; i++ {
var ( var (
path = testutil.RandBytes(32) path = testrand.Bytes(32)
node = testutil.RandomNode() blob = testrand.Bytes(100)
node = trienode.New(crypto.Keccak256Hash(blob), blob)
) )
nodes[common.Hash{}][string(path)] = trienode.New(node.Hash, node.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) nodes[common.Hash{}] = make(map[string]*trienode.Node)
for i := 0; i < 3000; i++ { for i := 0; i < 3000; i++ {
var ( var (
path = testutil.RandBytes(32) path = testrand.Bytes(32)
node = testutil.RandomNode() blob = testrand.Bytes(100)
node = trienode.New(crypto.Keccak256Hash(blob), blob)
) )
nodes[common.Hash{}][string(path)] = trienode.New(node.Hash, node.Blob) nodes[common.Hash{}][string(path)] = trienode.New(node.Hash, node.Blob)
} }

View file

@ -26,7 +26,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie/trienode" "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" "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 // update implements the layer interface, returning a new diff layer on top
// with the given state set. // 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) 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. // 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() { if h.meta.root != dl.rootHash() {
return nil, errUnexpectedHistory return nil, errUnexpectedHistory
} }
if dl.id == 0 { if dl.id == 0 {
return nil, fmt.Errorf("%w: zero state id", errStateUnrecoverable) 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 // Apply the reverse state changes upon the current state. This must
// be done before holding the lock in order to access state in "this" // be done before holding the lock in order to access state in "this"
// layer. // 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 { if err != nil {
return nil, err return nil, err
} }

View file

@ -27,7 +27,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "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" "golang.org/x/exp/slices"
) )
@ -242,7 +242,7 @@ type history struct {
} }
// newHistory constructs the state history object with provided state change set. // 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 ( var (
accountList []common.Address accountList []common.Address
storageList = make(map[common.Address][]common.Hash) storageList = make(map[common.Address][]common.Hash)

View file

@ -26,32 +26,32 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/testrand"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie/testutil" "github.com/ethereum/go-ethereum/triedb/state"
"github.com/ethereum/go-ethereum/trie/triestate"
) )
// randomStateSet generates a random state change set. // randomStateSet generates a random state change set.
func randomStateSet(n int) *triestate.Set { func randomStateSet(n int) *state.Origin {
var ( var (
accounts = make(map[common.Address][]byte) accounts = make(map[common.Address][]byte)
storages = make(map[common.Address]map[common.Hash][]byte) storages = make(map[common.Address]map[common.Hash][]byte)
) )
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
addr := testutil.RandomAddress() addr := testrand.Address()
storages[addr] = make(map[common.Hash][]byte) storages[addr] = make(map[common.Hash][]byte)
for j := 0; j < 3; j++ { for j := 0; j < 3; j++ {
v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testutil.RandBytes(32))) v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testrand.Bytes(32)))
storages[addr][testutil.RandomHash()] = v storages[addr][testrand.Hash()] = v
} }
account := generateAccount(types.EmptyRootHash) account := generateAccount(types.EmptyRootHash)
accounts[addr] = types.SlimAccountRLP(account) accounts[addr] = types.SlimAccountRLP(account)
} }
return triestate.New(accounts, storages) return state.NewOrigin(accounts, storages)
} }
func makeHistory() *history { 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 { func makeHistories(n int) []*history {
@ -60,7 +60,7 @@ func makeHistories(n int) []*history {
result []*history result []*history
) )
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
root := testutil.RandomHash() root := testrand.Hash()
h := newHistory(root, parent, uint64(i), randomStateSet(3)) h := newHistory(root, parent, uint64(i), randomStateSet(3))
parent = root parent = root
result = append(result, h) result = append(result, h)

View file

@ -30,7 +30,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/triedb/state"
) )
var ( var (
@ -239,7 +239,7 @@ func (db *Database) loadDiffLayer(parent layer, r *rlp.Stream) (layer, error) {
} }
storages[entry.Account] = set 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 // journal implements the layer interface, marshaling the un-flushed trie nodes

View file

@ -24,7 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/trie/trienode" "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. // 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. // 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 // 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 // happen for clique networks and proof-of-stake networks where empty blocks
// don't modify the state (0 block subsidy). // don't modify the state (0 block subsidy).

View file

@ -24,7 +24,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/trie/trienode" "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" "golang.org/x/exp/slices"
) )
@ -133,24 +133,24 @@ func hash(states map[common.Hash][]byte) (common.Hash, []byte) {
return crypto.Keccak256Hash(input), input return crypto.Keccak256Hash(input), input
} }
type hashLoader struct { type hashOpener struct {
accounts map[common.Hash][]byte accounts map[common.Hash]map[common.Hash][]byte
storages 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 { func newHashOpener(accounts map[common.Hash]map[common.Hash][]byte, storages map[common.Hash]map[common.Hash]map[common.Hash][]byte) *hashOpener {
return &hashLoader{ return &hashOpener{
accounts: accounts, accounts: accounts,
storages: storages, storages: storages,
} }
} }
// OpenTrie opens the main account trie. // OpenTrie opens the main account trie.
func (l *hashLoader) OpenTrie(root common.Hash) (triestate.Trie, error) { func (l *hashOpener) OpenTrie(root common.Hash) (state.Trie, error) {
return newTestHasher(common.Hash{}, root, l.accounts) return newTestHasher(common.Hash{}, root, l.accounts[root])
} }
// OpenStorageTrie opens the storage trie of an account. // OpenStorageTrie opens the storage trie of an account.
func (l *hashLoader) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (triestate.Trie, error) { func (l *hashOpener) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (state.Trie, error) {
return newTestHasher(addrHash, root, l.storages[addrHash]) return newTestHasher(addrHash, root, l.storages[stateRoot][addrHash])
} }

View file

@ -14,19 +14,16 @@
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/> // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>
package triestate package state
import ( import (
"errors" "errors"
"fmt" "fmt"
"sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie/trienode" "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 // 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) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error)
} }
// TrieLoader wraps functions to load tries. // TrieOpener wraps functions to load tries.
type TrieLoader interface { type TrieOpener interface {
// OpenTrie opens the main account trie. // OpenTrie opens the main account trie.
OpenTrie(root common.Hash) (Trie, error) OpenTrie(root common.Hash) (Trie, error)
@ -55,55 +52,20 @@ type TrieLoader interface {
OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (Trie, error) 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. // context wraps all fields for executing state diffs.
type context struct { type context struct {
prevRoot common.Hash prevRoot common.Hash
postRoot common.Hash postRoot common.Hash
accounts map[common.Address][]byte accounts map[common.Hash][]byte
storages map[common.Address]map[common.Hash][]byte storages map[common.Hash]map[common.Hash][]byte
accountTrie Trie accountTrie Trie
nodes *trienode.MergedNodeSet nodes *trienode.MergedNodeSet
} }
// Apply traverses the provided state diffs, apply them in the associated // Apply traverses the provided state diffs, applying them in the associated
// post-state and return the generated dirty trie nodes. The state can be // post-state and return the produced trie changes.
// loaded via the provided trie loader. 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) {
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 := opener.OpenTrie(postRoot)
tr, err := loader.OpenTrie(postRoot)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -115,12 +77,12 @@ func Apply(prevRoot common.Hash, postRoot common.Hash, accounts map[common.Addre
accountTrie: tr, accountTrie: tr,
nodes: trienode.NewMergedNodeSet(), nodes: trienode.NewMergedNodeSet(),
} }
for addr, account := range accounts { for addrHash, account := range accounts {
var err error var err error
if len(account) == 0 { if len(account) == 0 {
err = deleteAccount(ctx, loader, addr) err = deleteAccount(ctx, opener, addrHash)
} else { } else {
err = updateAccount(ctx, loader, addr) err = updateAccount(ctx, opener, addrHash)
} }
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to revert state, err: %w", err) 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 // 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 // existent in post-state. Apply the reverse diff and verify if the storage
// root matches the one in prev-state account. // 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 // The account was present in prev-state, decode it from the
// 'slim-rlp' format bytes. // 'slim-rlp' format bytes.
h := newHasher() prev, err := types.FullAccount(ctx.accounts[addrHash])
defer h.release()
addrHash := h.hash(addr.Bytes())
prev, err := types.FullAccount(ctx.accounts[addr])
if err != nil { if err != nil {
return err 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. // 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 { if err != nil {
return err return err
} }
for key, val := range ctx.storages[addr] { for key, val := range ctx.storages[addrHash] {
var err error var err error
if len(val) == 0 { if len(val) == 0 {
err = st.Delete(key.Bytes()) 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 // 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 // to be existent in post-state. Apply the reverse diff and verify if the
// account and storage is wiped out correctly. // 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. // 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()) blob, err := ctx.accountTrie.Get(addrHash.Bytes())
if err != nil { if err != nil {
return err return err
@ -223,11 +177,11 @@ func deleteAccount(ctx *context, loader TrieLoader, addr common.Address) error {
if err := rlp.DecodeBytes(blob, &post); err != nil { if err := rlp.DecodeBytes(blob, &post); err != nil {
return err return err
} }
st, err := loader.OpenStorageTrie(ctx.postRoot, addrHash, post.Root) st, err := opener.OpenStorageTrie(ctx.postRoot, addrHash, post.Root)
if err != nil { if err != nil {
return err return err
} }
for key, val := range ctx.storages[addr] { for key, val := range ctx.storages[addrHash] {
if len(val) != 0 { if len(val) != 0 {
return errors.New("expect storage deletion") 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. // Delete the post-state account from the main trie.
return ctx.accountTrie.Delete(addrHash.Bytes()) 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)
}

59
triedb/state/types.go Normal file
View file

@ -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 <http://www.gnu.org/licenses/>
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
}