core: compute intrinsic gas in TransactionToMessage(..)

This commit is contained in:
lightclient 2025-03-12 12:17:14 -06:00
parent 7dc804397b
commit f1ded86873
No known key found for this signature in database
GPG key ID: 657913021EF45A6A
14 changed files with 94 additions and 55 deletions

View file

@ -233,7 +233,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
rejectedTxs = append(rejectedTxs, &rejectedTx{i, errMsg})
continue
}
msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee)
rules := evm.ChainConfig().Rules(evm.Context.BlockNumber, evm.Context.Difficulty.BitLen() == 0, evm.Context.Time)
msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee, &rules)
if err != nil {
log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", err)
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})

View file

@ -104,7 +104,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
Data: data,
GasPrice: gasPrice,
}
gas, _ := types.IntrinsicGas(txdata, &rules)
gas := types.IntrinsicGas(txdata, &rules)
txdata.Gas = gas
tx, _ := types.SignNewTx(benchRootKey, signer, txdata)
gen.AddTx(tx)

View file

@ -51,6 +51,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
blockContext = NewEVMBlockContext(header, p.chain, nil)
evm = vm.NewEVM(blockContext, statedb, p.config, cfg)
signer = types.MakeSigner(p.config, header.Number, header.Time)
rules = evm.Rules()
)
// Iterate over and process the individual transactions
byzantium := p.config.IsByzantium(block.Number())
@ -60,7 +61,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
return
}
// Convert the transaction into an executable message and pre-cache its sender
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
msg, err := TransactionToMessage(tx, signer, header.BaseFee, rules)
if err != nil {
return // Also invalid block, bail out
}

View file

