Merge branch 'master' into rpc-otel-buildPayload

This commit is contained in:
jonny rhea 2026-03-04 12:04:07 -06:00
commit f73cfaa0e6
51 changed files with 675 additions and 337 deletions

View file

@ -149,15 +149,13 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
isEIP4762 = chainConfig.IsVerkle(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp) isEIP4762 = chainConfig.IsVerkle(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)
statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre, isEIP4762) statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre, isEIP4762)
signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp)
gaspool = new(core.GasPool) gaspool = core.NewGasPool(pre.Env.GasLimit)
blockHash = common.Hash{0x13, 0x37} blockHash = common.Hash{0x13, 0x37}
rejectedTxs []*rejectedTx rejectedTxs []*rejectedTx
includedTxs types.Transactions includedTxs types.Transactions
gasUsed = uint64(0)
blobGasUsed = uint64(0) blobGasUsed = uint64(0)
receipts = make(types.Receipts, 0) receipts = make(types.Receipts, 0)
) )
gaspool.AddGas(pre.Env.GasLimit)
vmContext := vm.BlockContext{ vmContext := vm.BlockContext{
CanTransfer: core.CanTransfer, CanTransfer: core.CanTransfer,
Transfer: core.Transfer, Transfer: core.Transfer,
@ -258,14 +256,14 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
statedb.SetTxContext(tx.Hash(), len(receipts)) statedb.SetTxContext(tx.Hash(), len(receipts))
var ( var (
snapshot = statedb.Snapshot() snapshot = statedb.Snapshot()
prevGas = gaspool.Gas() gp = gaspool.Snapshot()
) )
receipt, err := core.ApplyTransactionWithEVM(msg, gaspool, statedb, vmContext.BlockNumber, blockHash, pre.Env.Timestamp, tx, &gasUsed, evm) receipt, err := core.ApplyTransactionWithEVM(msg, gaspool, statedb, vmContext.BlockNumber, blockHash, pre.Env.Timestamp, tx, evm)
if err != nil { if err != nil {
statedb.RevertToSnapshot(snapshot) statedb.RevertToSnapshot(snapshot)
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err) log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
gaspool.SetGas(prevGas) gaspool.Set(gp)
continue continue
} }
if receipt.Logs == nil { if receipt.Logs == nil {
@ -352,7 +350,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
Receipts: receipts, Receipts: receipts,
Rejected: rejectedTxs, Rejected: rejectedTxs,
Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty), Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty),
GasUsed: (math.HexOrDecimal64)(gasUsed), GasUsed: (math.HexOrDecimal64)(gaspool.Used()),
BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee), BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee),
} }
if pre.Env.Withdrawals != nil { if pre.Env.Withdrawals != nil {

View file

@ -22,7 +22,6 @@ import (
"os" "os"
"slices" "slices"
"sort" "sort"
"strconv"
"time" "time"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
@ -316,18 +315,6 @@ func prepare(ctx *cli.Context) {
case !ctx.IsSet(utils.NetworkIdFlag.Name): case !ctx.IsSet(utils.NetworkIdFlag.Name):
log.Info("Starting Geth on Ethereum mainnet...") log.Info("Starting Geth on Ethereum mainnet...")
} }
// If we're a full node on mainnet without --cache specified, bump default cache allowance
if !ctx.IsSet(utils.CacheFlag.Name) && !ctx.IsSet(utils.NetworkIdFlag.Name) {
// Make sure we're not on any supported preconfigured testnet either
if !ctx.IsSet(utils.HoleskyFlag.Name) &&
!ctx.IsSet(utils.SepoliaFlag.Name) &&
!ctx.IsSet(utils.HoodiFlag.Name) &&
!ctx.IsSet(utils.DeveloperFlag.Name) {
// Nope, we're really on mainnet. Bump that cache up!
log.Info("Bumping default cache on mainnet", "provided", ctx.Int(utils.CacheFlag.Name), "updated", 4096)
ctx.Set(utils.CacheFlag.Name, strconv.Itoa(4096))
}
}
} }
// geth is the main entry point into the system if no special subcommand is run. // geth is the main entry point into the system if no special subcommand is run.

View file

@ -35,12 +35,12 @@ require (
github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect github.com/tklauser/numcpus v0.6.1 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel v1.39.0 // indirect go.opentelemetry.io/otel v1.40.0 // indirect
go.opentelemetry.io/otel/metric v1.39.0 // indirect go.opentelemetry.io/otel/metric v1.40.0 // indirect
go.opentelemetry.io/otel/trace v1.39.0 // indirect go.opentelemetry.io/otel/trace v1.40.0 // indirect
golang.org/x/crypto v0.44.0 // indirect golang.org/x/crypto v0.44.0 // indirect
golang.org/x/sync v0.18.0 // indirect golang.org/x/sync v0.18.0 // indirect
golang.org/x/sys v0.39.0 // indirect golang.org/x/sys v0.40.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
) )

View file

@ -119,14 +119,14 @@ github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+F
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
@ -137,8 +137,8 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

View file

