core/state, eth/protocols/snap: rework code interface

This commit is contained in:
Gary Rong 2024-11-28 14:29:08 +08:00
parent f2d1a11aaf
commit e049615dc6
4 changed files with 38 additions and 69 deletions

View file

@ -344,10 +344,7 @@ func (bc *BlockChain) stateRecoverable(root common.Hash) bool {
// ContractCodeWithPrefix retrieves a blob of data associated with a contract // ContractCodeWithPrefix retrieves a blob of data associated with a contract
// hash either from ephemeral in-memory cache, or from persistent storage. // hash either from ephemeral in-memory cache, or from persistent storage.
// func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) []byte {
// If the code doesn't exist in the in-memory cache, check the storage with
// new code scheme.
func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) {
// TODO(rjl493456442) The associated account address is also required // TODO(rjl493456442) The associated account address is also required
// in Verkle scheme. Fix it once snap-sync is supported for Verkle. // in Verkle scheme. Fix it once snap-sync is supported for Verkle.
return bc.statedb.ContractCodeWithPrefix(common.Address{}, hash) return bc.statedb.ContractCodeWithPrefix(common.Address{}, hash)

View file

@ -17,7 +17,6 @@
package state package state
import ( import (
"errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -174,11 +173,7 @@ func NewDatabaseForTesting() *CachingDB {
// Reader returns a state reader associated with the specified state root. // Reader returns a state reader associated with the specified state root.
func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) { func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
var readers []Reader var readers []StateReader
// Construct the code reader with retained caches. These caches are
// thread safe.
cReader := newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache)
// Set up the state snapshot reader if available. This feature // Set up the state snapshot reader if available. This feature
// is optional and may be partially useful if it's not fully // is optional and may be partially useful if it's not fully
@ -186,7 +181,7 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
if db.snap != nil { if db.snap != nil {
snap := db.snap.Snapshot(stateRoot) snap := db.snap.Snapshot(stateRoot)
if snap != nil { if snap != nil {
readers = append(readers, newSingleReader(cReader, newFlatReader(snap))) readers = append(readers, newFlatReader(snap))
} }
} }
// Set up the trie reader, which is expected to always be available // Set up the trie reader, which is expected to always be available
@ -195,9 +190,13 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
readers = append(readers, newSingleReader(cReader, tr)) readers = append(readers, tr)
return newMultiReader(readers...) combined, err := newMultiStateReader(readers...)
if err != nil {
return nil, err
}
return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), combined), nil
} }
// OpenTrie opens the main account trie at a specific root hash. // OpenTrie opens the main account trie at a specific root hash.
@ -230,18 +229,17 @@ func (db *CachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Addre
// ContractCodeWithPrefix retrieves a particular contract's code. If the // ContractCodeWithPrefix retrieves a particular contract's code. If the
// code can't be found in the cache, then check the existence with **new** // code can't be found in the cache, then check the existence with **new**
// db scheme. // db scheme.
func (db *CachingDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) ([]byte, error) { func (db *CachingDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) []byte {
code, _ := db.codeCache.Get(codeHash) code, _ := db.codeCache.Get(codeHash)
if len(code) > 0 { if len(code) > 0 {
return code, nil return code
} }
code = rawdb.ReadCodeWithPrefix(db.disk, codeHash) code = rawdb.ReadCodeWithPrefix(db.disk, codeHash)
if len(code) > 0 { if len(code) > 0 {
db.codeCache.Add(codeHash, code) db.codeCache.Add(codeHash, code)
db.codeSizeCache.Add(codeHash, len(code)) db.codeSizeCache.Add(codeHash, len(code))
return code, nil
} }
return nil, errors.New("not found") return code
} }
// TrieDB retrieves any intermediate trie-node caching layer. // TrieDB retrieves any intermediate trie-node caching layer.

View file

@ -290,46 +290,32 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
return value, nil return value, nil
} }
// singleReader is the wrapper of ContractCodeReader and StateReader interface. // multiStateReader is the aggregation of a list of StateReader interface,
type singleReader struct { // providing state access by leveraging all readers. The checking priority
ContractCodeReader // is determined by the position in the reader list.
StateReader type multiStateReader struct {
readers []StateReader // List of state readers, sorted by checking priority
} }
// newSingleReader constructs a reader with the supplied code reader and state reader. // newMultiStateReader constructs a multiStateReader instance with the given
func newSingleReader(codeReader ContractCodeReader, stateReader StateReader) *singleReader { // readers. The priority among readers is assumed to be sorted. Note, it must
return &singleReader{ // contain at least one reader for constructing a multiStateReader.
ContractCodeReader: codeReader, func newMultiStateReader(readers ...StateReader) (*multiStateReader, error) {
StateReader: stateReader,
}
}
// multiReader is the aggregation of a list of Reader interface, providing state
// access by leveraging all readers. The checking priority is determined by the
// position in the reader list.
type multiReader struct {
readers []Reader // List of readers, sorted by checking priority
}
// newMultiReader constructs a multiReader instance with the given readers. The
// priority among readers is assumed to be sorted. Note, it must contain at least
// one reader for constructing a multiReader.
func newMultiReader(readers ...Reader) (*multiReader, error) {
if len(readers) == 0 { if len(readers) == 0 {
return nil, errors.New("empty reader set") return nil, errors.New("empty reader set")
} }
return &multiReader{ return &multiStateReader{
readers: readers, readers: readers,
}, nil }, nil
} }
// Account implementing Reader interface, retrieving the account associated with // Account implementing StateReader interface, retrieving the account associated
// a particular address. // with a particular address.
// //
// - Returns a nil account if it does not exist // - Returns a nil account if it does not exist
// - Returns an error only if an unexpected issue occurs // - Returns an error only if an unexpected issue occurs
// - The returned account is safe to modify after the call // - The returned account is safe to modify after the call
func (r *multiReader) Account(addr common.Address) (*types.StateAccount, error) { func (r *multiStateReader) Account(addr common.Address) (*types.StateAccount, error) {
var errs []error var errs []error
for _, reader := range r.readers { for _, reader := range r.readers {
acct, err := reader.Account(addr) acct, err := reader.Account(addr)
@ -341,13 +327,13 @@ func (r *multiReader) Account(addr common.Address) (*types.StateAccount, error)
return nil, errors.Join(errs...) return nil, errors.Join(errs...)
} }
// Storage implementing Reader interface, retrieving the storage slot associated // Storage implementing StateReader interface, retrieving the storage slot
// with a particular account address and slot key. // associated with a particular account address and slot key.
// //
// - Returns an empty slot if it does not exist // - Returns an empty slot if it does not exist
// - Returns an error only if an unexpected issue occurs // - Returns an error only if an unexpected issue occurs
// - The returned storage slot is safe to modify after the call // - The returned storage slot is safe to modify after the call
func (r *multiReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) { func (r *multiStateReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
var errs []error var errs []error
for _, reader := range r.readers { for _, reader := range r.readers {
slot, err := reader.Storage(addr, slot) slot, err := reader.Storage(addr, slot)
@ -359,28 +345,16 @@ func (r *multiReader) Storage(addr common.Address, slot common.Hash) (common.Has
return common.Hash{}, errors.Join(errs...) return common.Hash{}, errors.Join(errs...)
} }
// ContractCode implements Reader, retrieving a particular contract's code. // reader is the wrapper of ContractCodeReader and StateReader interface.
func (r *multiReader) Code(addr common.Address, codeHash common.Hash) ([]byte, error) { type reader struct {
var errs []error ContractCodeReader
for _, reader := range r.readers { StateReader
code, err := reader.Code(addr, codeHash)
if err == nil {
return code, nil
}
errs = append(errs, err)
}
return nil, errors.Join(errs...)
} }
// ContractCodeSize implements Reader, retrieving a particular contracts code's size. // newReader constructs a reader with the supplied code reader and state reader.
func (r *multiReader) CodeSize(addr common.Address, codeHash common.Hash) (int, error) { func newReader(codeReader ContractCodeReader, stateReader StateReader) *reader {
var errs []error return &reader{
for _, reader := range r.readers { ContractCodeReader: codeReader,
size, err := reader.CodeSize(addr, codeHash) StateReader: stateReader,
if err == nil {
return size, nil
}
errs = append(errs, err)
} }
return 0, errors.Join(errs...)
} }

View file

@ -454,7 +454,7 @@ func ServiceGetByteCodesQuery(chain *core.BlockChain, req *GetByteCodesPacket) [
// Peers should not request the empty code, but if they do, at // Peers should not request the empty code, but if they do, at
// least sent them back a correct response without db lookups // least sent them back a correct response without db lookups
codes = append(codes, []byte{}) codes = append(codes, []byte{})
} else if blob, err := chain.ContractCodeWithPrefix(hash); err == nil { } else if blob := chain.ContractCodeWithPrefix(hash); len(blob) > 0 {
codes = append(codes, blob) codes = append(codes, blob)
bytes += uint64(len(blob)) bytes += uint64(len(blob))
} }