@ -69,29 +69,29 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
misc.ApplyDAOHardFork(statedb)
}
var (
context vm.BlockContext
signer = types.MakeSigner(p.config, header.Number, header.Time)
)
// Apply pre-execution system calls.
var tracingStateDB = vm.StateDB(statedb)
if hooks := cfg.Tracer; hooks != nil {
tracingStateDB = state.NewHookedState(statedb, hooks)
}
context = NewEVMBlockContext(header, p.chain, nil)
evm := vm.NewEVM(context, tracingStateDB, p.config, cfg)
var (
context = NewEVMBlockContext(header, p.chain, nil)
evm = vm.NewEVM(context, tracingStateDB, p.config, cfg)
rules = evm.Rules()
signer = types.MakeSigner(p.config, header.Number, header.Time)
)
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
ProcessBeaconBlockRoot(*beaconRoot, evm)
}
if p.config.IsPrague(block.Number(), block.Time()) || p.config.IsVerkle(block.Number(), block.Time()) {
if rules.IsPrague || rules.IsVerkle {
ProcessParentBlockHash(block.ParentHash(), evm)
}
// Iterate over and process the individual transactions
for i, tx := range block.Transactions() {
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
msg, err := TransactionToMessage(tx, signer, header.BaseFee, rules)
if err != nil {
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
}
@ -133,13 +133,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// and uses the input parameters for its environment similar to ApplyTransaction. However,
// 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, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) {
rules := evm.ChainConfig().Rules(evm.Context.BlockNumber, evm.Context.Random != nil, evm.Context.Time)
// Intrinsic gas
gas, err := tx.IntrinsicGas(&rules)
if err != nil {
return nil, err
}
msg.Gas = gas
if hooks := evm.Config.Tracer; hooks != nil {
if hooks.OnTxStart != nil {
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
@ -208,7 +201,7 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
// for the transaction, gas used and an error if the transaction failed,
// 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) {
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, evm.Rules())
if err != nil {
return nil, err
}

View file

@ -110,7 +110,7 @@ type Message struct {
}
// TransactionToMessage converts a transaction into a Message.
func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) {
func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int, rules *params.Rules) (*Message, error) {
msg := &Message{
Nonce: tx.Nonce(),
GasLimit: tx.Gas(),
@ -134,7 +134,14 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
msg.GasPrice = msg.GasFeeCap
}
}
var err error
// Fill in intrinsic gas.
gas, err := tx.IntrinsicGas(rules)
if err != nil {
return nil, err
}
msg.Gas = gas
// Recover sender.
msg.From, err = types.Sender(s, tx)
return msg, err
}

View file

@ -583,11 +583,20 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e
// IntrinsicGas returns the 'intrinsic gas' computed for a message with the given data.
func (tx *Transaction) IntrinsicGas(rules *params.Rules) (uint64, error) {
return IntrinsicGas(tx.inner, rules)
return calcIntrinsicGas(tx.inner, rules)
}
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
func IntrinsicGas(txdata TxData, rules *params.Rules) (uint64, error) {
func IntrinsicGas(txdata TxData, rules *params.Rules) uint64 {
gas, err := calcIntrinsicGas(txdata, rules)
if err != nil {
panic(err)
}
return gas
}
// calcIntrinsicGas is an internal function that drives exported versions of the function.
func calcIntrinsicGas(txdata TxData, rules *params.Rules) (uint64, error) {
// Set the starting gas for the raw transaction
var gas uint64
if txdata.to() == nil && rules.IsHomestead {

View file

@ -591,6 +591,11 @@ func (evm *EVM) resolveCodeHash(addr common.Address) common.Hash {
// ChainConfig returns the environment's chain configuration
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
func (evm *EVM) Rules() *params.Rules {
rules := evm.ChainConfig().Rules(evm.Context.BlockNumber, evm.Context.Difficulty.BitLen() == 0, evm.Context.Time)
return &rules
}
func (evm *EVM) captureBegin(depth int, typ OpCode, from common.Address, to common.Address, input []byte, startGas uint64, value *big.Int) {
tracer := evm.Config.Tracer
if tracer.OnEnter != nil {

View file

@ -235,13 +235,16 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
return nil, vm.BlockContext{}, nil, nil, err
}
// Insert parent beacon block root in the state as per EIP-4788.
context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
evm := vm.NewEVM(context, statedb, eth.blockchain.Config(), vm.Config{})
var (
context = core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
evm = vm.NewEVM(context, statedb, eth.blockchain.Config(), vm.Config{})
rules = evm.Rules()
)
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
}
// If prague hardfork, insert parent block hash in the state as per EIP-2935.
if eth.blockchain.Config().IsPrague(block.Number(), block.Time()) {
if rules.IsPrague {
core.ProcessParentBlockHash(block.ParentHash(), evm)
}
if txIndex == 0 && len(block.Transactions()) == 0 {
@ -254,7 +257,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
return tx, context, statedb, release, nil
}
// Assemble the transaction call message and return if the requested offset
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee(), rules)
// Not yet the searched for transaction, execute on top of the current state
statedb.SetTxContext(tx.Hash(), idx)

View file

@ -269,10 +269,11 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
var (
signer = types.MakeSigner(api.backend.ChainConfig(), task.block.Number(), task.block.Time())
blockCtx = core.NewEVMBlockContext(task.block.Header(), api.chainContext(ctx), nil)
rules = api.backend.ChainConfig().Rules(task.block.Number(), task.block.Difficulty().BitLen() == 0, task.block.Time())
)
// Trace all the transactions contained within
for i, tx := range task.block.Transactions() {
msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee())
msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee(), &rules)
txctx := &Context{
BlockHash: task.block.Hash(),
BlockNumber: task.block.Number(),
@ -540,14 +541,15 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
}
if chainConfig.IsPrague(block.Number(), block.Time()) {
rules := evm.Rules()
if rules.IsPrague {
core.ProcessParentBlockHash(block.ParentHash(), evm)
}
for i, tx := range block.Transactions() {
if err := ctx.Err(); err != nil {
return nil, err
}
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee(), rules)
statedb.SetTxContext(tx.Hash(), i)
if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil {
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
@ -604,7 +606,8 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
}
if api.backend.ChainConfig().IsPrague(block.Number(), block.Time()) {
rules := evm.Rules()
if rules.IsPrague {
core.ProcessParentBlockHash(block.ParentHash(), evm)
}
@ -625,7 +628,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
)
for i, tx := range txs {
// Generate the next state snapshot fast without tracing
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee(), rules)
txctx := &Context{
BlockHash: blockHash,
BlockNumber: block.Number(),
@ -652,6 +655,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time())
results = make([]*txTraceResult, len(txs))
pend sync.WaitGroup
rules = api.backend.ChainConfig().Rules(block.Number(), block.Difficulty().BitLen() == 0, block.Time())
)
threads := runtime.NumCPU()
if threads > len(txs) {
@ -664,7 +668,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
defer pend.Done()
// Fetch and execute the next transaction trace tasks
for task := range jobs {
msg, _ := core.TransactionToMessage(txs[task.index], signer, block.BaseFee())
msg, _ := core.TransactionToMessage(txs[task.index], signer, block.BaseFee(), &rules)
txctx := &Context{
BlockHash: blockHash,
BlockNumber: block.Number(),
@ -703,7 +707,7 @@ txloop:
}
// Generate the next state snapshot fast without tracing
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee(), &rules)
statedb.SetTxContext(tx.Hash(), i)
if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil {
failed = err
@ -781,13 +785,14 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
}
if chainConfig.IsPrague(block.Number(), block.Time()) {
rules := evm.Rules()
if rules.IsPrague {
core.ProcessParentBlockHash(block.ParentHash(), evm)
}
for i, tx := range block.Transactions() {
// Prepare the transaction for un-traced execution
var (
msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee())
msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee(), rules)
vmConf vm.Config
dump *os.File
writer *bufio.Writer
@ -883,7 +888,8 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
return nil, err
}
defer release()
msg, err := core.TransactionToMessage(tx, types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()), block.BaseFee())
rules := api.backend.ChainConfig().Rules(block.Number(), block.Difficulty().BitLen() == 0, block.Time())
msg, err := core.TransactionToMessage(tx, types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()), block.BaseFee(), &rules)
if err != nil {
return nil, err
}

View file

@ -125,11 +125,11 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
if tracer.Hooks != nil {
logState = state.NewHookedState(st.StateDB, tracer.Hooks)
}
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
evm := vm.NewEVM(context, logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee, evm.Rules())
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
evm := vm.NewEVM(context, logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
@ -201,20 +201,20 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
if err := tx.UnmarshalBinary(common.FromHex(test.Input)); err != nil {
b.Fatalf("failed to parse testcase input: %v", err)
}
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
context := test.Context.toBlockContext(test.Genesis)
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
if err != nil {
b.Fatalf("failed to prepare transaction for tracing: %v", err)
}
state := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme)
defer state.Close()
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
context := test.Context.toBlockContext(test.Genesis)
evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{})
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee, evm.Rules())
if err != nil {
b.Fatalf("failed to prepare transaction for tracing: %v", err)
}
b.ReportAllocs()
b.ResetTimer()
evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{})
for i := 0; i < b.N; i++ {
snap := state.StateDB.Snapshot()
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil, test.Genesis.Config)
@ -370,7 +370,7 @@ func TestInternals(t *testing.T) {
t.Fatalf("test %v: failed to sign transaction: %v", tc.name, err)
}
evm := vm.NewEVM(context, logState, config, vm.Config{Tracer: tc.tracer.Hooks})
msg, err := core.TransactionToMessage(tx, signer, big.NewInt(0))
msg, err := core.TransactionToMessage(tx, signer, big.NewInt(0), evm.Rules())
if err != nil {
t.Fatalf("test %v: failed to create message: %v", tc.name, err)
}

