diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 09288d401e..0f36b3373f 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -272,8 +272,8 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM // about the transaction and calling mechanisms. vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{}) gaspool := new(core.GasPool).AddGas(math.MaxBig256) - ret, gasUsed, _, failed, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() - return ret, gasUsed, failed, err + ret, gasUsed, _, vmerr, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() + return ret, gasUsed, vmerr != nil, err } // SendTransaction updates the pending block to include the given transaction. diff --git a/core/state_processor.go b/core/state_processor.go index 689c83785f..02b5920f2f 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -98,7 +98,7 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common // about the transaction and calling mechanisms. vmenv := vm.NewEVM(context, statedb, config, cfg) // Apply the transaction to the current state (included in the env) - _, gas, failed, err := ApplyMessage(vmenv, msg, gp) + _, gas, vmerr, err := ApplyMessage(vmenv, msg, gp) if err != nil { return nil, nil, err } @@ -114,7 +114,7 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx // based on the eip phase, we're passing wether the root touch-delete accounts. - receipt := types.NewReceipt(root, failed, usedGas) + receipt := types.NewReceipt(root, vmerr != nil, usedGas) receipt.TxHash = tx.Hash() receipt.GasUsed = new(big.Int).Set(gas) // if the transaction created a contract, store the creation address in the receipt. diff --git a/core/state_transition.go b/core/state_transition.go index e7a0685893..2182ac8838 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -124,12 +124,13 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition // ApplyMessage returns the bytes returned by any EVM execution (if it took place), // the gas used (which includes gas refunds) and an error if it failed. An error always // indicates a core error meaning that the message would always fail for that particular -// state and would never be accepted within a block. -func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, bool, error) { +// state and would never be accepted within a block. A VM error indicates an error whilst +// executing the code. +func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error, error) { st := NewStateTransition(evm, msg, gp) - ret, _, gasUsed, failed, err := st.TransitionDb() - return ret, gasUsed, failed, err + ret, _, gasUsed, vmerr, err := st.TransitionDb() + return ret, gasUsed, vmerr, err } func (st *StateTransition) from() vm.AccountRef { @@ -209,7 +210,7 @@ func (st *StateTransition) preCheck() error { // TransitionDb will transition the state by applying the current message and returning the result // including the required gas for the operation as well as the used gas. It returns an error if it // failed. An error indicates a consensus issue. -func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, failed bool, err error) { +func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, vmerr error, err error) { if err = st.preCheck(); err != nil { return } @@ -223,18 +224,14 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big // TODO convert to uint64 intrinsicGas := IntrinsicGas(st.data, contractCreation, homestead) if intrinsicGas.BitLen() > 64 { - return nil, nil, nil, false, vm.ErrOutOfGas + return nil, nil, nil, nil, vm.ErrOutOfGas } if err = st.useGas(intrinsicGas.Uint64()); err != nil { - return nil, nil, nil, false, err + return nil, nil, nil, nil, err } var ( evm = st.evm - // vm errors do not effect consensus and are therefor - // not assigned to err, except for insufficient balance - // error. - vmerr error ) if contractCreation { ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value) @@ -249,7 +246,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big // sufficient balance to make the transfer happen. The first // balance transfer may never fail. if vmerr == vm.ErrInsufficientBalance { - return nil, nil, nil, false, vmerr + return nil, nil, nil, vmerr, vmerr } } requiredGas = new(big.Int).Set(st.gasUsed()) @@ -257,7 +254,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big st.refundGas() st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(st.gasUsed(), st.gasPrice)) - return ret, requiredGas, st.gasUsed(), vmerr != nil, err + return ret, requiredGas, st.gasUsed(), vmerr, err } func (st *StateTransition) refundGas() { diff --git a/eth/api.go b/eth/api.go index e91f51bb99..a3d2e3b839 100644 --- a/eth/api.go +++ b/eth/api.go @@ -523,7 +523,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common. // Run the transaction with tracing enabled. vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{Debug: true, Tracer: tracer}) - ret, gas, failed, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) + ret, gas, vmerr, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { return nil, fmt.Errorf("tracing failed: %v", err) } @@ -531,7 +531,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common. case *vm.StructLogger: return ðapi.ExecutionResult{ Gas: gas, - Failed: failed, + Failed: vmerr != nil, ReturnValue: fmt.Sprintf("%x", ret), StructLogs: ethapi.FormatLogs(tracer.StructLogs()), }, nil diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 59a29d7226..1eab4776fa 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -580,12 +580,12 @@ type CallArgs struct { Data hexutil.Bytes `json:"data"` } -func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, *big.Int, bool, error) { +func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, *big.Int, error, error) { defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr) if state == nil || err != nil { - return nil, common.Big0, false, err + return nil, common.Big0, nil, err } // Set sender address or use a default if none specified addr := args.From @@ -623,7 +623,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr // Get a new instance of the EVM. evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg) if err != nil { - return nil, common.Big0, false, err + return nil, common.Big0, nil, err } // Wait for the context to be done and cancel the evm. Even if the // EVM has finished, cancelling may be done (repeatedly) @@ -635,11 +635,11 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr // Setup the gas pool (also for unmetered requests) // and apply the message. gp := new(core.GasPool).AddGas(math.MaxBig256) - res, gas, failed, err := core.ApplyMessage(evm, msg, gp) + res, gas, vmerr, err := core.ApplyMessage(evm, msg, gp) if err := vmError(); err != nil { - return nil, common.Big0, false, err + return nil, common.Big0, nil, err } - return res, gas, failed, err + return res, gas, vmerr, err } // Call executes the given transaction on the state for the given block number. @@ -673,8 +673,8 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (* // Create a helper to check if a gas allowance results in an executable transaction executable := func(gas uint64) bool { (*big.Int)(&args.Gas).SetUint64(gas) - _, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{}) - if err != nil || failed { + _, _, vmerr, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{}) + if err != nil || vmerr != nil { return false } return true