From d6d48f3713cbfc7d474f17a552557787424c6206 Mon Sep 17 00:00:00 2001 From: Jared Wasinger Date: Mon, 19 Feb 2024 21:10:13 -0800 Subject: [PATCH] more --- core/state/trie_prefetcher.go | 59 +++++------------------------------ core/types/block.go | 12 ------- core/vm/instructions.go | 7 +++-- eth/api_debug.go | 25 +++++---------- internal/web3ext/web3ext.go | 4 +-- tests/block_test_util.go | 8 ++--- trie/trie.go | 10 +++--- triedb/database.go | 5 --- 8 files changed, 31 insertions(+), 99 deletions(-) diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go index e624e65162..c6332576b3 100644 --- a/core/state/trie_prefetcher.go +++ b/core/state/trie_prefetcher.go @@ -38,7 +38,6 @@ var ( type triePrefetcher struct { db Database // Database to fetch trie nodes through 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 deliveryMissMeter metrics.Meter @@ -71,16 +70,14 @@ func newTriePrefetcher(db Database, root common.Hash, namespace string) *triePre } 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 -// and reports the stats to the metrics subsystem. +// close iterates over all the subfetchers, waits on any that were left spinning +// and reports the stats to the metrics subsystem. close should not be called +// more than once on a triePrefetcher instance. func (p *triePrefetcher) close() { for _, fetcher := range p.fetchers { + fetcher.wait() // safe to do multiple times + if metrics.Enabled { if fetcher.root == p.root { 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 is called from two locations: // 1. Finalize of the state-objects storage roots. This happens at the end // of every transaction, meaning that if several transactions touches @@ -138,12 +111,6 @@ func (p *triePrefetcher) copy() *triePrefetcher { // repeated. // 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) { - // If the prefetcher is an inactive one, bail out - if p.fetches != nil { - return - } - - // Active fetcher, schedule the retrievals id := p.trieID(owner, root) fetcher := p.fetchers[id] 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 // have it. func (p *triePrefetcher) trie(owner common.Hash, root common.Hash) Trie { - // If the prefetcher is inactive, return from existing deep copies - id := 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] + // Bail if no trie was prefetched for this root + fetcher := p.fetchers[p.trieID(owner, root)] if fetcher == nil { p.deliveryMissMeter.Mark(1) return nil diff --git a/core/types/block.go b/core/types/block.go index 71dec45a26..1a357baa3a 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -319,18 +319,6 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error { 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. func (b *Block) EncodeRLP(w io.Writer) error { return rlp.Encode(w, &extblock{ diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 95c18f3ff3..c5aba00faa 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" + "math" ) 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() if overflow { - uint64CodeOffset = 0xffffffffffffffff + uint64CodeOffset = math.MaxUint64 } codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64()) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) @@ -383,7 +384,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ) uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() if overflow { - uint64CodeOffset = 0xffffffffffffffff + uint64CodeOffset = math.MaxUint64 } addr := common.Address(a.Bytes20()) 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) { slot.Clear() } 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 { + _ = interpreter.evm.StateDB.GetCode(address) // ensure the account leaf is fetched and included in the witness witness.AddCodeHash(interpreter.evm.StateDB.GetCodeHash(address)) } slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes()) diff --git a/eth/api_debug.go b/eth/api_debug.go index 97701d4214..0de9a2d5b5 100644 --- a/eth/api_debug.go +++ b/eth/api_debug.go @@ -20,8 +20,6 @@ import ( "context" "errors" "fmt" - "github.com/ethereum/go-ethereum/eth/tracers/logger" - "os" "time" "github.com/ethereum/go-ethereum/core" @@ -449,7 +447,9 @@ func (api *DebugAPI) GetTrieFlushInterval() (string, error) { 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 { panic("cannot build genesis block proof") } @@ -459,19 +459,8 @@ func BuildProof(number uint64, bc *core.BlockChain) ([]byte, error) { return nil, err } db.EnableWitnessRecording() - db.StartPrefetcher("apidebug") + db.StartPrefetcher("debug_buildStatelessProof") 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()) _, _, _, err = stateProcessor.Process(block, db, vm.Config{}) if err != nil { @@ -489,6 +478,8 @@ func BuildProof(number uint64, bc *core.BlockChain) ([]byte, error) { return enc, nil } -func (api *DebugAPI) BuildProof(num rpc.BlockNumber) ([]byte, error) { - return BuildProof(uint64(num), api.eth.blockchain) +// BuildStatelessProof executes a block, collecting the accessed pre-state into +// a Witness. The RLP-encoded witness is returned. +func (api *DebugAPI) BuildStatelessProof(num rpc.BlockNumber) ([]byte, error) { + return BuildStatelessProof(uint64(num), api.eth.blockchain) } diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index b51577a819..404fac4b14 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -502,8 +502,8 @@ web3._extend({ params: 0 }), new web3._extend.Method({ - name: 'buildProof', - call: 'debug_buildProof', + name: 'buildStatelessProof', + call: 'debug_buildStatelessProof', params: 1 }), ], diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 8dd611aa39..b0df656545 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -59,8 +59,8 @@ func (t *BlockTest) UnmarshalJSON(in []byte) error { type btJSON struct { Blocks []btBlock `json:"blocks"` Genesis btHeader `json:"genesisBlockHeader"` - Pre core.GenesisAlloc `json:"pre"` - Post core.GenesisAlloc `json:"postState"` + Pre types.GenesisAlloc `json:"pre"` + Post types.GenesisAlloc `json:"postState"` BestBlock common.UnprefixedHash `json:"lastblockhash"` Network string `json:"network"` 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 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 { cache.SnapshotLimit = 1 cache.SnapshotWait = true @@ -197,7 +197,7 @@ func (t *BlockTest) run(stateless bool, snapshotter bool, scheme string, tracer if stateless { 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 { return fmt.Errorf("failed to build proof: %v", err) } diff --git a/trie/trie.go b/trie/trie.go index 70a66a5b94..c7311594ca 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -107,7 +107,7 @@ func NewEmpty(db database.Database) *Trie { } // 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 { it, err := t.NodeIterator(start) if err != nil { @@ -581,10 +581,6 @@ func (t *Trie) resolve(n node, prefix []byte) (node, error) { 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 // 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 @@ -606,10 +602,14 @@ func (t *Trie) Hash() common.Hash { 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 { 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) { accessList := t.tracer.accessList // Commit will reset the tracer accessList, so after this diff --git a/triedb/database.go b/triedb/database.go index fdfd82d255..939a21f147 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -45,11 +45,6 @@ var HashDefaults = &Config{ HashDB: hashdb.Defaults, } -var PathDefaults = &Config{ - Preimages: false, - PathDB: pathdb.Defaults, -} - // backend defines the methods needed to access/update trie nodes in different // state scheme. type backend interface {