@ -491,7 +491,7 @@ var (
CacheFlag = &cli.IntFlag{ CacheFlag = &cli.IntFlag{
Name: "cache", Name: "cache",
Usage: "Megabytes of memory allocated to internal caching (default = 4096 mainnet full node, 128 light mode)", Usage: "Megabytes of memory allocated to internal caching (default = 4096 mainnet full node, 128 light mode)",
Value: 1024, Value: 4096,
Category: flags.PerfCategory, Category: flags.PerfCategory,
} }
CacheDatabaseFlag = &cli.IntFlag{ CacheDatabaseFlag = &cli.IntFlag{
@ -2475,8 +2475,6 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
} }
vmcfg := vm.Config{ vmcfg := vm.Config{
EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name), EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name),
EnableWitnessStats: ctx.Bool(VMWitnessStatsFlag.Name),
StatelessSelfValidation: ctx.Bool(VMStatelessSelfValidationFlag.Name) || ctx.Bool(VMWitnessStatsFlag.Name),
} }
if ctx.IsSet(VMTraceFlag.Name) { if ctx.IsSet(VMTraceFlag.Name) {
if name := ctx.String(VMTraceFlag.Name); name != "" { if name := ctx.String(VMTraceFlag.Name); name != "" {
@ -2490,6 +2488,9 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
} }
options.VmConfig = vmcfg options.VmConfig = vmcfg
options.StatelessSelfValidation = ctx.Bool(VMStatelessSelfValidationFlag.Name) || ctx.Bool(VMWitnessStatsFlag.Name)
options.EnableWitnessStats = ctx.Bool(VMWitnessStatsFlag.Name)
chain, err := core.NewBlockChain(chainDb, gspec, engine, options) chain, err := core.NewBlockChain(chainDb, gspec, engine, options)
if err != nil { if err != nil {
Fatalf("Can't create BlockChain: %v", err) Fatalf("Can't create BlockChain: %v", err)

View file

@ -219,6 +219,10 @@ type BlockChainConfig struct {
// detailed statistics will be logged. Negative value means disabled (default), // detailed statistics will be logged. Negative value means disabled (default),
// zero logs all blocks, positive value filters blocks by execution time. // zero logs all blocks, positive value filters blocks by execution time.
SlowBlockThreshold time.Duration SlowBlockThreshold time.Duration
// Execution configs
StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose)
EnableWitnessStats bool // Whether trie access statistics collection is enabled
} }
// DefaultConfig returns the default config. // DefaultConfig returns the default config.
@ -1279,6 +1283,8 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
func (bc *BlockChain) writeHeadBlock(block *types.Block) { func (bc *BlockChain) writeHeadBlock(block *types.Block) {
// Add the block to the canonical chain number scheme and mark as the head // Add the block to the canonical chain number scheme and mark as the head
batch := bc.db.NewBatch() batch := bc.db.NewBatch()
defer batch.Close()
rawdb.WriteHeadHeaderHash(batch, block.Hash()) rawdb.WriteHeadHeaderHash(batch, block.Hash())
rawdb.WriteHeadFastBlockHash(batch, block.Hash()) rawdb.WriteHeadFastBlockHash(batch, block.Hash())
rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64()) rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64())
@ -1653,6 +1659,8 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
batch = bc.db.NewBatch() batch = bc.db.NewBatch()
start = time.Now() start = time.Now()
) )
defer batch.Close()
rawdb.WriteBlock(batch, block) rawdb.WriteBlock(batch, block)
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receipts) rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receipts)
rawdb.WritePreimages(batch, statedb.Preimages()) rawdb.WritePreimages(batch, statedb.Preimages())
@ -1990,7 +1998,15 @@ func (bc *BlockChain) insertChain(ctx context.Context, chain types.Blocks, setHe
} }
// The traced section of block import. // The traced section of block import.
start := time.Now() start := time.Now()
res, err := bc.ProcessBlock(ctx, parent.Root, block, setHead, makeWitness && len(chain) == 1) config := ExecuteConfig{
WriteState: true,
WriteHead: setHead,
EnableTracer: true,
MakeWitness: makeWitness && len(chain) == 1,
StatelessSelfValidation: bc.cfg.StatelessSelfValidation,
EnableWitnessStats: bc.cfg.EnableWitnessStats,
}
res, err := bc.ProcessBlock(ctx, parent.Root, block, config)
if err != nil { if err != nil {
return nil, it.index, err return nil, it.index, err
} }
@ -2073,9 +2089,36 @@ func (bpr *blockProcessingResult) Stats() *ExecuteStats {
return bpr.stats return bpr.stats
} }
// ExecuteConfig defines optional behaviors during execution.
type ExecuteConfig struct {
// WriteState controls whether the computed state changes are persisted to
// the underlying storage. If false, execution is performed in-memory only.
WriteState bool
// WriteHead indicates whether the execution result should update the canonical
// chain head. It's only relevant with WriteState == True.
WriteHead bool
// EnableTracer enables execution tracing. This is typically used for debugging
// or analysis and may significantly impact performance.
EnableTracer bool
// MakeWitness indicates whether to generate execution witness data during
// execution. Enabling this may introduce additional memory and CPU overhead.
MakeWitness bool
// StatelessSelfValidation indicates whether the execution witnesses generation
// and self-validation (testing purpose) is enabled.
StatelessSelfValidation bool
// EnableWitnessStats indicates whether to enable collection of witness trie
// access statistics
EnableWitnessStats bool
}
// ProcessBlock executes and validates the given block. If there was no error // ProcessBlock executes and validates the given block. If there was no error
// it writes the block and associated state to database. // it writes the block and associated state to database.
func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (result *blockProcessingResult, blockEndErr error) { func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, block *types.Block, config ExecuteConfig) (result *blockProcessingResult, blockEndErr error) {
var ( var (
err error err error
startTime = time.Now() startTime = time.Now()
@ -2138,12 +2181,12 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
// Generate witnesses either if we're self-testing, or if it's the // Generate witnesses either if we're self-testing, or if it's the
// only block being inserted. A bit crude, but witnesses are huge, // only block being inserted. A bit crude, but witnesses are huge,
// so we refuse to make an entire chain of them. // so we refuse to make an entire chain of them.
if bc.cfg.VmConfig.StatelessSelfValidation || makeWitness { if config.StatelessSelfValidation || config.MakeWitness {
witness, err = stateless.NewWitness(block.Header(), bc) witness, err = stateless.NewWitness(block.Header(), bc)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if bc.cfg.VmConfig.EnableWitnessStats { if config.EnableWitnessStats {
witnessStats = stateless.NewWitnessStats() witnessStats = stateless.NewWitnessStats()
} }
} }
@ -2151,17 +2194,20 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
defer statedb.StopPrefetcher() defer statedb.StopPrefetcher()
} }
if bc.logger != nil && bc.logger.OnBlockStart != nil { // Instrument the blockchain tracing
bc.logger.OnBlockStart(tracing.BlockEvent{ if config.EnableTracer {
Block: block, if bc.logger != nil && bc.logger.OnBlockStart != nil {
Finalized: bc.CurrentFinalBlock(), bc.logger.OnBlockStart(tracing.BlockEvent{
Safe: bc.CurrentSafeBlock(), Block: block,
}) Finalized: bc.CurrentFinalBlock(),
} Safe: bc.CurrentSafeBlock(),
if bc.logger != nil && bc.logger.OnBlockEnd != nil { })
defer func() { }
bc.logger.OnBlockEnd(blockEndErr) if bc.logger != nil && bc.logger.OnBlockEnd != nil {
}() defer func() {
bc.logger.OnBlockEnd(blockEndErr)
}()
}
} }
// Process block using the parent state as reference point // Process block using the parent state as reference point
@ -2191,7 +2237,7 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
// witness builder/runner, which would otherwise be impossible due to the // witness builder/runner, which would otherwise be impossible due to the
// various invalid chain states/behaviors being contained in those tests. // various invalid chain states/behaviors being contained in those tests.
xvstart := time.Now() xvstart := time.Now()
if witness := statedb.Witness(); witness != nil && bc.cfg.VmConfig.StatelessSelfValidation { if witness := statedb.Witness(); witness != nil && config.StatelessSelfValidation {
log.Warn("Running stateless self-validation", "block", block.Number(), "hash", block.Hash()) log.Warn("Running stateless self-validation", "block", block.Number(), "hash", block.Hash())
// Remove critical computed fields from the block to force true recalculation // Remove critical computed fields from the block to force true recalculation
@ -2244,31 +2290,29 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
stats.CrossValidation = xvtime // The time spent on stateless cross validation stats.CrossValidation = xvtime // The time spent on stateless cross validation
// Write the block to the chain and get the status. // Write the block to the chain and get the status.
var ( var status WriteStatus
wstart = time.Now() if config.WriteState {
status WriteStatus wstart := time.Now()
) if !config.WriteHead {
if !setHead { // Don't set the head, only insert the block
// Don't set the head, only insert the block err = bc.writeBlockWithState(block, res.Receipts, statedb)
err = bc.writeBlockWithState(block, res.Receipts, statedb) } else {
} else { status, err = bc.writeBlockAndSetHead(block, res.Receipts, res.Logs, statedb, false)
status, err = bc.writeBlockAndSetHead(block, res.Receipts, res.Logs, statedb, false) }
} if err != nil {
if err != nil { return nil, err
return nil, err }
// Update the metrics touched during block commit
stats.AccountCommits = statedb.AccountCommits // Account commits are complete, we can mark them
stats.StorageCommits = statedb.StorageCommits // Storage commits are complete, we can mark them
stats.SnapshotCommit = statedb.SnapshotCommits // Snapshot commits are complete, we can mark them
stats.TrieDBCommit = statedb.TrieDBCommits // Trie database commits are complete, we can mark them
stats.BlockWrite = time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits
} }
// Report the collected witness statistics // Report the collected witness statistics
if witnessStats != nil { if witnessStats != nil {
witnessStats.ReportMetrics(block.NumberU64()) witnessStats.ReportMetrics(block.NumberU64())
} }
// Update the metrics touched during block commit
stats.AccountCommits = statedb.AccountCommits // Account commits are complete, we can mark them
stats.StorageCommits = statedb.StorageCommits // Storage commits are complete, we can mark them
stats.SnapshotCommit = statedb.SnapshotCommits // Snapshot commits are complete, we can mark them
stats.TrieDBCommit = statedb.TrieDBCommits // Trie database commits are complete, we can mark them
stats.BlockWrite = time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits
elapsed := time.Since(startTime) + 1 // prevent zero division elapsed := time.Since(startTime) + 1 // prevent zero division
stats.TotalTime = elapsed stats.TotalTime = elapsed
stats.MgasPerSecond = float64(res.GasUsed) * 1000 / float64(elapsed) stats.MgasPerSecond = float64(res.GasUsed) * 1000 / float64(elapsed)
@ -2626,6 +2670,8 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
// Delete useless indexes right now which includes the non-canonical // Delete useless indexes right now which includes the non-canonical
// transaction indexes, canonical chain indexes which above the head. // transaction indexes, canonical chain indexes which above the head.
batch := bc.db.NewBatch() batch := bc.db.NewBatch()
defer batch.Close()
for _, tx := range types.HashDifference(deletedTxs, rebirthTxs) { for _, tx := range types.HashDifference(deletedTxs, rebirthTxs) {
rawdb.DeleteTxLookupEntry(batch, tx) rawdb.DeleteTxLookupEntry(batch, tx)
} }

View file

@ -64,7 +64,7 @@ func (b *BlockGen) SetCoinbase(addr common.Address) {
panic("coinbase can only be set once") panic("coinbase can only be set once")
} }
b.header.Coinbase = addr b.header.Coinbase = addr
b.gasPool = new(GasPool).AddGas(b.header.GasLimit) b.gasPool = NewGasPool(b.header.GasLimit)
} }
// SetExtra sets the extra data field of the generated block. // SetExtra sets the extra data field of the generated block.
@ -118,10 +118,12 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig) evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig)
) )
b.statedb.SetTxContext(tx.Hash(), len(b.txs)) b.statedb.SetTxContext(tx.Hash(), len(b.txs))
receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed) receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx)
if err != nil { if err != nil {
panic(err) panic(err)
} }
b.header.GasUsed = b.gasPool.Used()
// Merge the tx-local access event into the "block-local" one, in order to collect // Merge the tx-local access event into the "block-local" one, in order to collect
// all values, so that the witness can be built. // all values, so that the witness can be built.
if b.statedb.Database().TrieDB().IsVerkle() { if b.statedb.Database().TrieDB().IsVerkle() {

View file

@ -58,6 +58,10 @@ var (
// by a transaction is higher than what's left in the block. // by a transaction is higher than what's left in the block.
ErrGasLimitReached = errors.New("gas limit reached") ErrGasLimitReached = errors.New("gas limit reached")
// ErrGasLimitOverflow is returned by the gas pool if the remaining gas
// exceeds the maximum value of uint64.
ErrGasLimitOverflow = errors.New("gas limit overflow")
// ErrInsufficientFundsForTransfer is returned if the transaction sender doesn't // ErrInsufficientFundsForTransfer is returned if the transaction sender doesn't
// have enough funds for transfer(topmost call only). // have enough funds for transfer(topmost call only).
ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer") ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer")

View file

@ -21,39 +21,87 @@ import (
"math" "math"
) )
// GasPool tracks the amount of gas available during execution of the transactions // GasPool tracks the amount of gas available for transaction execution
// in a block. The zero value is a pool with zero gas available. // within a block, along with the cumulative gas consumed.
type GasPool uint64 type GasPool struct {
remaining uint64
initial uint64
cumulativeUsed uint64
}
// AddGas makes gas available for execution. // NewGasPool initializes the gasPool with the given amount.
func (gp *GasPool) AddGas(amount uint64) *GasPool { func NewGasPool(amount uint64) *GasPool {
if uint64(*gp) > math.MaxUint64-amount { return &GasPool{
panic("gas pool pushed above uint64") remaining: amount,
initial: amount,
} }
*(*uint64)(gp) += amount
return gp
} }
// SubGas deducts the given amount from the pool if enough gas is // SubGas deducts the given amount from the pool if enough gas is
// available and returns an error otherwise. // available and returns an error otherwise.
func (gp *GasPool) SubGas(amount uint64) error { func (gp *GasPool) SubGas(amount uint64) error {
if uint64(*gp) < amount { if gp.remaining < amount {
return ErrGasLimitReached return ErrGasLimitReached
} }
*(*uint64)(gp) -= amount gp.remaining -= amount
return nil
}
// ReturnGas adds the refunded gas back to the pool and updates
// the cumulative gas usage accordingly.
func (gp *GasPool) ReturnGas(returned uint64, gasUsed uint64) error {
if gp.remaining > math.MaxUint64-returned {
return fmt.Errorf("%w: remaining: %d, returned: %d", ErrGasLimitOverflow, gp.remaining, returned)
}
// The returned gas calculation differs across forks.
//
// - Pre-Amsterdam:
// returned = purchased - remaining (refund included)
//
// - Post-Amsterdam:
// returned = purchased - gasUsed (refund excluded)
gp.remaining += returned
// gasUsed = max(txGasUsed - gasRefund, calldataFloorGasCost)
// regardless of Amsterdam is activated or not.
gp.cumulativeUsed += gasUsed
return nil return nil
} }
// Gas returns the amount of gas remaining in the pool. // Gas returns the amount of gas remaining in the pool.
func (gp *GasPool) Gas() uint64 { func (gp *GasPool) Gas() uint64 {
return uint64(*gp) return gp.remaining
} }
// SetGas sets the amount of gas with the provided number. // CumulativeUsed returns the amount of cumulative consumed gas (refunded included).
func (gp *GasPool) SetGas(gas uint64) { func (gp *GasPool) CumulativeUsed() uint64 {
*(*uint64)(gp) = gas return gp.cumulativeUsed
}
// Used returns the amount of consumed gas.
func (gp *GasPool) Used() uint64 {
if gp.initial < gp.remaining {
panic("gas used underflow")
}
return gp.initial - gp.remaining
}
// Snapshot returns the deep-copied object as the snapshot.
func (gp *GasPool) Snapshot() *GasPool {
return &GasPool{
initial: gp.initial,
remaining: gp.remaining,
cumulativeUsed: gp.cumulativeUsed,
}
}
// Set sets the content of gasPool with the provided one.
func (gp *GasPool) Set(other *GasPool) {
gp.initial = other.initial
gp.remaining = other.remaining
gp.cumulativeUsed = other.cumulativeUsed
} }
func (gp *GasPool) String() string { func (gp *GasPool) String() string {
return fmt.Sprintf("%d", *gp) return fmt.Sprintf("initial: %d, remaining: %d, cumulative used: %d", gp.initial, gp.remaining, gp.cumulativeUsed)
} }

View file

@ -253,6 +253,11 @@ func (b *tableBatch) Reset() {
b.batch.Reset() b.batch.Reset()
} }
// Close closes the batch and releases all associated resources.
func (b *tableBatch) Close() {
b.batch.Close()
}
// tableReplayer is a wrapper around a batch replayer which truncates // tableReplayer is a wrapper around a batch replayer which truncates
// the added prefix. // the added prefix.
type tableReplayer struct { type tableReplayer struct {

View file

@ -1342,6 +1342,7 @@ func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool, noStorag
if err := batch.Write(); err != nil { if err := batch.Write(); err != nil {
return nil, err return nil, err
} }
batch.Close()
} }
if !ret.empty() { if !ret.empty() {
// If snapshotting is enabled, update the snapshot tree with this new version // If snapshotting is enabled, update the snapshot tree with this new version

View file

@ -107,7 +107,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
// We attempt to apply a transaction. The goal is not to execute // We attempt to apply a transaction. The goal is not to execute
// the transaction successfully, rather to warm up touched data slots. // the transaction successfully, rather to warm up touched data slots.
if _, err := ApplyMessage(evm, msg, new(GasPool).AddGas(block.GasLimit())); err != nil { if _, err := ApplyMessage(evm, msg, nil); err != nil {
fails.Add(1) fails.Add(1)
return nil // Ugh, something went horribly wrong, bail out return nil // Ugh, something went horribly wrong, bail out
} }

View file

@ -63,14 +63,12 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated
var ( var (
config = p.chainConfig() config = p.chainConfig()
receipts types.Receipts receipts types.Receipts
usedGas = new(uint64)
header = block.Header() header = block.Header()
blockHash = block.Hash() blockHash = block.Hash()
blockNumber = block.Number() blockNumber = block.Number()
allLogs []*types.Log allLogs []*types.Log
gp = new(GasPool).AddGas(block.GasLimit()) gp = NewGasPool(block.GasLimit())
) )
var tracingStateDB = vm.StateDB(statedb) var tracingStateDB = vm.StateDB(statedb)
if hooks := cfg.Tracer; hooks != nil { if hooks := cfg.Tracer; hooks != nil {
tracingStateDB = state.NewHookedState(statedb, hooks) tracingStateDB = state.NewHookedState(statedb, hooks)
@ -107,13 +105,15 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated
telemetry.StringAttribute("tx.hash", tx.Hash().Hex()), telemetry.StringAttribute("tx.hash", tx.Hash().Hex()),
telemetry.Int64Attribute("tx.index", int64(i)), telemetry.Int64Attribute("tx.index", int64(i)),
) )
receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, usedGas, evm)
spanEnd(&err) receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, evm)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
} }
receipts = append(receipts, receipt) receipts = append(receipts, receipt)
allLogs = append(allLogs, receipt.Logs...) allLogs = append(allLogs, receipt.Logs...)
spanEnd(&err)
} }
requests, err := postExecution(ctx, config, block, allLogs, evm) requests, err := postExecution(ctx, config, block, allLogs, evm)
if err != nil { if err != nil {
@ -127,7 +127,7 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated
Receipts: receipts, Receipts: receipts,
Requests: requests, Requests: requests,
Logs: allLogs, Logs: allLogs,
GasUsed: *usedGas, GasUsed: gp.Used(),
}, nil }, nil
} }
@ -159,7 +159,7 @@ func postExecution(ctx context.Context, config *params.ChainConfig, block *types
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database // ApplyTransactionWithEVM attempts to apply a transaction to the given state database
// and uses the input parameters for its environment similar to ApplyTransaction. However, // and uses the input parameters for its environment similar to ApplyTransaction. However,
// this method takes an already created EVM instance as input. // this method takes an already created EVM instance as input.
func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, evm *vm.EVM) (receipt *types.Receipt, err error) {
if hooks := evm.Config.Tracer; hooks != nil { if hooks := evm.Config.Tracer; hooks != nil {
if hooks.OnTxStart != nil { if hooks.OnTxStart != nil {
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
@ -180,27 +180,31 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB,
} else { } else {
root = statedb.IntermediateRoot(evm.ChainConfig().IsEIP158(blockNumber)).Bytes() root = statedb.IntermediateRoot(evm.ChainConfig().IsEIP158(blockNumber)).Bytes()
} }
*usedGas += result.UsedGas
// Merge the tx-local access event into the "block-local" one, in order to collect // Merge the tx-local access event into the "block-local" one, in order to collect
// all values, so that the witness can be built. // all values, so that the witness can be built.
if statedb.Database().TrieDB().IsVerkle() { if statedb.Database().TrieDB().IsVerkle() {
statedb.AccessEvents().Merge(evm.AccessEvents) statedb.AccessEvents().Merge(evm.AccessEvents)
} }
return MakeReceipt(evm, result, statedb, blockNumber, blockHash, blockTime, tx, *usedGas, root), nil return MakeReceipt(evm, result, statedb, blockNumber, blockHash, blockTime, tx, gp.CumulativeUsed(), root), nil
} }
// MakeReceipt generates the receipt object for a transaction given its execution result. // MakeReceipt generates the receipt object for a transaction given its execution result.
func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt { func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, cumulativeGas uint64, root []byte) *types.Receipt {
// Create a new receipt for the transaction, storing the intermediate root and gas used // Create a new receipt for the transaction, storing the intermediate root
// by the tx. // and gas used by the tx.
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas} //
// The cumulative gas used equals the sum of gasUsed across all preceding
// txs with refunded gas deducted.
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: cumulativeGas}
if result.Failed() { if result.Failed() {
receipt.Status = types.ReceiptStatusFailed receipt.Status = types.ReceiptStatusFailed
} else { } else {
receipt.Status = types.ReceiptStatusSuccessful receipt.Status = types.ReceiptStatusSuccessful
} }
receipt.TxHash = tx.Hash() receipt.TxHash = tx.Hash()
// GasUsed = max(tx_gas_used - gas_refund, calldata_floor_gas_cost), unchanged
// in the Amsterdam fork.
receipt.GasUsed = result.UsedGas receipt.GasUsed = result.UsedGas
if tx.Type() == types.BlobTxType { if tx.Type() == types.BlobTxType {
@ -224,15 +228,15 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
// ApplyTransaction attempts to apply a transaction to the given state database // ApplyTransaction attempts to apply a transaction to the given state database
// and uses the input parameters for its environment. It returns the receipt // and uses the input parameters for its environment. It returns the receipt
// for the transaction, gas used and an error if the transaction failed, // for the transaction and an error if the transaction failed,
// indicating the block was invalid. // indicating the block was invalid.
func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, error) { func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction) (*types.Receipt, error) {
msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee) msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Create a new context to be used in the EVM environment // Create a new context to be used in the EVM environment
return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, usedGas, evm) return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, evm)
} }
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root // ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root

