diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index bc8341fff91f..eee1e5aba0aa 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -19,7 +19,6 @@ package core import ( "sync/atomic" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" @@ -69,7 +68,11 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c return // Also invalid block, bail out } statedb.SetTxContext(tx.Hash(), i) - if err := precacheTransaction(msg, gaspool, evm); err != nil { + coinbaseOwner := statedb.GetOwner(evm.Context.Coinbase) + + // We attempt to apply a transaction. The goal is not to execute + // the transaction successfully, rather to warm up touched data slots. + if _, err := ApplyMessage(evm, msg, gaspool, coinbaseOwner); err != nil { return // Ugh, something went horribly wrong, bail out } // If we're pre-byzantium, pre-load trie nodes for the intermediate root @@ -82,14 +85,3 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c statedb.IntermediateRoot(true) } } - -// precacheTransaction attempts to apply a transaction to the given state database -// and uses the input parameters for its environment. The goal is not to execute -// the transaction successfully, rather to warm up touched data slots. -func precacheTransaction(msg *Message, gaspool *GasPool, evm *vm.EVM) error { - // Update the evm with the new transaction context. - evm.SetTxContext(NewEVMTxContext(msg)) - // Add addresses to access list if applicable - _, err := ApplyMessage(evm, msg, gaspool, common.Address{}) - return err -} diff --git a/core/state_processor.go b/core/state_processor.go index cab905a0a247..26442124508d 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -281,6 +281,9 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, balanceFee *big.Int, coinbaseOwner common.Address) (receipt *types.Receipt, gasUsed uint64, tokenFeeUsed bool, err error) { if hooks := evm.Config.Tracer; hooks != nil { if hooks.OnTxStart != nil { + // OnTxStart runs before ApplyMessage, so the execution tx context must be visible + // here too. This is XDPoS-specific because msg.GasPrice can differ from the raw tx. + evm.SetTxContext(NewEVMTxContext(msg)) hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) } if hooks.OnTxEnd != nil { @@ -308,10 +311,6 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, return ApplyEmptyTransaction(msg, config, statedb, blockNumber, blockHash, tx, usedGas, evm) } - // Create a new context to be used in the EVM environment - txContext := NewEVMTxContext(msg) - evm.SetTxContext(txContext) - applyHistoricalBalanceBypass(statedb, blockNumber, msg.From) // Apply the transaction to the current state (included in the env) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 040d06c08cf2..59d8c787fb75 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -596,6 +596,101 @@ func TestApplyTransactionWithEVMStateChangeHooks(t *testing.T) { } } +func TestApplyTransactionWithEVMOnTxStartUsesExecutionGasPrice(t *testing.T) { + var ( + config = ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + Eip1559Block: big.NewInt(0), + Ethash: new(params.EthashConfig), + } + signer = types.LatestSigner(config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + sender = crypto.PubkeyToAddress(testKey.PublicKey) + recipient = common.HexToAddress("0x1234567890123456789012345678901234567890") + rawGasPrice = big.NewInt(20000000000) + executionGasPrice = big.NewInt(7) + ) + + db := rawdb.NewMemoryDatabase() + gspec := &Genesis{ + Config: config, + Alloc: types.GenesisAlloc{ + sender: { + Balance: big.NewInt(1000000000000000000), + Nonce: 0, + }, + }, + } + genesis := gspec.MustCommit(db) + blockchain, err := NewBlockChain(db, nil, gspec, ethash.NewFaker(), vm.Config{}) + if err != nil { + t.Fatalf("Failed to create blockchain: %v", err) + } + defer blockchain.Stop() + + statedb, err := blockchain.State() + if err != nil { + t.Fatalf("Failed to get state: %v", err) + } + + tx := types.NewTransaction(0, recipient, big.NewInt(1), 21000, rawGasPrice, nil) + signedTx, err := types.SignTx(tx, signer, testKey) + if err != nil { + t.Fatalf("Failed to sign tx: %v", err) + } + + var seenGasPrice *big.Int + hooks := &tracing.Hooks{ + OnTxStart: func(vmContext *tracing.VMContext, tx *types.Transaction, from common.Address) { + if tx == nil { + t.Fatal("OnTxStart called with nil transaction") + } + if from != sender { + t.Fatalf("OnTxStart called with wrong from address: got %v want %v", from, sender) + } + if vmContext.GasPrice == nil { + t.Fatal("OnTxStart saw nil gas price") + } + seenGasPrice = new(big.Int).Set(vmContext.GasPrice) + }, + } + + msg, err := TransactionToMessage(signedTx, signer, nil, big.NewInt(1), nil) + if err != nil { + t.Fatalf("Failed to build message: %v", err) + } + msg.GasPrice = new(big.Int).Set(executionGasPrice) + + gasPool := new(GasPool).AddGas(1000000) + vmContext := NewEVMBlockContext(blockchain.CurrentBlock(), blockchain, nil) + evmenv := vm.NewEVM(vmContext, statedb, nil, blockchain.Config(), vm.Config{Tracer: hooks}) + + var usedGas uint64 + _, _, _, err = ApplyTransactionWithEVM(msg, gasPool, statedb, big.NewInt(1), genesis.Hash(), signedTx, &usedGas, evmenv, nil, common.Address{}) + if err != nil { + t.Fatalf("ApplyTransactionWithEVM failed: %v", err) + } + if seenGasPrice == nil { + t.Fatal("expected OnTxStart to observe gas price") + } + if seenGasPrice.Cmp(executionGasPrice) != 0 { + t.Fatalf("OnTxStart saw wrong execution gas price: got %v want %v", seenGasPrice, executionGasPrice) + } + if seenGasPrice.Cmp(rawGasPrice) == 0 { + t.Fatalf("OnTxStart unexpectedly saw raw tx gas price: %v", seenGasPrice) + } +} + func TestApplyTransactionWithEVMRejectsValueOverflow(t *testing.T) { t.Parallel() diff --git a/core/state_transition.go b/core/state_transition.go index b2950505053d..388a84185c8b 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -206,10 +206,11 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, balanceFee, blo // indicates a core error meaning that the message would always fail for that particular // state and would never be accepted within a block. func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool, owner common.Address) (*ExecutionResult, error) { - return NewStateTransition(evm, msg, gp).TransitionDb(owner) + evm.SetTxContext(NewEVMTxContext(msg)) + return newStateTransition(evm, msg, gp).execute(owner) } -// StateTransition represents a state transition. +// stateTransition represents a state transition. // // == The State Transitioning Model // @@ -231,7 +232,7 @@ func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool, owner common.Address) // // 5. Run Script section // 6. Derive new state root -type StateTransition struct { +type stateTransition struct { gp *GasPool msg *Message gasRemaining uint64 @@ -240,9 +241,9 @@ type StateTransition struct { evm *vm.EVM } -// NewStateTransition initialises and returns a new state transition object. -func NewStateTransition(evm *vm.EVM, msg *Message, gp *GasPool) *StateTransition { - return &StateTransition{ +// newStateTransition initialises and returns a new state transition object. +func newStateTransition(evm *vm.EVM, msg *Message, gp *GasPool) *stateTransition { + return &stateTransition{ gp: gp, evm: evm, msg: msg, @@ -250,7 +251,7 @@ func NewStateTransition(evm *vm.EVM, msg *Message, gp *GasPool) *StateTransition } } -func (st *StateTransition) from() common.Address { +func (st *stateTransition) from() common.Address { f := st.msg.From if !st.state.Exist(f) { st.state.CreateAccount(f) @@ -258,7 +259,7 @@ func (st *StateTransition) from() common.Address { return f } -func (st *StateTransition) to() common.Address { +func (st *stateTransition) to() common.Address { if st.msg == nil { return common.Address{} } @@ -272,7 +273,7 @@ func (st *StateTransition) to() common.Address { return *to } -func (st *StateTransition) buyGas() error { +func (st *stateTransition) buyGas() error { mgval := new(big.Int).SetUint64(st.msg.GasLimit) mgval = mgval.Mul(mgval, st.msg.GasPrice) if st.msg.BalanceTokenFee == nil { @@ -304,7 +305,7 @@ func (st *StateTransition) buyGas() error { return nil } -func (st *StateTransition) preCheck() error { +func (st *stateTransition) preCheck() error { // Only check transactions that are not fake msg := st.msg if !msg.SkipNonceChecks { @@ -371,20 +372,17 @@ func (st *StateTransition) preCheck() error { return st.buyGas() } -// TransitionDb will transition the state by applying the current message and +// execute will transition the state by applying the current message and // returning the evm execution result with following fields. // -// - used gas: -// total gas used (including gas being refunded) -// - returndata: -// the returned data from evm -// - concrete execution error: -// various **EVM** error which aborts the execution, -// e.g. ErrOutOfGas, ErrExecutionReverted +// - used gas: total gas used (including gas being refunded) +// - returndata: the returned data from evm +// - concrete execution error: various EVM errors which abort the execution, e.g. +// ErrOutOfGas, ErrExecutionReverted // // However if any consensus issue encountered, return the error directly with // nil evm execution result. -func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, error) { +func (st *stateTransition) execute(owner common.Address) (*ExecutionResult, error) { // First check this message satisfies all consensus rules before // applying the message. The rules include these clauses // @@ -523,7 +521,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, } // validateAuthorization validates an EIP-7702 authorization against the state. -func (st *StateTransition) validateAuthorization(auth *types.SetCodeAuthorization) (authority common.Address, err error) { +func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorization) (authority common.Address, err error) { // Verify chain ID is null or equal to current chain ID. if !auth.ChainID.IsZero() && auth.ChainID.CmpBig(st.evm.ChainConfig().ChainID) != 0 { return authority, ErrAuthorizationWrongChainID @@ -554,7 +552,7 @@ func (st *StateTransition) validateAuthorization(auth *types.SetCodeAuthorizatio } // applyAuthorization applies an EIP-7702 code delegation to the state. -func (st *StateTransition) applyAuthorization(msg *Message, auth *types.SetCodeAuthorization) error { +func (st *stateTransition) applyAuthorization(msg *Message, auth *types.SetCodeAuthorization) error { authority, err := st.validateAuthorization(auth) if err != nil { return err @@ -581,7 +579,7 @@ func (st *StateTransition) applyAuthorization(msg *Message, auth *types.SetCodeA } // calcRefund computes refund counter, capped to a refund quotient. -func (st *StateTransition) calcRefund() uint64 { +func (st *stateTransition) calcRefund() uint64 { var refund uint64 if !st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) { // Before EIP-3529: refunds were capped to gasUsed / 2 @@ -601,7 +599,7 @@ func (st *StateTransition) calcRefund() uint64 { // returnGas returns ETH for remaining gas, // exchanged at the original rate. -func (st *StateTransition) returnGas() { +func (st *stateTransition) returnGas() { if st.msg.BalanceTokenFee == nil { remaining := new(big.Int).SetUint64(st.gasRemaining) remaining.Mul(remaining, st.msg.GasPrice) @@ -618,6 +616,6 @@ func (st *StateTransition) returnGas() { } // gasUsed returns the amount of gas used up by the state transition. -func (st *StateTransition) gasUsed() uint64 { +func (st *stateTransition) gasUsed() uint64 { return st.initialGas - st.gasRemaining } diff --git a/core/token_validator.go b/core/token_validator.go index 11f889631c96..de6289e52f53 100644 --- a/core/token_validator.go +++ b/core/token_validator.go @@ -108,12 +108,10 @@ func CallContractWithState(call ethereum.CallMsg, chain consensus.ChainContext, // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. - txContext := NewEVMTxContext(msg) evmContext := NewEVMBlockContext(chain.CurrentHeader(), chain, nil) evm := vm.NewEVM(evmContext, statedb, nil, chain.Config(), vm.Config{}) - evm.SetTxContext(txContext) gaspool := new(GasPool).AddGas(1000000) - result, err := NewStateTransition(evm, msg, gaspool).TransitionDb(common.Address{}) + result, err := ApplyMessage(evm, msg, gaspool, common.Address{}) if err != nil { return nil, err } diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 9b04c5c07fe5..c4cebddba17b 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -63,9 +63,12 @@ type VMContext struct { BlockNumber *big.Int Time uint64 Random *common.Hash - // Effective tx gas price + BaseFee *big.Int + StateDB StateDB + + // XDPoS tracers need the execution-time gas price because TransactionToMessage + // may rewrite it for TRC21 and fixed-price fee paths. GasPrice *big.Int - StateDB StateDB } // BlockEvent is emitted upon tracing an incoming block. diff --git a/core/vm/evm.go b/core/vm/evm.go index b1420d4f5dc7..27b956284884 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -633,7 +633,10 @@ func (evm *EVM) GetVMContext() *tracing.VMContext { BlockNumber: evm.Context.BlockNumber, Time: evm.Context.Time, Random: evm.Context.Random, - GasPrice: evm.TxContext.GasPrice, + BaseFee: evm.Context.BaseFee, StateDB: evm.StateDB, + + // Keep GasPrice in the tracer context for XDPoS-specific execution pricing. + GasPrice: evm.TxContext.GasPrice, } } diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index c5ab3184d16f..9de5b3b7259d 100644 --- a/eth/gasestimator/gasestimator.go +++ b/eth/gasestimator/gasestimator.go @@ -174,17 +174,15 @@ func execute(ctx context.Context, call *core.Message, opts *Options, gasLimit ui func run(ctx context.Context, call *core.Message, opts *Options) (*core.ExecutionResult, error) { // Assemble the call and the call context var ( - msgContext = core.NewEVMTxContext(call) evmContext = core.NewEVMBlockContext(opts.Header, opts.Chain, nil) dirtyState = opts.State.Copy() ) // Lower the basefee to 0 to avoid breaking EVM // invariants (basefee < feecap). - if msgContext.GasPrice.Sign() == 0 { + if call.GasPrice.Sign() == 0 { evmContext.BaseFee = new(big.Int) } evm := vm.NewEVM(evmContext, dirtyState, nil, opts.Config, vm.Config{NoBaseFee: true}) - evm.SetTxContext(msgContext) // Monitor the outer context and interrupt the EVM upon cancellation. To avoid // a dangling goroutine until the outer estimation finishes, create an internal diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 8ac2f1f80a8e..ecdf0d83e480 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -230,8 +230,6 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, } // Assemble the transaction call message and return if the requested offset msg, _ := core.TransactionToMessage(tx, signer, balance, block.Number(), block.BaseFee()) - txContext := core.NewEVMTxContext(msg) - evm.SetTxContext(txContext) // Not yet the searched for transaction, execute on top of the current state statedb.SetTxContext(tx.Hash(), idx) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index c86fa00d6107..6c6356941431 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -539,11 +539,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config balance = value } } - var ( - msg, _ = core.TransactionToMessage(tx, signer, balance, block.Number(), block.BaseFee()) - txContext = core.NewEVMTxContext(msg) - ) - evm.SetTxContext(txContext) + msg, _ := core.TransactionToMessage(tx, signer, balance, block.Number(), block.BaseFee()) statedb.SetTxContext(tx.Hash(), i) if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit), common.Address{}); err != nil { log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err) @@ -724,7 +720,6 @@ txloop: header := block.Header() msg, _ := core.TransactionToMessage(tx, signer, balance, header.Number, header.BaseFee) statedb.SetTxContext(tx.Hash(), i) - evm.SetTxContext(core.NewEVMTxContext(msg)) if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit), common.Address{}); err != nil { failed = err break txloop @@ -902,7 +897,6 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor } tracingStateDB := state.NewHookedState(statedb, tracer.Hooks) evm := vm.NewEVM(vmctx, tracingStateDB, nil, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) - evm.SetTxContext(vm.TxContext{GasPrice: message.GasPrice}) // Define a meaningful timeout of a single transaction trace if config.Timeout != nil { diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 75d1b7fdefac..cb237b84dc01 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -183,8 +183,6 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block return tx, context, statedb, release, nil } msg, _ := core.TransactionToMessage(tx, signer, nil, block.Number(), block.BaseFee()) - txContext := core.NewEVMTxContext(msg) - evm.SetTxContext(txContext) if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{}); err != nil { return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index 8f40348db0f0..a68cf37198aa 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -217,11 +217,6 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { b.Fatalf("failed to parse testcase input: %v", err) } signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number))) - origin, _ := signer.Sender(tx) - txContext := vm.TxContext{ - Origin: origin, - GasPrice: tx.GasPrice(), - } context := test.Context.toBlockContext(test.Genesis) msg, err := core.TransactionToMessage(tx, signer, nil, nil, context.BaseFee) if err != nil { @@ -232,7 +227,6 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { b.ReportAllocs() evm := vm.NewEVM(context, state, nil, test.Genesis.Config, vm.Config{}) - evm.SetTxContext(txContext) for b.Loop() { snap := state.Snapshot() @@ -241,10 +235,17 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { b.Fatalf("failed to create call tracer: %v", err) } evm.Config.Tracer = tracer.Hooks - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - if _, err = st.TransitionDb(common.Address{}); err != nil { + evm.SetTxContext(core.NewEVMTxContext(msg)) + if tracer.OnTxStart != nil { + tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) + } + result, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{}) + if err != nil { b.Fatalf("failed to execute transaction: %v", err) } + if tracer.OnTxEnd != nil { + tracer.OnTxEnd(&types.Receipt{GasUsed: result.UsedGas}, nil) + } if _, err = tracer.GetResult(); err != nil { b.Fatal(err) } @@ -381,16 +382,12 @@ func TestInternals(t *testing.T) { if err != nil { t.Fatalf("test %v: failed to sign transaction: %v", tc.name, err) } - txContext := vm.TxContext{ - Origin: origin, - GasPrice: tx.GasPrice(), - } evm := vm.NewEVM(context, logState, nil, config, vm.Config{Tracer: tc.tracer.Hooks}) - evm.SetTxContext(txContext) msg, err := core.TransactionToMessage(tx, signer, nil, nil, big.NewInt(0)) if err != nil { t.Fatalf("test %v: failed to create message: %v", tc.name, err) } + evm.SetTxContext(core.NewEVMTxContext(msg)) tc.tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{}) if err != nil { @@ -444,12 +441,7 @@ func testContractTracer(tracerName string, dirPath string, t *testing.T) { } // Configure a blockchain with the given prestate var ( - signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number))) - origin, _ = signer.Sender(tx) - txContext = vm.TxContext{ - Origin: origin, - GasPrice: tx.GasPrice(), - } + signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number))) context = vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, @@ -467,13 +459,12 @@ func testContractTracer(tracerName string, dirPath string, t *testing.T) { t.Fatalf("failed to create call tracer: %v", err) } evm := vm.NewEVM(context, state, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) - evm.SetTxContext(txContext) msg, err := core.TransactionToMessage(tx, signer, nil, nil, nil) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - if _, err = st.TransitionDb(common.Address{}); err != nil { + _, err = core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{}) + if err != nil { t.Fatalf("failed to execute transaction: %v", err) } // Retrieve the trace result and compare against the expected. diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index d38918b3f8b1..bc181bad3b2e 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -259,6 +259,8 @@ func (t *jsTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from t.activePrecompiles = vm.ActivePrecompiles(rules) t.ctx["block"] = t.vm.ToValue(t.env.BlockNumber.Uint64()) t.ctx["gas"] = t.vm.ToValue(tx.Gas()) + // Read the execution-time gas price from VMContext. In XDPoS it may diverge + // from the raw tx price due to TRC21 and fixed-price fee handling. gasPriceBig, err := t.toBig(t.vm, env.GasPrice.String()) if err != nil { t.err = err diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index 856bb4e69257..6795721725cf 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -46,7 +46,7 @@ type vmContext struct { } func testCtx() *vmContext { - return &vmContext{ctx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txContext: vm.TxContext{GasPrice: big.NewInt(100000)}} + return &vmContext{ctx: vm.BlockContext{BlockNumber: big.NewInt(1), BaseFee: big.NewInt(0)}, txContext: vm.TxContext{GasPrice: big.NewInt(100000)}} } func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { @@ -63,7 +63,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo contract.Code = contractCode } - tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller()) + tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit, GasPrice: vmctx.txContext.GasPrice}), contract.Caller()) tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig()) ret, err := evm.Run(contract, []byte{}, false) tracer.OnExit(0, ret, startGas-contract.Gas, err, true) @@ -229,6 +229,26 @@ func TestNoStepExec(t *testing.T) { } } +func TestTxStartUsesExecutionGasPrice(t *testing.T) { + chainConfig := params.TestChainConfig + tracer, err := newJsTracer("{step: function() {}, fault: function() {}, result: function(ctx) { return ctx.gasPrice; }}", nil, nil, chainConfig) + if err != nil { + t.Fatal(err) + } + evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1), BaseFee: big.NewInt(0)}, &dummyStatedb{}, nil, chainConfig, vm.Config{Tracer: tracer.Hooks}) + evm.SetTxContext(vm.TxContext{GasPrice: big.NewInt(100)}) + tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{GasPrice: big.NewInt(1)}), common.Address{}) + tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 1000, big.NewInt(0)) + tracer.OnExit(0, nil, 0, nil, false) + ret, err := tracer.GetResult() + if err != nil { + t.Fatal(err) + } + if string(ret) != `"100"` { + t.Fatalf("unexpected gasPrice in tracer context, have %s want \"100\"", ret) + } +} + func TestIsPrecompile(t *testing.T) { chaincfg := ¶ms.ChainConfig{ ChainID: big.NewInt(1), diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 09f46a122d94..fe2764a45148 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -47,10 +47,6 @@ func BenchmarkTransactionTraceV2(b *testing.B) { if err != nil { b.Fatal(err) } - txContext := vm.TxContext{ - Origin: from, - GasPrice: tx.GasPrice(), - } context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, @@ -82,17 +78,14 @@ func BenchmarkTransactionTraceV2(b *testing.B) { state := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc) evm := vm.NewEVM(context, state, nil, params.AllEthashProtocolChanges, vm.Config{}) - evm.SetTxContext(txContext) msg, err := core.TransactionToMessage(tx, signer, nil, nil, context.BaseFee) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) } - b.ResetTimer() b.ReportAllocs() - - for i := 0; i < b.N; i++ { - tracer := logger.NewStructLogger(&logger.Config{Debug: false}).Hooks() + for b.Loop() { + tracer := logger.NewStructLogger(&logger.Config{}).Hooks() tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) evm.Config.Tracer = tracer diff --git a/ethclient/simulated/backend.go b/ethclient/simulated/backend.go index 9b1d8d79443f..10248deaf7d7 100644 --- a/ethclient/simulated/backend.go +++ b/ethclient/simulated/backend.go @@ -990,14 +990,12 @@ func (b *Backend) callContract(ctx context.Context, call ethereum.CallMsg, block // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. - txContext := core.NewEVMTxContext(msg) evmContext := core.NewEVMBlockContext(block, b.blockchain, nil) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. evm := vm.NewEVM(evmContext, stateDB, nil, b.config, vm.Config{NoBaseFee: true}) - evm.SetTxContext(txContext) gaspool := new(core.GasPool).AddGas(gomath.MaxUint64) - return core.NewStateTransition(evm, msg, gaspool).TransitionDb(common.Address{}) + return core.ApplyMessage(evm, msg, gaspool, common.Address{}) } // SendTransaction updates the pending block to include the given transaction. diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index a16b2d6d7c15..290ab47407b7 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1166,7 +1166,6 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s if precompiles != nil { evm.SetPrecompiles(precompiles) } - evm.SetTxContext(core.NewEVMTxContext(msg)) res, err := applyMessageWithEVM(ctx, evm, msg, timeout, gp) // If an internal state error occurred, let that have precedence. Otherwise, // a "trie root missing" type of error will masquerade as e.g. "insufficient gas" @@ -1810,7 +1809,6 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH if msg.GasPrice.Sign() == 0 { evm.Context.BaseFee = new(big.Int) } - evm.SetTxContext(core.NewEVMTxContext(msg)) res, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit), common.Address{}) if err != nil { return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.ToTransaction(types.LegacyTxType).Hash(), err) diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 58865c046ba1..1720e20df7dd 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -210,7 +210,6 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, tracer.reset(tx.Hash(), uint(i)) // EoA check is always skipped, even in validation mode. msg := call.ToMessage(sim.b, header.BaseFee, !sim.validate) - evm.SetTxContext(core.NewEVMTxContext(msg)) result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp) if err != nil { txErr := txValidationError(err) diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 67ea8dc8afc0..54adae588bc0 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -178,12 +178,10 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD } // Prepare the EVM. - txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) context.GetHash = vmTestBlockHash context.BaseFee = baseFee evm := vm.NewEVM(context, statedb, nil, config, vmconfig) - evm.SetTxContext(txContext) // Execute the message. snapshot := statedb.Snapshot()