all: refactor state database interface

This commit is contained in:
Gary Rong 2023-09-05 11:17:45 +08:00
parent e104cbc902
commit 75b588ee07
36 changed files with 677 additions and 398 deletions

View file

@ -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
}

View file

@ -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
}

View file

@ -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) != "" {

View file

@ -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
}

View file

@ -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)

View file

@ -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.

View file

@ -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
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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)

View file

@ -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")
}

View file

@ -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)
}

262
core/state/merkledb.go Normal file
View file

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

View file

@ -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,44 +188,12 @@ 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 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{}
}
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)
}
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()

View file

@ -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)

View file

@ -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
// 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
// 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
hasher crypto.KeccakState
snaps *snapshot.Tree // Nil if snapshot is not available
snap snapshot.Snapshot // Nil if snapshot is not available
// 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)
}
}
// 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 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 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 data == 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)
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 := s.snaps.Cap(root, 128); err != nil {
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
}

View file

@ -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)
}

View file

@ -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

View file

@ -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
}

View file

@ -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")

View file

@ -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()

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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()

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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 {

View file

@ -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)

View file

@ -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)
}

View file

@ -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.

View file

@ -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

View file

@ -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)

View file

@ -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
}