View file

@ -34,7 +34,7 @@ import (
// ExecutionResult includes all output after executing given evm // ExecutionResult includes all output after executing given evm
// message no matter the execution itself is successful or not. // message no matter the execution itself is successful or not.
type ExecutionResult struct { type ExecutionResult struct {
UsedGas uint64 // Total used gas, not including the refunded gas UsedGas uint64 // Total used gas, refunded gas is deducted
MaxUsedGas uint64 // Maximum gas consumed during execution, excluding gas refunds. MaxUsedGas uint64 // Maximum gas consumed during execution, excluding gas refunds.
Err error // Any error encountered during the execution(listed in core/vm/errors.go) Err error // Any error encountered during the execution(listed in core/vm/errors.go)
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
@ -210,6 +210,11 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
// indicates a core error meaning that the message would always fail for that particular // indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block. // state and would never be accepted within a block.
func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, error) { func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, error) {
// Do not panic if the gas pool is nil. This is allowed when executing
// a single message via RPC invocation.
if gp == nil {
gp = NewGasPool(msg.GasLimit)
}
evm.SetTxContext(NewEVMTxContext(msg)) evm.SetTxContext(NewEVMTxContext(msg))
return newStateTransition(evm, msg, gp).execute() return newStateTransition(evm, msg, gp).execute()
} }
@ -300,8 +305,8 @@ func (st *stateTransition) buyGas() error {
st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, tracing.GasChangeTxInitialBalance) st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, tracing.GasChangeTxInitialBalance)
} }
st.gasRemaining = st.msg.GasLimit st.gasRemaining = st.msg.GasLimit
st.initialGas = st.msg.GasLimit st.initialGas = st.msg.GasLimit
mgvalU256, _ := uint256.FromBig(mgval) mgvalU256, _ := uint256.FromBig(mgval)
st.state.SubBalance(st.msg.From, mgvalU256, tracing.BalanceDecreaseGasBuy) st.state.SubBalance(st.msg.From, mgvalU256, tracing.BalanceDecreaseGasBuy)
return nil return nil
@ -542,8 +547,20 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
peakGasUsed = floorDataGas peakGasUsed = floorDataGas
} }
} }
// Return gas to the user
st.returnGas() st.returnGas()
// Return gas to the gas pool
if rules.IsAmsterdam {
// Refund is excluded for returning
err = st.gp.ReturnGas(st.initialGas-peakGasUsed, st.gasUsed())
} else {
// Refund is included for returning
err = st.gp.ReturnGas(st.gasRemaining, st.gasUsed())
}
if err != nil {
return nil, err
}
effectiveTip := msg.GasPrice effectiveTip := msg.GasPrice
if rules.IsLondon { if rules.IsLondon {
effectiveTip = new(big.Int).Sub(msg.GasPrice, st.evm.Context.BaseFee) effectiveTip = new(big.Int).Sub(msg.GasPrice, st.evm.Context.BaseFee)
@ -564,7 +581,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true, math.MaxUint64) st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true, math.MaxUint64)
} }
} }
return &ExecutionResult{ return &ExecutionResult{
UsedGas: st.gasUsed(), UsedGas: st.gasUsed(),
MaxUsedGas: peakGasUsed, MaxUsedGas: peakGasUsed,
@ -660,10 +676,6 @@ func (st *stateTransition) returnGas() {
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 { if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 {
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, tracing.GasChangeTxLeftOverReturned) st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, tracing.GasChangeTxLeftOverReturned)
} }
// Also return remaining gas to the block gas counter so it is
// available for the next transaction.
st.gp.AddGas(st.gasRemaining)
} }
// gasUsed returns the amount of gas used up by the state transition. // gasUsed returns the amount of gas used up by the state transition.

View file

