diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index fe920e9d73..af120d342f 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -777,9 +777,10 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM evmContext := core.NewEVMBlockContext(block, b.blockchain, nil) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. - vmenv := vm.NewEVM(evmContext, txContext, stateDB, nil, b.config, vm.Config{NoBaseFee: true}) + 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(vmenv, msg, gaspool).TransitionDb(common.Address{}) + return core.NewStateTransition(evm, msg, gaspool).TransitionDb(common.Address{}) } // SendTransaction updates the pending block to include the given transaction. diff --git a/consensus/tests/engine_v1_tests/helper.go b/consensus/tests/engine_v1_tests/helper.go index b50a359a11..d89d95610b 100644 --- a/consensus/tests/engine_v1_tests/helper.go +++ b/consensus/tests/engine_v1_tests/helper.go @@ -371,7 +371,9 @@ func createBlockFromHeader(bc *core.BlockChain, customHeader *types.Header, txs var receipts types.Receipts for i, tx := range txs { statedb.SetTxContext(tx.Hash(), i) - receipt, _, _, err := core.ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{}) + blockContext := core.NewEVMBlockContext(&header, bc, &header.Coinbase) + evm := vm.NewEVM(blockContext, statedb, nil, bc.Config(), vm.Config{}) + receipt, _, _, err := core.ApplyTransaction(bc.Config(), nil, evm, gp, statedb, &header, tx, gasUsed) if err != nil { return nil, fmt.Errorf("%v when applying transaction", err) } diff --git a/consensus/tests/engine_v2_tests/helper.go b/consensus/tests/engine_v2_tests/helper.go index 9dd6d7c006..30c3b7cfd1 100644 --- a/consensus/tests/engine_v2_tests/helper.go +++ b/consensus/tests/engine_v2_tests/helper.go @@ -901,7 +901,9 @@ func createBlockFromHeader(bc *core.BlockChain, customHeader *types.Header, txs var receipts types.Receipts for i, tx := range txs { statedb.SetTxContext(tx.Hash(), i) - receipt, _, _, err := core.ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{}) + blockContext := core.NewEVMBlockContext(&header, bc, &header.Coinbase) + evm := vm.NewEVM(blockContext, statedb, nil, bc.Config(), vm.Config{}) + receipt, _, _, err := core.ApplyTransaction(bc.Config(), nil, evm, gp, statedb, &header, tx, gasUsed) if err != nil { return nil, fmt.Errorf("%v when applying transaction", err) } diff --git a/core/chain_makers.go b/core/chain_makers.go index 6d96386bd4..64871dc948 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -87,7 +87,9 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti } feeCapacity := b.statedb.GetTRC21FeeCapacityFromState() b.statedb.SetTxContext(tx.Hash(), len(b.txs)) - receipt, gas, tokenFeeUsed, err := ApplyTransaction(b.config, feeCapacity, bc, &b.header.Coinbase, b.gasPool, b.statedb, nil, b.header, tx, &b.header.GasUsed, vmConfig) + blockContext := NewEVMBlockContext(b.header, bc, &b.header.Coinbase) + evm := vm.NewEVM(blockContext, b.statedb, nil, b.config, vmConfig) + receipt, gas, tokenFeeUsed, err := ApplyTransaction(b.config, feeCapacity, evm, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed) if err != nil { panic(err) } @@ -235,7 +237,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse if config.IsPrague(b.header.Number) { // EIP-2935 blockContext := NewEVMBlockContext(b.header, chainReader, &b.header.Coinbase) - evm := vm.NewEVM(blockContext, vm.TxContext{}, statedb, nil, config, vm.Config{}) + evm := vm.NewEVM(blockContext, statedb, nil, config, vm.Config{}) ProcessParentBlockHash(b.header.ParentHash, evm, statedb) } diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 1d79eca6e9..bc8341fff9 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -53,7 +53,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c header = block.Header() gaspool = new(GasPool).AddGas(block.GasLimit()) blockContext = NewEVMBlockContext(header, p.bc, nil) - evm = vm.NewEVM(blockContext, vm.TxContext{}, statedb, nil, p.config, cfg) + evm = vm.NewEVM(blockContext, statedb, nil, p.config, cfg) signer = types.MakeSigner(p.config, header.Number) ) // Iterate over and process the individual transactions @@ -69,7 +69,7 @@ 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, p.config, gaspool, statedb, header, evm); err != nil { + if err := precacheTransaction(msg, gaspool, evm); err != nil { return // Ugh, something went horribly wrong, bail out } // If we're pre-byzantium, pre-load trie nodes for the intermediate root @@ -86,9 +86,9 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c // 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, config *params.ChainConfig, gaspool *GasPool, statedb *state.StateDB, header *types.Header, evm *vm.EVM) error { +func precacheTransaction(msg *Message, gaspool *GasPool, evm *vm.EVM) error { // Update the evm with the new transaction context. - evm.Reset(NewEVMTxContext(msg), statedb) + 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 f3fa5d1880..45d1033003 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -97,13 +97,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra totalFeeUsed := big.NewInt(0) // Apply pre-execution system calls. - blockContext := NewEVMBlockContext(header, p.bc, nil) - vmenv := vm.NewEVM(blockContext, vm.TxContext{}, tracingStateDB, tradingState, p.config, cfg) + context := NewEVMBlockContext(header, p.bc, nil) + evm := vm.NewEVM(context, tracingStateDB, tradingState, p.config, cfg) signer := types.MakeSigner(p.config, blockNumber) coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil) if p.config.IsPrague(block.Number()) { - ProcessParentBlockHash(block.ParentHash(), vmenv, tracingStateDB) + ProcessParentBlockHash(block.ParentHash(), evm, tracingStateDB) } // Iterate over and process the individual transactions @@ -146,7 +146,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra } statedb.SetTxContext(tx.Hash(), i) - receipt, gas, tokenFeeUsed, err := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, balanceFee, coinbaseOwner) + receipt, gas, tokenFeeUsed, err := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, evm, balanceFee, coinbaseOwner) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } @@ -204,13 +204,13 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated } // Apply pre-execution system calls. - blockContext := NewEVMBlockContext(header, p.bc, nil) - vmenv := vm.NewEVM(blockContext, vm.TxContext{}, tracingStateDB, tradingState, p.config, cfg) + context := NewEVMBlockContext(header, p.bc, nil) + evm := vm.NewEVM(context, tracingStateDB, tradingState, p.config, cfg) signer := types.MakeSigner(p.config, blockNumber) coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil) if p.config.IsPrague(block.Number()) { - ProcessParentBlockHash(block.ParentHash(), vmenv, tracingStateDB) + ProcessParentBlockHash(block.ParentHash(), evm, tracingStateDB) } // Iterate over and process the individual transactions @@ -253,7 +253,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated } statedb.SetTxContext(tx.Hash(), i) - receipt, gas, tokenFeeUsed, err := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, balanceFee, coinbaseOwner) + receipt, gas, tokenFeeUsed, err := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, evm, balanceFee, coinbaseOwner) if err != nil { return nil, nil, 0, err } @@ -280,11 +280,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated // 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, config *params.ChainConfig, 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) { - // Initialize tracer at the beginning to ensure all transaction types - // (including non-EVM special transactions) are properly traced. - var tracingStateDB = vm.StateDB(statedb) if hooks := evm.Config.Tracer; hooks != nil { - tracingStateDB = state.NewHookedState(statedb, hooks) if hooks.OnTxStart != nil { hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) } @@ -314,7 +310,7 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo // Create a new context to be used in the EVM environment txContext := NewEVMTxContext(msg) - evm.Reset(txContext, tracingStateDB) + evm.SetTxContext(txContext) // Bypass denylist address maxBlockNumber := new(big.Int).SetInt64(9147459) @@ -469,7 +465,7 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo // Update the state with pending changes. var root []byte if config.IsByzantium(blockNumber) { - tracingStateDB.Finalise(true) + evm.StateDB.Finalise(true) } else { root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes() } @@ -524,7 +520,7 @@ func getCoinbaseOwner(bc *BlockChain, statedb *state.StateDB, header *types.Head // and uses the input parameters for its environment. It returns the receipt // for the transaction, gas used and an error if the transaction failed, // indicating the block was invalid. -func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, bool, error) { +func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, bool, error) { var balanceFee *big.Int if tx.To() != nil { if value, ok := tokensFee[*tx.To()]; ok { @@ -532,16 +528,13 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* } } - // Create a new context to be used in the EVM environment - blockContext := NewEVMBlockContext(header, bc, author) - vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, XDCxState, config, cfg) signer := types.MakeSigner(config, header.Number) msg, err := TransactionToMessage(tx, signer, balanceFee, header.Number, header.BaseFee) if err != nil { return nil, 0, false, err } - coinbaseOwner := getCoinbaseOwner(bc, statedb, header, author) - return ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv, balanceFee, coinbaseOwner) + coinbaseOwner := statedb.GetOwner(evm.Context.Coinbase) + return ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, evm, balanceFee, coinbaseOwner) } func ApplySignTransaction(msg *Message, config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, gasUsed uint64, tokenFeeUsed bool, err error) { @@ -726,7 +719,7 @@ func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb vm.Stat To: ¶ms.HistoryStorageAddress, Data: prevHash.Bytes(), } - vmenv.Reset(NewEVMTxContext(msg), statedb) + vmenv.SetTxContext(NewEVMTxContext(msg)) statedb.AddAddressToAccessList(params.HistoryStorageAddress) _, _, err := vmenv.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560) if err != nil { diff --git a/core/state_processor_test.go b/core/state_processor_test.go index ada7dc02f1..7aff0b1f31 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -467,7 +467,7 @@ func TestApplyTransactionWithEVMTracer(t *testing.T) { blockHash := genesis.Hash() vmContext := NewEVMBlockContext(blockchain.CurrentBlock(), blockchain, nil) - evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, nil, blockchain.Config(), vmConfig) + evm := vm.NewEVM(vmContext, statedb, nil, blockchain.Config(), vmConfig) // Apply transaction var usedGas uint64 @@ -493,6 +493,81 @@ func TestApplyTransactionWithEVMTracer(t *testing.T) { } } +func TestApplyTransactionWithEVMStateChangeHooks(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") + hookInvoked bool + ) + + db := rawdb.NewMemoryDatabase() + gspec := &Genesis{ + Config: config, + Alloc: types.GenesisAlloc{ + sender: { + Balance: big.NewInt(1000000000000000000), + Nonce: 0, + }, + }, + } + genesis := gspec.MustCommit(db) + blockchain, _ := NewBlockChain(db, nil, gspec, ethash.NewFaker(), vm.Config{}) + 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, big.NewInt(20000000000), nil) + signedTx, err := types.SignTx(tx, signer, testKey) + if err != nil { + t.Fatalf("Failed to sign tx: %v", err) + } + + hooks := &tracing.Hooks{ + OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { + hookInvoked = true + }, + } + hookedState := state.NewHookedState(statedb, hooks) + + vmContext := NewEVMBlockContext(blockchain.CurrentBlock(), blockchain, nil) + evmenv := vm.NewEVM(vmContext, hookedState, nil, blockchain.Config(), vm.Config{Tracer: hooks}) + + msg, err := TransactionToMessage(signedTx, signer, nil, big.NewInt(1), nil) + if err != nil { + t.Fatalf("Failed to build message: %v", err) + } + + gasPool := new(GasPool).AddGas(1000000) + var usedGas uint64 + _, _, _, err = ApplyTransactionWithEVM(msg, config, gasPool, statedb, big.NewInt(1), genesis.Hash(), signedTx, &usedGas, evmenv, nil, common.Address{}) + if err != nil { + t.Fatalf("ApplyTransactionWithEVM failed: %v", err) + } + if !hookInvoked { + t.Fatal("expected OnBalanceChange to be invoked, but it was not") + } +} + func TestProcessParentBlockHash(t *testing.T) { var ( chainConfig = params.MergedTestChainConfig @@ -508,11 +583,11 @@ func TestProcessParentBlockHash(t *testing.T) { statedb.IntermediateRoot(true) vmContext := NewEVMBlockContext(header, nil, &coinbase) - evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, nil, chainConfig, vm.Config{}) + evm := vm.NewEVM(vmContext, statedb, nil, chainConfig, vm.Config{}) ProcessParentBlockHash(header.ParentHash, evm, statedb) vmContext = NewEVMBlockContext(parent, nil, &coinbase) - evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, nil, chainConfig, vm.Config{}) + evm = vm.NewEVM(vmContext, statedb, nil, chainConfig, vm.Config{}) ProcessParentBlockHash(parent.ParentHash, evm, statedb) // make sure that the state is correct @@ -548,8 +623,8 @@ func TestProcessParentBlockHashPragueGuard(t *testing.T) { BaseFee: nil, Random: &random, } - evmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, nil, &config, vm.Config{}) - ProcessParentBlockHash(common.Hash{0x01}, evmenv, statedb) + evm := vm.NewEVM(blockContext, statedb, nil, &config, vm.Config{}) + ProcessParentBlockHash(common.Hash{0x01}, evm, statedb) if code := statedb.GetCode(params.HistoryStorageAddress); len(code) != 0 { t.Fatalf("unexpected history contract code predeploy: %x", code) @@ -586,8 +661,8 @@ func TestProcessParentBlockHashBackfillMissingHistory(t *testing.T) { BaseFee: nil, Random: &random, } - evmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, nil, &config, vm.Config{}) - ProcessParentBlockHash(common.Hash{0x01}, evmenv, statedb) + evm := vm.NewEVM(blockContext, statedb, nil, &config, vm.Config{}) + ProcessParentBlockHash(common.Hash{0x01}, evm, statedb) if have := getParentBlockHash(statedb, 1); have != available[1] { t.Fatalf("expected hash at slot 1, have %v", have) @@ -619,14 +694,14 @@ func TestProcessParentBlockHashCodeMismatchPanics(t *testing.T) { BaseFee: nil, Random: &random, } - evmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, nil, &config, vm.Config{}) + evm := vm.NewEVM(blockContext, statedb, nil, &config, vm.Config{}) defer func() { if recover() == nil { t.Fatal("expected panic on history storage code mismatch") } }() - ProcessParentBlockHash(common.Hash{0x01}, evmenv, statedb) + ProcessParentBlockHash(common.Hash{0x01}, evm, statedb) } func getParentBlockHash(statedb *state.StateDB, number uint64) common.Hash { diff --git a/core/token_validator.go b/core/token_validator.go index 8c087e92ff..28e301f906 100644 --- a/core/token_validator.go +++ b/core/token_validator.go @@ -110,9 +110,10 @@ func CallContractWithState(call ethereum.CallMsg, chain consensus.ChainContext, // about the transaction and calling mechanisms. txContext := NewEVMTxContext(msg) evmContext := NewEVMBlockContext(chain.CurrentHeader(), chain, nil) - vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, chain.Config(), vm.Config{}) + evm := vm.NewEVM(evmContext, statedb, nil, chain.Config(), vm.Config{}) + evm.SetTxContext(txContext) gaspool := new(GasPool).AddGas(1000000) - result, err := NewStateTransition(vmenv, msg, gaspool).TransitionDb(common.Address{}) + result, err := NewStateTransition(evm, msg, gaspool).TransitionDb(common.Address{}) if err != nil { return nil, err } diff --git a/core/vm/evm.go b/core/vm/evm.go index 75c0ab0008..2be72c4a86 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -132,12 +132,13 @@ type EVM struct { returnData []byte // Last CALL's return data for subsequent reuse } -// NewEVM returns a new EVM. The returned EVM is not thread safe and should -// only ever be used *once*. -func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, config Config) *EVM { +// NewEVM constructs an EVM instance with the supplied block context, state +// database and several configs. It is meant to be used throughout the entire +// state transition of a block, with the transaction context switched as +// needed by calling evm.SetTxContext. +func NewEVM(blockCtx BlockContext, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, config Config) *EVM { evm := &EVM{ Context: blockCtx, - TxContext: txCtx, StateDB: statedb, tradingStateDB: tradingStateDB, Config: config, @@ -198,6 +199,11 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStat return evm } +// SetTracer sets the tracer for following state transition. +func (evm *EVM) SetTracer(tracer *tracing.Hooks) { + evm.Config.Tracer = tracer +} + // SetPrecompiles sets the precompiled contracts for the EVM. // This method is only used through RPC calls. // It is not thread-safe. @@ -205,11 +211,10 @@ func (evm *EVM) SetPrecompiles(precompiles PrecompiledContracts) { evm.precompiles = precompiles } -// Reset resets the EVM with a new transaction context.Reset +// SetTxContext updates the EVM with a new transaction context. // This is not threadsafe and should only be done very cautiously. -func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) { +func (evm *EVM) SetTxContext(txCtx TxContext) { evm.TxContext = txCtx - evm.StateDB = statedb } // Cancel cancels any running EVM operation. This may be called concurrently and diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index becf082287..d518ebb726 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -96,16 +96,16 @@ func TestEIP2200(t *testing.T) { CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true }, Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {}, } - vmenv := NewEVM(vmctx, TxContext{}, statedb, nil, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) + evm := NewEVM(vmctx, statedb, nil, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) - _, gas, err := vmenv.Call(common.Address{}, address, nil, tt.gaspool, new(uint256.Int)) + _, gas, err := evm.Call(common.Address{}, address, nil, tt.gaspool, new(uint256.Int)) if !errors.Is(err, tt.failure) { t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure) } if used := tt.gaspool - gas; used != tt.used { t.Errorf("test %d: gas used mismatch: have %v, want %v", i, used, tt.used) } - if refund := vmenv.StateDB.GetRefund(); refund != tt.refund { + if refund := evm.StateDB.GetRefund(); refund != tt.refund { t.Errorf("test %d: gas refund mismatch: have %v, want %v", i, refund, tt.refund) } } @@ -151,9 +151,9 @@ func TestCreateGas(t *testing.T) { config.ExtraEips = []int{3860} } - vmenv := NewEVM(vmctx, TxContext{}, statedb, nil, params.AllEthashProtocolChanges, config) + evm := NewEVM(vmctx, statedb, nil, params.AllEthashProtocolChanges, config) var startGas = uint64(testGas) - ret, gas, err := vmenv.Call(common.Address{}, address, nil, startGas, new(uint256.Int)) + ret, gas, err := evm.Call(common.Address{}, address, nil, startGas, new(uint256.Int)) if err != nil { return false } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index f262fd3367..0399d98f3d 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -98,7 +98,7 @@ func init() { func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) { var ( - evm = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) + evm = NewEVM(BlockContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) ) @@ -196,7 +196,7 @@ func TestSAR(t *testing.T) { func TestAddMod(t *testing.T) { var ( - evm = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) + evm = NewEVM(BlockContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) ) @@ -239,7 +239,7 @@ func TestWriteExpectedValues(t *testing.T) { // getResult is a convenience function to generate the expected values getResult := func(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { var ( - evm = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) + evm = NewEVM(BlockContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) ) @@ -289,7 +289,7 @@ func TestJsonTestcases(t *testing.T) { func opBenchmark(bench *testing.B, op executionFunc, args ...string) { var ( - evm = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) + evm = NewEVM(BlockContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() scope = &ScopeContext{nil, stack, nil} ) @@ -526,7 +526,7 @@ func BenchmarkOpIsZero(b *testing.B) { func TestOpMstore(t *testing.T) { var ( - evm = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) + evm = NewEVM(BlockContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() ) @@ -550,7 +550,7 @@ func TestOpMstore(t *testing.T) { func BenchmarkOpMstore(bench *testing.B) { var ( - evm = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) + evm = NewEVM(BlockContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() ) @@ -570,7 +570,7 @@ func BenchmarkOpMstore(bench *testing.B) { func TestOpTstore(t *testing.T) { var ( statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase())) - evm = NewEVM(BlockContext{}, TxContext{}, statedb, nil, params.TestChainConfig, Config{}) + evm = NewEVM(BlockContext{}, statedb, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() caller = common.Address{} @@ -609,7 +609,7 @@ func TestOpTstore(t *testing.T) { func BenchmarkOpKeccak256(bench *testing.B) { var ( - evm = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) + evm = NewEVM(BlockContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() ) @@ -710,7 +710,7 @@ func TestRandom(t *testing.T) { {name: "hash(0x010203)", random: crypto.Keccak256Hash([]byte{0x01, 0x02, 0x03})}, } { var ( - evm = NewEVM(BlockContext{Random: &tt.random}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) + evm = NewEVM(BlockContext{Random: &tt.random}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) ) @@ -750,7 +750,7 @@ func TestBlobHash(t *testing.T) { {name: "out-of-bounds (nil)", idx: 25, expect: zero, hashes: nil}, } { var ( - evm = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) + evm = NewEVM(BlockContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) ) @@ -853,7 +853,7 @@ func TestOpMCopy(t *testing.T) { }, } { var ( - evm = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) + evm = NewEVM(BlockContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) ) @@ -909,7 +909,7 @@ func TestOpMCopy(t *testing.T) { } func TestOpCLZ(t *testing.T) { - evm := NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) + evm := NewEVM(BlockContext{}, nil, nil, params.TestChainConfig, Config{}) tests := []struct { inputHex string diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 233d132ba4..293599105e 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -39,5 +39,7 @@ func NewEnv(cfg *Config) *vm.EVM { Random: cfg.Random, } - return vm.NewEVM(blockContext, txContext, cfg.State, nil, cfg.ChainConfig, cfg.EVMConfig) + evm := vm.NewEVM(blockContext, cfg.State, nil, cfg.ChainConfig, cfg.EVMConfig) + evm.SetTxContext(txContext) + return evm } diff --git a/eth/api_backend.go b/eth/api_backend.go index edf5ca10b1..d1de45afee 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -32,7 +32,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/contracts" @@ -40,7 +39,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/bloombits" "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" - "github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" @@ -264,20 +262,18 @@ func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int { return b.eth.blockchain.GetTdByHash(hash) } -func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) (*vm.EVM, func() error, error) { +func (b *EthAPIBackend) GetEVM(ctx context.Context, state *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) (*vm.EVM, func() error, error) { vmError := func() error { return nil } if vmConfig == nil { vmConfig = b.eth.blockchain.GetVMConfig() } - state.SetBalance(msg.From, math.MaxBig256, tracing.BalanceChangeUnspecified) - txContext := core.NewEVMTxContext(msg) var context vm.BlockContext if blockCtx != nil { context = *blockCtx } else { context = core.NewEVMBlockContext(header, b.eth.BlockChain(), nil) } - return vm.NewEVM(context, txContext, state, XDCxState, b.eth.chainConfig, *vmConfig), vmError, nil + return vm.NewEVM(context, state, XDCxState, b.eth.chainConfig, *vmConfig), vmError, nil } func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index 7f0583f7ca..58ca9420bf 100644 --- a/eth/gasestimator/gasestimator.go +++ b/eth/gasestimator/gasestimator.go @@ -176,7 +176,8 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio if msgContext.GasPrice.Sign() == 0 { evmContext.BaseFee = new(big.Int) } - evm := vm.NewEVM(evmContext, msgContext, dirtyState, nil, opts.Config, vm.Config{NoBaseFee: true}) + 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 // context for the lifetime of this method call. diff --git a/eth/state_accessor.go b/eth/state_accessor.go index d1805502b9..c453c23a53 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -206,11 +206,11 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, if err != nil { return nil, vm.BlockContext{}, nil, nil, err } + context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) + evm := vm.NewEVM(context, statedb, nil, eth.blockchain.Config(), vm.Config{}) // If prague hardfork, insert parent block hash in the state as per EIP-2935. if eth.blockchain.Config().IsPrague(block.Number()) { - context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, nil, eth.blockchain.Config(), vm.Config{}) - core.ProcessParentBlockHash(block.ParentHash(), vmenv, statedb) + core.ProcessParentBlockHash(block.ParentHash(), evm, statedb) } if txIndex == 0 && len(block.Transactions()) == 0 { return nil, vm.BlockContext{}, statedb, release, nil @@ -219,6 +219,9 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, signer := types.MakeSigner(eth.blockchain.Config(), block.Number()) feeCapacity := statedb.GetTRC21FeeCapacityFromState() for idx, tx := range block.Transactions() { + if idx == txIndex { + return tx, context, statedb, release, nil + } var balance *big.Int if tx.To() != nil { if value, ok := feeCapacity[*tx.To()]; ok { @@ -228,19 +231,16 @@ 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) - context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) - if idx == txIndex { - return tx, context, statedb, release, nil - } + evm.SetTxContext(txContext) + // Not yet the searched for transaction, execute on top of the current state - vmenv := vm.NewEVM(context, txContext, statedb, nil, eth.blockchain.Config(), vm.Config{}) statedb.SetTxContext(tx.Hash(), idx) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{}); err != nil { + 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) } // Ensure any modifications are committed to the state // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) + statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number())) } return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 8dd3c0adcf..f6462c6c7f 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -382,8 +382,8 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed // Insert parent hash in history contract. if api.backend.ChainConfig().IsPrague(next.Number()) { context := core.NewEVMBlockContext(next.Header(), api.chainContext(ctx), nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, nil, api.backend.ChainConfig(), vm.Config{}) - core.ProcessParentBlockHash(next.ParentHash(), vmenv, statedb) + evm := vm.NewEVM(context, statedb, nil, api.backend.ChainConfig(), vm.Config{}) + core.ProcessParentBlockHash(next.ParentHash(), evm, statedb) } // Clean out any pending release functions of trace state. Note this // step must be done after constructing tracing state, because the @@ -519,8 +519,9 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) deleteEmptyObjects = chainConfig.IsEIP158(block.Number()) ) + evm := vm.NewEVM(vmctx, statedb, nil, chainConfig, vm.Config{}) if chainConfig.IsPrague(block.Number()) { - core.ProcessParentBlockHash(block.ParentHash(), vm.NewEVM(vmctx, vm.TxContext{}, statedb, nil, chainConfig, vm.Config{}), statedb) + core.ProcessParentBlockHash(block.ParentHash(), evm, statedb) } feeCapacity := statedb.GetTRC21FeeCapacityFromState() for i, tx := range block.Transactions() { @@ -540,10 +541,10 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config var ( msg, _ = core.TransactionToMessage(tx, signer, balance, block.Number(), block.BaseFee()) txContext = core.NewEVMTxContext(msg) - vmenv = vm.NewEVM(vmctx, txContext, statedb, nil, chainConfig, vm.Config{}) ) + evm.SetTxContext(txContext) statedb.SetTxContext(tx.Hash(), i) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit), common.Address{}); err != nil { + 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) // 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 @@ -587,9 +588,9 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac defer release() blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + evm := vm.NewEVM(blockCtx, statedb, nil, api.backend.ChainConfig(), vm.Config{}) if api.backend.ChainConfig().IsPrague(block.Number()) { - vmenv := vm.NewEVM(blockCtx, vm.TxContext{}, statedb, nil, api.backend.ChainConfig(), vm.Config{}) - core.ProcessParentBlockHash(block.ParentHash(), vmenv, statedb) + core.ProcessParentBlockHash(block.ParentHash(), evm, statedb) } // JS tracers have high overhead. In this case run a parallel @@ -692,6 +693,8 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat feeCapacity := statedb.GetTRC21FeeCapacityFromState() var failed error blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + evm := vm.NewEVM(blockCtx, statedb, nil, api.backend.ChainConfig(), vm.Config{}) + txloop: for i, tx := range txs { // Send the trace task over for execution @@ -717,14 +720,14 @@ txloop: header := block.Header() msg, _ := core.TransactionToMessage(tx, signer, balance, header.Number, header.BaseFee) statedb.SetTxContext(tx.Hash(), i) - vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, nil, api.backend.ChainConfig(), vm.Config{}) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit), common.Address{}); err != nil { + 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 } // Finalize the state so any modifications are written to the trie // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) + statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number())) } close(jobs) @@ -894,7 +897,8 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor } } // The actual TxContext will be created as part of ApplyTransactionWithEVM. - vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: message.GasPrice}, statedb, nil, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) + evm := vm.NewEVM(vmctx, statedb, 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 { @@ -908,7 +912,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) { tracer.Stop(errors.New("execution timeout")) // Stop evm execution. Note cancellation is not necessarily immediate. - vmenv.Cancel() + evm.Cancel() } }() defer cancel() @@ -923,7 +927,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor // Call SetTxContext to clear out the statedb access list statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) - _, _, _, err = core.ApplyTransactionWithEVM(message, api.backend.ChainConfig(), new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, vmenv, balance, common.Address{}) + _, _, _, err = core.ApplyTransactionWithEVM(message, api.backend.ChainConfig(), new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, evm, balance, common.Address{}) if err != nil { return nil, fmt.Errorf("tracing failed: %w", err) } diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 3b9a9d19d2..5b36093daa 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -174,18 +174,19 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block } // Recompute transactions up to the target index. signer := types.MakeSigner(b.chainConfig, block.Number()) + context := core.NewEVMBlockContext(block.Header(), b.chain, nil) + evm := vm.NewEVM(context, statedb, nil, b.chainConfig, vm.Config{}) for idx, tx := range block.Transactions() { - msg, _ := core.TransactionToMessage(tx, signer, nil, block.Number(), block.BaseFee()) - txContext := core.NewEVMTxContext(msg) - context := core.NewEVMBlockContext(block.Header(), b.chain, nil) if idx == txIndex { return tx, context, statedb, release, nil } - vmenv := vm.NewEVM(context, txContext, statedb, nil, b.chainConfig, vm.Config{}) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{}); err != 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) } - statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) + statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number())) } return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) } diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index 14dd6ae533..a77acbbdf1 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -142,7 +142,8 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - evm := vm.NewEVM(context, core.NewEVMTxContext(msg), logState, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + evm := vm.NewEVM(context, logState, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + evm.SetTxContext(core.NewEVMTxContext(msg)) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{}) if err != nil { @@ -228,13 +229,17 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { state := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc) b.ReportAllocs() + + evm := vm.NewEVM(context, state, nil, test.Genesis.Config, vm.Config{}) + evm.SetTxContext(txContext) + for b.Loop() { + snap := state.Snapshot() tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil, test.Genesis.Config) if err != nil { b.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, txContext, state, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) - snap := state.Snapshot() + evm.Config.Tracer = tracer.Hooks st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if _, err = st.TransitionDb(common.Address{}); err != nil { b.Fatalf("failed to execute transaction: %v", err) @@ -379,7 +384,8 @@ func TestInternals(t *testing.T) { Origin: origin, GasPrice: tx.GasPrice(), } - evm := vm.NewEVM(context, txContext, logState, nil, config, vm.Config{Tracer: tc.tracer.Hooks}) + 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) @@ -459,7 +465,8 @@ func testContractTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, txContext, state, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + 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) diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index 7e38f76e1d..d65895befd 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -105,7 +105,8 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string if err != nil { return fmt.Errorf("failed to prepare transaction for tracing: %v", err) } - evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + evm := vm.NewEVM(context, state, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + evm.SetTxContext(core.NewEVMTxContext(msg)) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{}) if err != nil { diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index c44220be2f..c418921d6b 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -114,7 +114,8 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + evm := vm.NewEVM(context, state, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + evm.SetTxContext(core.NewEVMTxContext(msg)) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{}) if err != nil { diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index 2d35f82826..856bb4e692 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -51,12 +51,13 @@ func testCtx() *vmContext { func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { var ( - evm = vm.NewEVM(vmctx.ctx, vmctx.txContext, &dummyStatedb{}, nil, chaincfg, vm.Config{Tracer: tracer.Hooks}) + evm = vm.NewEVM(vmctx.ctx, &dummyStatedb{}, nil, chaincfg, vm.Config{Tracer: tracer.Hooks}) gasLimit uint64 = 31000 startGas uint64 = 10000 value = new(uint256.Int) contract = vm.NewContract(common.Address{}, common.Address{}, value, startGas, nil) ) + evm.SetTxContext(vmctx.txContext) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} if contractCode != nil { contract.Code = contractCode @@ -178,8 +179,9 @@ func TestHaltBetweenSteps(t *testing.T) { scope := &vm.ScopeContext{ Contract: vm.NewContract(common.Address{}, common.Address{}, new(uint256.Int), 0, nil), } - env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, nil, chainConfig, vm.Config{Tracer: tracer.Hooks}) - tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) + evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, nil, chainConfig, vm.Config{Tracer: tracer.Hooks}) + evm.SetTxContext(vm.TxContext{GasPrice: big.NewInt(1)}) + tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 0, big.NewInt(0)) tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil) timeout := errors.New("stahp") @@ -201,8 +203,9 @@ func TestNoStepExec(t *testing.T) { if err != nil { t.Fatal(err) } - env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, nil, chainConfig, vm.Config{Tracer: tracer.Hooks}) - tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) + evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, nil, chainConfig, vm.Config{Tracer: tracer.Hooks}) + evm.SetTxContext(vm.TxContext{GasPrice: big.NewInt(100)}) + tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{}), 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() diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index 5b2c75e606..8b4aac685a 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -42,7 +42,7 @@ func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) co func TestStoreCapture(t *testing.T) { var ( logger = NewStructLogger(nil) - evm = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Tracer: logger.Hooks()}) + evm = vm.NewEVM(vm.BlockContext{}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Tracer: logger.Hooks()}) contract = vm.NewContract(common.Address{}, common.Address{}, new(uint256.Int), 100000, nil) ) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 6285a689d0..e222d59011 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -88,7 +88,8 @@ func BenchmarkTransactionTrace(b *testing.B) { //EnableMemory: false, //EnableReturnData: false, }) - evm := vm.NewEVM(context, txContext, state, nil, params.AllEthashProtocolChanges, vm.Config{Tracer: tracer.Hooks()}) + evm := vm.NewEVM(context, state, nil, params.AllEthashProtocolChanges, vm.Config{Tracer: tracer.Hooks()}) + 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) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index acbf1d6316..63aae07048 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1274,7 +1274,8 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s if msg.GasPrice.Sign() == 0 { blockContext.BaseFee = new(big.Int) } - evm, vmError, err := b.GetEVM(ctx, msg, state, XDCxState, header, vmConfig, blockContext) + state.SetBalance(msg.From, math.MaxBig256, tracing.BalanceChangeUnspecified) + evm, vmError, err := b.GetEVM(ctx, state, XDCxState, header, vmConfig, blockContext) if err != nil { return nil, err } @@ -1284,7 +1285,7 @@ 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" @@ -1888,16 +1889,18 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH // Apply the transaction with the access list tracer tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles) config := vm.Config{Tracer: tracer.Hooks(), NoBaseFee: true} - vmenv, _, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &config, nil) + statedb.SetBalance(msg.From, math.MaxBig256, tracing.BalanceChangeUnspecified) + evm, _, err := b.GetEVM(ctx, statedb, XDCxState, header, &config, nil) if err != nil { return nil, 0, nil, err } // Lower the basefee to 0 to avoid breaking EVM // invariants (basefee < feecap). if msg.GasPrice.Sign() == 0 { - vmenv.Context.BaseFee = new(big.Int) + evm.Context.BaseFee = new(big.Int) } - res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit), common.Address{}) + 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/backend.go b/internal/ethapi/backend.go index a50b3e88b7..ad2c0ab68f 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -75,7 +75,7 @@ type Backend interface { PendingBlockAndReceipts() (*types.Block, types.Receipts) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) GetTd(ctx context.Context, hash common.Hash) *big.Int - GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) (*vm.EVM, func() error, error) + GetEVM(ctx context.Context, state *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) (*vm.EVM, func() error, error) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 5bed782b90..267487db9d 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -175,12 +175,12 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, NoBaseFee: !sim.validate, Tracer: tracer.Hooks(), } - evm = vm.NewEVM(blockContext, vm.TxContext{GasPrice: new(big.Int)}, sim.state, nil, sim.chainConfig, *vmConfig) ) var tracingStateDB = vm.StateDB(sim.state) if hooks := tracer.Hooks(); hooks != nil { tracingStateDB = state.NewHookedState(sim.state, hooks) } + evm := vm.NewEVM(blockContext, tracingStateDB, nil, sim.chainConfig, *vmConfig) // It is possible to override precompiles with EVM bytecode, or // move them to another address. if precompiles != nil { @@ -198,7 +198,7 @@ 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, true) - evm.Reset(core.NewEVMTxContext(msg), tracingStateDB) + evm.SetTxContext(core.NewEVMTxContext(msg)) result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp) if err != nil { txErr := txValidationError(err) diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index 55f37aff6a..173ae9b061 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -349,7 +349,7 @@ func (b *backendMock) GetTd(ctx context.Context, hash common.Hash) *big.Int { return nil } -func (b *backendMock) GetEVM(context.Context, *core.Message, *state.StateDB, *tradingstate.TradingStateDB, *types.Header, *vm.Config, *vm.BlockContext) (*vm.EVM, func() error, error) { +func (b *backendMock) GetEVM(context.Context, *state.StateDB, *tradingstate.TradingStateDB, *types.Header, *vm.Config, *vm.BlockContext) (*vm.EVM, func() error, error) { return nil, nil, nil } diff --git a/miner/worker.go b/miner/worker.go index 415b13901d..cf2f2604ec 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -86,14 +86,15 @@ type Agent interface { type Work struct { config *params.ChainConfig signer types.Signer + state *state.StateDB // apply state changes here + tcount int // tx count in cycle + evm *vm.EVM - state *state.StateDB // apply state changes here parentState *state.StateDB tradingState *tradingstate.TradingStateDB lendingState *lendingstate.LendingStateDB ancestors mapset.Set[common.Hash] // ancestor set (used for checking uncle parent validity) family mapset.Set[common.Hash] // family set (used for checking uncle invalidity) - tcount int // tx count in cycle Block *types.Block // the new block @@ -636,6 +637,7 @@ func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error { ancestors: mapset.NewSet[common.Hash](), family: mapset.NewSet[common.Hash](), header: header, + evm: vm.NewEVM(core.NewEVMBlockContext(header, w.chain, &header.Coinbase), state, XDCxState, w.config, vm.Config{}), uncles: make(map[common.Hash]*types.Header), createdAt: time.Now(), } @@ -788,9 +790,7 @@ func (w *worker) commitNewWork() { work.state.DeleteAddress(common.BlockSignersBinary) } if w.config.IsPrague(header.Number) { - context := core.NewEVMBlockContext(header, w.chain, nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, w.current.state, nil, w.config, vm.Config{}) - core.ProcessParentBlockHash(header.ParentHash, vmenv, w.current.state) + core.ProcessParentBlockHash(header.ParentHash, work.evm, work.state) } // won't grasp txs at checkpoint var ( @@ -1049,7 +1049,7 @@ func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Addr log.Trace("Skipping account with special transaction invalid nonce", "sender", from, "nonce", nonce, "tx nonce ", tx.Nonce(), "to", to) continue } - logs, tokenFeeUsed, gas, err := w.commitTransaction(balanceFee, tx, bc, coinbase, gp) + logs, tokenFeeUsed, gas, err := w.commitTransaction(balanceFee, tx, gp) switch { case errors.Is(err, core.ErrNonceTooLow): // New head notification data race between the transaction pool and miner, shift @@ -1159,7 +1159,7 @@ func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Addr txs.Pop() continue } - logs, tokenFeeUsed, gas, err := w.commitTransaction(balanceFee, tx, bc, coinbase, gp) + logs, tokenFeeUsed, gas, err := w.commitTransaction(balanceFee, tx, gp) switch { case errors.Is(err, core.ErrGasLimitReached): // Pop the current out-of-gas transaction without shifting in the next from the account @@ -1222,10 +1222,10 @@ func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Addr } } -func (w *Work) commitTransaction(balanceFee map[common.Address]*big.Int, tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) ([]*types.Log, bool, uint64, error) { +func (w *Work) commitTransaction(balanceFee map[common.Address]*big.Int, tx *types.Transaction, gp *core.GasPool) ([]*types.Log, bool, uint64, error) { snap := w.state.Snapshot() - receipt, gas, tokenFeeUsed, err := core.ApplyTransaction(w.config, balanceFee, bc, &coinbase, gp, w.state, w.tradingState, w.header, tx, &w.header.GasUsed, vm.Config{}) + receipt, gas, tokenFeeUsed, err := core.ApplyTransaction(w.config, balanceFee, w.evm, gp, w.state, w.header, tx, &w.header.GasUsed) if err != nil { w.state.RevertToSnapshot(snap) return nil, false, 0, err diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 350e18556a..8b7535cb88 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -182,7 +182,8 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) context.GetHash = vmTestBlockHash context.BaseFee = baseFee - evm := vm.NewEVM(context, txContext, statedb, nil, config, vmconfig) + evm := vm.NewEVM(context, statedb, nil, config, vmconfig) + evm.SetTxContext(txContext) // Execute the message. snapshot := statedb.Snapshot() diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index 2209fb5629..505f49c21d 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -148,7 +148,9 @@ func (t *VMTest) newEVM(statedb *state.StateDB, vmconfig vm.Config) *vm.EVM { GasLimit: t.json.Env.GasLimit, Difficulty: t.json.Env.Difficulty, } - return vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vmconfig) + evm := vm.NewEVM(context, statedb, nil, params.MainnetChainConfig, vmconfig) + evm.SetTxContext(txContext) + return evm } func vmTestBlockHash(n uint64) common.Hash {