go-ethereum/core/stateless.go
Jonny Rhea d6f222a081
core, core/vm: precompile result caching (#35388)
Blocks heavy in precompile calls (e.g. Aztec's proof settlement txs)
spend most of their processing time on operations (ECMUL, pairings, KZG
point evaluation, MODEXP) that the state prefetcher has already computed
and thrown away.

This PR adds a precompile result cache shared between the prefetcher and
block processing (and the miner), following the JumpDestCache pattern.
Note that cached precompiles are keyed by address and input, with
entries partitioned by the active precompile set, so a fork that changes
the behavior behind an address can never be served results from before
it.

---------

Co-authored-by: Gary Rong <garyrong0905@gmail.com>
2026-07-28 10:47:33 +08:00

82 lines
3.7 KiB
Go

// Copyright 2024 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 core
import (
"context"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/stateless"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb"
)
// ExecuteStateless runs a stateless execution based on a witness, verifies
// everything it can locally and returns the state root and receipt root, that
// need the other side to explicitly check.
//
// This method is a bit of a sore thumb here, but:
// - It cannot be placed in core/stateless, because state.New produces a circular dep
// - It cannot be placed outside of core, because it needs to construct a dud headerchain
//
// TODO(karalabe): Would be nice to resolve both issues above somehow and move it.
func ExecuteStateless(ctx context.Context, config *params.ChainConfig, vmconfig vm.Config, block *types.Block, witness *stateless.Witness) (common.Hash, common.Hash, error) {
// Sanity check if the supplied block accidentally contains a set root or
// receipt hash. If so, be very loud, but still continue.
if block.Root() != (common.Hash{}) {
log.Error("stateless runner received state root it's expected to calculate (faulty consensus client)", "block", block.Number())
}
if block.ReceiptHash() != (common.Hash{}) {
log.Error("stateless runner received receipt root it's expected to calculate (faulty consensus client)", "block", block.Number())
}
// Create and populate the state database to serve as the stateless backend
memdb := witness.MakeHashDB()
db, err := state.New(witness.Root(), state.NewDatabase(triedb.NewDatabase(memdb, triedb.HashDefaults), state.NewCodeDB(memdb)))
if err != nil {
return common.Hash{}, common.Hash{}, err
}
// Create a blockchain that is idle, but can be used to access headers through
chain := &HeaderChain{
config: config,
chainDb: memdb,
headerCache: lru.NewCache[common.Hash, *types.Header](256),
engine: beacon.New(ethash.NewFaker()),
}
processor := NewStateProcessor(chain)
validator := NewBlockValidator(config, nil) // No chain, we only validate the state, not the block
// Run the stateless blocks processing and self-validate certain fields
res, err := processor.Process(ctx, block, db, nil, nil, vmconfig, nil)
if err != nil {
return common.Hash{}, common.Hash{}, err
}
if err = validator.ValidateState(block, db, res, true); err != nil {
return common.Hash{}, common.Hash{}, err
}
// Almost everything validated, but receipt and state root needs to be returned
receiptRoot := types.DeriveSha(res.Receipts, trie.NewStackTrie(nil))
stateRoot := db.IntermediateRoot(config.IsEIP158(block.Number()))
return stateRoot, receiptRoot, nil
}