@ -358,7 +358,7 @@ const (
// this generates an increase in gas. There is at most one of such gas change per transaction. // this generates an increase in gas. There is at most one of such gas change per transaction.
GasChangeTxRefunds GasChangeReason = 3 GasChangeTxRefunds GasChangeReason = 3
// GasChangeTxLeftOverReturned is the amount of gas left over at the end of transaction's execution that will be returned // GasChangeTxLeftOverReturned is the amount of gas left over at the end of transaction's execution that will be returned
// to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas // to the account. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas
// left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller. // left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller.
// There is at most one of such gas change per transaction. // There is at most one of such gas change per transaction.
GasChangeTxLeftOverReturned GasChangeReason = 4 GasChangeTxLeftOverReturned GasChangeReason = 4

View file

@ -946,24 +946,34 @@ func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, erro
return nil, errStopToken return nil, errStopToken
} }
// decodeSingle decodes the immediate operand of a backward-compatible DUPN or SWAPN instruction (EIP-8024)
// https://eips.ethereum.org/EIPS/eip-8024
func decodeSingle(x byte) int { func decodeSingle(x byte) int {
if x <= 90 { // Depths 1-16 are already covered by the legacy opcodes. The forbidden byte range [91, 127] removes
return int(x) + 17 // 37 values from the 256 possible immediates, leaving 219 usable values, so this encoding covers depths
} // 17 through 235. The immediate is encoded as (x + 111) % 256, where 111 is chosen so that these values
return int(x) - 20 // avoid the forbidden range. Decoding is simply the modular inverse (i.e. 111+145=256).
return (int(x) + 145) % 256
} }
// decodePair decodes the immediate operand of a backward-compatible EXCHANGE
// instruction (EIP-8024) into stack indices (n, m) where 1 <= n < m
// and n + m <= 30. The forbidden byte range [82, 127] removes 46 values from
// the 256 possible immediates, leaving exactly 210 usable bytes.
// https://eips.ethereum.org/EIPS/eip-8024
func decodePair(x byte) (int, int) { func decodePair(x byte) (int, int) {
var k int // XOR with 143 remaps the forbidden bytes [82, 127] to an unused corner
if x <= 79 { // of the 16x16 grid below.
k = int(x) k := int(x ^ 143)
} else { // Split into row q and column r of a 16x16 grid. The 210 valid pairs
k = int(x) - 48 // occupy two triangles within this grid.
}
q, r := k/16, k%16 q, r := k/16, k%16
// Upper triangle (q < r): pairs where m <= 16, encoded directly as
// (q+1, r+1).
if q < r { if q < r {
return q + 1, r + 1 return q + 1, r + 1
} }
// Lower triangle: pairs where m > 16, recovered as (r+1, 29-q).
return r + 1, 29 - q return r + 1, 29 - q
} }
@ -1034,8 +1044,8 @@ func opExchange(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
} }
// This range is excluded both to preserve compatibility with existing opcodes // This range is excluded both to preserve compatibility with existing opcodes
// and to keep decode_pairs 16-aligned arithmetic mapping valid (079, 128255). // and to keep decode_pairs 16-aligned arithmetic mapping valid (081, 128255).
if x > 79 && x < 128 { if x > 81 && x < 128 {
return nil, &ErrInvalidOpCode{opcode: OpCode(x)} return nil, &ErrInvalidOpCode{opcode: OpCode(x)}
} }
n, m := decodePair(x) n, m := decodePair(x)

View file

@ -1022,16 +1022,7 @@ func TestEIP8024_Execution(t *testing.T) {
}{ }{
{ {
name: "DUPN", name: "DUPN",
codeHex: "60016000808080808080808080808080808080e600", codeHex: "60016000808080808080808080808080808080e680",
wantVals: []uint64{
1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1,
},
},
{
name: "DUPN_MISSING_IMMEDIATE",
codeHex: "60016000808080808080808080808080808080e6",
wantVals: []uint64{ wantVals: []uint64{
1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -1040,7 +1031,7 @@ func TestEIP8024_Execution(t *testing.T) {
}, },
{ {
name: "SWAPN", name: "SWAPN",
codeHex: "600160008080808080808080808080808080806002e700", codeHex: "600160008080808080808080808080808080806002e780",
wantVals: []uint64{ wantVals: []uint64{
1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -1048,22 +1039,23 @@ func TestEIP8024_Execution(t *testing.T) {
}, },
}, },
{ {
name: "SWAPN_MISSING_IMMEDIATE", name: "EXCHANGE_MISSING_IMMEDIATE",
codeHex: "600160008080808080808080808080808080806002e7", codeHex: "600260008080808080600160008080808080808080e8",
wantVals: []uint64{ wantVals: []uint64{
1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, // 10th from top
2, 0, 0, 0, 0, 0, 0,
1, // bottom
}, },
}, },
{ {
name: "EXCHANGE", name: "EXCHANGE",
codeHex: "600060016002e801", codeHex: "600060016002e88e",
wantVals: []uint64{2, 0, 1}, wantVals: []uint64{2, 0, 1},
}, },
{ {
name: "EXCHANGE_MISSING_IMMEDIATE", name: "EXCHANGE",
codeHex: "600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060016002e8", codeHex: "600080808080808080808080808080808080808080808080808080808060016002e88f",
wantVals: []uint64{ wantVals: []uint64{
2, 2,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -1077,68 +1069,31 @@ func TestEIP8024_Execution(t *testing.T) {
wantOpcode: SWAPN, wantOpcode: SWAPN,
}, },
{ {
name: "JUMP over INVALID_DUPN", name: "JUMP_OVER_INVALID_DUPN",
codeHex: "600456e65b", codeHex: "600456e65b",
wantErr: nil, wantErr: nil,
}, },
{ {
name: "UNDERFLOW_DUPN_1", name: "EXCHANGE",
codeHex: "6000808080808080808080808080808080e600", codeHex: "60008080e88e15",
wantVals: []uint64{1, 0, 0},
},
{
name: "INVALID_EXCHANGE",
codeHex: "e852",
wantErr: &ErrInvalidOpCode{},
wantOpcode: EXCHANGE,
},
{
name: "UNDERFLOW_DUPN",
codeHex: "6000808080808080808080808080808080e680",
wantErr: &ErrStackUnderflow{}, wantErr: &ErrStackUnderflow{},
wantOpcode: DUPN, wantOpcode: DUPN,
}, },
// Additional test cases // Additional test cases
{
name: "INVALID_DUPN_LOW",
codeHex: "e65b",
wantErr: &ErrInvalidOpCode{},
wantOpcode: DUPN,
},
{
name: "INVALID_EXCHANGE_LOW",
codeHex: "e850",
wantErr: &ErrInvalidOpCode{},
wantOpcode: EXCHANGE,
},
{
name: "INVALID_DUPN_HIGH",
codeHex: "e67f",
wantErr: &ErrInvalidOpCode{},
wantOpcode: DUPN,
},
{
name: "INVALID_SWAPN_HIGH",
codeHex: "e77f",
wantErr: &ErrInvalidOpCode{},
wantOpcode: SWAPN,
},
{
name: "INVALID_EXCHANGE_HIGH",
codeHex: "e87f",
wantErr: &ErrInvalidOpCode{},
wantOpcode: EXCHANGE,
},
{
name: "UNDERFLOW_DUPN_2",
codeHex: "5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5fe600", // (n=17, need 17 items, have 16)
wantErr: &ErrStackUnderflow{},
wantOpcode: DUPN,
},
{
name: "UNDERFLOW_SWAPN",
codeHex: "5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5fe700", // (n=17, need 18 items, have 17)
wantErr: &ErrStackUnderflow{},
wantOpcode: SWAPN,
},
{
name: "UNDERFLOW_EXCHANGE",
codeHex: "60016002e801", // (n,m)=(1,2), need 3 items, have 2
wantErr: &ErrStackUnderflow{},
wantOpcode: EXCHANGE,
},
{ {
name: "PC_INCREMENT", name: "PC_INCREMENT",
codeHex: "600060006000e80115", codeHex: "600060006000e88e15",
wantVals: []uint64{1, 0, 0}, wantVals: []uint64{1, 0, 0},
}, },
} }

View file

@ -27,13 +27,11 @@ import (
// Config are the configuration options for the Interpreter // Config are the configuration options for the Interpreter
type Config struct { type Config struct {
Tracer *tracing.Hooks Tracer *tracing.Hooks
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
ExtraEips []int // Additional EIPS that are to be enabled ExtraEips []int // Additional EIPS that are to be enabled
StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose)
EnableWitnessStats bool // Whether trie access statistics collection is enabled
} }
// ScopeContext contains the things that are per-call, such as stack and memory, // ScopeContext contains the things that are per-call, such as stack and memory,

View file

@ -28,6 +28,8 @@ func LookupInstructionSet(rules params.Rules) (JumpTable, error) {
switch { switch {
case rules.IsVerkle: case rules.IsVerkle:
return newCancunInstructionSet(), errors.New("verkle-fork not defined yet") return newCancunInstructionSet(), errors.New("verkle-fork not defined yet")
case rules.IsAmsterdam:
return newAmsterdamInstructionSet(), nil
case rules.IsOsaka: case rules.IsOsaka:
return newOsakaInstructionSet(), nil return newOsakaInstructionSet(), nil
case rules.IsPrague: case rules.IsPrague:

View file

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/stateless"
@ -493,34 +494,22 @@ func (api *DebugAPI) StateSize(blockHashOrNumber *rpc.BlockNumberOrHash) (interf
}, nil }, nil
} }
func (api *DebugAPI) ExecutionWitness(bn rpc.BlockNumber) (*stateless.ExtWitness, error) { func (api *DebugAPI) ExecutionWitness(bn rpc.BlockNumberOrHash) (*stateless.ExtWitness, error) {
bc := api.eth.blockchain bc := api.eth.blockchain
block, err := api.eth.APIBackend.BlockByNumber(context.Background(), bn) block, err := api.eth.APIBackend.BlockByNumberOrHash(context.Background(), bn)
if err != nil { if err != nil {
return &stateless.ExtWitness{}, fmt.Errorf("block number %v not found", bn) return &stateless.ExtWitness{}, fmt.Errorf("block %v not found", bn)
} }
parent := bc.GetHeader(block.ParentHash(), block.NumberU64()-1) parent := bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
if parent == nil { if parent == nil {
return &stateless.ExtWitness{}, fmt.Errorf("block number %v found, but parent missing", bn) return &stateless.ExtWitness{}, fmt.Errorf("block %v found, but parent missing", bn)
} }
result, err := bc.ProcessBlock(context.Background(), parent.Root, block, false, true) config := core.ExecuteConfig{
if err != nil { WriteState: false,
return nil, err EnableTracer: false,
} MakeWitness: true,
return result.Witness().ToExtWitness(), nil }
} result, err := bc.ProcessBlock(context.Background(), parent.Root, block, config)
func (api *DebugAPI) ExecutionWitnessByHash(hash common.Hash) (*stateless.ExtWitness, error) {
bc := api.eth.blockchain
block := bc.GetBlockByHash(hash)
if block == nil {
return &stateless.ExtWitness{}, fmt.Errorf("block hash %x not found", hash)
}
parent := bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return &stateless.ExtWitness{}, fmt.Errorf("block number %x found, but parent missing", hash)
}
result, err := bc.ProcessBlock(context.Background(), parent.Root, block, false, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -237,8 +237,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
TxLookupLimit: int64(min(config.TransactionHistory, math.MaxInt64)), TxLookupLimit: int64(min(config.TransactionHistory, math.MaxInt64)),
VmConfig: vm.Config{ VmConfig: vm.Config{
EnablePreimageRecording: config.EnablePreimageRecording, EnablePreimageRecording: config.EnablePreimageRecording,
EnableWitnessStats: config.EnableWitnessStats,
StatelessSelfValidation: config.StatelessSelfValidation,
}, },
// Enables file journaling for the trie database. The journal files will be stored // Enables file journaling for the trie database. The journal files will be stored
// within the data directory. The corresponding paths will be either: // within the data directory. The corresponding paths will be either:
@ -247,6 +245,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
TrieJournalDirectory: stack.ResolvePath("triedb"), TrieJournalDirectory: stack.ResolvePath("triedb"),
StateSizeTracking: config.EnableStateSizeTracking, StateSizeTracking: config.EnableStateSizeTracking,
SlowBlockThreshold: config.SlowBlockThreshold, SlowBlockThreshold: config.SlowBlockThreshold,
StatelessSelfValidation: config.StatelessSelfValidation,
EnableWitnessStats: config.EnableWitnessStats,
} }
) )
if config.VMTrace != "" { if config.VMTrace != "" {

View file

@ -103,6 +103,8 @@ type SimulatedBeacon struct {
func payloadVersion(config *params.ChainConfig, time uint64) engine.PayloadVersion { func payloadVersion(config *params.ChainConfig, time uint64) engine.PayloadVersion {
switch config.LatestFork(time) { switch config.LatestFork(time) {
case forks.Amsterdam:
return engine.PayloadV4
case forks.BPO5, forks.BPO4, forks.BPO3, forks.BPO2, forks.BPO1, forks.Osaka, forks.Prague, forks.Cancun: case forks.BPO5, forks.BPO4, forks.BPO3, forks.BPO2, forks.BPO1, forks.Osaka, forks.Prague, forks.Cancun:
return engine.PayloadV3 return engine.PayloadV3
case forks.Paris, forks.Shanghai: case forks.Paris, forks.Shanghai:
@ -200,18 +202,23 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u
// simulating an incoming engine API request from a real consensus client. // simulating an incoming engine API request from a real consensus client.
var random [32]byte var random [32]byte
rand.Read(random[:]) rand.Read(random[:])
fcCtx, fcSpanEnd := telemetry.StartServerSpan(context.Background(), tracer, telemetry.RPCInfo{ attribute := &engine.PayloadAttributes{
System: "jsonrpc",
Service: "engine",
Method: "forkchoiceUpdatedV" + fmt.Sprintf("%d", version),
})
fcResponse, err := c.engineAPI.forkchoiceUpdated(fcCtx, c.curForkchoiceState, &engine.PayloadAttributes{
Timestamp: timestamp, Timestamp: timestamp,
SuggestedFeeRecipient: feeRecipient, SuggestedFeeRecipient: feeRecipient,
Withdrawals: withdrawals, Withdrawals: withdrawals,
Random: random, Random: random,
BeaconRoot: &common.Hash{}, BeaconRoot: &common.Hash{},
}, version, false) }
if c.eth.BlockChain().Config().LatestFork(timestamp) == forks.Amsterdam {
slotNumber := uint64(0)
attribute.SlotNumber = &slotNumber
}
fcCtx, fcSpanEnd := telemetry.StartServerSpan(context.Background(), tracer, telemetry.RPCInfo{
System: "jsonrpc",
Service: "engine",
Method: "forkchoiceUpdatedV" + fmt.Sprintf("%d", version),
})
fcResponse, err := c.engineAPI.forkchoiceUpdated(fcCtx, c.curForkchoiceState, attribute, version, false)
fcSpanEnd(&err) fcSpanEnd(&err)
if err != nil { if err != nil {
return err return err

View file

@ -20,7 +20,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"math"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -268,7 +267,7 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio
evm.Cancel() evm.Cancel()
}() }()
// Execute the call, returning a wrapped error or the result // Execute the call, returning a wrapped error or the result
result, err := core.ApplyMessage(evm, call, new(core.GasPool).AddGas(math.MaxUint64)) result, err := core.ApplyMessage(evm, call, nil)
if vmerr := dirtyState.Error(); vmerr != nil { if vmerr := dirtyState.Error(); vmerr != nil {
return nil, vmerr return nil, vmerr
} }

View file

@ -265,7 +265,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
// Not yet the searched for transaction, execute on top of the current state // Not yet the searched for transaction, execute on top of the current state
statedb.SetTxContext(tx.Hash(), idx) statedb.SetTxContext(tx.Hash(), idx)
if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { if _, err := core.ApplyMessage(evm, msg, nil); err != nil {
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
} }
// Ensure any modifications are committed to the state // Ensure any modifications are committed to the state

View file

@ -551,7 +551,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
} }
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
statedb.SetTxContext(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { if _, err := core.ApplyMessage(evm, msg, nil); err != nil {
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err) log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
// We intentionally don't return the error here: if we do, then the RPC server will not // We intentionally don't return the error here: if we do, then the RPC server will not
// return the roots. Most likely, the caller already knows that a certain transaction fails to // return the roots. Most likely, the caller already knows that a certain transaction fails to
@ -707,7 +707,7 @@ txloop:
// Generate the next state snapshot fast without tracing // Generate the next state snapshot fast without tracing
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
statedb.SetTxContext(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { if _, err := core.ApplyMessage(evm, msg, nil); err != nil {
failed = err failed = err
break txloop break txloop
} }
@ -792,7 +792,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
if txHash != (common.Hash{}) && tx.Hash() != txHash { if txHash != (common.Hash{}) && tx.Hash() != txHash {
// Process the tx to update state, but don't trace it. // Process the tx to update state, but don't trace it.
_, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)) _, err := core.ApplyMessage(evm, msg, nil)
if err != nil { if err != nil {
return dumps, err return dumps, err
} }
@ -827,7 +827,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
if tracer.OnTxStart != nil { if tracer.OnTxStart != nil {
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
} }
_, err = core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)) _, err = core.ApplyMessage(evm, msg, nil)
if writer != nil { if writer != nil {
writer.Flush() writer.Flush()
} }
@ -1011,7 +1011,6 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
tracer *Tracer tracer *Tracer
err error err error
timeout = defaultTraceTimeout timeout = defaultTraceTimeout
usedGas uint64
) )
if config == nil { if config == nil {
config = &TraceConfig{} config = &TraceConfig{}
@ -1055,7 +1054,8 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
// Call Prepare to clear out the statedb access list // Call Prepare to clear out the statedb access list
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
_, err = core.ApplyTransactionWithEVM(message, new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, vmctx.Time, tx, &usedGas, evm)
_, err = core.ApplyTransactionWithEVM(message, core.NewGasPool(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, vmctx.Time, tx, evm)
if err != nil { if err != nil {
return nil, fmt.Errorf("tracing failed: %w", err) return nil, fmt.Errorf("tracing failed: %w", err)
} }

View file

@ -188,7 +188,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
return tx, context, statedb, release, nil return tx, context, statedb, release, nil
} }
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { if _, err := core.ApplyMessage(evm, msg, nil); err != nil {
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
} }
statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number())) statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number()))

View file

@ -132,7 +132,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
} }
evm := vm.NewEVM(context, logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) evm := vm.NewEVM(context, logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) vmRet, err := core.ApplyMessage(evm, msg, nil)
if err != nil { if err != nil {
t.Fatalf("failed to execute transaction: %v", err) t.Fatalf("failed to execute transaction: %v", err)
} }
@ -224,7 +224,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
if tracer.OnTxStart != nil { if tracer.OnTxStart != nil {
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
} }
_, err = core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) _, err = core.ApplyMessage(evm, msg, nil)
if err != nil { if err != nil {
b.Fatalf("failed to execute transaction: %v", err) b.Fatalf("failed to execute transaction: %v", err)
} }
@ -374,7 +374,7 @@ func TestInternals(t *testing.T) {
t.Fatalf("test %v: failed to create message: %v", tc.name, err) t.Fatalf("test %v: failed to create message: %v", tc.name, err)
} }
tc.tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) tc.tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) vmRet, err := core.ApplyMessage(evm, msg, nil)
if err != nil { if err != nil {
t.Fatalf("test %v: failed to execute transaction: %v", tc.name, err) t.Fatalf("test %v: failed to execute transaction: %v", tc.name, err)
} }