View file

@ -107,11 +107,11 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
return fmt.Errorf("failed to create call tracer: %v", err)
}
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee, evm.Rules())
if err != nil {
return fmt.Errorf("failed to prepare transaction for tracing: %v", err)
}
evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {

View file

@ -99,11 +99,11 @@ func testPrestateTracer(tracerName string, dirPath string, t *testing.T) {
t.Fatalf("failed to create call tracer: %v", err)
}
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee, evm.Rules())
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {

View file

@ -80,7 +80,7 @@ func BenchmarkTransactionTraceV2(b *testing.B) {
evm := vm.NewEVM(context, state.StateDB, params.AllEthashProtocolChanges, vm.Config{})
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee, evm.Rules())
if err != nil {
b.Fatalf("failed to prepare transaction for tracing: %v", err)
}

View file

@ -257,6 +257,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
return st, common.Hash{}, 0, UnsupportedForkError{subtest.Fork}
}
vmconfig.ExtraEips = eips
rules := config.Rules(new(big.Int).SetUint64(t.json.Env.Number), t.json.Env.Random != nil, t.json.Env.Timestamp)
block := t.genesis(config).ToBlock()
st = MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter, scheme)
@ -271,7 +272,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
}
}
post := t.json.Post[subtest.Fork][subtest.Index]
msg, err := t.json.Tx.toMessage(post, baseFee)
msg, err := t.json.Tx.toMessage(post, baseFee, &rules)
if err != nil {
return st, common.Hash{}, 0, err
}
@ -376,7 +377,7 @@ func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis {
return genesis
}
func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Message, error) {
func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int, rules *params.Rules) (*core.Message, error) {
var from common.Address
// If 'sender' field is present, use that
if tx.Sender != nil {
@ -463,11 +464,24 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess
}
}
// Compute the intrinsic gas for stTransaction. Since the test file doesn't
// specify the transaction type we need to infer. This is hacky but we can use
// dynamic fee tx to represent all txs (with respect to instrinsic gas calc at
// least) types except the set code tx type.
var txdata types.TxData
if to == nil {
txdata = &types.DynamicFeeTx{To: to, Data: data, AccessList: accessList}
} else {
txdata = &types.SetCodeTx{To: *to, Data: data, AccessList: accessList, AuthList: authList}
}
gas := types.IntrinsicGas(txdata, rules)
msg := &core.Message{
From: from,
To: to,
Nonce: tx.Nonce,
Value: value,
Gas: gas,
GasLimit: gasLimit,
GasPrice: gasPrice,
GasFeeCap: tx.MaxFeePerGas,