core/state, triedb/database: refactor state reader (#30712)

Co-authored-by: Martin HS <martin@swende.se>
This commit is contained in:
rjl493456442 2024-11-09 08:08:06 +08:00 committed by GitHub
parent 55fdbb7e7b
commit 74ef47462f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 50 additions and 26 deletions

View File

@ -186,9 +186,9 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
// is optional and may be partially useful if it's not fully // is optional and may be partially useful if it's not fully
// generated. // generated.
if db.snap != nil { if db.snap != nil {
sr, err := newStateReader(stateRoot, db.snap) snap := db.snap.Snapshot(stateRoot)
if err == nil { if snap != nil {
readers = append(readers, sr) // snap reader is optional readers = append(readers, newStateReader(snap)) // snap reader is optional
} }
} }
// Set up the trie reader, which is expected to always be available // Set up the trie reader, which is expected to always be available

View File

@ -21,13 +21,13 @@ import (
"maps" "maps"
"github.com/ethereum/go-ethereum/common" "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/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/utils" "github.com/ethereum/go-ethereum/trie/utils"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/database"
) )
// Reader defines the interface for accessing accounts and storage slots // Reader defines the interface for accessing accounts and storage slots
@ -52,23 +52,18 @@ type Reader interface {
Copy() Reader Copy() Reader
} }
// stateReader is a wrapper over the state snapshot and implements the Reader // stateReader wraps a database state reader.
// interface. It provides an efficient way to access flat state.
type stateReader struct { type stateReader struct {
snap snapshot.Snapshot reader database.StateReader
buff crypto.KeccakState buff crypto.KeccakState
} }
// newStateReader constructs a flat state reader with on the specified state root. // newStateReader constructs a state reader with on the given state root.
func newStateReader(root common.Hash, snaps *snapshot.Tree) (*stateReader, error) { func newStateReader(reader database.StateReader) *stateReader {
snap := snaps.Snapshot(root)
if snap == nil {
return nil, errors.New("snapshot is not available")
}
return &stateReader{ return &stateReader{
snap: snap, reader: reader,
buff: crypto.NewKeccakState(), buff: crypto.NewKeccakState(),
}, nil }
} }
// Account implements Reader, retrieving the account specified by the address. // Account implements Reader, retrieving the account specified by the address.
@ -78,18 +73,18 @@ func newStateReader(root common.Hash, snaps *snapshot.Tree) (*stateReader, error
// //
// The returned account might be nil if it's not existent. // The returned account might be nil if it's not existent.
func (r *stateReader) Account(addr common.Address) (*types.StateAccount, error) { func (r *stateReader) Account(addr common.Address) (*types.StateAccount, error) {
ret, err := r.snap.Account(crypto.HashData(r.buff, addr.Bytes())) account, err := r.reader.Account(crypto.HashData(r.buff, addr.Bytes()))
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ret == nil { if account == nil {
return nil, nil return nil, nil
} }
acct := &types.StateAccount{ acct := &types.StateAccount{
Nonce: ret.Nonce, Nonce: account.Nonce,
Balance: ret.Balance, Balance: account.Balance,
CodeHash: ret.CodeHash, CodeHash: account.CodeHash,
Root: common.BytesToHash(ret.Root), Root: common.BytesToHash(account.Root),
} }
if len(acct.CodeHash) == 0 { if len(acct.CodeHash) == 0 {
acct.CodeHash = types.EmptyCodeHash.Bytes() acct.CodeHash = types.EmptyCodeHash.Bytes()
@ -110,7 +105,7 @@ func (r *stateReader) Account(addr common.Address) (*types.StateAccount, error)
func (r *stateReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) { func (r *stateReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
addrHash := crypto.HashData(r.buff, addr.Bytes()) addrHash := crypto.HashData(r.buff, addr.Bytes())
slotHash := crypto.HashData(r.buff, key.Bytes()) slotHash := crypto.HashData(r.buff, key.Bytes())
ret, err := r.snap.Storage(addrHash, slotHash) ret, err := r.reader.Storage(addrHash, slotHash)
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
@ -131,7 +126,7 @@ func (r *stateReader) Storage(addr common.Address, key common.Hash) (common.Hash
// Copy implements Reader, returning a deep-copied snap reader. // Copy implements Reader, returning a deep-copied snap reader.
func (r *stateReader) Copy() Reader { func (r *stateReader) Copy() Reader {
return &stateReader{ return &stateReader{
snap: r.snap, reader: r.reader,
buff: crypto.NewKeccakState(), buff: crypto.NewKeccakState(),
} }
} }

View File

@ -18,6 +18,7 @@ package database
import ( import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
) )
// NodeReader wraps the Node method of a backing trie reader. // NodeReader wraps the Node method of a backing trie reader.
@ -37,3 +38,31 @@ type NodeDatabase interface {
// An error will be returned if the specified state is not available. // An error will be returned if the specified state is not available.
NodeReader(stateRoot common.Hash) (NodeReader, error) NodeReader(stateRoot common.Hash) (NodeReader, error)
} }
// StateReader wraps the Account and Storage method of a backing state reader.
type StateReader interface {
// Account directly retrieves the account associated with a particular hash in
// the slim data format. An error will be returned if the read operation exits
// abnormally. Specifically, if the layer is already stale.
//
// Note:
// - the returned account object is safe to modify
// - no error will be returned if the requested account is not found in database
Account(hash common.Hash) (*types.SlimAccount, error)
// Storage directly retrieves the storage data associated with a particular hash,
// within a particular account. An error will be returned if the read operation
// exits abnormally.
//
// Note:
// - the returned storage data is not a copy, please don't modify it
// - no error will be returned if the requested slot is not found in database
Storage(accountHash, storageHash common.Hash) ([]byte, error)
}
// StateDatabase wraps the methods of a backing state store.
type StateDatabase interface {
// StateReader returns a state reader associated with the specific state.
// An error will be returned if the specified state is not available.
StateReader(stateRoot common.Hash) (StateReader, error)
}