View file

@ -124,7 +124,7 @@ func TestErc7562Tracer(t *testing.T) {
} }
evm := vm.NewEVM(context, logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) evm := vm.NewEVM(context, logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) vmRet, err := core.ApplyMessage(evm, msg, nil)
if err != nil { if err != nil {
t.Fatalf("failed to execute transaction: %v", err) t.Fatalf("failed to execute transaction: %v", err)
} }

View file

@ -113,7 +113,7 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
} }
evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) vmRet, err := core.ApplyMessage(evm, msg, nil)
if err != nil { if err != nil {
return fmt.Errorf("failed to execute transaction: %v", err) return fmt.Errorf("failed to execute transaction: %v", err)
} }

View file

@ -105,7 +105,7 @@ func testPrestateTracer(tracerName string, dirPath string, t *testing.T) {
} }
evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) vmRet, err := core.ApplyMessage(evm, msg, nil)
if err != nil { if err != nil {
t.Fatalf("failed to execute transaction: %v", err) t.Fatalf("failed to execute transaction: %v", err)
} }

View file

@ -620,8 +620,7 @@ func TestSelfdestructStateTracer(t *testing.T) {
} }
context := core.NewEVMBlockContext(block.Header(), blockchain, nil) context := core.NewEVMBlockContext(block.Header(), blockchain, nil)
evm := vm.NewEVM(context, hookedState, tt.genesis.Config, vm.Config{Tracer: tracer.Hooks()}) evm := vm.NewEVM(context, hookedState, tt.genesis.Config, vm.Config{Tracer: tracer.Hooks()})
usedGas := uint64(0) _, err = core.ApplyTransactionWithEVM(msg, core.NewGasPool(msg.GasLimit), statedb, block.Number(), block.Hash(), block.Time(), tx, evm)
_, err = core.ApplyTransactionWithEVM(msg, new(core.GasPool).AddGas(tx.Gas()), statedb, block.Number(), block.Hash(), block.Time(), tx, &usedGas, evm)
if err != nil { if err != nil {
t.Fatalf("failed to execute transaction: %v", err) t.Fatalf("failed to execute transaction: %v", err)
} }

View file

@ -91,7 +91,7 @@ func BenchmarkTransactionTraceV2(b *testing.B) {
evm.Config.Tracer = tracer evm.Config.Tracer = tracer
snap := state.StateDB.Snapshot() snap := state.StateDB.Snapshot()
_, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) _, err := core.ApplyMessage(evm, msg, nil)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }

View file

@ -37,6 +37,9 @@ type Batch interface {
// Replay replays the batch contents. // Replay replays the batch contents.
Replay(w KeyValueWriter) error Replay(w KeyValueWriter) error
// Close closes the batch and releases all associated resources.
Close()
} }
// Batcher wraps the NewBatch method of a backing data store. // Batcher wraps the NewBatch method of a backing data store.

View file

