This commit is contained in:
Jared Wasinger 2024-02-19 21:10:13 -08:00
parent 100d2e34e1
commit d6d48f3713
8 changed files with 31 additions and 99 deletions

View file

@ -38,7 +38,6 @@ var (
type triePrefetcher struct { type triePrefetcher struct {
db Database // Database to fetch trie nodes through db Database // Database to fetch trie nodes through
root common.Hash // Root hash of the account trie for metrics root common.Hash // Root hash of the account trie for metrics
fetches map[string]Trie // Partially or fully fetched tries. Only populated for inactive copies.
fetchers map[string]*subfetcher // Subfetchers for each trie fetchers map[string]*subfetcher // Subfetchers for each trie
deliveryMissMeter metrics.Meter deliveryMissMeter metrics.Meter
@ -71,16 +70,14 @@ func newTriePrefetcher(db Database, root common.Hash, namespace string) *triePre
} }
return p return p
} }
func (p *triePrefetcher) wait() {
for _, fetcher := range p.fetchers {
fetcher.wait()
}
}
// close iterates over all the subfetchers, aborts any that were left spinning // close iterates over all the subfetchers, waits on any that were left spinning
// and reports the stats to the metrics subsystem. // and reports the stats to the metrics subsystem. close should not be called
// more than once on a triePrefetcher instance.
func (p *triePrefetcher) close() { func (p *triePrefetcher) close() {
for _, fetcher := range p.fetchers { for _, fetcher := range p.fetchers {
fetcher.wait() // safe to do multiple times
if metrics.Enabled { if metrics.Enabled {
if fetcher.root == p.root { if fetcher.root == p.root {
p.accountLoadMeter.Mark(int64(len(fetcher.seen))) p.accountLoadMeter.Mark(int64(len(fetcher.seen)))
@ -103,34 +100,10 @@ func (p *triePrefetcher) close() {
} }
} }
} }
// Clear out all fetchers (will crash on a second call, deliberate)
p.fetchers = nil
}
// copy creates a deep-but-inactive copy of the trie prefetcher. Any trie data
// already loaded will be copied over, but no goroutines will be started. This
// is mostly used in the miner which creates a copy of it's actively mutated
// state to be sealed while it may further mutate the state.
func (p *triePrefetcher) copy() *triePrefetcher {
copy := &triePrefetcher{
db: p.db,
root: p.root,
fetches: make(map[string]Trie), // Active prefetchers use the fetches map
deliveryMissMeter: p.deliveryMissMeter,
accountLoadMeter: p.accountLoadMeter,
accountDupMeter: p.accountDupMeter,
accountSkipMeter: p.accountSkipMeter,
accountWasteMeter: p.accountWasteMeter,
storageLoadMeter: p.storageLoadMeter,
storageDupMeter: p.storageDupMeter,
storageSkipMeter: p.storageSkipMeter,
storageWasteMeter: p.storageWasteMeter,
}
return copy
} }
// prefetch schedules a batch of trie items to prefetch. // prefetch schedules a batch of trie items to prefetch.
//
// prefetch is called from two locations: // prefetch is called from two locations:
// 1. Finalize of the state-objects storage roots. This happens at the end // 1. Finalize of the state-objects storage roots. This happens at the end
// of every transaction, meaning that if several transactions touches // of every transaction, meaning that if several transactions touches
@ -138,12 +111,6 @@ func (p *triePrefetcher) copy() *triePrefetcher {
// repeated. // repeated.
// 2. Finalize of the main account trie. This happens only once per block. // 2. Finalize of the main account trie. This happens only once per block.
func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr common.Address, keys [][]byte) { func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr common.Address, keys [][]byte) {
// If the prefetcher is an inactive one, bail out
if p.fetches != nil {
return
}
// Active fetcher, schedule the retrievals
id := p.trieID(owner, root) id := p.trieID(owner, root)
fetcher := p.fetchers[id] fetcher := p.fetchers[id]
if fetcher == nil { if fetcher == nil {
@ -156,18 +123,8 @@ func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr comm
// trie returns the trie matching the root hash, or nil if the prefetcher doesn't // trie returns the trie matching the root hash, or nil if the prefetcher doesn't
// have it. // have it.
func (p *triePrefetcher) trie(owner common.Hash, root common.Hash) Trie { func (p *triePrefetcher) trie(owner common.Hash, root common.Hash) Trie {
// If the prefetcher is inactive, return from existing deep copies // Bail if no trie was prefetched for this root
id := p.trieID(owner, root) fetcher := p.fetchers[p.trieID(owner, root)]
if p.fetches != nil {
trie := p.fetches[id]
if trie == nil {
p.deliveryMissMeter.Mark(1)
return nil
}
return p.db.CopyTrie(trie)
}
// Otherwise the prefetcher is active, bail if no trie was prefetched for this root
fetcher := p.fetchers[id]
if fetcher == nil { if fetcher == nil {
p.deliveryMissMeter.Mark(1) p.deliveryMissMeter.Mark(1)
return nil return nil

View file

@ -319,18 +319,6 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error {
return nil return nil
} }
// EncodeRLPWithZeroRoot encodes a block (with header state root set to 0x00...0) to RLP
func (b *Block) EncodeRLPWithZeroRoot(w io.Writer) error {
old := b.header.Root
b.header.Root = common.Hash{}
err := b.EncodeRLP(w)
b.header.Root = old
if err != nil {
return err
}
return nil
}
// EncodeRLP serializes a block as RLP. // EncodeRLP serializes a block as RLP.
func (b *Block) EncodeRLP(w io.Writer) error { func (b *Block) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, &extblock{ return rlp.Encode(w, &extblock{

View file

@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"math"
) )
func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
@ -365,7 +366,7 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
) )
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow { if overflow {
uint64CodeOffset = 0xffffffffffffffff uint64CodeOffset = math.MaxUint64
} }
codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64()) codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64())
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
@ -383,7 +384,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
) )
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow { if overflow {
uint64CodeOffset = 0xffffffffffffffff uint64CodeOffset = math.MaxUint64
} }
addr := common.Address(a.Bytes20()) addr := common.Address(a.Bytes20())
if witness := interpreter.evm.StateDB.Witness(); witness != nil { if witness := interpreter.evm.StateDB.Witness(); witness != nil {
@ -428,8 +429,8 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
if interpreter.evm.StateDB.Empty(address) { if interpreter.evm.StateDB.Empty(address) {
slot.Clear() slot.Clear()
} else { } else {
_ = interpreter.evm.StateDB.GetCode(address) // ensure the account leaf is fetched and included in the witness
if witness := interpreter.evm.StateDB.Witness(); witness != nil { if witness := interpreter.evm.StateDB.Witness(); witness != nil {
_ = interpreter.evm.StateDB.GetCode(address) // ensure the account leaf is fetched and included in the witness
witness.AddCodeHash(interpreter.evm.StateDB.GetCodeHash(address)) witness.AddCodeHash(interpreter.evm.StateDB.GetCodeHash(address))
} }
slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes()) slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes())

View file

@ -20,8 +20,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"os"
"time" "time"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
@ -449,7 +447,9 @@ func (api *DebugAPI) GetTrieFlushInterval() (string, error) {
return api.eth.blockchain.GetTrieFlushInterval().String(), nil return api.eth.blockchain.GetTrieFlushInterval().String(), nil
} }
func BuildProof(number uint64, bc *core.BlockChain) ([]byte, error) { // BuildStatelessProof executes a block, collecting the accessed pre-state into
// a Witness. The RLP-encoded witness is returned.
func BuildStatelessProof(number uint64, bc *core.BlockChain) ([]byte, error) {
if number == 0 { if number == 0 {
panic("cannot build genesis block proof") panic("cannot build genesis block proof")
} }
@ -459,19 +459,8 @@ func BuildProof(number uint64, bc *core.BlockChain) ([]byte, error) {
return nil, err return nil, err
} }
db.EnableWitnessRecording() db.EnableWitnessRecording()
db.StartPrefetcher("apidebug") db.StartPrefetcher("debug_buildStatelessProof")
block := bc.GetBlockByNumber(number) block := bc.GetBlockByNumber(number)
logconfig := &logger.Config{
EnableMemory: false,
DisableStack: false,
DisableStorage: false,
EnableReturnData: true,
Debug: true,
}
tracer := logger.NewJSONLogger(logconfig, os.Stdout)
_ = tracer
stateProcessor := core.NewStateProcessor(bc.Config(), bc, bc.Engine()) stateProcessor := core.NewStateProcessor(bc.Config(), bc, bc.Engine())
_, _, _, err = stateProcessor.Process(block, db, vm.Config{}) _, _, _, err = stateProcessor.Process(block, db, vm.Config{})
if err != nil { if err != nil {
@ -489,6 +478,8 @@ func BuildProof(number uint64, bc *core.BlockChain) ([]byte, error) {
return enc, nil return enc, nil
} }
func (api *DebugAPI) BuildProof(num rpc.BlockNumber) ([]byte, error) { // BuildStatelessProof executes a block, collecting the accessed pre-state into
return BuildProof(uint64(num), api.eth.blockchain) // a Witness. The RLP-encoded witness is returned.
func (api *DebugAPI) BuildStatelessProof(num rpc.BlockNumber) ([]byte, error) {
return BuildStatelessProof(uint64(num), api.eth.blockchain)
} }

View file

@ -502,8 +502,8 @@ web3._extend({
params: 0 params: 0
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'buildProof', name: 'buildStatelessProof',
call: 'debug_buildProof', call: 'debug_buildStatelessProof',
params: 1 params: 1
}), }),
], ],

View file

@ -59,8 +59,8 @@ func (t *BlockTest) UnmarshalJSON(in []byte) error {
type btJSON struct { type btJSON struct {
Blocks []btBlock `json:"blocks"` Blocks []btBlock `json:"blocks"`
Genesis btHeader `json:"genesisBlockHeader"` Genesis btHeader `json:"genesisBlockHeader"`
Pre core.GenesisAlloc `json:"pre"` Pre types.GenesisAlloc `json:"pre"`
Post core.GenesisAlloc `json:"postState"` Post types.GenesisAlloc `json:"postState"`
BestBlock common.UnprefixedHash `json:"lastblockhash"` BestBlock common.UnprefixedHash `json:"lastblockhash"`
Network string `json:"network"` Network string `json:"network"`
SealEngine string `json:"sealEngine"` SealEngine string `json:"sealEngine"`
@ -155,7 +155,7 @@ func (t *BlockTest) run(stateless bool, snapshotter bool, scheme string, tracer
// Wrap the original engine within the beacon-engine // Wrap the original engine within the beacon-engine
engine := beacon.New(ethash.NewFaker()) engine := beacon.New(ethash.NewFaker())
cache := &core.CacheConfig{TrieCleanLimit: 0, StateScheme: scheme, Preimages: true, TrieDirtyDisabled: true} cache := &core.CacheConfig{TrieCleanLimit: 0, StateScheme: scheme, Preimages: true}
if snapshotter { if snapshotter {
cache.SnapshotLimit = 1 cache.SnapshotLimit = 1
cache.SnapshotWait = true cache.SnapshotWait = true
@ -197,7 +197,7 @@ func (t *BlockTest) run(stateless bool, snapshotter bool, scheme string, tracer
if stateless { if stateless {
for _, blk := range validBlocks { for _, blk := range validBlocks {
proof, err := eth.BuildProof(blk.BlockHeader.Number.Uint64(), chain) proof, err := eth.BuildStatelessProof(blk.BlockHeader.Number.Uint64(), chain)
if err != nil { if err != nil {
return fmt.Errorf("failed to build proof: %v", err) return fmt.Errorf("failed to build proof: %v", err)
} }

View file

@ -107,7 +107,7 @@ func NewEmpty(db database.Database) *Trie {
} }
// MustNodeIterator is a wrapper of NodeIterator and will omit any encountered // MustNodeIterator is a wrapper of NodeIterator and will omit any encountered
// error but just printg out an error message. // error but just print out an error message.
func (t *Trie) MustNodeIterator(start []byte) NodeIterator { func (t *Trie) MustNodeIterator(start []byte) NodeIterator {
it, err := t.NodeIterator(start) it, err := t.NodeIterator(start)
if err != nil { if err != nil {
@ -581,10 +581,6 @@ func (t *Trie) resolve(n node, prefix []byte) (node, error) {
return n, nil return n, nil
} }
// TODO: for resolveAndTrack, differentiate between hash node resolve failure in stateless
// vs normal execution. In normal mode, it represents an error with database
// consistency. In stateless execution, it means that the witness is incomplete.
// resolveAndTrack loads node from the underlying store with the given node hash // resolveAndTrack loads node from the underlying store with the given node hash
// and path prefix and also tracks the loaded node blob in tracer treated as the // and path prefix and also tracks the loaded node blob in tracer treated as the
// node's original value. The rlp-encoded blob is preferred to be loaded from // node's original value. The rlp-encoded blob is preferred to be loaded from
@ -606,10 +602,14 @@ func (t *Trie) Hash() common.Hash {
return common.BytesToHash(hash.(hashNode)) return common.BytesToHash(hash.(hashNode))
} }
// AccessList returns a map of path->blob containing all trie nodes that have
// been accessed.
func (t *Trie) AccessList() map[string][]byte { func (t *Trie) AccessList() map[string][]byte {
return t.tracer.accessList return t.tracer.accessList
} }
// CommitAndObtainAccessList does the same thing as Commit, but also returns
// the access list of the trie.
func (t *Trie) CommitAndObtainAccessList(collectLeaf bool) (common.Hash, *trienode.NodeSet, map[string][]byte, error) { func (t *Trie) CommitAndObtainAccessList(collectLeaf bool) (common.Hash, *trienode.NodeSet, map[string][]byte, error) {
accessList := t.tracer.accessList accessList := t.tracer.accessList
// Commit will reset the tracer accessList, so after this // Commit will reset the tracer accessList, so after this

View file

@ -45,11 +45,6 @@ var HashDefaults = &Config{
HashDB: hashdb.Defaults, HashDB: hashdb.Defaults,
} }
var PathDefaults = &Config{
Preimages: false,
PathDB: pathdb.Defaults,
}
// backend defines the methods needed to access/update trie nodes in different // backend defines the methods needed to access/update trie nodes in different
// state scheme. // state scheme.
type backend interface { type backend interface {