diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 08a7260d264f..ba62d58a700a 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -379,10 +379,12 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.Cal msg.CallMsg.BalanceTokenFee = value } } - evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil) + + txContext := core.NewEVMTxContext(msg) + evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. - vmenv := vm.NewEVM(evmContext, statedb, nil, b.config, vm.Config{}) + vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, b.config, vm.Config{}) gaspool := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} ret, usedGas, failed, err, _ = core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner) diff --git a/core/evm.go b/core/evm.go index bc1c72ce5bc2..48d0aca12239 100644 --- a/core/evm.go +++ b/core/evm.go @@ -26,8 +26,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/crypto" ) -// NewEVMContext creates a new context for use in the EVM. -func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainContext, author *common.Address) vm.Context { +// NewEVMBlockContext creates a new context for use in the EVM. +func NewEVMBlockContext(header *types.Header, chain consensus.ChainContext, author *common.Address) vm.BlockContext { // If we don't have an explicit author (i.e. not mining), extract from the header var ( beneficiary common.Address @@ -40,21 +40,27 @@ func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainConte } // since xdpos chain do not use difficulty and mixdigest, we use hash of the block number as random random = crypto.Keccak256Hash(header.Number.Bytes()) - return vm.Context{ + return vm.BlockContext{ CanTransfer: CanTransfer, Transfer: Transfer, GetHash: GetHashFn(header, chain), - Origin: msg.From(), Coinbase: beneficiary, BlockNumber: new(big.Int).Set(header.Number), Time: new(big.Int).Set(header.Time), Difficulty: new(big.Int).Set(header.Difficulty), GasLimit: header.GasLimit, - GasPrice: new(big.Int).Set(msg.GasPrice()), Random: &random, } } +// NewEVMTxContext creates a new transaction context for a single transaction. +func NewEVMTxContext(msg Message) vm.TxContext { + return vm.TxContext{ + Origin: msg.From(), + GasPrice: new(big.Int).Set(msg.GasPrice()), + } +} + // GetHashFn returns a GetHashFunc which retrieves header hashes by number func GetHashFn(ref *types.Header, chain consensus.ChainContext) func(n uint64) common.Hash { // Cache will initially contain [refHash.parent], diff --git a/core/state_processor.go b/core/state_processor.go index e4bab6797bc1..292538c39d7d 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -86,6 +86,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra InitSignerInTransactions(p.config, header, block.Transactions()) balanceUpdated := map[common.Address]*big.Int{} totalFeeUsed := big.NewInt(0) + blockContext := NewEVMBlockContext(header, p.bc, nil) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg) + // Iterate over and process the individual transactions for i, tx := range block.Transactions() { // check black-list txs after hf if (block.Number().Uint64() >= common.BlackListHFNumber) && !common.IsTestnet { @@ -113,7 +116,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra } } statedb.Prepare(tx.Hash(), block.Hash(), i) - receipt, gas, err, tokenFeeUsed := ApplyTransaction(p.config, balanceFee, p.bc, nil, gp, statedb, tradingState, header, tx, usedGas, cfg) + receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, p.bc, nil, gp, statedb, tradingState, header, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, err } @@ -159,6 +162,8 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated if cBlock.stop { return nil, nil, 0, ErrStopPreparingBlock } + blockContext := NewEVMBlockContext(header, p.bc, nil) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg) // Iterate over and process the individual transactions receipts = make([]*types.Receipt, block.Transactions().Len()) for i, tx := range block.Transactions() { @@ -188,7 +193,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated } } statedb.Prepare(tx.Hash(), block.Hash(), i) - receipt, gas, err, tokenFeeUsed := ApplyTransaction(p.config, balanceFee, p.bc, nil, gp, statedb, tradingState, header, tx, usedGas, cfg) + receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, p.bc, nil, gp, statedb, tradingState, header, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, err } @@ -210,11 +215,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated return receipts, allLogs, *usedGas, nil } -// ApplyTransaction attempts to apply a transaction to the given state database -// 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, error, bool) { +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, evm *vm.EVM) (*types.Receipt, uint64, error, bool) { to := tx.To() if to != nil && *to == common.BlockSignersBinary && config.IsTIPSigning(header.Number) { return ApplySignTransaction(config, statedb, header, tx, usedGas) @@ -243,11 +244,12 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* if err != nil { return nil, 0, err, false } - // Create a new context to be used in the EVM environment. - context := NewEVMContext(msg, header, bc, author) - // Create a new environment which holds all relevant information - // about the transaction and calling mechanisms. - vmenv := vm.NewEVM(context, statedb, XDCxState, config, cfg) + + // Create a new context to be used in the EVM environment + txContext := NewEVMTxContext(msg) + + // Update the evm with the new transaction context. + evm.Reset(txContext, statedb) // If we don't have an explicit author (i.e. not mining), extract from the header var beneficiary common.Address @@ -404,7 +406,7 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* // End Bypass blacklist address // Apply the transaction to the current state (included in the env) - _, gas, failed, err, _ := ApplyMessage(vmenv, msg, gp, coinbaseOwner) + _, gas, failed, err, _ := ApplyMessage(evm, msg, gp, coinbaseOwner) if err != nil { return nil, 0, err, false @@ -432,7 +434,7 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* // If the transaction created a contract, store the creation address in the receipt. if msg.To() == nil { - receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce()) + receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) } // Set the receipt logs and create the bloom filter. @@ -447,6 +449,17 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* return receipt, gas, err, balanceFee != nil } +// ApplyTransaction attempts to apply a transaction to the given state database +// 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, error, bool) { + // 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) + return applyTransaction(config, tokensFee, bc, author, gp, statedb, XDCxState, header, tx , usedGas, vmenv) +} + func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error, bool) { // Update the state with pending changes var root []byte diff --git a/core/state_transition.go b/core/state_transition.go index 3e1120ceb986..591f2b423a02 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -216,16 +216,37 @@ func (st *StateTransition) preCheck() error { } // TransitionDb will transition the state by applying the current message and -// returning the result including the the used gas. It returns an error if it -// failed. An error indicates a consensus issue. +// 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 +// +// However if any consensus issue encountered, return the error directly with +// nil evm execution result. func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedGas uint64, failed bool, err error, vmErr error) { + // First check this message satisfies all consensus rules before + // applying the message. The rules include these clauses + // + // 1. the nonce of the message caller is correct + // 2. caller has enough balance to cover transaction fee(gaslimit * gasprice) + // 3. the amount of gas required is available in the block + // 4. the purchased gas is enough to cover intrinsic usage + // 5. there is no overflow when calculating intrinsic gas + // 6. caller has enough balance to cover asset transfer for **topmost** call + + // Check clauses 1-3, buy gas if everything is correct if err = st.preCheck(); err != nil { return } msg := st.msg sender := st.from() // err checked in preCheck - homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber) + homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber) contractCreation := msg.To() == nil // Pay intrinsic gas @@ -274,12 +295,12 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG } st.refundGas() - if st.evm.BlockNumber.Cmp(common.TIPTRC21Fee) > 0 { + if st.evm.Context.BlockNumber.Cmp(common.TIPTRC21Fee) > 0 { if (owner != common.Address{}) { st.state.AddBalance(owner, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) } } else { - st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) + st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) } return ret, st.gasUsed(), vmerr != nil, nil, vmerr diff --git a/core/token_validator.go b/core/token_validator.go index f604be504f35..59a4faa5a432 100644 --- a/core/token_validator.go +++ b/core/token_validator.go @@ -108,10 +108,11 @@ func CallContractWithState(call ethereum.CallMsg, chain consensus.ChainContext, msg.CallMsg.BalanceTokenFee = value } } - evmContext := NewEVMContext(msg, chain.CurrentHeader(), chain, nil) + txContext := NewEVMTxContext(msg) + evmContext := NewEVMBlockContext(chain.CurrentHeader(), chain, nil) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. - vmenv := vm.NewEVM(evmContext, statedb, nil, chain.Config(), vm.Config{}) + vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, chain.Config(), vm.Config{}) gaspool := new(GasPool).AddGas(1000000) owner := common.Address{} rval, _, _, err, _ := NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner) diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index 4d8a4c762fc1..1c0b1e18474e 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -519,7 +519,7 @@ func testXDCxPrecompiled(addr string, test precompiledTest, t *testing.T) { tradingStateDB.SetLastPrice(tradingstate.GetTradingOrderBookHash(common.HexToAddress(BTCAddress), common.HexToAddress(USDTAddress)), BTCUSDTLastPrice) tradingStateDB.SetMediumPriceBeforeEpoch(tradingstate.GetTradingOrderBookHash(common.HexToAddress(BTCAddress), common.HexToAddress(USDTAddress)), BTCUSDTEpochPrice) - evm := NewEVM(Context{BlockNumber: common.Big1}, nil, tradingStateDB, ¶ms.ChainConfig{ByzantiumBlock: common.Big0}, Config{}) + evm := NewEVM(BlockContext{BlockNumber: common.Big1}, TxContext{}, nil, tradingStateDB, ¶ms.ChainConfig{ByzantiumBlock: common.Big0}, Config{}) contractAddr := common.HexToAddress(addr) p := PrecompiledContractsByzantium[contractAddr] in := common.Hex2Bytes(test.input) @@ -536,7 +536,7 @@ func testXDCxPrecompiled(addr string, test precompiledTest, t *testing.T) { } func testPrecompiledWithEmptyTradingState(addr string, test precompiledTest, t *testing.T) { - evm := NewEVM(Context{BlockNumber: common.Big1}, nil, nil, ¶ms.ChainConfig{ByzantiumBlock: common.Big0}, Config{}) + evm := NewEVM(BlockContext{BlockNumber: common.Big1}, TxContext{}, nil, nil, ¶ms.ChainConfig{ByzantiumBlock: common.Big0}, Config{}) contractAddr := common.HexToAddress(addr) p := PrecompiledContractsByzantium[contractAddr] diff --git a/core/vm/evm.go b/core/vm/evm.go index 4f61fc3c80a0..c84d905b8d32 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -63,7 +63,7 @@ func (evm *EVM) precompile2(addr common.Address) (PrecompiledContract, bool) { switch { case evm.chainRules.IsXDCxDisable: precompiles = PrecompiledContractsXDCv2 - case evm.chainRules.IsIstanbul && evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber): + case evm.chainRules.IsIstanbul && evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber): precompiles = PrecompiledContractsIstanbul case evm.chainRules.IsByzantium: precompiles = PrecompiledContractsByzantium @@ -78,7 +78,7 @@ func (evm *EVM) precompile2(addr common.Address) (PrecompiledContract, bool) { func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) { if contract.CodeAddr != nil { if p, isPrecompile := evm.precompile(*contract.CodeAddr); isPrecompile { - if evm.chainConfig.IsTIPXDCXReceiver(evm.BlockNumber) { + if evm.chainConfig.IsTIPXDCXReceiver(evm.Context.BlockNumber) { switch p := p.(type) { case *XDCxEpochPrice: p.SetTradingState(evm.tradingStateDB) @@ -89,7 +89,7 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err return RunPrecompiledContract(p, input, contract) } } - if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber) { + if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber) { for _, interpreter := range evm.interpreters { if interpreter.CanRun(contract.Code) { if evm.interpreter != interpreter { @@ -110,9 +110,9 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err return nil, errors.New("no compatible interpreter") } -// Context provides the EVM with auxiliary information. Once provided +// BlockContext provides the EVM with auxiliary information. Once provided // it shouldn't be modified. -type Context struct { +type BlockContext struct { // CanTransfer returns whether the account contains // sufficient ether to transfer the value CanTransfer CanTransferFunc @@ -121,10 +121,6 @@ type Context struct { // GetHash returns the hash corresponding to n GetHash GetHashFunc - // Message information - Origin common.Address // Provides information for ORIGIN - GasPrice *big.Int // Provides information for GASPRICE - // Block information Coinbase common.Address // Provides information for COINBASE GasLimit uint64 // Provides information for GASLIMIT @@ -134,6 +130,14 @@ type Context struct { Random *common.Hash // Provides information for PREVRANDAO } +// TxContext provides the EVM with information about a transaction. +// All fields can change between transactions. +type TxContext struct { + // Message information + Origin common.Address // Provides information for ORIGIN + GasPrice *big.Int // Provides information for GASPRICE +} + // EVM is the Ethereum Virtual Machine base object and provides // the necessary tools to run a contract on the given state with // the provided context. It should be noted that any error @@ -144,8 +148,9 @@ type Context struct { // // The EVM should never be reused and is not thread safe. type EVM struct { - // Context provides auxiliary blockchain related information - Context + // BlockContext provides auxiliary blockchain related information + Context BlockContext + TxContext // StateDB gives access to the underlying state StateDB StateDB @@ -176,14 +181,15 @@ type EVM struct { // NewEVM returns a new EVM. The returned EVM is not thread safe and should // only ever be used *once*. -func NewEVM(ctx Context, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { +func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { evm := &EVM{ - Context: ctx, + Context: blockCtx, + TxContext: txCtx, StateDB: statedb, tradingStateDB: tradingStateDB, vmConfig: vmConfig, chainConfig: chainConfig, - chainRules: chainConfig.Rules(ctx.BlockNumber), + chainRules: chainConfig.Rules(blockCtx.BlockNumber), interpreters: make([]Interpreter, 0, 1), } @@ -195,6 +201,13 @@ func NewEVM(ctx Context, statedb StateDB, tradingStateDB *tradingstate.TradingSt return evm } +// Reset resets the EVM with a new transaction context.Reset +// This is not threadsafe and should only be done very cautiously. +func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) { + evm.TxContext = txCtx + evm.StateDB = statedb +} + // Cancel cancels any running EVM operation. This may be called concurrently and // it's safe to be called multiple times. func (evm *EVM) Cancel() { @@ -245,7 +258,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } evm.StateDB.CreateAccount(addr) } - evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) + evm.Context.Transfer(evm.StateDB, caller.Address(), to.Address(), value) // Capture the tracer start/end events in debug mode if evm.vmConfig.Debug { @@ -390,7 +403,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte contract := NewContract(caller, to, new(big.Int), gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber) { + if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber) { // We do an AddBalance of zero here, just in order to trigger a touch. // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, // but is the correct thing to do and matters on other networks, in tests, and potential @@ -409,7 +422,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in Homestead this also counts for code storage gas errors. - ret, err = run(evm, contract, input, evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber)) + ret, err = run(evm, contract, input, evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber)) gas = contract.Gas if err != nil { evm.StateDB.RevertToSnapshot(snapshot) @@ -439,7 +452,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.depth > int(params.CallCreateDepth) { return nil, common.Address{}, gas, ErrDepth } - if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { + if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, common.Address{}, gas, ErrInsufficientBalance } nonce := evm.StateDB.GetNonce(caller.Address()) @@ -463,7 +476,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.chainRules.IsEIP158 { evm.StateDB.SetNonce(address, 1) } - evm.Transfer(evm.StateDB, caller.Address(), address, value) + evm.Context.Transfer(evm.StateDB, caller.Address(), address, value) // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index 501a0364c65b..c9c7e9244dd1 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -88,11 +88,11 @@ func TestEIP2200(t *testing.T) { statedb.SetState(address, common.Hash{}, common.BytesToHash([]byte{tt.original})) statedb.Finalise(true) // Push the state into the "original" slot - vmctx := Context{ + vmctx := BlockContext{ CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, } - vmenv := NewEVM(vmctx, statedb, nil, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) + vmenv := NewEVM(vmctx, TxContext{}, statedb, nil, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int)) if err != tt.failure { diff --git a/core/vm/instructions.go b/core/vm/instructions.go index b2bf296c1faf..e5d51fe30eb0 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -447,14 +447,14 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( return nil, nil } var upper, lower uint64 - upper = interpreter.evm.BlockNumber.Uint64() + upper = interpreter.evm.Context.BlockNumber.Uint64() if upper < 257 { lower = 0 } else { lower = upper - 256 } if num64 >= lower && num64 < upper { - num.SetBytes(interpreter.evm.GetHash(num64).Bytes()) + num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes()) } else { num.Clear() } @@ -462,24 +462,24 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( } func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes())) + scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes())) return nil, nil } func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - v, _ := uint256.FromBig(interpreter.evm.Time) + v, _ := uint256.FromBig(interpreter.evm.Context.Time) scope.Stack.push(v) return nil, nil } func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - v, _ := uint256.FromBig(interpreter.evm.BlockNumber) + v, _ := uint256.FromBig(interpreter.evm.Context.BlockNumber) scope.Stack.push(v) return nil, nil } func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - v, _ := uint256.FromBig(interpreter.evm.Difficulty) + v, _ := uint256.FromBig(interpreter.evm.Context.Difficulty) scope.Stack.push(v) return nil, nil } @@ -496,7 +496,7 @@ func opRandom(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit)) + scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit)) return nil, nil } @@ -851,7 +851,7 @@ func makeLog(size int) executionFunc { Data: d, // This is a non-consensus field, but assigned here because // core/state doesn't know the current block number. - BlockNumber: interpreter.evm.BlockNumber.Uint64(), + BlockNumber: interpreter.evm.Context.BlockNumber.Uint64(), }) return nil, nil diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 580a008b4e72..c4a631729382 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -95,7 +95,7 @@ func init() { func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) { var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) evmInterpreter = env.interpreter.(*EVMInterpreter) @@ -194,7 +194,7 @@ func TestSAR(t *testing.T) { func TestAddMod(t *testing.T) { var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) pc = uint64(0) @@ -233,7 +233,7 @@ func TestAddMod(t *testing.T) { // getResult is a convenience function to generate the expected values func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) interpreter = env.interpreter.(*EVMInterpreter) @@ -289,7 +289,7 @@ func TestJsonTestcases(t *testing.T) { func opBenchmark(bench *testing.B, op executionFunc, args ...string) { var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) @@ -523,7 +523,7 @@ func BenchmarkOpIsZero(b *testing.B) { func TestOpMstore(t *testing.T) { var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) @@ -549,7 +549,7 @@ func TestOpMstore(t *testing.T) { func BenchmarkOpMstore(bench *testing.B) { var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) @@ -571,7 +571,7 @@ func BenchmarkOpMstore(bench *testing.B) { func BenchmarkOpKeccak256(bench *testing.B) { var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) @@ -676,7 +676,7 @@ func TestRandom(t *testing.T) { {name: "hash(0x010203)", random: crypto.Keccak256Hash([]byte{0x01, 0x02, 0x03})}, } { var ( - env = NewEVM(Context{Random: &tt.random}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{Random: &tt.random}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) evmInterpreter = NewEVMInterpreter(env, env.vmConfig) diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index f668d068fb75..108656fee11c 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -50,7 +50,7 @@ func (*dummyStatedb) GetRefund() uint64 { return 1337 } func TestStoreCapture(t *testing.T) { var ( - env = NewEVM(Context{}, &dummyStatedb{}, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, &dummyStatedb{}, nil, params.TestChainConfig, Config{}) logger = NewStructLogger(nil) contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) scope = &ScopeContext{ diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 24d3a7930182..89d44ad83311 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -22,18 +22,20 @@ import ( ) func NewEnv(cfg *Config) *vm.EVM { - context := vm.Context{ + txContext := vm.TxContext{ + Origin: cfg.Origin, + GasPrice: cfg.GasPrice, + } + blockContext := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, GetHash: cfg.GetHashFn, - Origin: cfg.Origin, Coinbase: cfg.Coinbase, BlockNumber: cfg.BlockNumber, Time: cfg.Time, Difficulty: cfg.Difficulty, GasLimit: cfg.GasLimit, - GasPrice: cfg.GasPrice, } - return vm.NewEVM(context, cfg.State, nil, cfg.ChainConfig, cfg.EVMConfig) + return vm.NewEVM(blockContext, txContext, cfg.State, nil, cfg.ChainConfig, cfg.EVMConfig) } diff --git a/eth/api_backend.go b/eth/api_backend.go index f9a585ba5e7f..98220fd1118f 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -258,8 +258,9 @@ func (b *EthApiBackend) GetEVM(ctx context.Context, msg core.Message, state *sta vmConfig = b.eth.blockchain.GetVMConfig() } state.SetBalance(msg.From(), math.MaxBig256) - context := core.NewEVMContext(msg, header, b.eth.BlockChain(), nil) - return vm.NewEVM(context, state, XDCxState, b.eth.chainConfig, *vmConfig), vmError, nil + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil) + return vm.NewEVM(context, txContext, state, XDCxState, b.eth.chainConfig, *vmConfig), vmError, nil } func (b *EthApiBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { diff --git a/eth/api_tracer.go b/eth/api_tracer.go index d0f5e8f14b63..3277dd7e0870 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -232,6 +232,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl // Fetch and execute the next block trace tasks for task := range tasks { signer := types.MakeSigner(api.config, task.block.Number()) + blockCtx := core.NewEVMBlockContext(task.block.Header(), api.eth.blockchain, nil) feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb) // Trace all the transactions contained within for i, tx := range task.block.Transactions() { @@ -247,9 +248,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl TxIndex: i, TxHash: tx.Hash(), } - vmctx := core.NewEVMContext(msg, task.block.Header(), api.eth.blockchain, nil) - - res, err := api.traceTx(ctx, msg, txctx, vmctx, task.statedb, config) + res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config) if err != nil { task.results[i] = &txTraceResult{Error: err.Error()} log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) @@ -473,11 +472,11 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, threads = len(txs) } blockHash := block.Hash() + blockCtx := core.NewEVMBlockContext(block.Header(), api.eth.blockchain, nil) for th := 0; th < threads; th++ { pend.Add(1) go func() { defer pend.Done() - // Fetch and execute the next transaction trace tasks for task := range jobs { feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb) @@ -493,9 +492,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, TxIndex: task.index, TxHash: txs[task.index].Hash(), } - vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) - - res, err := api.traceTx(ctx, msg, txctx, vmctx, task.statedb, config) + res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config) if err != nil { results[task.index] = &txTraceResult{Error: err.Error()} continue @@ -522,10 +519,10 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, } // Generate the next state snapshot fast without tracing msg, _ := tx.AsMessage(signer, balacne, block.Number()) + txContext := core.NewEVMTxContext(msg) statedb.Prepare(tx.Hash(), block.Hash(), i) - vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) - vmenv := vm.NewEVM(vmctx, statedb, XDCxState, api.config, vm.Config{}) + vmenv := vm.NewEVM(blockCtx, txContext, statedb, XDCxState, api.config, vm.Config{}) owner := common.Address{} if _, _, _, err, _ := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner); err != nil { failed = err @@ -693,7 +690,7 @@ func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.Transacti } // Execute the trace msg := args.ToMessage(api.eth.ApiBackend, block.Number(), api.eth.ApiBackend.RPCGasCap()) - vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) + vmctx := core.NewEVMBlockContext(block.Header(), api.eth.blockchain, nil) var traceConfig *TraceConfig if config != nil { traceConfig = &config.TraceConfig @@ -704,11 +701,12 @@ func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.Transacti // traceTx configures a new tracer according to the provided configuration, and // executes the given message in the provided environment. The return value will // be tracer dependent. -func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, txctx *tracers.Context, vmctx vm.Context, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { +func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, txctx *tracers.Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { // Assemble the structured logger or the JavaScript tracer var ( - tracer vm.EVMLogger - err error + tracer vm.EVMLogger + err error + txContext = core.NewEVMTxContext(message) ) switch { case config == nil: @@ -739,7 +737,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t tracer = vm.NewStructLogger(config.LogConfig) } // Run the transaction with tracing enabled. - vmenv := vm.NewEVM(vmctx, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer}) + vmenv := vm.NewEVM(vmctx, txContext, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer}) // Call Prepare to clear out the statedb access list statedb.Prepare(txctx.TxHash, txctx.BlockHash, txctx.TxIndex) @@ -768,19 +766,19 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t } // computeTxEnv returns the execution environment of a certain transaction. -func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, reexec uint64) (core.Message, vm.Context, *state.StateDB, error) { +func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { // Create the parent state database block := api.eth.blockchain.GetBlockByHash(blockHash) if block == nil { - return nil, vm.Context{}, nil, fmt.Errorf("block %x not found", blockHash) + return nil, vm.BlockContext{}, nil, fmt.Errorf("block %x not found", blockHash) } parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) if parent == nil { - return nil, vm.Context{}, nil, fmt.Errorf("parent %x not found", block.ParentHash()) + return nil, vm.BlockContext{}, nil, fmt.Errorf("parent %x not found", block.ParentHash()) } statedb, XDCxState, err := api.computeStateDB(parent, reexec) if err != nil { - return nil, vm.Context{}, nil, err + return nil, vm.BlockContext{}, nil, err } // Recompute transactions up to the target index. feeCapacity := state.GetTRC21FeeCapacityFromState(statedb) @@ -804,14 +802,14 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree } msg, err := tx.AsMessage(types.MakeSigner(api.config, block.Header().Number), balanceFee, block.Number()) if err != nil { - return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err) + return nil, vm.BlockContext{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err) } - context := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) + context := core.NewEVMBlockContext(block.Header(), api.eth.blockchain, nil) return msg, context, statedb, nil } _, gas, err, tokenFeeUsed := core.ApplyTransaction(api.config, feeCapacity, api.eth.blockchain, nil, gp, statedb, XDCxState, block.Header(), tx, usedGas, vm.Config{}) if err != nil { - return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err) + return nil, vm.BlockContext{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err) } if tokenFeeUsed { @@ -822,5 +820,5 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree } } statedb.DeleteSuicides() - return nil, vm.Context{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash) + return nil, vm.BlockContext{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash) } diff --git a/eth/tracers/testing/calltrace_test.go b/eth/tracers/testing/calltrace_test.go index 9db470251927..718231e79559 100644 --- a/eth/tracers/testing/calltrace_test.go +++ b/eth/tracers/testing/calltrace_test.go @@ -95,7 +95,11 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { var ( signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number))) origin, _ = signer.Sender(tx) - context = vm.Context{ + txContext = vm.TxContext{ + Origin: origin, + GasPrice: tx.GasPrice(), + } + context = vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, Coinbase: test.Context.Miner, @@ -103,8 +107,6 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { Time: new(big.Int).SetUint64(uint64(test.Context.Time)), Difficulty: (*big.Int)(test.Context.Difficulty), GasLimit: uint64(test.Context.GasLimit), - Origin: origin, - GasPrice: tx.GasPrice(), } statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc) ) @@ -112,7 +114,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) msg, err := tx.AsMessage(signer, nil, nil) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) @@ -204,7 +206,11 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { b.Fatalf("failed to prepare transaction for tracing: %v", err) } origin, _ := signer.Sender(tx) - context := vm.Context{ + txContext := vm.TxContext{ + Origin: origin, + GasPrice: tx.GasPrice(), + } + context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, Coinbase: test.Context.Miner, @@ -212,8 +218,6 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { Time: new(big.Int).SetUint64(uint64(test.Context.Time)), Difficulty: (*big.Int)(test.Context.Difficulty), GasLimit: uint64(test.Context.GasLimit), - Origin: origin, - GasPrice: tx.GasPrice(), } statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc) @@ -224,7 +228,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { if err != nil { b.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) snap := statedb.Snapshot() st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if _, _, _, err, _ = st.TransitionDb(common.Address{}); err != nil { diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go index bbec5ad8a952..9e27a04dade2 100644 --- a/eth/tracers/tracer.go +++ b/eth/tracers/tracer.go @@ -670,7 +670,7 @@ func (jst *JsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad jst.ctx["to"] = to jst.ctx["input"] = input jst.ctx["gas"] = gas - jst.ctx["gasPrice"] = env.Context.GasPrice + jst.ctx["gasPrice"] = env.TxContext.GasPrice jst.ctx["value"] = value // Initialize the context diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go index d1b81cfbc6e1..7721dbed303a 100644 --- a/eth/tracers/tracer_test.go +++ b/eth/tracers/tracer_test.go @@ -51,19 +51,20 @@ func (*dummyStatedb) GetRefund() uint64 { return 1337 } func (*dummyStatedb) GetBalance(addr common.Address) *big.Int { return new(big.Int) } type vmContext struct { - ctx vm.Context // future pr should distinguish blockContext and txContext + ctx vm.BlockContext + txContext vm.TxContext } -func testCtx() *vmContext { - return &vmContext{ctx: vm.Context{BlockNumber: big.NewInt(1), GasPrice: big.NewInt(100000)}} -} - -func runTrace(tracer Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) { - env := vm.NewEVM(vmctx.ctx, &dummyStatedb{}, nil, chaincfg, vm.Config{Debug: true, Tracer: tracer}) +func runTrace(tracer Tracer, blockNumber *big.Int, chaincfg *params.ChainConfig) (json.RawMessage, error) { var ( - startGas uint64 = 10000 - value = big.NewInt(0) + startGas uint64 = 10000 + value = big.NewInt(0) + ctx = vm.BlockContext{BlockNumber: blockNumber} + txContext = vm.TxContext{GasPrice: big.NewInt(100000)} ) + + env := vm.NewEVM(ctx, txContext, &dummyStatedb{}, nil, chaincfg, vm.Config{Debug: true, Tracer: tracer}) + contract := vm.NewContract(account{}, account{}, value, startGas) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} @@ -83,7 +84,7 @@ func TestTracer(t *testing.T) { if err != nil { t.Fatal(err) } - ret, err := runTrace(tracer, testCtx(), params.TestChainConfig) + ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig) if err != nil { return nil, err.Error() // Stringify to allow comparison without nil checks } @@ -138,7 +139,7 @@ func TestHalt(t *testing.T) { time.Sleep(1 * time.Second) tracer.Stop(timeout) }() - if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err.Error() != "stahp in server-side tracer function 'step'" { + if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err.Error() != "stahp in server-side tracer function 'step'" { t.Errorf("Expected timeout error, got %v", err) } } @@ -148,7 +149,7 @@ func TestHaltBetweenSteps(t *testing.T) { if err != nil { t.Fatal(err) } - env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), } @@ -165,8 +166,10 @@ func TestHaltBetweenSteps(t *testing.T) { // TestNoStepExec tests a regular value transfer (no exec), and accessing the statedb // in 'result' func TestNoStepExec(t *testing.T) { - runEmptyTrace := func(tracer Tracer, vmctx *vmContext) (json.RawMessage, error) { - env := vm.NewEVM(vmctx.ctx, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) + runEmptyTrace := func(tracer Tracer) (json.RawMessage, error) { + ctx := vm.BlockContext{BlockNumber: big.NewInt(1)} + txContext := vm.TxContext{GasPrice: big.NewInt(100000)} + env := vm.NewEVM(ctx, txContext, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) startGas := uint64(10000) contract := vm.NewContract(account{}, account{}, big.NewInt(0), startGas) tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, big.NewInt(0)) @@ -179,7 +182,7 @@ func TestNoStepExec(t *testing.T) { if err != nil { t.Fatal(err) } - ret, err := runEmptyTrace(tracer, testCtx()) + ret, err := runEmptyTrace(tracer) if err != nil { t.Fatal(err) } @@ -205,12 +208,11 @@ func TestIsPrecompile(t *testing.T) { chaincfg.ByzantiumBlock = big.NewInt(100) chaincfg.IstanbulBlock = big.NewInt(200) chaincfg.BerlinBlock = big.NewInt(300) - ctx := vm.Context{BlockNumber: big.NewInt(150), GasPrice: big.NewInt(100000)} tracer, err := New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context)) if err != nil { t.Fatal(err) } - res, err := runTrace(tracer, &vmContext{ctx}, chaincfg) + res, err := runTrace(tracer, big.NewInt(150), chaincfg) if err != nil { t.Error(err) } @@ -219,8 +221,7 @@ func TestIsPrecompile(t *testing.T) { } tracer, _ = New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context)) - ctx = vm.Context{BlockNumber: big.NewInt(250), GasPrice: big.NewInt(100000)} - res, err = runTrace(tracer, &vmContext{ctx}, chaincfg) + res, err = runTrace(tracer, big.NewInt(250), chaincfg) if err != nil { t.Error(err) } @@ -267,7 +268,7 @@ func TestRegressionPanicSlice(t *testing.T) { if err != nil { t.Fatal(err) } - if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err != nil { + if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err != nil { t.Fatal(err) } } @@ -278,7 +279,7 @@ func TestRegressionPanicPeek(t *testing.T) { if err != nil { t.Fatal(err) } - if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err != nil { + if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err != nil { t.Fatal(err) } } @@ -289,7 +290,7 @@ func TestRegressionPanicGetUint(t *testing.T) { if err != nil { t.Fatal(err) } - if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err != nil { + if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err != nil { t.Fatal(err) } } @@ -300,7 +301,7 @@ func TestTracing(t *testing.T) { t.Fatal(err) } - ret, err := runTrace(tracer, testCtx(), params.TestChainConfig) + ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig) if err != nil { t.Fatal(err) } @@ -315,7 +316,7 @@ func TestStack(t *testing.T) { t.Fatal(err) } - ret, err := runTrace(tracer, testCtx(), params.TestChainConfig) + ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig) if err != nil { t.Fatal(err) } @@ -330,7 +331,7 @@ func TestOpcodes(t *testing.T) { t.Fatal(err) } - ret, err := runTrace(tracer, testCtx(), params.TestChainConfig) + ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig) if err != nil { t.Fatal(err) } diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 74c3ff284539..a0e17e7c538f 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -118,7 +118,11 @@ func TestZeroValueToNotExitCall(t *testing.T) { t.Fatalf("err %v", err) } origin, _ := signer.Sender(tx) - context := vm.Context{ + txContext := vm.TxContext{ + Origin: origin, + GasPrice: big.NewInt(1), + } + context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, Coinbase: common.Address{}, @@ -126,8 +130,6 @@ func TestZeroValueToNotExitCall(t *testing.T) { Time: new(big.Int).SetUint64(5), Difficulty: big.NewInt(0x30000), GasLimit: uint64(6000000), - Origin: origin, - GasPrice: big.NewInt(1), } var code = []byte{ byte(vm.PUSH1), 0x0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), // in and outs zero @@ -150,7 +152,7 @@ func TestZeroValueToNotExitCall(t *testing.T) { if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) msg, err := tx.AsMessage(signer, nil, nil) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) @@ -200,16 +202,18 @@ func TestPrestateTracerCreate2(t *testing.T) { result: 0x60f3f640a8508fC6a86d45DF051962668E1e8AC7 */ origin, _ := signer.Sender(tx) - context := vm.Context{ + txContext := vm.TxContext{ + Origin: origin, + GasPrice: big.NewInt(1), + } + context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, - Origin: origin, Coinbase: common.Address{}, BlockNumber: new(big.Int).SetUint64(8000000), Time: new(big.Int).SetUint64(5), Difficulty: big.NewInt(0x30000), GasLimit: uint64(6000000), - GasPrice: big.NewInt(1), } alloc := core.GenesisAlloc{} @@ -233,7 +237,7 @@ func TestPrestateTracerCreate2(t *testing.T) { if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) msg, err := tx.AsMessage(signer, nil, nil) if err != nil { @@ -291,7 +295,11 @@ func BenchmarkTransactionTrace(b *testing.B) { if err != nil { b.Fatal(err) } - context := vm.Context{ + txContext := vm.TxContext{ + Origin: from, + GasPrice: tx.GasPrice(), + } + context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, Coinbase: common.Address{}, @@ -300,8 +308,6 @@ func BenchmarkTransactionTrace(b *testing.B) { Difficulty: big.NewInt(0xffffffff), GasLimit: gas, // BaseFee: big.NewInt(8), - Origin: from, - GasPrice: tx.GasPrice(), } alloc := core.GenesisAlloc{} // The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns @@ -329,7 +335,7 @@ func BenchmarkTransactionTrace(b *testing.B) { //EnableMemory: false, //EnableReturnData: false, }) - evm := vm.NewEVM(context, statedb, nil, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, nil, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer}) msg, err := tx.AsMessage(signer, nil, nil) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) diff --git a/les/api_backend.go b/les/api_backend.go index 876a1dcbe9f8..c5873dc5dbfe 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -184,8 +184,9 @@ func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *sta vmConfig = new(vm.Config) } state.SetBalance(msg.From(), math.MaxBig256) - context := core.NewEVMContext(msg, header, b.eth.blockchain, nil) - return vm.NewEVM(context, state, XDCxState, b.eth.chainConfig, *vmConfig), state.Error, nil + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(header, b.eth.blockchain, nil) + return vm.NewEVM(context, txContext, state, XDCxState, b.eth.chainConfig, *vmConfig), state.Error, nil } func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { diff --git a/les/odr_test.go b/les/odr_test.go index 1234bd28536b..1495398379a0 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -135,8 +135,9 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai } msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)} - context := core.NewEVMContext(msg, header, bc, nil) - vmenv := vm.NewEVM(context, statedb, nil, config, vm.Config{}) + context := core.NewEVMBlockContext(header, bc, nil) + txContext := core.NewEVMTxContext(msg) + vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{}) //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) @@ -154,8 +155,9 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai balanceTokenFee = value } msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)} - context := core.NewEVMContext(msg, header, lc, nil) - vmenv := vm.NewEVM(context, statedb, nil, config, vm.Config{}) + context := core.NewEVMBlockContext(header, lc, nil) + txContext := core.NewEVMTxContext(msg) + vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) diff --git a/light/odr_test.go b/light/odr_test.go index 83e1c807caf6..c2c22d265712 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -185,8 +185,9 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain balanceTokenFee = value } msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)} - context := core.NewEVMContext(msg, header, chain, nil) - vmenv := vm.NewEVM(context, st, nil, config, vm.Config{}) + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(header, chain, nil) + vmenv := vm.NewEVM(context, txContext, st, nil, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 742d2c595544..afb35c9d02db 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -137,9 +137,10 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD } // Prepare the EVM. - context := core.NewEVMContext(msg, block.Header(), nil, &t.json.Env.Coinbase) + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) context.GetHash = vmTestBlockHash - evm := vm.NewEVM(context, statedb, nil, config, vmconfig) + evm := vm.NewEVM(context, txContext, statedb, nil, config, vmconfig) // Execute the message. snapshot := statedb.Snapshot() diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index b9bdffe48552..0dfa4f634922 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -132,19 +132,21 @@ func (t *VMTest) newEVM(statedb *state.StateDB, vmconfig vm.Config) *vm.EVM { return core.CanTransfer(db, address, amount) } transfer := func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {} - context := vm.Context{ + txContext := vm.TxContext{ + Origin: t.json.Exec.Origin, + GasPrice: t.json.Exec.GasPrice, + } + context := vm.BlockContext{ CanTransfer: canTransfer, Transfer: transfer, GetHash: vmTestBlockHash, - Origin: t.json.Exec.Origin, Coinbase: t.json.Env.Coinbase, BlockNumber: new(big.Int).SetUint64(t.json.Env.Number), Time: new(big.Int).SetUint64(t.json.Env.Timestamp), GasLimit: t.json.Env.GasLimit, Difficulty: t.json.Env.Difficulty, - GasPrice: t.json.Exec.GasPrice, } - return vm.NewEVM(context, statedb, nil, params.MainnetChainConfig, vmconfig) + return vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vmconfig) } func vmTestBlockHash(n uint64) common.Hash {