@ -518,6 +518,9 @@ func (b *batch) Replay(w ethdb.KeyValueWriter) error {
return b.b.Replay(&replayer{writer: w}) return b.b.Replay(&replayer{writer: w})
} }
// Close closes the batch and releases all associated resources.
func (b *batch) Close() {}
// replayer is a small wrapper to implement the correct replay methods. // replayer is a small wrapper to implement the correct replay methods.
type replayer struct { type replayer struct {
writer ethdb.KeyValueWriter writer ethdb.KeyValueWriter

View file

@ -338,6 +338,9 @@ func (b *batch) Replay(w ethdb.KeyValueWriter) error {
return nil return nil
} }
// Close closes the batch and releases all associated resources.
func (b *batch) Close() {}
// iterator can walk over the (potentially partial) keyspace of a memory key // iterator can walk over the (potentially partial) keyspace of a memory key
// value store. Internally it is a deep copy of the entire iterated state, // value store. Internally it is a deep copy of the entire iterated state,
// sorted by keys. // sorted by keys.

View file

@ -731,6 +731,12 @@ func (b *batch) Replay(w ethdb.KeyValueWriter) error {
} }
} }
// Close closes the batch and releases all associated resources. After it is
// closed, any subsequent operations on this batch are undefined.
func (b *batch) Close() {
b.b.Close()
}
// pebbleIterator is a wrapper of underlying iterator in storage engine. // pebbleIterator is a wrapper of underlying iterator in storage engine.
// The purpose of this structure is to implement the missing APIs. // The purpose of this structure is to implement the missing APIs.
// //

10
go.mod
View file

@ -61,17 +61,17 @@ require (
github.com/supranational/blst v0.3.16 github.com/supranational/blst v0.3.16
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
github.com/urfave/cli/v2 v2.27.5 github.com/urfave/cli/v2 v2.27.5
go.opentelemetry.io/otel v1.39.0 go.opentelemetry.io/otel v1.40.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0
go.opentelemetry.io/otel/sdk v1.39.0 go.opentelemetry.io/otel/sdk v1.40.0
go.opentelemetry.io/otel/trace v1.39.0 go.opentelemetry.io/otel/trace v1.40.0
go.uber.org/automaxprocs v1.5.2 go.uber.org/automaxprocs v1.5.2
go.uber.org/goleak v1.3.0 go.uber.org/goleak v1.3.0
golang.org/x/crypto v0.44.0 golang.org/x/crypto v0.44.0
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df
golang.org/x/sync v0.18.0 golang.org/x/sync v0.18.0
golang.org/x/sys v0.39.0 golang.org/x/sys v0.40.0
golang.org/x/text v0.31.0 golang.org/x/text v0.31.0
golang.org/x/time v0.9.0 golang.org/x/time v0.9.0
golang.org/x/tools v0.38.0 golang.org/x/tools v0.38.0
@ -86,7 +86,7 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/metric v1.39.0 // indirect go.opentelemetry.io/otel/metric v1.40.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b // indirect google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect

24
go.sum
View file

@ -380,20 +380,20 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 h1:Ckwye2FpXkYgiHX7fyVrN1uA/UYd9ounqqTuSNAv0k4= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 h1:Ckwye2FpXkYgiHX7fyVrN1uA/UYd9ounqqTuSNAv0k4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0/go.mod h1:teIFJh5pW2y+AN7riv6IBPX2DuesS3HgP39mwOspKwU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0/go.mod h1:teIFJh5pW2y+AN7riv6IBPX2DuesS3HgP39mwOspKwU=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME= go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME=
@ -477,8 +477,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=

View file

@ -741,11 +741,10 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S
// Make sure the context is cancelled when the call has completed // Make sure the context is cancelled when the call has completed
// this makes sure resources are cleaned up. // this makes sure resources are cleaned up.
defer cancel() defer cancel()
gp := new(core.GasPool)
gp := core.NewGasPool(globalGasCap)
if globalGasCap == 0 { if globalGasCap == 0 {
gp.AddGas(gomath.MaxUint64) gp = core.NewGasPool(gomath.MaxUint64)
} else {
gp.AddGas(globalGasCap)
} }
return applyMessage(ctx, b, args, state, header, timeout, gp, &blockCtx, &vm.Config{NoBaseFee: true}, precompiles) return applyMessage(ctx, b, args, state, header, timeout, gp, &blockCtx, &vm.Config{NoBaseFee: true}, precompiles)
} }
@ -855,12 +854,11 @@ func (api *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrO
gasCap = gomath.MaxUint64 gasCap = gomath.MaxUint64
} }
sim := &simulator{ sim := &simulator{
b: api.b, b: api.b,
state: state, state: state,
base: base, base: base,
chainConfig: api.b.ChainConfig(), chainConfig: api.b.ChainConfig(),
// Each tx and all the series of txes shouldn't consume more gas than cap gasRemaining: gasCap,
gp: new(core.GasPool).AddGas(gasCap),
traceTransfers: opts.TraceTransfers, traceTransfers: opts.TraceTransfers,
validate: opts.Validation, validate: opts.Validation,
fullTx: opts.ReturnFullTransactions, fullTx: opts.ReturnFullTransactions,
@ -1369,7 +1367,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 { if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 {
evm.Context.BlobBaseFee = new(big.Int) evm.Context.BlobBaseFee = new(big.Int)
} }
res, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)) res, err := core.ApplyMessage(evm, msg, nil)
if err != nil { if err != nil {
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.ToTransaction(types.LegacyTxType).Hash(), err) return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.ToTransaction(types.LegacyTxType).Hash(), err)
} }

View file

