diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 04d581cdea..7fcb0818e3 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -147,8 +147,9 @@ func (b *SimulatedBackend) Rollback() { func (b *SimulatedBackend) rollback(parent *types.Block) { blocks, _ := core.GenerateChain(b.config, parent, ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {}) + sdb := state.NewDatabase(b.blockchain.CodeDB(), b.blockchain.TrieDB(), b.blockchain.Snapshots()) b.pendingBlock = blocks[0] - b.pendingState, _ = state.New(b.pendingBlock.Root(), state.NewDatabase(b.blockchain.CodeDB(), b.blockchain.TrieDB()), nil) + b.pendingState, _ = state.New(b.pendingBlock.Root(), sdb) } // Fork creates a side-chain that can be used to simulate reorgs. @@ -703,7 +704,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa stateDB, _ := b.blockchain.State() b.pendingBlock = blocks[0] - b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil) + b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database()) b.pendingReceipts = receipts[0] return nil } @@ -810,7 +811,7 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error { defer b.mu.Unlock() if len(b.pendingBlock.Transactions()) != 0 { - return errors.New("Could not adjust time on non-empty block") + return errors.New("could not adjust time on non-empty block") } // Get the last block block := b.blockchain.GetBlockByHash(b.pendingBlock.ParentHash()) @@ -824,8 +825,7 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error { stateDB, _ := b.blockchain.State() b.pendingBlock = blocks[0] - b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil) - + b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database()) return nil } diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 779c89714b..9f5e14f192 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -328,7 +328,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, } // Re-create statedb instance with new root upon the updated database // for accessing latest states. - statedb, err = state.New(root, statedb.Database(), nil) + statedb, err = state.New(root, statedb.Database()) if err != nil { return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not reopen state: %v", err)) } @@ -337,8 +337,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB { tdb := trie.NewDatabase(db, &trie.Config{Preimages: true}) - sdb := state.NewDatabase(state.NewCodeDB(db), tdb) - statedb, _ := state.New(types.EmptyRootHash, sdb, nil) + sdb := state.NewDatabase(state.NewCodeDB(db), tdb, nil) + statedb, _ := state.New(types.EmptyRootHash, sdb) for addr, a := range accounts { statedb.SetCode(addr, a.Code) statedb.SetNonce(addr, a.Nonce) @@ -349,7 +349,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB } // Commit and re-open to start with a clean state. root, _ := statedb.Commit(0, false) - statedb, _ = state.New(root, sdb, nil) + statedb, _ = state.New(root, sdb) return statedb } diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 5472d3c62c..6711316c92 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -149,8 +149,8 @@ func runCmd(ctx *cli.Context) error { }) defer triedb.Close() genesis := gen.MustCommit(db, triedb) - sdb := state.NewDatabase(state.NewCodeDB(db), triedb) - statedb, _ = state.New(genesis.Root(), sdb, nil) + sdb := state.NewDatabase(state.NewCodeDB(db), triedb, nil) + statedb, _ = state.New(genesis.Root(), sdb) chainConfig = gen.Config } else { db := rawdb.NewMemoryDatabase() @@ -159,8 +159,8 @@ func runCmd(ctx *cli.Context) error { HashDB: hashdb.Defaults, }) defer triedb.Close() - sdb := state.NewDatabase(state.NewCodeDB(db), triedb) - statedb, _ = state.New(types.EmptyRootHash, sdb, nil) + sdb := state.NewDatabase(state.NewCodeDB(db), triedb, nil) + statedb, _ = state.New(types.EmptyRootHash, sdb) genesisConfig = new(core.Genesis) } if ctx.String(SenderFlag.Name) != "" { diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 08e53ad3ad..850e410a61 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -476,7 +476,7 @@ func dump(ctx *cli.Context) error { triedb := utils.MakeTrieDatabase(ctx, db, true, false) // always enable preimage lookup defer triedb.Close() - state, err := state.New(root, state.NewDatabase(state.NewCodeDB(db), triedb), nil) + state, err := state.New(root, state.NewDatabase(state.NewCodeDB(db), triedb, nil)) if err != nil { return err } diff --git a/core/blockchain.go b/core/blockchain.go index 5559aa9829..c0e62a9504 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1781,7 +1781,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) if parent == nil { parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1) } - statedb, err := state.New(parent.Root, state.NewDatabase(bc.codedb, bc.triedb), bc.snaps) + statedb, err := state.New(parent.Root, state.NewDatabase(bc.codedb, bc.triedb, bc.snaps)) if err != nil { return it.index, err } @@ -1794,7 +1794,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) var followupInterrupt atomic.Bool if !bc.cacheConfig.TrieCleanNoPrefetch { if followup, err := it.peek(); followup != nil && err == nil { - throwaway, _ := state.New(parent.Root, state.NewDatabase(bc.codedb, bc.triedb), bc.snaps) + throwaway, _ := state.New(parent.Root, state.NewDatabase(bc.codedb, bc.triedb, bc.snaps)) go func(start time.Time, followup *types.Block, throwaway *state.StateDB) { bc.prefetcher.Prefetch(followup, throwaway, bc.vmConfig, &followupInterrupt) diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 5143edb0fa..918ad4694b 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -323,7 +323,7 @@ func (bc *BlockChain) State() (*state.StateDB, error) { // StateAt returns a new mutable state based on a particular point in time. func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { - return state.New(root, state.NewDatabase(bc.codedb, bc.triedb), bc.snaps) + return state.New(root, state.NewDatabase(bc.codedb, bc.triedb, bc.snaps)) } // Config retrieves the chain's fork configuration. diff --git a/core/blockchain_test.go b/core/blockchain_test.go index a6e311e25a..b3c24633e4 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -158,8 +158,8 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { } return err } - sdb := state.NewDatabase(blockchain.CodeDB(), blockchain.TrieDB()) - statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), sdb, nil) + sdb := state.NewDatabase(blockchain.CodeDB(), blockchain.TrieDB(), nil) + statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), sdb) if err != nil { return err } diff --git a/core/chain_makers.go b/core/chain_makers.go index 30d2827e2c..55d8bd3819 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -346,7 +346,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse codedb := state.NewCodeDB(db) for i := 0; i < n; i++ { - statedb, err := state.New(parent.Root(), state.NewDatabase(codedb, triedb), nil) + statedb, err := state.New(parent.Root(), state.NewDatabase(codedb, triedb, nil)) if err != nil { panic(err) } diff --git a/core/genesis.go b/core/genesis.go index 3cd905968f..28b57f9663 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -125,8 +125,8 @@ func (ga *GenesisAlloc) deriveHash() (common.Hash, error) { // Create an ephemeral in-memory database for computing hash, // all the derived states will be discarded to not pollute disk. memorydb := rawdb.NewMemoryDatabase() - db := state.NewDatabase(state.NewCodeDB(memorydb), trie.NewDatabase(memorydb, trie.HashDefaults)) - statedb, err := state.New(types.EmptyRootHash, db, nil) + db := state.NewDatabase(state.NewCodeDB(memorydb), trie.NewDatabase(memorydb, trie.HashDefaults), nil) + statedb, err := state.New(types.EmptyRootHash, db) if err != nil { return common.Hash{}, err } @@ -147,7 +147,7 @@ func (ga *GenesisAlloc) deriveHash() (common.Hash, error) { // all the generated states will be persisted into the given database. // Also, the genesis state specification will be flushed as well. func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error { - statedb, err := state.New(types.EmptyRootHash, state.NewDatabase(state.NewCodeDB(db), triedb), nil) + statedb, err := state.New(types.EmptyRootHash, state.NewDatabase(state.NewCodeDB(db), triedb, nil)) if err != nil { return err } diff --git a/core/state/database.go b/core/state/database.go index 4a159d5f33..a47d4b198c 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -17,11 +17,9 @@ package state import ( - "fmt" - "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" @@ -40,6 +38,7 @@ type CodeReader interface { // CodeWriter wraps the WriteCodes method of a backing contract code store, // providing an interface for writing contract codes back to database. type CodeWriter interface { + // WriteCodes writes the provided a list of contract codes into database. WriteCodes(addresses []common.Address, codeHashes []common.Hash, codes [][]byte) error } @@ -50,12 +49,26 @@ type CodeStore interface { CodeWriter } +// StateReader defines the interface for accessing accounts or storage slots +// associated with a specific state. +type StateReader interface { + // Account retrieves the account associated with a particular address. + Account(addr common.Address) (*types.StateAccount, error) + + // Storage retrieves the storage slot associated with a particular account + // address and slot key. + Storage(addr common.Address, slot common.Hash) (common.Hash, error) +} + // Database defines the essential methods for reading and writing ethereum states, // providing a comprehensive interface for ethereum state management. type Database interface { CodeStore - // OpenTrie opens the main account trie. + // StateReader returns a state reader interface with the specified state root. + StateReader(stateRoot common.Hash) (StateReader, error) + + // OpenTrie opens the main account trie at a specific root hash. OpenTrie(root common.Hash) (Trie, error) // OpenStorageTrie opens the storage trie of an account. @@ -66,6 +79,9 @@ type Database interface { // TrieDB returns the underlying trie database for managing trie nodes. TrieDB() *trie.Database + + // Snapshot returns the associated state snapshot; it may be nil if not configured. + Snapshot() *snapshot.Tree } // Trie is a Ethereum state trie interface, defining the essential methods @@ -138,68 +154,3 @@ type Trie interface { // with the node that proves the absence of the key. Prove(key []byte, proofDb ethdb.KeyValueWriter) error } - -// NewDatabase creates a state database with the provided contract code store -// and trie node database. -func NewDatabase(codedb *CodeDB, triedb *trie.Database) Database { - return &cachingDB{ - codedb: codedb, - triedb: triedb, - } -} - -// NewDatabaseForTesting is similar to NewDatabase, but it sets up a local code -// store and trie database with default config by using the provided database, -// specifically intended for testing. -func NewDatabaseForTesting(db ethdb.Database) Database { - return NewDatabase(NewCodeDB(db), trie.NewDatabase(db, nil)) -} - -// cachingDB is the implementation of Database interface, designed for providing -// functionalities to read and write states. -type cachingDB struct { - codedb *CodeDB - triedb *trie.Database -} - -// OpenTrie opens the main account trie at a specific root hash. -func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { - return trie.NewStateTrie(trie.StateTrieID(root), db.triedb) -} - -// OpenStorageTrie opens the storage trie of an account. -func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (Trie, error) { - return trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.triedb) -} - -// CopyTrie returns an independent copy of the given trie. -func (db *cachingDB) CopyTrie(t Trie) Trie { - switch t := t.(type) { - case *trie.StateTrie: - return t.Copy() - default: - panic(fmt.Errorf("unknown trie type %T", t)) - } -} - -// ReadCode implements CodeReader, retrieving a particular contract's code. -func (db *cachingDB) ReadCode(address common.Address, codeHash common.Hash) ([]byte, error) { - return db.codedb.ReadCode(address, codeHash) -} - -// ReadCodeSize implements CodeReader, retrieving a particular contracts -// code's size. -func (db *cachingDB) ReadCodeSize(addr common.Address, codeHash common.Hash) (int, error) { - return db.codedb.ReadCodeSize(addr, codeHash) -} - -// WriteCodes implements CodeWriter, writing the provided a list of contract -// codes into database. -func (db *cachingDB) WriteCodes(addresses []common.Address, hashes []common.Hash, codes [][]byte) error { - return db.codedb.WriteCodes(addresses, hashes, codes) -} - -// TrieDB retrieves any intermediate trie-node caching layer. -func (db *cachingDB) TrieDB() *trie.Database { - return db.triedb -} diff --git a/core/state/dump.go b/core/state/dump.go index 9ce6cd394b..ba2a897488 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -137,10 +137,15 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] start = time.Now() logged = time.Now() ) - log.Info("Trie dumping started", "root", s.trie.Hash()) - c.OnRoot(s.trie.Hash()) + tr, err := s.accountTrie() + if err != nil { + log.Error("Failed to load account trie", "err", err) + return nil + } + log.Info("Trie dumping started", "root", tr.Hash()) + c.OnRoot(tr.Hash()) - trieIt, err := s.trie.NodeIterator(conf.Start) + trieIt, err := tr.NodeIterator(conf.Start) if err != nil { return nil } @@ -158,7 +163,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] SecureKey: it.Key, } var ( - addrBytes = s.trie.GetKey(it.Key) + addrBytes = tr.GetKey(it.Key) addr = common.BytesToAddress(addrBytes) address *common.Address ) @@ -177,7 +182,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] } if !conf.SkipStorage { account.Storage = make(map[common.Hash]string) - tr, err := obj.getTrie() + tr, err := obj.storageTrie() if err != nil { log.Error("Failed to load storage trie", "err", err) continue @@ -194,7 +199,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] log.Error("Failed to decode the value returned by iterator", "error", err) continue } - account.Storage[common.BytesToHash(s.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(content) + account.Storage[common.BytesToHash(tr.GetKey(storageIt.Key))] = common.Bytes2Hex(content) } } c.OnAccount(address, account) diff --git a/core/state/iterator.go b/core/state/iterator.go index ca0c6fb204..fd95f767e4 100644 --- a/core/state/iterator.go +++ b/core/state/iterator.go @@ -76,9 +76,12 @@ func (it *nodeIterator) step() error { return nil } // Initialize the iterator if we've just started - var err error + tr, err := it.state.accountTrie() + if err != nil { + return err + } if it.stateIt == nil { - it.stateIt, err = it.state.trie.NodeIterator(nil) + it.stateIt, err = tr.NodeIterator(nil) if err != nil { return err } @@ -116,7 +119,7 @@ func (it *nodeIterator) step() error { return err } // Lookup the preimage of account hash - preimage := it.state.trie.GetKey(it.stateIt.LeafKey()) + preimage := tr.GetKey(it.stateIt.LeafKey()) if preimage == nil { return errors.New("account address is not available") } diff --git a/core/state/iterator_test.go b/core/state/iterator_test.go index 73cc22490b..26456d7a89 100644 --- a/core/state/iterator_test.go +++ b/core/state/iterator_test.go @@ -35,7 +35,7 @@ func testNodeIteratorCoverage(t *testing.T, scheme string) { db, sdb, ndb, root, _ := makeTestState(scheme) ndb.Commit(root, false) - state, err := New(root, sdb, nil) + state, err := New(root, sdb) if err != nil { t.Fatalf("failed to create state trie at %x: %v", root, err) } diff --git a/core/state/merkledb.go b/core/state/merkledb.go new file mode 100644 index 0000000000..63bc334126 --- /dev/null +++ b/core/state/merkledb.go @@ -0,0 +1,262 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state/snapshot" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +// merkleReader implements the StateReader interface, offering methods to access +// accounts and storage slots in the Merkle-Patricia-Tree manner. +type merkleReader struct { + root common.Hash // State root which uniquely represent a state. + db *trie.Database // Database for loading trie + hasher crypto.KeccakState // The reusable hasher for keccak256 hashing. + + // The associated state snapshot, which may be nil if the snapshot is + // not enabled, it may be not functional if the snapshot is not fully + // generated. + snap snapshot.Snapshot + + // The associated account trie, opened in the constructor, serves as a + // fallback for accessing states if the snapshot is not functional. + accountTrie Trie + + // The map of storage roots, filled up when resolving accounts. + storageRoots map[common.Address]common.Hash + + // The group of storage tries, loaded only when needed. It serves as a + // fallback for accessing storage slots if the snapshot is not functional. + storageTries map[common.Address]Trie +} + +// newMerkleReader constructs a merkle reader of the specific state. +func newMerkleReader(root common.Hash, db *trie.Database, snaps *snapshot.Tree) (*merkleReader, error) { + // Open the account trie, bail out if it's not available. + t, err := trie.NewStateTrie(trie.StateTrieID(root), db) + if err != nil { + return nil, err + } + // Opens the optional state snapshot, which can significantly improve + // state read efficiency but may have limited functionality(not fully + // generated). + var snap snapshot.Snapshot + if snaps != nil { + snap = snaps.Snapshot(root) + } + return &merkleReader{ + root: root, + db: db, + snap: snap, + hasher: crypto.NewKeccakState(), + accountTrie: t, + storageRoots: make(map[common.Address]common.Hash), + storageTries: make(map[common.Address]Trie), + }, nil +} + +// account is the internal version of Account, retrieving the account specified +// by the address from the associated state. +func (r *merkleReader) account(addr common.Address) (*types.StateAccount, error) { + // Try to read account from snapshot, which is more read-efficient. + if r.snap != nil { + ret, err := r.snap.Account(crypto.HashData(r.hasher, addr.Bytes())) + if err == nil { + if ret == nil { + return nil, nil + } + acct := &types.StateAccount{ + Nonce: ret.Nonce, + Balance: ret.Balance, + CodeHash: ret.CodeHash, + Root: common.BytesToHash(ret.Root), + } + if len(acct.CodeHash) == 0 { + acct.CodeHash = types.EmptyCodeHash.Bytes() + } + if acct.Root == (common.Hash{}) { + acct.Root = types.EmptyRootHash + } + return acct, nil + } + } + // If snapshot unavailable or reading from it failed, read account + // from merkle tree as fallback. + return r.accountTrie.GetAccount(addr) +} + +// Account implements StateReader, retrieving the account specified by the address +// from the associated state. +func (r *merkleReader) Account(addr common.Address) (*types.StateAccount, error) { + acct, err := r.account(addr) + if err != nil { + return acct, err + } + if acct == nil { + r.storageRoots[addr] = types.EmptyRootHash + } else { + r.storageRoots[addr] = acct.Root + } + return acct, nil +} + +// storageTrie returns the associated storage trie with the provided account +// address. The trie will be opened and cached locally if it's not loaded yet. +func (r *merkleReader) storageTrie(addr common.Address) (Trie, error) { + // Short circuit if the storage trie is already cached. + if t, ok := r.storageTries[addr]; ok { + return t, nil + } + // Open the storage trie specified by the address. + root := r.storageRoots[addr] + if root == (common.Hash{}) { + acct, err := r.Account(addr) + if err != nil { + return nil, err + } + if acct == nil { + root = types.EmptyRootHash + } else { + root = acct.Root + } + } + t, err := trie.NewStateTrie(trie.StorageTrieID(r.root, crypto.HashData(r.hasher, addr.Bytes()), root), r.db) + if err != nil { + return nil, err + } + r.storageTries[addr] = t + return t, nil +} + +// Storage implements StateReader, retrieving the storage slot specified by the +// address and slot key from the associated state. +func (r *merkleReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) { + // Try to read storage slot from snapshot first, which is more read-efficient. + if r.snap != nil { + addrHash, slotHash := crypto.HashData(r.hasher, addr.Bytes()), crypto.HashData(r.hasher, key.Bytes()) + ret, err := r.snap.Storage(addrHash, slotHash) + if err == nil { + if len(ret) == 0 { + return common.Hash{}, nil + } + _, content, _, err := rlp.Split(ret) + if err != nil { + return common.Hash{}, err + } + var slot common.Hash + slot.SetBytes(content) + return slot, nil + } + } + // If snapshot unavailable or reading from it failed, read storage slot + // from merkle tree as fallback. + t, err := r.storageTrie(addr) + if err != nil { + return common.Hash{}, err + } + ret, err := t.GetStorage(addr, key.Bytes()) + if err != nil { + return common.Hash{}, err + } + var slot common.Hash + slot.SetBytes(ret) + return slot, nil +} + +// NewDatabase creates a merkleDB instance with provided components. +func NewDatabase(codeDB CodeStore, trieDB *trie.Database, snaps *snapshot.Tree) Database { + return &merkleDB{ + codeDB: codeDB, + trieDB: trieDB, + snaps: snaps, + } +} + +// NewDatabaseForTesting is similar to NewDatabase, but it sets up a local code +// store and trie database with default config by using the provided database, +// specifically intended for testing. +func NewDatabaseForTesting(db ethdb.Database) Database { + return NewDatabase(NewCodeDB(db), trie.NewDatabase(db, nil), nil) +} + +// merkleDB is the implementation of Database interface, designed for providing +// functionalities to read and write states. +type merkleDB struct { + snaps *snapshot.Tree + codeDB CodeStore + trieDB *trie.Database +} + +// StateReader constructs a reader for the specific state. +func (db *merkleDB) StateReader(stateRoot common.Hash) (StateReader, error) { + return newMerkleReader(stateRoot, db.trieDB, db.snaps) +} + +// OpenTrie opens the main account trie at a specific root hash. +func (db *merkleDB) OpenTrie(root common.Hash) (Trie, error) { + return trie.NewStateTrie(trie.StateTrieID(root), db.trieDB) +} + +// OpenStorageTrie opens the storage trie of an account. +func (db *merkleDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (Trie, error) { + return trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.trieDB) +} + +// CopyTrie returns an independent copy of the given trie. +func (db *merkleDB) CopyTrie(t Trie) Trie { + switch t := t.(type) { + case *trie.StateTrie: + return t.Copy() + default: + panic(fmt.Errorf("unknown trie type %T", t)) + } +} + +// ReadCode implements CodeReader, retrieving a particular contract's code. +func (db *merkleDB) ReadCode(address common.Address, codeHash common.Hash) ([]byte, error) { + return db.codeDB.ReadCode(address, codeHash) +} + +// ReadCodeSize implements CodeReader, retrieving a particular contracts +// code's size. +func (db *merkleDB) ReadCodeSize(addr common.Address, codeHash common.Hash) (int, error) { + return db.codeDB.ReadCodeSize(addr, codeHash) +} + +// WriteCodes implements CodeWriter, writing the provided a list of contract +// codes into database. +func (db *merkleDB) WriteCodes(addresses []common.Address, hashes []common.Hash, codes [][]byte) error { + return db.codeDB.WriteCodes(addresses, hashes, codes) +} + +// TrieDB returns the associated trie database. +func (db *merkleDB) TrieDB() *trie.Database { + return db.trieDB +} + +// Snapshot returns the associated state snapshot, it may be nil if not configured. +func (db *merkleDB) Snapshot() *snapshot.Tree { + return db.snaps +} diff --git a/core/state/state_object.go b/core/state/state_object.go index c40400bfc4..68c1dfda34 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -67,8 +67,10 @@ type stateObject struct { origin *types.StateAccount // Account original data without any change applied, nil means it was not existent data types.StateAccount // Account data with all mutations applied in the scope of block - // Write caches. - trie Trie // storage trie, which becomes non-nil on first access + // trie represents the associated storage trie, initially nil and set + // upon first access. It's reset to nil when state object is committed + // to ensure a reload with the new root after the commit. + trie Trie code Code // contract bytecode, which gets set when code is loaded originStorage Storage // Storage cache of original entries to dedup rewrites @@ -134,22 +136,24 @@ func (s *stateObject) touch() { } } -// getTrie returns the associated storage trie. The trie will be opened +// storageTrie returns the associated storage trie. The trie will be opened // if it's not loaded previously. An error will be returned if trie can't // be loaded. -func (s *stateObject) getTrie() (Trie, error) { +func (s *stateObject) storageTrie() (Trie, error) { if s.trie == nil { - // Try fetching from prefetcher first + // Attempt to load the storage trie from the prefetcher if enabled, + // benefiting from cached hot data. if s.data.Root != types.EmptyRootHash && s.db.prefetcher != nil { - // When the miner is creating the pending state, there is no prefetcher s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root) } + // Load the storage trie from database as the fallback if it's not + // available in prefetcher. if s.trie == nil { - tr, err := s.db.db.OpenStorageTrie(s.db.originalRoot, s.address, s.data.Root) + t, err := s.db.db.OpenStorageTrie(s.db.originalRoot, s.address, s.data.Root) if err != nil { return nil, err } - s.trie = tr + s.trie = t } } return s.trie, nil @@ -184,43 +188,11 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { if _, destructed := s.db.stateObjectsDestruct[s.address]; destructed { return common.Hash{} } - // If no live objects are available, attempt to use snapshots - var ( - enc []byte - err error - value common.Hash - ) - if s.db.snap != nil { - start := time.Now() - enc, err = s.db.snap.Storage(s.addrHash, crypto.Keccak256Hash(key.Bytes())) - if metrics.EnabledExpensive { - s.db.SnapshotStorageReads += time.Since(start) - } - if len(enc) > 0 { - _, content, _, err := rlp.Split(enc) - if err != nil { - s.db.setError(err) - } - value.SetBytes(content) - } - } - // If the snapshot is unavailable or reading from it fails, load from the database. - if s.db.snap == nil || err != nil { - start := time.Now() - tr, err := s.getTrie() - if err != nil { - s.db.setError(err) - return common.Hash{} - } - val, err := tr.GetStorage(s.address, key.Bytes()) - if metrics.EnabledExpensive { - s.db.StorageReads += time.Since(start) - } - if err != nil { - s.db.setError(err) - return common.Hash{} - } - value.SetBytes(val) + // If no live objects are available, attempt to read from database. + value, err := s.db.reader.Storage(s.address, key) + if err != nil { + s.db.setError(err) + return common.Hash{} } s.originStorage[key] = value return value @@ -286,10 +258,10 @@ func (s *stateObject) updateTrie() (Trie, error) { var ( storage map[common.Hash][]byte origin map[common.Hash][]byte + hasher = crypto.NewKeccakState() ) - tr, err := s.getTrie() + tr, err := s.storageTrie() if err != nil { - s.db.setError(err) return nil, err } // Insert all the pending storage updates into the trie @@ -305,7 +277,6 @@ func (s *stateObject) updateTrie() (Trie, error) { var encoded []byte // rlp-encoded value to be used by the snapshot if (value == common.Hash{}) { if err := tr.DeleteStorage(s.address, key[:]); err != nil { - s.db.setError(err) return nil, err } s.db.StorageDeleted += 1 @@ -314,7 +285,6 @@ func (s *stateObject) updateTrie() (Trie, error) { trimmed := common.TrimLeftZeroes(value[:]) encoded, _ = rlp.EncodeToBytes(trimmed) if err := tr.UpdateStorage(s.address, key[:], trimmed); err != nil { - s.db.setError(err) return nil, err } s.db.StorageUpdated += 1 @@ -326,7 +296,7 @@ func (s *stateObject) updateTrie() (Trie, error) { s.db.storages[s.addrHash] = storage } } - khash := crypto.HashData(s.db.hasher, key[:]) + khash := crypto.HashData(hasher, key[:]) storage[khash] = encoded // encoded will be nil if it's deleted // Cache the original value of mutated storage slots @@ -360,9 +330,13 @@ func (s *stateObject) updateTrie() (Trie, error) { // new storage trie root. func (s *stateObject) updateRoot() { // Flush cached storage mutations into trie, short circuit if any error - // is occurred or there is not change in the trie. + // is occurred or there is no change in the trie. tr, err := s.updateTrie() - if err != nil || tr == nil { + if err != nil { + s.db.setError(err) + return + } + if tr == nil { return } // Track the amount of time wasted on hashing the storage trie @@ -395,7 +369,7 @@ func (s *stateObject) commit() (*trienode.NodeSet, error) { s.data.Root = root // Update original account data after commit - s.origin = s.data.Copy() + s.origin, s.trie = s.data.Copy(), nil return nodes, nil } @@ -434,16 +408,16 @@ func (s *stateObject) setBalance(amount *big.Int) { s.data.Balance = amount } -func (s *stateObject) deepCopy(db *StateDB) *stateObject { +func (s *stateObject) deepCopy() *stateObject { obj := &stateObject{ - db: db, + db: s.db, address: s.address, addrHash: s.addrHash, origin: s.origin, data: s.data, } if s.trie != nil { - obj.trie = db.db.CopyTrie(s.trie) + obj.trie = s.db.db.CopyTrie(s.trie) } obj.code = s.code obj.dirtyStorage = s.dirtyStorage.Copy() diff --git a/core/state/state_test.go b/core/state/state_test.go index a35925352a..35c3b690f3 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -37,7 +37,7 @@ type stateEnv struct { func newStateEnv() *stateEnv { db := rawdb.NewMemoryDatabase() - sdb, _ := New(types.EmptyRootHash, NewDatabaseForTesting(db), nil) + sdb, _ := New(types.EmptyRootHash, NewDatabaseForTesting(db)) return &stateEnv{db: db, state: sdb} } @@ -46,7 +46,7 @@ func TestDump(t *testing.T) { tdb := trie.NewDatabase(db, &trie.Config{Preimages: true}) defer tdb.Close() - sdb, _ := New(types.EmptyRootHash, NewDatabase(NewCodeDB(db), tdb), nil) + sdb, _ := New(types.EmptyRootHash, NewDatabase(NewCodeDB(db), tdb, nil)) s := &stateEnv{db: db, state: sdb} // generate a few entries @@ -63,7 +63,7 @@ func TestDump(t *testing.T) { root, _ := s.state.Commit(0, false) // check that DumpToCollector contains the state objects that are in trie - s.state, _ = New(root, NewDatabase(NewCodeDB(db), tdb), nil) + s.state, _ = New(root, NewDatabase(NewCodeDB(db), tdb, nil)) got := string(s.state.Dump(nil)) want := `{ "root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2", @@ -102,7 +102,7 @@ func TestIterativeDump(t *testing.T) { tdb := trie.NewDatabase(db, &trie.Config{Preimages: true}) defer tdb.Close() - sdb, _ := New(types.EmptyRootHash, NewDatabase(NewCodeDB(db), tdb), nil) + sdb, _ := New(types.EmptyRootHash, NewDatabase(NewCodeDB(db), tdb, nil)) s := &stateEnv{db: db, state: sdb} // generate a few entries @@ -119,7 +119,7 @@ func TestIterativeDump(t *testing.T) { s.state.updateStateObject(obj1) s.state.updateStateObject(obj2) root, _ := s.state.Commit(0, false) - s.state, _ = New(root, NewDatabase(NewCodeDB(db), tdb), nil) + s.state, _ = New(root, NewDatabase(NewCodeDB(db), tdb, nil)) b := &bytes.Buffer{} s.state.IterativeDump(nil, json.NewEncoder(b)) @@ -195,7 +195,7 @@ func TestSnapshotEmpty(t *testing.T) { } func TestSnapshot2(t *testing.T) { - state, _ := New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + state, _ := New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase())) stateobjaddr0 := common.BytesToAddress([]byte("so0")) stateobjaddr1 := common.BytesToAddress([]byte("so1")) @@ -217,7 +217,7 @@ func TestSnapshot2(t *testing.T) { state.setStateObject(so0) root, _ := state.Commit(0, false) - state, _ = New(root, state.db, state.snaps) + state, _ = New(root, state.db) // and one with deleted == true so1 := state.getStateObject(stateobjaddr1) diff --git a/core/state/statedb.go b/core/state/statedb.go index bce5fb54e2..fcbc9ef3af 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -47,24 +47,25 @@ type revision struct { journalIndex int } -// StateDB structs within the ethereum protocol are used to store anything -// within the merkle trie. StateDBs take care of caching and storing -// nested states. It's the general query interface to retrieve: -// -// * Contracts -// * Accounts -// -// Once the state is committed, tries cached in stateDB (including account -// trie, storage tries) will no longer be functional. A new state instance -// must be created with new root and updated database for accessing post- -// commit states. +// StateDB structs within the ethereum protocol are used to read, write and +// hash ethereum states. type StateDB struct { - db Database + db Database + + // This reader is associated with a specific state and gets re-constructed + // with a new state root after a commit operation. + reader StateReader + + // This tool preloads essential trie elements and caches them within + // the trie structure, reducing the time required for node resolution + // in the late trie operation. It's optional and should be configured + // explicitly. prefetcher *triePrefetcher - trie Trie - hasher crypto.KeccakState - snaps *snapshot.Tree // Nil if snapshot is not available - snap snapshot.Snapshot // Nil if snapshot is not available + + // primaryTrie represents the main state trie, initially nil and set upon + // first access. It's reset to nil when stateDB is committed to ensure a + // reload with the new root after the commit. + trie Trie // originalRoot is the pre-state root, before any changes were made. // It will be updated when the Commit is called. @@ -141,16 +142,15 @@ type StateDB struct { } // New creates a new state from a given trie. -func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) { - tr, err := db.OpenTrie(root) +func New(root common.Hash, db Database) (*StateDB, error) { + r, err := db.StateReader(root) if err != nil { return nil, err } - sdb := &StateDB{ + return &StateDB{ db: db, - trie: tr, + reader: r, originalRoot: root, - snaps: snaps, accounts: make(map[common.Hash][]byte), storages: make(map[common.Hash]map[common.Hash][]byte), accountsOrigin: make(map[common.Address][]byte), @@ -164,12 +164,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) journal: newJournal(), accessList: newAccessList(), transientStorage: newTransientStorage(), - hasher: crypto.NewKeccakState(), - } - if sdb.snaps != nil { - sdb.snap = sdb.snaps.Snapshot(root) - } - return sdb, nil + }, nil } // StartPrefetcher initializes a new trie prefetcher to pull in nodes from the @@ -180,9 +175,7 @@ func (s *StateDB) StartPrefetcher(namespace string) { s.prefetcher.close() s.prefetcher = nil } - if s.snap != nil { - s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, namespace) - } + s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, namespace) } // StopPrefetcher terminates a running prefetcher and reports any leftover stats @@ -227,6 +220,7 @@ func (s *StateDB) GetLogs(hash common.Hash, blockNumber uint64, blockHash common return logs } +// Logs returns all cached logs in a list. func (s *StateDB) Logs() []*types.Log { var logs []*types.Log for _, lgs := range s.logs { @@ -294,7 +288,6 @@ func (s *StateDB) GetNonce(addr common.Address) uint64 { if stateObject != nil { return stateObject.Nonce() } - return 0 } @@ -461,7 +454,6 @@ func (s *StateDB) Selfdestruct6780(addr common.Address) { if stateObject == nil { return } - if stateObject.created { s.SelfDestruct(addr) } @@ -505,12 +497,17 @@ func (s *StateDB) updateStateObject(obj *stateObject) { defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) } // Encode the account and update the account trie - addr := obj.Address() - if err := s.trie.UpdateAccount(addr, &obj.data); err != nil { - s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err)) + tr, err := s.accountTrie() + if err != nil { + s.setError(fmt.Errorf("failed to load account trie (%x) error: %v", obj.Address(), err)) + return + } + if err := tr.UpdateAccount(obj.Address(), &obj.data); err != nil { + s.setError(fmt.Errorf("updateStateObject (%x) error: %v", obj.Address(), err)) + return } if obj.dirtyCode { - s.trie.UpdateContractCode(obj.Address(), common.BytesToHash(obj.CodeHash()), obj.code) + tr.UpdateContractCode(obj.Address(), common.BytesToHash(obj.CodeHash()), obj.code) } // Cache the data until commit. Note, this update mechanism is not symmetric // to the deletion, because whereas it is enough to track account updates @@ -537,9 +534,13 @@ func (s *StateDB) deleteStateObject(obj *stateObject) { defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) } // Delete the account from the trie - addr := obj.Address() - if err := s.trie.DeleteAccount(addr); err != nil { - s.setError(fmt.Errorf("deleteStateObject (%x) error: %v", addr[:], err)) + tr, err := s.accountTrie() + if err != nil { + s.setError(fmt.Errorf("failed to load account trie (%x) error: %v", obj.Address(), err)) + return + } + if err := tr.DeleteAccount(obj.Address()); err != nil { + s.setError(fmt.Errorf("deleteStateObject (%x) error: %v", obj.Address(), err)) } } @@ -562,50 +563,18 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { if obj := s.stateObjects[addr]; obj != nil { return obj } - // If no live objects are available, attempt to use snapshots - var data *types.StateAccount - if s.snap != nil { - start := time.Now() - acc, err := s.snap.Account(crypto.HashData(s.hasher, addr.Bytes())) - if metrics.EnabledExpensive { - s.SnapshotAccountReads += time.Since(start) - } - if err == nil { - if acc == nil { - return nil - } - data = &types.StateAccount{ - Nonce: acc.Nonce, - Balance: acc.Balance, - CodeHash: acc.CodeHash, - Root: common.BytesToHash(acc.Root), - } - if len(data.CodeHash) == 0 { - data.CodeHash = types.EmptyCodeHash.Bytes() - } - if data.Root == (common.Hash{}) { - data.Root = types.EmptyRootHash - } - } + // If no live objects are available, attempt to load it from database + acct, err := s.reader.Account(addr) + if err != nil { + s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %w", addr.Bytes(), err)) + return nil } - // If snapshot unavailable or reading from it failed, load from the database - if data == nil { - start := time.Now() - var err error - data, err = s.trie.GetAccount(addr) - if metrics.EnabledExpensive { - s.AccountReads += time.Since(start) - } - if err != nil { - s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %w", addr.Bytes(), err)) - return nil - } - if data == nil { - return nil - } + // Short circuit if the requested account is not present. + if acct == nil { + return nil } // Insert into the live set - obj := newObject(s, addr, data) + obj := newObject(s, addr, acct) s.setStateObject(obj) return obj } @@ -691,7 +660,6 @@ func (s *StateDB) Copy() *StateDB { // Copy all the basic fields, initialize the memory ones state := &StateDB{ db: s.db, - trie: s.db.CopyTrie(s.trie), originalRoot: s.originalRoot, accounts: make(map[common.Hash][]byte), storages: make(map[common.Hash]map[common.Hash][]byte), @@ -706,14 +674,14 @@ func (s *StateDB) Copy() *StateDB { logSize: s.logSize, preimages: make(map[common.Hash][]byte, len(s.preimages)), journal: newJournal(), - hasher: crypto.NewKeccakState(), + } + // Create a new state reader for the copied state because the state + // reader is stateful and not thread-safe. + state.reader, _ = s.db.StateReader(s.originalRoot) - // In order for the block producer to be able to use and make additions - // to the snapshot tree, we need to copy that as well. Otherwise, any - // block mined by ourselves will cause gaps in the tree, and force the - // miner to operate trie-backed only. - snaps: s.snaps, - snap: s.snap, + // Deep copy the associated account trie if it's already loaded. + if s.trie != nil { + state.trie = s.db.CopyTrie(s.trie) } // Copy the dirty states, logs, and preimages for addr := range s.journal.dirties { @@ -725,7 +693,7 @@ func (s *StateDB) Copy() *StateDB { // Even though the original object is dirty, we are not copying the journal, // so we need to make sure that any side-effect the journal would have caused // during a commit (or similar op) is already applied to the copy. - state.stateObjects[addr] = object.deepCopy(state) + state.stateObjects[addr] = object.deepCopy() state.stateObjectsDirty[addr] = struct{}{} // Mark the copy dirty to force internal (code/state) commits state.stateObjectsPending[addr] = struct{}{} // Mark the copy pending to force external (account) commits @@ -737,13 +705,13 @@ func (s *StateDB) Copy() *StateDB { // of copies. for addr := range s.stateObjectsPending { if _, exist := state.stateObjects[addr]; !exist { - state.stateObjects[addr] = s.stateObjects[addr].deepCopy(state) + state.stateObjects[addr] = s.stateObjects[addr].deepCopy() } state.stateObjectsPending[addr] = struct{}{} } for addr := range s.stateObjectsDirty { if _, exist := state.stateObjects[addr]; !exist { - state.stateObjects[addr] = s.stateObjects[addr].deepCopy(state) + state.stateObjects[addr] = s.stateObjects[addr].deepCopy() } state.stateObjectsDirty[addr] = struct{}{} } @@ -869,6 +837,28 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { s.clearJournalAndRefund() } +// accountTrie returns the associated account trie, load it if it's +// not opened yet. +func (s *StateDB) accountTrie() (Trie, error) { + if s.trie == nil { + // Attempt to load the account trie from the prefetcher if enabled, + // benefiting from cached hot data. + if s.prefetcher != nil && s.originalRoot != types.EmptyRootHash { + s.trie = s.prefetcher.trie(common.Hash{}, s.originalRoot) + } + // Load the account trie from database as the fallback if it's not + // available in prefetcher. + if s.trie == nil { + t, err := s.db.OpenTrie(s.originalRoot) + if err != nil { + return nil, err + } + s.trie = t + } + } + return s.trie, nil +} + // IntermediateRoot computes the current root hash of the state trie. // It is called in between transactions to get the root hash that // goes into transaction receipts. @@ -900,14 +890,6 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { obj.updateRoot() } } - // Now we're about to start to write changes to the trie. The trie is so far - // _untouched_. We can check with the prefetcher, if it can give us a trie - // which has the same root, but also has some content loaded into it. - if prefetcher != nil { - if trie := prefetcher.trie(common.Hash{}, s.originalRoot); trie != nil { - s.trie = trie - } - } usedAddrs := make([][]byte, 0, len(s.stateObjectsPending)) for addr := range s.stateObjectsPending { if obj := s.stateObjects[addr]; obj.deleted { @@ -929,7 +911,11 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now()) } - return s.trie.Hash() + tr, err := s.accountTrie() + if err != nil { + return common.Hash{} + } + return tr.Hash() } // SetTxContext sets the current transaction hash and index which are @@ -952,8 +938,8 @@ func (s *StateDB) clearJournalAndRefund() { // of a specific account. It leverages the associated state snapshot for fast // storage iteration and constructs trie node deletion markers by creating // stack trie with iterated slots. -func (s *StateDB) fastDeleteStorage(addrHash common.Hash, root common.Hash) (bool, common.StorageSize, map[common.Hash][]byte, *trienode.NodeSet, error) { - iter, err := s.snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{}) +func (s *StateDB) fastDeleteStorage(addrHash common.Hash, root common.Hash, snaps *snapshot.Tree) (bool, common.StorageSize, map[common.Hash][]byte, *trienode.NodeSet, error) { + iter, err := snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{}) if err != nil { return false, 0, nil, nil, err } @@ -1047,10 +1033,11 @@ func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root // The fast approach can be failed if the snapshot is not fully // generated, or it's internally corrupted. Fallback to the slow // one just in case. - if s.snap != nil { - aborted, size, slots, nodes, err = s.fastDeleteStorage(addrHash, root) + snap := s.db.Snapshot() + if snap != nil { + aborted, size, slots, nodes, err = s.fastDeleteStorage(addrHash, root, snap) } - if s.snap == nil || err != nil { + if snap == nil || err != nil { aborted, size, slots, nodes, err = s.slowDeleteStorage(addr, addrHash, root) } if err != nil { @@ -1233,10 +1220,16 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er if metrics.EnabledExpensive { start = time.Now() } - root, set, err := s.trie.Commit(true) + tr, err := s.accountTrie() if err != nil { return common.Hash{}, err } + root, set, err := tr.Commit(true) + if err != nil { + return common.Hash{}, err + } + s.trie = nil // reset trie to nil, force reloading in next access. + // Merge the dirty nodes of account trie into global set if set != nil { if err := nodes.Merge(set); err != nil { @@ -1258,41 +1251,36 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er s.AccountUpdated, s.AccountDeleted = 0, 0 s.StorageUpdated, s.StorageDeleted = 0, 0 } + root, origin := types.TrieRootHash(root), types.TrieRootHash(s.originalRoot) + // If snapshotting is enabled, update the snapshot tree with this new version - if s.snap != nil { + snaps := s.db.Snapshot() + if snaps != nil && origin != root { start := time.Now() // Only update if there's a state transition (skip empty Clique blocks) - if parent := s.snap.Root(); parent != root { - if err := s.snaps.Update(root, parent, s.convertAccountSet(s.stateObjectsDestruct), s.accounts, s.storages); err != nil { - log.Warn("Failed to update snapshot tree", "from", parent, "to", root, "err", err) - } - // Keep 128 diff layers in the memory, persistent layer is 129th. - // - head layer is paired with HEAD state - // - head-1 layer is paired with HEAD-1 state - // - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state - if err := s.snaps.Cap(root, 128); err != nil { - log.Warn("Failed to cap snapshot tree", "root", root, "layers", 128, "err", err) - } + if err := snaps.Update(root, origin, s.convertAccountSet(s.stateObjectsDestruct), s.accounts, s.storages); err != nil { + log.Warn("Failed to update snapshot tree", "from", origin, "to", root, "err", err) + } + // Keep 128 diff layers in the memory, persistent layer is 129th. + // - head layer is paired with HEAD state + // - head-1 layer is paired with HEAD-1 state + // - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state + if err := snaps.Cap(root, 128); err != nil { + log.Warn("Failed to cap snapshot tree", "root", root, "layers", 128, "err", err) } if metrics.EnabledExpensive { s.SnapshotCommits += time.Since(start) } - s.snap = nil - } - if root == (common.Hash{}) { - root = types.EmptyRootHash - } - origin := s.originalRoot - if origin == (common.Hash{}) { - origin = types.EmptyRootHash } + // Commits state changes to the trie database. Unlike state snapshot, which is + // optional, writing dirty nodes to the trie database is mandatory to complete + // the state transition. if root != origin { start := time.Now() set := triestate.New(s.accountsOrigin, s.storagesOrigin, incomplete) if err := s.db.TrieDB().Update(root, origin, block, nodes, set); err != nil { return common.Hash{}, err } - s.originalRoot = root if metrics.EnabledExpensive { s.TrieDBCommits += time.Since(start) } @@ -1300,6 +1288,8 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er s.onCommit(set) } } + s.originalRoot = root + // Clear all internal flags at the end of commit operation. s.accounts = make(map[common.Hash][]byte) s.storages = make(map[common.Hash]map[common.Hash][]byte) @@ -1307,6 +1297,14 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er s.storagesOrigin = make(map[common.Address]map[common.Hash][]byte) s.stateObjectsDirty = make(map[common.Address]struct{}) s.stateObjectsDestruct = make(map[common.Address]*types.StateAccount) + + // Reset the state reader with a new state root to make new states visible. + reader, err := s.db.StateReader(root) + if err != nil { + return common.Hash{}, err + } + s.reader = reader + return root, nil } diff --git a/core/state/statedb_fuzz_test.go b/core/state/statedb_fuzz_test.go index 4c3863d074..5879910e94 100644 --- a/core/state/statedb_fuzz_test.go +++ b/core/state/statedb_fuzz_test.go @@ -182,7 +182,6 @@ func (test *stateTest) run() bool { } disk = rawdb.NewMemoryDatabase() tdb = trie.NewDatabase(disk, &trie.Config{PathDB: pathdb.Defaults}) - sdb = NewDatabase(NewCodeDB(disk), tdb) byzantium = rand.Intn(2) == 0 ) defer disk.Close() @@ -197,12 +196,14 @@ func (test *stateTest) run() bool { AsyncBuild: false, }, disk, tdb, types.EmptyRootHash) } + sdb := NewDatabase(NewCodeDB(disk), tdb, snaps) + for i, actions := range test.actions { root := types.EmptyRootHash if i != 0 { root = roots[len(roots)-1] } - state, err := New(root, sdb, snaps) + state, err := New(root, sdb) if err != nil { panic(err) } diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index f9667ec7f3..86fef2d9c5 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -19,7 +19,6 @@ package state import ( "bytes" "encoding/binary" - "errors" "fmt" "math" "math/big" @@ -51,7 +50,7 @@ func TestUpdateLeaks(t *testing.T) { db = rawdb.NewMemoryDatabase() tdb = trie.NewDatabase(db, nil) ) - state, _ := New(types.EmptyRootHash, NewDatabase(NewCodeDB(db), tdb), nil) + state, _ := New(types.EmptyRootHash, NewDatabase(NewCodeDB(db), tdb, nil)) // Update it with some accounts for i := byte(0); i < 255; i++ { @@ -87,8 +86,8 @@ func TestIntermediateLeaks(t *testing.T) { finalDb := rawdb.NewMemoryDatabase() transNdb := trie.NewDatabase(transDb, nil) finalNdb := trie.NewDatabase(finalDb, nil) - transState, _ := New(types.EmptyRootHash, NewDatabase(NewCodeDB(transDb), transNdb), nil) - finalState, _ := New(types.EmptyRootHash, NewDatabase(NewCodeDB(finalDb), finalNdb), nil) + transState, _ := New(types.EmptyRootHash, NewDatabase(NewCodeDB(transDb), transNdb, nil)) + finalState, _ := New(types.EmptyRootHash, NewDatabase(NewCodeDB(finalDb), finalNdb, nil)) modify := func(state *StateDB, addr common.Address, i, tweak byte) { state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak))) @@ -163,7 +162,7 @@ func TestIntermediateLeaks(t *testing.T) { // https://github.com/ethereum/go-ethereum/pull/15549. func TestCopy(t *testing.T) { // Create a random state test to copy and modify "independently" - orig, _ := New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + orig, _ := New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase())) for i := byte(0); i < 255; i++ { obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) @@ -423,7 +422,7 @@ func (test *snapshotTest) String() string { func (test *snapshotTest) run() bool { // Run all actions and create snapshots. var ( - state, _ = New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + state, _ = New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase())) snapshotRevs = make([]int, len(test.snapshots)) sindex = 0 ) @@ -437,7 +436,7 @@ func (test *snapshotTest) run() bool { // Revert all snapshots in reverse order. Each revert must yield a state // that is equivalent to fresh state with all actions up the snapshot applied. for sindex--; sindex >= 0; sindex-- { - checkstate, _ := New(types.EmptyRootHash, state.Database(), nil) + checkstate, _ := New(types.EmptyRootHash, state.Database()) for _, action := range test.actions[:test.snapshots[sindex]] { action.fn(action, checkstate) } @@ -455,7 +454,7 @@ func forEachStorage(s *StateDB, addr common.Address, cb func(key, value common.H if so == nil { return nil } - tr, err := so.getTrie() + tr, err := so.storageTrie() if err != nil { return err } @@ -466,7 +465,7 @@ func forEachStorage(s *StateDB, addr common.Address, cb func(key, value common.H it := trie.NewIterator(trieIt) for it.Next() { - key := common.BytesToHash(s.trie.GetKey(it.Key)) + key := common.BytesToHash(tr.GetKey(it.Key)) if value, dirty := so.dirtyStorage[key]; dirty { if !cb(key, value) { return nil @@ -535,7 +534,7 @@ func TestTouchDelete(t *testing.T) { s := newStateEnv() s.state.GetOrNewStateObject(common.Address{}) root, _ := s.state.Commit(0, false) - s.state, _ = New(root, s.state.db, s.state.snaps) + s.state, _ = New(root, s.state.db) snapshot := s.state.Snapshot() s.state.AddBalance(common.Address{}, new(big.Int)) @@ -552,7 +551,7 @@ func TestTouchDelete(t *testing.T) { // TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy. // See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512 func TestCopyOfCopy(t *testing.T) { - state, _ := New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + state, _ := New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase())) addr := common.HexToAddress("aaaa") state.SetBalance(addr, big.NewInt(42)) @@ -570,7 +569,7 @@ func TestCopyOfCopy(t *testing.T) { // See https://github.com/ethereum/go-ethereum/issues/20106. func TestCopyCommitCopy(t *testing.T) { tdb := NewDatabaseForTesting(rawdb.NewMemoryDatabase()) - state, _ := New(types.EmptyRootHash, tdb, nil) + state, _ := New(types.EmptyRootHash, tdb) // Create an account and check if the retrieved balance is correct addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe") @@ -623,7 +622,7 @@ func TestCopyCommitCopy(t *testing.T) { } // Commit state, ensure states can be loaded from disk root, _ := state.Commit(0, false) - state, _ = New(root, tdb, nil) + state, _ = New(root, tdb) if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { t.Fatalf("state post-commit balance mismatch: have %v, want %v", balance, 42) } @@ -643,7 +642,7 @@ func TestCopyCommitCopy(t *testing.T) { // // See https://github.com/ethereum/go-ethereum/issues/20106. func TestCopyCopyCommitCopy(t *testing.T) { - state, _ := New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + state, _ := New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase())) // Create an account and check if the retrieved balance is correct addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe") @@ -712,7 +711,7 @@ func TestCopyCopyCommitCopy(t *testing.T) { // TestCommitCopy tests the copy from a committed state is not functional. func TestCommitCopy(t *testing.T) { - state, _ := New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + state, _ := New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase())) // Create an account and check if the retrieved balance is correct addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe") @@ -735,24 +734,21 @@ func TestCommitCopy(t *testing.T) { if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) { t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } - // Copy the committed state database, the copied one is not functional. + // Copy the committed state database, the copied one should still be functional. state.Commit(0, true) copied := state.Copy() - if balance := copied.GetBalance(addr); balance.Cmp(big.NewInt(0)) != 0 { + if balance := copied.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { t.Fatalf("unexpected balance: have %v", balance) } - if code := copied.GetCode(addr); code != nil { + if code := copied.GetCode(addr); !bytes.Equal(code, []byte("hello")) { t.Fatalf("unexpected code: have %x", code) } - if val := copied.GetState(addr, skey); val != (common.Hash{}) { + if val := copied.GetState(addr, skey); val != sval { t.Fatalf("unexpected storage slot: have %x", val) } - if val := copied.GetCommittedState(addr, skey); val != (common.Hash{}) { + if val := copied.GetCommittedState(addr, skey); val != sval { t.Fatalf("unexpected storage slot: have %x", val) } - if !errors.Is(copied.Error(), trie.ErrCommitted) { - t.Fatalf("unexpected state error, %v", copied.Error()) - } } // TestDeleteCreateRevert tests a weird state transition corner case that we hit @@ -765,13 +761,13 @@ func TestCommitCopy(t *testing.T) { // first, but the journal wiped the entire state object on create-revert. func TestDeleteCreateRevert(t *testing.T) { // Create an initial state with a single contract - state, _ := New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + state, _ := New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase())) addr := common.BytesToAddress([]byte("so")) state.SetBalance(addr, big.NewInt(1)) root, _ := state.Commit(0, false) - state, _ = New(root, state.db, state.snaps) + state, _ = New(root, state.db) // Simulate self-destructing in one transaction, then create-reverting in another state.SelfDestruct(addr) @@ -783,7 +779,7 @@ func TestDeleteCreateRevert(t *testing.T) { // Commit the entire state and make sure we don't crash and have the correct state root, _ = state.Commit(0, true) - state, _ = New(root, state.db, state.snaps) + state, _ = New(root, state.db) if state.getStateObject(addr) != nil { t.Fatalf("self-destructed contract came alive") @@ -814,10 +810,10 @@ func testMissingTrieNodes(t *testing.T, scheme string) { CleanCacheSize: 0, }}) // disable caching } - db := NewDatabase(NewCodeDB(memDb), triedb) + db := NewDatabase(NewCodeDB(memDb), triedb, nil) var root common.Hash - state, _ := New(types.EmptyRootHash, db, nil) + state, _ := New(types.EmptyRootHash, db) addr := common.BytesToAddress([]byte("so")) { state.SetBalance(addr, big.NewInt(1)) @@ -831,7 +827,7 @@ func testMissingTrieNodes(t *testing.T, scheme string) { triedb.Commit(root, false) } // Create a new state on the old root - state, _ = New(root, db, nil) + state, _ = New(root, db) // Now we clear out the memdb it := memDb.NewIterator(nil, nil) for it.Next() { @@ -866,7 +862,7 @@ func TestStateDBAccessList(t *testing.T) { memDb := rawdb.NewMemoryDatabase() db := NewDatabaseForTesting(memDb) - state, _ := New(types.EmptyRootHash, db, nil) + state, _ := New(types.EmptyRootHash, db) state.accessList = newAccessList() verifyAddrs := func(astrings ...string) { @@ -1036,8 +1032,8 @@ func TestFlushOrderDataLoss(t *testing.T) { var ( memdb = rawdb.NewMemoryDatabase() triedb = trie.NewDatabase(memdb, nil) - statedb = NewDatabase(NewCodeDB(memdb), triedb) - state, _ = New(types.EmptyRootHash, statedb, nil) + statedb = NewDatabase(NewCodeDB(memdb), triedb, nil) + state, _ = New(types.EmptyRootHash, statedb) ) for a := byte(0); a < 10; a++ { state.CreateAccount(common.Address{a}) @@ -1057,7 +1053,7 @@ func TestFlushOrderDataLoss(t *testing.T) { t.Fatalf("failed to commit state trie: %v", err) } // Reopen the state trie from flushed disk and verify it - state, err = New(root, NewDatabaseForTesting(memdb), nil) + state, err = New(root, NewDatabaseForTesting(memdb)) if err != nil { t.Fatalf("failed to reopen state trie: %v", err) } @@ -1073,7 +1069,7 @@ func TestFlushOrderDataLoss(t *testing.T) { func TestStateDBTransientStorage(t *testing.T) { memDb := rawdb.NewMemoryDatabase() db := NewDatabaseForTesting(memDb) - state, _ := New(types.EmptyRootHash, db, nil) + state, _ := New(types.EmptyRootHash, db) key := common.Hash{0x01} value := common.Hash{0x02} @@ -1108,9 +1104,9 @@ func TestResetObject(t *testing.T) { var ( disk = rawdb.NewMemoryDatabase() tdb = trie.NewDatabase(disk, nil) - db = NewDatabase(NewCodeDB(disk), tdb) snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash) - state, _ = New(types.EmptyRootHash, db, snaps) + db = NewDatabase(NewCodeDB(disk), tdb, snaps) + state, _ = New(types.EmptyRootHash, db) addr = common.HexToAddress("0x1") slotA = common.HexToHash("0x1") slotB = common.HexToHash("0x2") @@ -1142,9 +1138,9 @@ func TestDeleteStorage(t *testing.T) { var ( disk = rawdb.NewMemoryDatabase() tdb = trie.NewDatabase(disk, nil) - db = NewDatabase(NewCodeDB(disk), tdb) snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash) - state, _ = New(types.EmptyRootHash, db, snaps) + db = NewDatabase(NewCodeDB(disk), tdb, snaps) + state, _ = New(types.EmptyRootHash, db) addr = common.HexToAddress("0x1") ) // Initialize account and populate storage @@ -1156,9 +1152,10 @@ func TestDeleteStorage(t *testing.T) { state.SetState(addr, slot, value) } root, _ := state.Commit(0, true) + // Init phase done, create two states, one with snap and one without - fastState, _ := New(root, db, snaps) - slowState, _ := New(root, db, nil) + fastState, _ := New(root, NewDatabase(NewCodeDB(disk), tdb, snaps)) + slowState, _ := New(root, NewDatabase(NewCodeDB(disk), tdb, nil)) obj := fastState.GetOrNewStateObject(addr) storageRoot := obj.data.Root diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 2f8932ab53..1a6962c38b 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -51,8 +51,8 @@ func makeTestState(scheme string) (ethdb.Database, Database, *trie.Database, com } db := rawdb.NewMemoryDatabase() nodeDb := trie.NewDatabase(db, config) - sdb := NewDatabase(NewCodeDB(db), nodeDb) - state, _ := New(types.EmptyRootHash, sdb, nil) + sdb := NewDatabase(NewCodeDB(db), nodeDb, nil) + state, _ := New(types.EmptyRootHash, sdb) // Fill it with some arbitrary data var accounts []*testAccount @@ -95,7 +95,7 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, scheme string, root com tdb := trie.NewDatabase(db, &config) defer tdb.Close() - state, err := New(root, NewDatabase(NewCodeDB(db), tdb), nil) + state, err := New(root, NewDatabase(NewCodeDB(db), tdb, nil)) if err != nil { t.Fatalf("failed to create state trie at %x: %v", root, err) } @@ -124,7 +124,7 @@ func checkStateConsistency(db ethdb.Database, scheme string, root common.Hash) e tdb := trie.NewDatabase(db, config) defer tdb.Close() - state, err := New(root, NewDatabase(NewCodeDB(db), tdb), nil) + state, err := New(root, NewDatabase(NewCodeDB(db), tdb, nil)) if err != nil { return err } diff --git a/core/state/trie_prefetcher_test.go b/core/state/trie_prefetcher_test.go index b9a14aadc8..68ae5097ac 100644 --- a/core/state/trie_prefetcher_test.go +++ b/core/state/trie_prefetcher_test.go @@ -27,7 +27,7 @@ import ( ) func filledStateDB() *StateDB { - state, _ := New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + state, _ := New(types.EmptyRootHash, NewDatabaseForTesting(rawdb.NewMemoryDatabase())) // Create an account and check if the retrieved balance is correct addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe") diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 54c29c4000..1c5fcbeefb 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -511,7 +511,7 @@ func TestOpenDrops(t *testing.T) { store.Close() // Create a blob pool out of the pre-seeded data - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewDatabase(memorydb.New())), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewDatabase(memorydb.New()))) statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), big.NewInt(1000000)) statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), big.NewInt(1000000)) statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), big.NewInt(1000000)) @@ -636,7 +636,7 @@ func TestOpenIndex(t *testing.T) { store.Close() // Create a blob pool out of the pre-seeded data - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewDatabase(memorydb.New())), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewDatabase(memorydb.New()))) statedb.AddBalance(addr, big.NewInt(1_000_000_000)) statedb.Commit(0, true) @@ -736,7 +736,7 @@ func TestOpenHeap(t *testing.T) { store.Close() // Create a blob pool out of the pre-seeded data - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewDatabase(memorydb.New())), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewDatabase(memorydb.New()))) statedb.AddBalance(addr1, big.NewInt(1_000_000_000)) statedb.AddBalance(addr2, big.NewInt(1_000_000_000)) statedb.AddBalance(addr3, big.NewInt(1_000_000_000)) @@ -816,7 +816,7 @@ func TestOpenCap(t *testing.T) { // with a high cap to ensure everything was persisted previously for _, datacap := range []uint64{2 * (txAvgSize + blobSize), 100 * (txAvgSize + blobSize)} { // Create a blob pool out of the pre-seeded data, but cap it to 2 blob transaction - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewDatabase(memorydb.New())), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewDatabase(memorydb.New()))) statedb.AddBalance(addr1, big.NewInt(1_000_000_000)) statedb.AddBalance(addr2, big.NewInt(1_000_000_000)) statedb.AddBalance(addr3, big.NewInt(1_000_000_000)) @@ -1203,7 +1203,7 @@ func TestAdd(t *testing.T) { keys = make(map[string]*ecdsa.PrivateKey) addrs = make(map[string]common.Address) ) - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewDatabase(memorydb.New())), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewDatabase(memorydb.New()))) for acc, seed := range tt.seeds { // Generate a new random key/address for the seed account keys[acc], _ = crypto.GenerateKey() diff --git a/core/txpool/legacypool/legacypool2_test.go b/core/txpool/legacypool/legacypool2_test.go index c5182254f9..add07fcb68 100644 --- a/core/txpool/legacypool/legacypool2_test.go +++ b/core/txpool/legacypool/legacypool2_test.go @@ -78,7 +78,7 @@ func TestTransactionFutureAttack(t *testing.T) { t.Parallel() // Create the pool to test the limit enforcement with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed)) config := testTxPoolConfig config.GlobalQueue = 100 @@ -115,7 +115,7 @@ func TestTransactionFutureAttack(t *testing.T) { func TestTransactionFuture1559(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) @@ -148,7 +148,7 @@ func TestTransactionFuture1559(t *testing.T) { func TestTransactionZAttack(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) @@ -216,7 +216,7 @@ func TestTransactionZAttack(t *testing.T) { func BenchmarkFutureAttack(b *testing.B) { // Create the pool to test the limit enforcement with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed)) config := testTxPoolConfig config.GlobalQueue = 100 diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index 27e7e061c1..3c49e81f8d 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -158,7 +158,7 @@ func setupPool() (*LegacyPool, *ecdsa.PrivateKey) { } func setupPoolWithConfig(config *params.ChainConfig) (*LegacyPool, *ecdsa.PrivateKey) { - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(config, 10000000, statedb, new(event.Feed)) key, _ := crypto.GenerateKey() @@ -252,7 +252,7 @@ func (c *testChain) State() (*state.StateDB, error) { // a state change between those fetches. stdb := c.statedb if *c.trigger { - c.statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + c.statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) // simulate that the new head block included tx0 and tx1 c.statedb.SetNonce(c.address, 2) c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether)) @@ -270,7 +270,7 @@ func TestStateChangeDuringReset(t *testing.T) { var ( key, _ = crypto.GenerateKey() address = crypto.PubkeyToAddress(key.PublicKey) - statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) trigger = false ) @@ -469,7 +469,7 @@ func TestChainFork(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) statedb.AddBalance(addr, big.NewInt(100000000000000)) pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed)) @@ -498,7 +498,7 @@ func TestDoubleNonce(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) statedb.AddBalance(addr, big.NewInt(100000000000000)) pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed)) @@ -698,7 +698,7 @@ func TestPostponing(t *testing.T) { t.Parallel() // Create the pool to test the postponing with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) @@ -911,7 +911,7 @@ func testQueueGlobalLimiting(t *testing.T, nolocals bool) { t.Parallel() // Create the pool to test the limit enforcement with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) config := testTxPoolConfig @@ -1004,7 +1004,7 @@ func testQueueTimeLimiting(t *testing.T, nolocals bool) { evictionInterval = time.Millisecond * 100 // Create the pool to test the non-expiration enforcement - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) config := testTxPoolConfig @@ -1190,7 +1190,7 @@ func TestPendingGlobalLimiting(t *testing.T) { t.Parallel() // Create the pool to test the limit enforcement with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) config := testTxPoolConfig @@ -1292,7 +1292,7 @@ func TestCapClearsFromAll(t *testing.T) { t.Parallel() // Create the pool to test the limit enforcement with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) config := testTxPoolConfig @@ -1327,7 +1327,7 @@ func TestPendingMinimumAllowance(t *testing.T) { t.Parallel() // Create the pool to test the limit enforcement with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) config := testTxPoolConfig @@ -1376,7 +1376,7 @@ func TestRepricing(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) @@ -1625,7 +1625,7 @@ func TestRepricingKeepsLocals(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) @@ -1699,7 +1699,7 @@ func TestUnderpricing(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) config := testTxPoolConfig @@ -1814,7 +1814,7 @@ func TestStableUnderpricing(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) config := testTxPoolConfig @@ -2047,7 +2047,7 @@ func TestDeduplication(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) @@ -2114,7 +2114,7 @@ func TestReplacement(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) @@ -2320,7 +2320,7 @@ func testJournaling(t *testing.T, nolocals bool) { os.Remove(journal) // Create the original pool to inject transaction into the journal - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) config := testTxPoolConfig @@ -2421,7 +2421,7 @@ func TestStatusCheck(t *testing.T) { t.Parallel() // Create the pool to test the status retrievals with - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index 1410ee143d..12067e483e 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -84,7 +84,7 @@ func TestEIP2200(t *testing.T) { for i, tt := range eip2200Tests { address := common.BytesToAddress([]byte("contract")) - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) statedb.CreateAccount(address) statedb.SetCode(address, hexutil.MustDecode(tt.input)) statedb.SetState(address, common.Hash{}, common.BytesToHash([]byte{tt.original})) @@ -136,7 +136,7 @@ func TestCreateGas(t *testing.T) { var gasUsed = uint64(0) doCheck := func(testGas int) bool { address := common.BytesToAddress([]byte("contract")) - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) statedb.CreateAccount(address) statedb.SetCode(address, hexutil.MustDecode(tt.code)) statedb.Finalise(true) diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 0aa66ba522..3e610e16f5 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -582,7 +582,7 @@ func BenchmarkOpMstore(bench *testing.B) { func TestOpTstore(t *testing.T) { var ( - statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) env = NewEVM(BlockContext{}, TxContext{}, statedb, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() diff --git a/core/vm/interpreter_test.go b/core/vm/interpreter_test.go index 86a5bdc629..2a59716bc0 100644 --- a/core/vm/interpreter_test.go +++ b/core/vm/interpreter_test.go @@ -43,7 +43,7 @@ func TestLoopInterrupt(t *testing.T) { } for i, tt := range loopInterruptTests { - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) statedb.CreateAccount(address) statedb.SetCode(address, common.Hex2Bytes(tt)) statedb.Finalise(true) diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 5c2cd92d0c..fe9b981981 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -109,7 +109,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { setDefaults(cfg) if cfg.State == nil { - cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) } var ( address = common.BytesToAddress([]byte("contract")) @@ -143,7 +143,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { setDefaults(cfg) if cfg.State == nil { - cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) } var ( vmenv = NewEnv(cfg) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 2c774048ba..dc43d313d8 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -103,7 +103,7 @@ func TestExecute(t *testing.T) { } func TestCall(t *testing.T) { - state, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + state, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) address := common.HexToAddress("0x0a") state.SetCode(address, []byte{ byte(vm.PUSH1), 10, @@ -159,7 +159,7 @@ func BenchmarkCall(b *testing.B) { } func benchmarkEVM_Create(bench *testing.B, code string) { var ( - statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) sender = common.BytesToAddress([]byte("sender")) receiver = common.BytesToAddress([]byte("receiver")) ) @@ -327,7 +327,7 @@ func TestBlockhash(t *testing.T) { func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode string, b *testing.B) { cfg := new(Config) setDefaults(cfg) - cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) cfg.GasLimit = gas if len(tracerCode) > 0 { tracer, err := tracers.DefaultDirectory.New(tracerCode, new(tracers.Context), nil) @@ -818,7 +818,7 @@ func TestRuntimeJSTracer(t *testing.T) { main := common.HexToAddress("0xaa") for i, jsTracer := range jsTracers { for j, tc := range tests { - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) statedb.SetCode(main, tc.code) statedb.SetCode(common.HexToAddress("0xbb"), calleeCode) statedb.SetCode(common.HexToAddress("0xcc"), calleeCode) @@ -860,7 +860,7 @@ func TestJSTracerCreateTx(t *testing.T) { exit: function(res) { this.exits++ }}` code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)} - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()), nil) + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting(rawdb.NewMemoryDatabase())) tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil) if err != nil { t.Fatal(err) diff --git a/eth/api_debug_test.go b/eth/api_debug_test.go index 2cbd9fb46f..09c58c1ed6 100644 --- a/eth/api_debug_test.go +++ b/eth/api_debug_test.go @@ -64,7 +64,7 @@ func TestAccountRange(t *testing.T) { var ( disk = rawdb.NewMemoryDatabase() tdb = trie.NewDatabase(disk, &trie.Config{Preimages: true}) - sdb, _ = state.New(types.EmptyRootHash, state.NewDatabase(state.NewCodeDB(disk), tdb), nil) + sdb, _ = state.New(types.EmptyRootHash, state.NewDatabase(state.NewCodeDB(disk), tdb, nil)) addrs = [AccountRangeMaxResults * 2]common.Address{} m = map[common.Address]bool{} ) @@ -82,7 +82,7 @@ func TestAccountRange(t *testing.T) { } } root, _ := sdb.Commit(0, true) - sdb, _ = state.New(root, state.NewDatabase(state.NewCodeDB(disk), tdb), nil) + sdb, _ = state.New(root, state.NewDatabase(state.NewCodeDB(disk), tdb, nil)) accountRangeTest(t, sdb, common.Hash{}, AccountRangeMaxResults/2, AccountRangeMaxResults/2) // test pagination @@ -132,11 +132,11 @@ func TestEmptyAccountRange(t *testing.T) { var ( statedb = state.NewDatabaseForTesting(rawdb.NewMemoryDatabase()) - st, _ = state.New(types.EmptyRootHash, statedb, nil) + st, _ = state.New(types.EmptyRootHash, statedb) ) // Commit(although nothing to flush) and re-init the statedb st.Commit(0, true) - st, _ = state.New(types.EmptyRootHash, statedb, nil) + st, _ = state.New(types.EmptyRootHash, statedb) results := st.IteratorDump(&state.DumpConfig{ SkipCode: true, @@ -159,7 +159,7 @@ func TestStorageRangeAt(t *testing.T) { var ( disk = rawdb.NewMemoryDatabase() tdb = trie.NewDatabase(disk, &trie.Config{Preimages: true}) - sdb, _ = state.New(types.EmptyRootHash, state.NewDatabase(state.NewCodeDB(disk), tdb), nil) + sdb, _ = state.New(types.EmptyRootHash, state.NewDatabase(state.NewCodeDB(disk), tdb, nil)) addr = common.Address{0x01} keys = []common.Hash{ // hashes of Keys of storage common.HexToHash("340dd630ad21bf010b4e676dbfa9ba9a02175262d1fa356232cfde6cb5b47ef2"), @@ -180,7 +180,7 @@ func TestStorageRangeAt(t *testing.T) { sdb.SetState(addr, *entry.Key, entry.Value) } root, _ := sdb.Commit(0, false) - sdb, _ = state.New(root, state.NewDatabase(state.NewCodeDB(disk), tdb), nil) + sdb, _ = state.New(root, state.NewDatabase(state.NewCodeDB(disk), tdb, nil)) // Check a few combinations of limit and start/end. tests := []struct { diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index 40efab307e..2c6a4b4ddf 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -535,7 +535,7 @@ func testGetNodeData(t *testing.T, protocol uint, drop bool) { accounts := []common.Address{testAddr, acc1Addr, acc2Addr} for i := uint64(0); i <= backend.chain.CurrentBlock().Number.Uint64(); i++ { root := backend.chain.GetBlockByNumber(i).Root() - reconstructed, _ := state.New(root, state.NewDatabaseForTesting(reconstructDB), nil) + reconstructed, _ := state.New(root, state.NewDatabaseForTesting(reconstructDB)) for j, acc := range accounts { state, _ := backend.chain.StateAt(root) bw := state.GetBalance(acc) diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 9c8646d17e..5efd6d4bc2 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -68,7 +68,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u // TODO(rjl493456442), clean cache is disabled to prevent memory leak, // please re-enable it for better performance. tdb := trie.NewDatabase(eth.chainDb, trie.HashDefaults) - if statedb, err = state.New(block.Root(), state.NewDatabase(state.NewCodeDB(eth.chainDb), tdb), nil); err == nil { + if statedb, err = state.New(block.Root(), state.NewDatabase(state.NewCodeDB(eth.chainDb), tdb, nil)); err == nil { log.Info("Found disk backend for state trie", "root", block.Root(), "number", block.Number()) return statedb, noopReleaser, nil } @@ -85,13 +85,13 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u // TODO(rjl493456442), clean cache is disabled to prevent memory leak, // please re-enable it for better performance. triedb = trie.NewDatabase(eth.chainDb, trie.HashDefaults) - database = state.NewDatabase(state.NewCodeDB(eth.chainDb), triedb) + database = state.NewDatabase(state.NewCodeDB(eth.chainDb), triedb, nil) // If we didn't check the live database, do check state over ephemeral database, // otherwise we would rewind past a persisted block (specific corner case is // chain tracing from the genesis). if !readOnly { - statedb, err = state.New(current.Root(), database, nil) + statedb, err = state.New(current.Root(), database) if err == nil { return statedb, noopReleaser, nil } @@ -110,7 +110,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u } current = parent - statedb, err = state.New(current.Root(), database, nil) + statedb, err = state.New(current.Root(), database) if err == nil { break } @@ -155,7 +155,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u return nil, nil, fmt.Errorf("stateAtBlock commit failed, number %d root %v: %w", current.NumberU64(), current.Root().Hex(), err) } - statedb, err = state.New(root, database, nil) + statedb, err = state.New(root, database) if err != nil { return nil, nil, fmt.Errorf("state reset after block %d failed: %v", current.NumberU64(), err) } diff --git a/light/odr_test.go b/light/odr_test.go index fa60310f19..485a24b0d5 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -162,7 +162,7 @@ func odrAccounts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc st = NewState(ctx, header, lc.Odr()) } else { header := bc.GetHeaderByHash(bhash) - st, _ = state.New(header.Root, state.NewDatabase(bc.CodeDB(), bc.TrieDB()), nil) + st, _ = state.New(header.Root, state.NewDatabase(bc.CodeDB(), bc.TrieDB(), nil)) } var res []byte @@ -196,7 +196,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain } else { chain = bc header = bc.GetHeaderByHash(bhash) - st, _ = state.New(header.Root, state.NewDatabase(bc.CodeDB(), bc.TrieDB()), nil) + st, _ = state.New(header.Root, state.NewDatabase(bc.CodeDB(), bc.TrieDB(), nil)) } // Perform read-only call. diff --git a/light/trie.go b/light/trie.go index bb472f8c5b..1108aed227 100644 --- a/light/trie.go +++ b/light/trie.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" @@ -37,7 +38,7 @@ var ( ) func NewState(ctx context.Context, head *types.Header, odr OdrBackend) *state.StateDB { - state, _ := state.New(head.Root, NewStateDatabase(ctx, head, odr), nil) + state, _ := state.New(head.Root, NewStateDatabase(ctx, head, odr)) return state } @@ -45,20 +46,96 @@ func NewStateDatabase(ctx context.Context, head *types.Header, odr OdrBackend) s return &odrDatabase{ctx, StateTrieID(head), odr} } +// odrStateReader implements interface state.StateReader, providing the methods +// to read accounts and storage slots. +type odrStateReader struct { + root common.Hash // The specific state root the reader belongs to + db *odrDatabase // The associated odr database + accountTrie state.Trie // The cached account trie + storageTries map[common.Address]state.Trie // The group of storage tries cached +} + +// newOdrStateReader constructs the state reader with specific state root. +func newOdrStateReader(root common.Hash, db *odrDatabase) *odrStateReader { + return &odrStateReader{ + root: root, + db: db, + storageTries: make(map[common.Address]state.Trie), + } +} + +// Account implements StateReader, retrieving the account specified by the address +// from the associated state. +func (r *odrStateReader) Account(addr common.Address) (*types.StateAccount, error) { + if r.accountTrie == nil { + r.accountTrie, _ = r.db.OpenTrie(r.root) + } + return r.accountTrie.GetAccount(addr) +} + +// storageTrie returns the associated storage trie with the provided account +// address. The trie will be opened and cached locally if it's not loaded yet. +func (r *odrStateReader) storageTrie(addr common.Address) (state.Trie, error) { + if t, ok := r.storageTries[addr]; ok { + return t, nil + } + acct, err := r.Account(addr) + if err != nil { + return nil, err + } + var t state.Trie + if acct == nil { + t, err = r.db.OpenStorageTrie(r.root, addr, types.EmptyRootHash) + } else { + t, err = r.db.OpenStorageTrie(r.root, addr, acct.Root) + } + if err != nil { + return nil, err + } + r.storageTries[addr] = t + return t, nil +} + +// Storage implements StateReader, retrieving the storage slot specified by the +// address and slot key from the associated state. +func (r *odrStateReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) { + t, err := r.storageTrie(addr) + if err != nil { + return common.Hash{}, err + } + ret, err := t.GetStorage(addr, key.Bytes()) + if err != nil { + return common.Hash{}, err + } + var slot common.Hash + slot.SetBytes(ret) + return slot, nil +} + +// merkleDB is the implementation of state.Database interface, designed for +// providing functionalities to read and write states in light client. type odrDatabase struct { ctx context.Context id *TrieID backend OdrBackend } +// StateReader constructs a reader for the specific state. +func (db *odrDatabase) StateReader(root common.Hash) (state.StateReader, error) { + return newOdrStateReader(root, db), nil +} + +// OpenTrie opens the main account trie at a specific root hash. func (db *odrDatabase) OpenTrie(root common.Hash) (state.Trie, error) { return &odrTrie{db: db, id: db.id}, nil } +// OpenStorageTrie opens the storage trie of an account. func (db *odrDatabase) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (state.Trie, error) { return &odrTrie{db: db, id: StorageTrieID(db.id, address, root)}, nil } +// CopyTrie returns an independent copy of the given trie. func (db *odrDatabase) CopyTrie(t state.Trie) state.Trie { switch t := t.(type) { case *odrTrie: @@ -72,6 +149,7 @@ func (db *odrDatabase) CopyTrie(t state.Trie) state.Trie { } } +// ReadCode implements CodeReader, retrieving a particular contract's code. func (db *odrDatabase) ReadCode(addr common.Address, codeHash common.Hash) ([]byte, error) { if codeHash == sha3Nil { return nil, nil @@ -87,19 +165,29 @@ func (db *odrDatabase) ReadCode(addr common.Address, codeHash common.Hash) ([]by return req.Data, err } +// ReadCodeSize implements CodeReader, retrieving a particular contracts +// code's size. func (db *odrDatabase) ReadCodeSize(addr common.Address, codeHash common.Hash) (int, error) { code, err := db.ReadCode(addr, codeHash) return len(code), err } +// WriteCodes implements CodeWriter, writing the provided a list of contract +// codes into database. func (db *odrDatabase) WriteCodes(addresses []common.Address, hashes []common.Hash, codes [][]byte) error { panic("not implemented") } +// TrieDB returns the associated trie database. func (db *odrDatabase) TrieDB() *trie.Database { return nil } +// Snapshot returns the associated state snapshot, it may be nil if not configured. +func (db *odrDatabase) Snapshot() *snapshot.Tree { + return nil +} + type odrTrie struct { db *odrDatabase id *TrieID diff --git a/miner/miner_test.go b/miner/miner_test.go index 99b4627d02..a7fccc41e0 100644 --- a/miner/miner_test.go +++ b/miner/miner_test.go @@ -301,7 +301,7 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) { if err != nil { t.Fatalf("can't create new chain %v", err) } - statedb, _ := state.New(bc.Genesis().Root(), state.NewDatabase(bc.CodeDB(), bc.TrieDB()), nil) + statedb, _ := state.New(bc.Genesis().Root(), state.NewDatabase(bc.CodeDB(), bc.TrieDB(), nil)) blockchain := &testBlockChain{chainConfig, statedb, 10000000, new(event.Feed)} pool := legacypool.New(testTxPoolConfig, blockchain) diff --git a/tests/state_test_util.go b/tests/state_test_util.go index e369bdf59d..cdcfb4d61c 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -220,7 +220,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bo if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) { return fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs) } - statedb, _ = state.New(root, statedb.Database(), snaps) + statedb, _ = state.New(root, statedb.Database()) return nil } @@ -314,8 +314,8 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo tconf.PathDB = pathdb.Defaults } triedb := trie.NewDatabase(db, tconf) - sdb := state.NewDatabase(state.NewCodeDB(db), triedb) - statedb, _ := state.New(types.EmptyRootHash, sdb, nil) + sdb := state.NewDatabase(state.NewCodeDB(db), triedb, nil) + statedb, _ := state.New(types.EmptyRootHash, sdb) for addr, a := range accounts { statedb.SetCode(addr, a.Code) statedb.SetNonce(addr, a.Nonce) @@ -337,7 +337,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo } snaps, _ = snapshot.New(snapconfig, db, triedb, root) } - statedb, _ = state.New(root, sdb, snaps) + statedb, _ = state.New(root, state.NewDatabase(state.NewCodeDB(db), triedb, snaps)) return triedb, snaps, statedb }