@ -2507,7 +2507,7 @@ func TestSimulateV1ChainLinkage(t *testing.T) {
state: stateDB, state: stateDB,
base: baseHeader, base: baseHeader,
chainConfig: backend.ChainConfig(), chainConfig: backend.ChainConfig(),
gp: new(core.GasPool).AddGas(math.MaxUint64), gasRemaining: math.MaxUint64,
traceTransfers: false, traceTransfers: false,
validate: false, validate: false,
fullTx: false, fullTx: false,
@ -2592,7 +2592,7 @@ func TestSimulateV1TxSender(t *testing.T) {
state: stateDB, state: stateDB,
base: baseHeader, base: baseHeader,
chainConfig: backend.ChainConfig(), chainConfig: backend.ChainConfig(),
gp: new(core.GasPool).AddGas(math.MaxUint64), gasRemaining: math.MaxUint64,
traceTransfers: false, traceTransfers: false,
validate: false, validate: false,
fullTx: true, fullTx: true,

View file

@ -59,6 +59,7 @@ type simCallResult struct {
ReturnValue hexutil.Bytes `json:"returnData"` ReturnValue hexutil.Bytes `json:"returnData"`
Logs []*types.Log `json:"logs"` Logs []*types.Log `json:"logs"`
GasUsed hexutil.Uint64 `json:"gasUsed"` GasUsed hexutil.Uint64 `json:"gasUsed"`
MaxUsedGas hexutil.Uint64 `json:"maxUsedGas"`
Status hexutil.Uint64 `json:"status"` Status hexutil.Uint64 `json:"status"`
Error *callError `json:"error,omitempty"` Error *callError `json:"error,omitempty"`
} }
@ -156,7 +157,7 @@ type simulator struct {
state *state.StateDB state *state.StateDB
base *types.Header base *types.Header
chainConfig *params.ChainConfig chainConfig *params.ChainConfig
gp *core.GasPool gasRemaining uint64
traceTransfers bool traceTransfers bool
validate bool validate bool
fullTx bool fullTx bool
@ -200,7 +201,13 @@ func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]*simBlo
return nil, err return nil, err
} }
headers[bi] = result.Header() headers[bi] = result.Header()
results[bi] = &simBlockResult{fullTx: sim.fullTx, chainConfig: sim.chainConfig, Block: result, Calls: callResults, senders: senders} results[bi] = &simBlockResult{
fullTx: sim.fullTx,
chainConfig: sim.chainConfig,
Block: result,
Calls: callResults,
senders: senders,
}
parent = result.Header() parent = result.Header()
} }
return results, nil return results, nil
@ -234,15 +241,19 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
blockContext.BlobBaseFee = block.BlockOverrides.BlobBaseFee.ToInt() blockContext.BlobBaseFee = block.BlockOverrides.BlobBaseFee.ToInt()
} }
precompiles := sim.activePrecompiles(header) precompiles := sim.activePrecompiles(header)
// State overrides are applied prior to execution of a block // State overrides are applied prior to execution of a block
if err := block.StateOverrides.Apply(sim.state, precompiles); err != nil { if err := block.StateOverrides.Apply(sim.state, precompiles); err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
var ( var (
gasUsed, blobGasUsed uint64 gp = core.NewGasPool(blockContext.GasLimit)
txes = make([]*types.Transaction, len(block.Calls)) blobGasUsed uint64
callResults = make([]simCallResult, len(block.Calls))
receipts = make([]*types.Receipt, len(block.Calls)) txes = make([]*types.Transaction, len(block.Calls))
callResults = make([]simCallResult, len(block.Calls))
receipts = make([]*types.Receipt, len(block.Calls))
// Block hash will be repaired after execution. // Block hash will be repaired after execution.
tracer = newTracer(sim.traceTransfers, blockContext.BlockNumber.Uint64(), blockContext.Time, common.Hash{}, common.Hash{}, 0) tracer = newTracer(sim.traceTransfers, blockContext.BlockNumber.Uint64(), blockContext.Time, common.Hash{}, common.Hash{}, 0)
vmConfig = &vm.Config{ vmConfig = &vm.Config{
@ -272,10 +283,11 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
} }
var allLogs []*types.Log var allLogs []*types.Log
for i, call := range block.Calls { for i, call := range block.Calls {
// Terminate if the context is cancelled
if err := ctx.Err(); err != nil { if err := ctx.Err(); err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
if err := sim.sanitizeCall(&call, sim.state, header, blockContext, &gasUsed); err != nil { if err := sim.sanitizeCall(&call, sim.state, header, gp); err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
var ( var (
@ -285,10 +297,11 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
txes[i] = tx txes[i] = tx
senders[txHash] = call.from() senders[txHash] = call.from()
tracer.reset(txHash, uint(i)) tracer.reset(txHash, uint(i))
sim.state.SetTxContext(txHash, i)
// EoA check is always skipped, even in validation mode. // EoA check is always skipped, even in validation mode.
sim.state.SetTxContext(txHash, i)
msg := call.ToMessage(header.BaseFee, !sim.validate) msg := call.ToMessage(header.BaseFee, !sim.validate)
result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp) result, err := applyMessageWithEVM(ctx, evm, msg, timeout, gp)
if err != nil { if err != nil {
txErr := txValidationError(err) txErr := txValidationError(err)
return nil, nil, nil, txErr return nil, nil, nil, txErr
@ -300,11 +313,18 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
} else { } else {
root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes() root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes()
} }
gasUsed += result.UsedGas receipts[i] = core.MakeReceipt(evm, result, sim.state, blockContext.BlockNumber, common.Hash{}, blockContext.Time, tx, gp.CumulativeUsed(), root)
receipts[i] = core.MakeReceipt(evm, result, sim.state, blockContext.BlockNumber, common.Hash{}, blockContext.Time, tx, gasUsed, root)
blobGasUsed += receipts[i].BlobGasUsed blobGasUsed += receipts[i].BlobGasUsed
// Make sure the gas cap is still enforced. It's only for
// internally protection.
if sim.gasRemaining < result.UsedGas {
return nil, nil, nil, fmt.Errorf("gas cap reached, required: %d, remaining: %d", result.UsedGas, sim.gasRemaining)
}
sim.gasRemaining -= result.UsedGas
logs := tracer.Logs() logs := tracer.Logs()
callRes := simCallResult{ReturnValue: result.Return(), Logs: logs, GasUsed: hexutil.Uint64(result.UsedGas)} callRes := simCallResult{ReturnValue: result.Return(), Logs: logs, GasUsed: hexutil.Uint64(result.UsedGas), MaxUsedGas: hexutil.Uint64(result.MaxUsedGas)}
if result.Failed() { if result.Failed() {
callRes.Status = hexutil.Uint64(types.ReceiptStatusFailed) callRes.Status = hexutil.Uint64(types.ReceiptStatusFailed)
if errors.Is(result.Err, vm.ErrExecutionReverted) { if errors.Is(result.Err, vm.ErrExecutionReverted) {
@ -320,12 +340,14 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
} }
callResults[i] = callRes callResults[i] = callRes
} }
header.GasUsed = gasUsed // Assign total consumed gas to the header
header.GasUsed = gp.Used()
if sim.chainConfig.IsCancun(header.Number, header.Time) { if sim.chainConfig.IsCancun(header.Number, header.Time) {
header.BlobGasUsed = &blobGasUsed header.BlobGasUsed = &blobGasUsed
} }
var requests [][]byte
// Process EIP-7685 requests // Process EIP-7685 requests
var requests [][]byte
if sim.chainConfig.IsPrague(header.Number, header.Time) { if sim.chainConfig.IsPrague(header.Number, header.Time) {
requests = [][]byte{} requests = [][]byte{}
// EIP-6110 // EIP-6110
@ -345,7 +367,11 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
reqHash := types.CalcRequestsHash(requests) reqHash := types.CalcRequestsHash(requests)
header.RequestsHash = &reqHash header.RequestsHash = &reqHash
} }
blockBody := &types.Body{Transactions: txes, Withdrawals: *block.BlockOverrides.Withdrawals}
blockBody := &types.Body{
Transactions: txes,
Withdrawals: *block.BlockOverrides.Withdrawals,
}
chainHeadReader := &simChainHeadReader{ctx, sim.b} chainHeadReader := &simChainHeadReader{ctx, sim.b}
b, err := sim.b.Engine().FinalizeAndAssemble(ctx, chainHeadReader, header, sim.state, blockBody, receipts) b, err := sim.b.Engine().FinalizeAndAssemble(ctx, chainHeadReader, header, sim.state, blockBody, receipts)
if err != nil { if err != nil {
@ -366,23 +392,20 @@ func repairLogs(calls []simCallResult, hash common.Hash) {
} }
} }
func (sim *simulator) sanitizeCall(call *TransactionArgs, state vm.StateDB, header *types.Header, blockContext vm.BlockContext, gasUsed *uint64) error { func (sim *simulator) sanitizeCall(call *TransactionArgs, state vm.StateDB, header *types.Header, gp *core.GasPool) error {
if call.Nonce == nil { if call.Nonce == nil {
nonce := state.GetNonce(call.from()) nonce := state.GetNonce(call.from())
call.Nonce = (*hexutil.Uint64)(&nonce) call.Nonce = (*hexutil.Uint64)(&nonce)
} }
// Let the call run wild unless explicitly specified. // Let the call run wild unless explicitly specified.
remaining := gp.Gas()
if call.Gas == nil { if call.Gas == nil {
remaining := blockContext.GasLimit - *gasUsed
call.Gas = (*hexutil.Uint64)(&remaining) call.Gas = (*hexutil.Uint64)(&remaining)
} }
if *gasUsed+uint64(*call.Gas) > blockContext.GasLimit { if remaining < uint64(*call.Gas) {
return &blockGasLimitReachedError{fmt.Sprintf("block gas limit reached: %d >= %d", *gasUsed, blockContext.GasLimit)} return &blockGasLimitReachedError{fmt.Sprintf("block gas limit reached: remaining: %d, required: %d", remaining, *call.Gas)}
} }
if err := call.CallDefaults(sim.gp.Gas(), header.BaseFee, sim.chainConfig.ChainID); err != nil { return call.CallDefaults(0, header.BaseFee, sim.chainConfig.ChainID)
return err
}
return nil
} }
func (sim *simulator) activePrecompiles(base *types.Header) vm.PrecompiledContracts { func (sim *simulator) activePrecompiles(base *types.Header) vm.PrecompiledContracts {
@ -473,12 +496,14 @@ func (sim *simulator) makeHeaders(blocks []simBlock) ([]*types.Header, error) {
} }
overrides := block.BlockOverrides overrides := block.BlockOverrides
var withdrawalsHash *common.Hash
number := overrides.Number.ToInt() number := overrides.Number.ToInt()
timestamp := (uint64)(*overrides.Time) timestamp := (uint64)(*overrides.Time)
var withdrawalsHash *common.Hash
if sim.chainConfig.IsShanghai(number, timestamp) { if sim.chainConfig.IsShanghai(number, timestamp) {
withdrawalsHash = &types.EmptyWithdrawalsHash withdrawalsHash = &types.EmptyWithdrawalsHash
} }
var parentBeaconRoot *common.Hash var parentBeaconRoot *common.Hash
if sim.chainConfig.IsCancun(number, timestamp) { if sim.chainConfig.IsCancun(number, timestamp) {
parentBeaconRoot = &common.Hash{} parentBeaconRoot = &common.Hash{}
@ -508,7 +533,11 @@ func (sim *simulator) makeHeaders(blocks []simBlock) ([]*types.Header, error) {
} }
func (sim *simulator) newSimulatedChainContext(ctx context.Context, headers []*types.Header) *ChainContext { func (sim *simulator) newSimulatedChainContext(ctx context.Context, headers []*types.Header) *ChainContext {
return NewChainContext(ctx, &simBackend{base: sim.base, b: sim.b, headers: headers}) return NewChainContext(ctx, &simBackend{
base: sim.base,
b: sim.b,
headers: headers,
})
} }
type simBackend struct { type simBackend struct {

View file

@ -17,6 +17,7 @@
package ethapi package ethapi
import ( import (
"math"
"math/big" "math/big"
"testing" "testing"
@ -80,7 +81,10 @@ func TestSimulateSanitizeBlockOrder(t *testing.T) {
err: "block timestamps must be in order: 72 <= 72", err: "block timestamps must be in order: 72 <= 72",
}, },
} { } {
sim := &simulator{base: &types.Header{Number: big.NewInt(int64(tc.baseNumber)), Time: tc.baseTimestamp}} sim := &simulator{
base: &types.Header{Number: big.NewInt(int64(tc.baseNumber)), Time: tc.baseTimestamp},
gasRemaining: math.MaxUint64,
}
res, err := sim.sanitizeChain(tc.blocks) res, err := sim.sanitizeChain(tc.blocks)
if err != nil { if err != nil {
if err.Error() == tc.err { if err.Error() == tc.err {

View file

@ -113,7 +113,7 @@ func SetupTelemetry(cfg node.OpenTelemetryConfig, stack *node.Node) error {
// Define batch span processor options // Define batch span processor options
batchOpts := []sdktrace.BatchSpanProcessorOption{ batchOpts := []sdktrace.BatchSpanProcessorOption{
// The maximum number of spans that can be queued before dropping // The maximum number of spans that can be queued before dropping
sdktrace.WithMaxQueueSize(sdktrace.DefaultMaxExportBatchSize), sdktrace.WithMaxQueueSize(sdktrace.DefaultMaxQueueSize),
// The maximum number of spans to export in a single batch // The maximum number of spans to export in a single batch
sdktrace.WithMaxExportBatchSize(sdktrace.DefaultMaxExportBatchSize), sdktrace.WithMaxExportBatchSize(sdktrace.DefaultMaxExportBatchSize),
// How long an export operation can take before timing out // How long an export operation can take before timing out

View file

@ -427,11 +427,6 @@ web3._extend({
params: 2, params: 2,
inputFormatter:[null, null], inputFormatter:[null, null],
}), }),
new web3._extend.Method({
name: 'freezeClient',
call: 'debug_freezeClient',
params: 1,
}),
new web3._extend.Method({ new web3._extend.Method({
name: 'getAccessibleState', name: 'getAccessibleState',
call: 'debug_getAccessibleState', call: 'debug_getAccessibleState',
@ -474,6 +469,12 @@ web3._extend({
params: 1, params: 1,
inputFormatter: [null], inputFormatter: [null],
}), }),
new web3._extend.Method({
name: 'executionWitness',
call: 'debug_executionWitness',
params: 1,
inputFormatter: [null],
}),
], ],
properties: [] properties: []
}); });

View file

@ -298,6 +298,17 @@ func (miner *Miner) buildPayload(ctx context.Context, args *BuildPayloadArgs, wi
for { for {
select { select {
case <-timer.C: case <-timer.C:
// When block building takes close to the full recommit interval,
// the timer fires near-instantly on the next iteration. If the
// payload was resolved during that build, both timer.C and
// payload.stop are ready and Go's select picks one at random.
// Check payload.stop first to avoid an unnecessary generateWork.
select {
case <-payload.stop:
log.Info("Stopping work on payload", "id", payload.id, "reason", "delivery")
return
default:
}
start := time.Now() start := time.Now()
iteration++ iteration++
miner.runBuildIteration(bCtx, start, iteration, payload, fullParams, witness) miner.runBuildIteration(bCtx, start, iteration, payload, fullParams, witness)

210
miner/stress/main.go Normal file
View file

@ -0,0 +1,210 @@
// Copyright 2018 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/>.
// This file contains a miner stress test based on the Engine API flow.
package main
import (
"crypto/ecdsa"
"math/big"
"math/rand"
"os"
"os/signal"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/catalyst"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
)
var refundContract = common.HexToAddress("0x1000000000000000000000000000000000000001")
func main() {
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
fdlimit.Raise(2048)
// Generate a batch of accounts to seal and fund with
faucets := make([]*ecdsa.PrivateKey, 128)
for i := 0; i < len(faucets); i++ {
faucets[i], _ = crypto.GenerateKey()
}
// Create a post-merge network where blocks are built/inserted through
// engine API calls driven by a simulated beacon client.
genesis := makeGenesis(faucets)
// Handle interrupts.
interruptCh := make(chan os.Signal, 5)
signal.Notify(interruptCh, os.Interrupt)
// Start one node that accepts transactions and builds/inserts blocks via
// Engine API (through the simulated beacon driver).
stack, backend, beacon, err := makeNode(genesis)
if err != nil {
panic(err)
}
defer stack.Close()
defer beacon.Stop()
// Start injecting transactions from the faucet like crazy
var (
sent uint64
nonces = make([]uint64, len(faucets))
signer = types.LatestSigner(genesis.Config)
refundSet = true // slot 0 starts as non-zero in genesis
)
for {
// Stop when interrupted.
select {
case <-interruptCh:
return
default:
}
var (
tx *types.Transaction
err error
)
// Every third tx targets a contract path that alternates set/clear.
// Clearing a previously non-zero slot triggers gas refund.
if sent%3 == 0 {
var data []byte
if refundSet {
data = nil // empty calldata => clear slot to zero (refund path)
} else {
data = []byte{0x01} // non-empty calldata => set slot to one
}
tx, err = types.SignTx(types.NewTransaction(nonces[0], refundContract, new(big.Int), 50000, big.NewInt(100000000000), data), signer, faucets[0])
if err != nil {
panic(err)
}
nonces[0]++
refundSet = !refundSet
} else {
index := 1 + rand.Intn(len(faucets)-1)
tx, err = types.SignTx(types.NewTransaction(nonces[index], crypto.PubkeyToAddress(faucets[index].PublicKey), new(big.Int), 21000, big.NewInt(100000000000), nil), signer, faucets[index])
if err != nil {
panic(err)
}
nonces[index]++
}
errs := backend.TxPool().Add([]*types.Transaction{tx}, true)
for _, err := range errs {
if err != nil {
panic(err)
}
}
sent++
// Create and import blocks through the engine API path.
if sent%256 == 0 {
beacon.Commit()
}
// Wait if we're too saturated
if pend, _ := backend.TxPool().Stats(); pend > 4096 {
beacon.Commit()
time.Sleep(50 * time.Millisecond)
}
}
}
// makeGenesis creates a post-merge genesis block.
func makeGenesis(faucets []*ecdsa.PrivateKey) *core.Genesis {
config := *params.AllDevChainProtocolChanges
config.ChainID = big.NewInt(18)
blockZero := uint64(0)
config.AmsterdamTime = &blockZero
config.BlobScheduleConfig.Amsterdam = &params.BlobConfig{
Target: 14,
Max: 21,
UpdateFraction: 13739630,
}
genesis := &core.Genesis{
Config: &config,
GasLimit: 25000000,
Alloc: types.GenesisAlloc{},
}
for _, faucet := range faucets {
genesis.Alloc[crypto.PubkeyToAddress(faucet.PublicKey)] = types.Account{
Balance: new(big.Int).Exp(big.NewInt(2), big.NewInt(128), nil),
}
}
// Runtime code:
// - empty calldata: SSTORE(0,0)
// - non-empty calldata: SSTORE(0,1)
// Slot 0 is initialized to 1 so the first clear includes gas refund.
genesis.Alloc[refundContract] = types.Account{
Code: common.FromHex("0x3615600b576001600055005b600060005500"),
Storage: map[common.Hash]common.Hash{
common.Hash{}: common.BigToHash(big.NewInt(1)),
},
}
return genesis
}
func makeNode(genesis *core.Genesis) (*node.Node, *eth.Ethereum, *catalyst.SimulatedBeacon, error) {
// Define the basic configurations for the Ethereum node
datadir, _ := os.MkdirTemp("", "")
config := &node.Config{
Name: "geth",
DataDir: datadir,
}
// Start the node and configure a full Ethereum node on it
stack, err := node.New(config)
if err != nil {
return nil, nil, nil, err
}
// Create and register the backend
ethBackend, err := eth.New(stack, &ethconfig.Config{
Genesis: genesis,
NetworkId: genesis.Config.ChainID.Uint64(),
SyncMode: downloader.FullSync,
DatabaseCache: 256,
DatabaseHandles: 256,
TxPool: legacypool.DefaultConfig,
GPO: ethconfig.Defaults.GPO,
Miner: ethconfig.Defaults.Miner,
SlowBlockThreshold: time.Second,
})
if err != nil {
return nil, nil, nil, err
}
if err := stack.Start(); err != nil {
return nil, nil, nil, err
}
driver, err := catalyst.NewSimulatedBeacon(0, common.Address{}, ethBackend)
if err != nil {
return nil, nil, nil, err
}
if err := driver.Start(); err != nil {
return nil, nil, nil, err
}
return stack, ethBackend, driver, nil
}

View file

@ -80,6 +80,11 @@ func (env *environment) txFitsSize(tx *types.Transaction) bool {
return env.size+tx.Size() < params.MaxBlockSize-maxBlockSizeBufferZone return env.size+tx.Size() < params.MaxBlockSize-maxBlockSizeBufferZone
} }
// discard terminates the background threads before discarding it.
func (env *environment) discard() {
env.state.StopPrefetcher()
}
const ( const (
commitInterruptNone int32 = iota commitInterruptNone int32 = iota
commitInterruptNewHead commitInterruptNewHead
@ -142,6 +147,7 @@ func (miner *Miner) generateWork(ctx context.Context, genParam *generateParams,
if err != nil { if err != nil {
return &newPayloadResult{err: err} return &newPayloadResult{err: err}
} }
defer work.discard()
// Check withdrawals fit max block size. // Check withdrawals fit max block size.
// Due to the cap on withdrawal count, this can actually never happen, but we still need to // Due to the cap on withdrawal count, this can actually never happen, but we still need to
@ -157,9 +163,6 @@ func (miner *Miner) generateWork(ctx context.Context, genParam *generateParams,
// If forceOverrides is true and overrideTxs is not empty, commit the override transactions // If forceOverrides is true and overrideTxs is not empty, commit the override transactions
// otherwise, fill the block with the current transactions from the txpool // otherwise, fill the block with the current transactions from the txpool
if genParam.forceOverrides && len(genParam.overrideTxs) > 0 { if genParam.forceOverrides && len(genParam.overrideTxs) > 0 {
if work.gasPool == nil {
work.gasPool = new(core.GasPool).AddGas(work.header.GasLimit)
}
for _, tx := range genParam.overrideTxs { for _, tx := range genParam.overrideTxs {
work.state.SetTxContext(tx.Hash(), work.tcount) work.state.SetTxContext(tx.Hash(), work.tcount)
if err := miner.commitTransaction(ctx, work, tx); err != nil { if err := miner.commitTransaction(ctx, work, tx); err != nil {
@ -325,19 +328,21 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase
if err != nil { if err != nil {
return nil, err return nil, err
} }
var bundle *stateless.Witness
if witness { if witness {
bundle, err := stateless.NewWitness(header, miner.chain) bundle, err = stateless.NewWitness(header, miner.chain)
if err != nil { if err != nil {
return nil, err return nil, err
} }
state.StartPrefetcher("miner", bundle, nil)
} }
state.StartPrefetcher("miner", bundle, nil)
// Note the passed coinbase may be different with header.Coinbase. // Note the passed coinbase may be different with header.Coinbase.
return &environment{ return &environment{
signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time), signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time),
state: state, state: state,
size: uint64(header.Size()), size: uint64(header.Size()),
coinbase: coinbase, coinbase: coinbase,
gasPool: core.NewGasPool(header.GasLimit),
header: header, header: header,
witness: state.Witness(), witness: state.Witness(),
evm: vm.NewEVM(core.NewEVMBlockContext(header, miner.chain, &coinbase), state, miner.chainConfig, vm.Config{}), evm: vm.NewEVM(core.NewEVMBlockContext(header, miner.chain, &coinbase), state, miner.chainConfig, vm.Config{}),
@ -393,26 +398,22 @@ func (miner *Miner) commitBlobTransaction(env *environment, tx *types.Transactio
func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*types.Receipt, error) { func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*types.Receipt, error) {
var ( var (
snap = env.state.Snapshot() snap = env.state.Snapshot()
gp = env.gasPool.Gas() gp = env.gasPool.Snapshot()
) )
receipt, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx, &env.header.GasUsed) receipt, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx)
if err != nil { if err != nil {
env.state.RevertToSnapshot(snap) env.state.RevertToSnapshot(snap)
env.gasPool.SetGas(gp) env.gasPool.Set(gp)
return nil, err
} }
return receipt, err env.header.GasUsed = env.gasPool.Used()
return receipt, nil
} }
func (miner *Miner) commitTransactions(ctx context.Context, env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error { func (miner *Miner) commitTransactions(ctx context.Context, env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {
ctx, _, spanEnd := telemetry.StartSpan(ctx, "miner.commitTransactions") ctx, _, spanEnd := telemetry.StartSpan(ctx, "miner.commitTransactions")
defer spanEnd(nil) defer spanEnd(nil)
var ( isCancun := miner.chainConfig.IsCancun(env.header.Number, env.header.Time)
isCancun = miner.chainConfig.IsCancun(env.header.Number, env.header.Time)
gasLimit = env.header.GasLimit
)
if env.gasPool == nil {
env.gasPool = new(core.GasPool).AddGas(gasLimit)
}
for { for {
// Check interruption signal and abort building if it's fired. // Check interruption signal and abort building if it's fired.
if interrupt != nil { if interrupt != nil {

View file

@ -161,9 +161,9 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t
Preimages: true, Preimages: true,
TxLookupLimit: -1, // disable tx indexing TxLookupLimit: -1, // disable tx indexing
VmConfig: vm.Config{ VmConfig: vm.Config{
Tracer: tracer, Tracer: tracer,
StatelessSelfValidation: witness,
}, },
StatelessSelfValidation: witness,
} }
if snapshotter { if snapshotter {
options.SnapshotLimit = 1 options.SnapshotLimit = 1

View file

@ -342,9 +342,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
} }
// Execute the message. // Execute the message.
snapshot := st.StateDB.Snapshot() snapshot := st.StateDB.Snapshot()
gaspool := new(core.GasPool) vmRet, err := core.ApplyMessage(evm, msg, core.NewGasPool(block.GasLimit()))
gaspool.AddGas(block.GasLimit())
vmRet, err := core.ApplyMessage(evm, msg, gaspool)
if err != nil { if err != nil {
st.StateDB.RevertToSnapshot(snapshot) st.StateDB.RevertToSnapshot(snapshot)
if tracer := evm.Config.Tracer; tracer != nil && tracer.OnTxEnd != nil { if tracer := evm.Config.Tracer; tracer != nil && tracer.OnTxEnd != nil {

View file

@ -880,6 +880,7 @@ func (b *spongeBatch) ValueSize() int { return 100 }
func (b *spongeBatch) Write() error { return nil } func (b *spongeBatch) Write() error { return nil }
func (b *spongeBatch) Reset() {} func (b *spongeBatch) Reset() {}
func (b *spongeBatch) Replay(w ethdb.KeyValueWriter) error { return nil } func (b *spongeBatch) Replay(w ethdb.KeyValueWriter) error { return nil }
func (b *spongeBatch) Close() {}
// TestCommitSequence tests that the trie.Commit operation writes the elements // TestCommitSequence tests that the trie.Commit operation writes the elements
// of the trie in the expected order. // of the trie in the expected order.

View file

@ -180,6 +180,8 @@ func (b *buffer) flush(root common.Hash, db ethdb.KeyValueStore, freezers []ethd
b.flushErr = err b.flushErr = err
return return
} }
batch.Close()
commitBytesMeter.Mark(int64(size)) commitBytesMeter.Mark(int64(size))
commitNodesMeter.Mark(int64(nodes)) commitNodesMeter.Mark(int64(nodes))
commitAccountsMeter.Mark(int64(accounts)) commitAccountsMeter.Mark(int64(accounts))