diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 159fca136e..786db91f5c 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -253,7 +253,7 @@ 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, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() + ret, gasUsed, _, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() return ret, gasUsed, err } diff --git a/core/state_processor.go b/core/state_processor.go index 90f5a4f60c..e09771b8f7 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, err := ApplyMessage(vmenv, msg, gp) + _, gas, reverted, err := ApplyMessage(vmenv, msg, gp) if err != nil { return nil, nil, err } @@ -111,6 +111,7 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common receipt := types.NewReceipt(root.Bytes(), usedGas) receipt.TxHash = tx.Hash() receipt.GasUsed = new(big.Int).Set(gas) + receipt.Reverted = reverted // 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()) diff --git a/core/state_transition.go b/core/state_transition.go index 0ae9d7fcbf..f64a0ca014 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -127,11 +127,11 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition // 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, error) { +func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, bool, error) { st := NewStateTransition(evm, msg, gp) - ret, _, gasUsed, err := st.TransitionDb() - return ret, gasUsed, err + ret, _, gasUsed, reverted, err := st.TransitionDb() + return ret, gasUsed, reverted, err } func (st *StateTransition) from() vm.AccountRef { @@ -208,7 +208,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, err error) { +func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, reverted bool, err error) { if err = st.preCheck(); err != nil { return } @@ -222,10 +222,10 @@ 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, vm.ErrOutOfGas + return nil, nil, nil, false, vm.ErrOutOfGas } if err = st.useGas(intrinsicGas.Uint64()); err != nil { - return nil, nil, nil, err + return nil, nil, nil, false, err } var ( @@ -236,11 +236,11 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big vmerr error ) if contractCreation { - ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value) + ret, _, st.gas, reverted, vmerr = evm.Create(sender, st.data, st.gas, st.value) } else { // Increment the nonce for the next transaction st.state.SetNonce(sender.Address(), st.state.GetNonce(sender.Address())+1) - ret, st.gas, vmerr = evm.Call(sender, st.to().Address(), st.data, st.gas, st.value) + ret, st.gas, reverted, vmerr = evm.Call(sender, st.to().Address(), st.data, st.gas, st.value) } if vmerr != nil { log.Debug("VM returned with error", "err", vmerr) @@ -248,7 +248,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, vmerr + return nil, nil, nil, false, vmerr } } requiredGas = new(big.Int).Set(st.gasUsed()) @@ -256,7 +256,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(), err + return ret, requiredGas, st.gasUsed(), reverted, err } func (st *StateTransition) refundGas() { diff --git a/core/vm/evm.go b/core/vm/evm.go index de1ee1240e..cdd916c1f1 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -124,18 +124,18 @@ func (evm *EVM) Cancel() { // Call executes the contract associated with the addr with the given input as parameters. It also handles any // necessary value transfer required and takes the necessary steps to create accounts and reverses the state in // case of an execution error or failed value transfer. -func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { +func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, reverted bool, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, gas, nil + return nil, gas, false, nil } // Depth check execution. Fail if we're trying to execute above the // limit. if evm.depth > int(params.CallCreateDepth) { - return nil, gas, ErrDepth + return nil, gas, false, ErrDepth } if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { - return nil, gas, ErrInsufficientBalance + return nil, gas, false, ErrInsufficientBalance } var ( @@ -144,7 +144,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas ) if !evm.StateDB.Exist(addr) { if PrecompiledContracts[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 { - return nil, gas, nil + return nil, gas, false, nil } evm.StateDB.CreateAccount(addr) @@ -165,7 +165,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas contract.UseGas(contract.Gas) evm.Revert(snapshot, contract) } - return ret, contract.Gas, err + return ret, contract.Gas, contract.Reverted, err } // CallCode executes the contract associated with the addr with the given input as parameters. It also handles any @@ -241,18 +241,18 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by } // Create creates a new contract using code as deployment code. -func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { +func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, reverted bool, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, common.Address{}, gas, nil + return nil, common.Address{}, gas, false, nil } // Depth check execution. Fail if we're trying to execute above the // limit. if evm.depth > int(params.CallCreateDepth) { - return nil, common.Address{}, gas, ErrDepth + return nil, common.Address{}, gas, false, ErrDepth } if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { - return nil, common.Address{}, gas, ErrInsufficientBalance + return nil, common.Address{}, gas, false, ErrInsufficientBalance } // Create a new account on the state @@ -304,7 +304,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I ret = nil } - return ret, contractAddr, contract.Gas, err + return ret, contractAddr, contract.Gas, contract.Reverted, err } // ChainConfig returns the evmironment's chain configuration diff --git a/core/vm/instructions.go b/core/vm/instructions.go index f5164fcdd4..12e43e83d0 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -551,7 +551,8 @@ func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S } contract.UseGas(gas) - _, addr, returnGas, suberr := evm.Create(contract, input, gas, value) + // ignore reverted here since it is only useful in vm entry. + _, addr, returnGas, _, suberr := evm.Create(contract, input, gas, value) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must @@ -588,8 +589,8 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta if value.Sign() != 0 { gas += params.CallStipend } - - ret, returnGas, err := evm.Call(contract, address, args, gas, value) + // ignore revert here, same with opCreate. + ret, returnGas, _, err := evm.Call(contract, address, args, gas, value) if err != nil { stack.push(new(big.Int)) } else { diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index aa386a995e..0bca6efb05 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -113,7 +113,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { // set the receiver's (the executing contract) code for execution. cfg.State.SetCode(address, code) // Call the code with the given configuration. - ret, _, err := vmenv.Call( + ret, _, _, err := vmenv.Call( sender, common.StringToAddress("contract"), input, @@ -141,7 +141,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { ) // Call the code with the given configuration. - code, address, leftOverGas, err := vmenv.Create( + code, address, leftOverGas, _, err := vmenv.Create( sender, input, cfg.GasLimit, @@ -162,7 +162,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er sender := cfg.State.GetOrNewStateObject(cfg.Origin) // Call the code with the given configuration. - ret, leftOverGas, err := vmenv.Call( + ret, leftOverGas, _, err := vmenv.Call( sender, address, input, diff --git a/eth/api.go b/eth/api.go index 81570988c6..b1afec0d18 100644 --- a/eth/api.go +++ b/eth/api.go @@ -543,7 +543,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, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) + ret, gas, reverted, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { return nil, fmt.Errorf("tracing failed: %v", err) } @@ -553,6 +553,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common. Gas: gas, ReturnValue: fmt.Sprintf("%x", ret), StructLogs: ethapi.FormatLogs(tracer.StructLogs()), + Reverted: reverted, }, nil case *ethapi.JavascriptTracer: return tracer.GetResult() @@ -590,7 +591,7 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int) (co vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{}) gp := new(core.GasPool).AddGas(tx.Gas()) - _, _, err := core.ApplyMessage(vmenv, msg, gp) + _, _, _,err := core.ApplyMessage(vmenv, msg, gp) if err != nil { return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index da5dc5d583..657d06bbca 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -643,7 +643,7 @@ 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, err := core.ApplyMessage(evm, msg, gp) + res, gas, _, err := core.ApplyMessage(evm, msg, gp) if err := vmError(); err != nil { return nil, common.Big0, err } @@ -696,6 +696,7 @@ type ExecutionResult struct { Gas *big.Int `json:"gas"` ReturnValue string `json:"returnValue"` StructLogs []StructLogRes `json:"structLogs"` + Reverted bool `json:"reverted"` } // StructLogRes stores a structured log emitted by the EVM while replaying a @@ -1089,6 +1090,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(hash common.Hash) (map[ "contractAddress": nil, "logs": receipt.Logs, "logsBloom": receipt.Bloom, + "reverted": receipt.Reverted, } if receipt.Logs == nil { fields["logs"] = [][]*types.Log{} diff --git a/les/odr_test.go b/les/odr_test.go index 532de4d80b..1aa3e1eb3d 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -128,7 +128,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxBig256) - ret, _, _ := core.ApplyMessage(vmenv, msg, gp) + ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp) res = append(res, ret...) } } else { @@ -146,7 +146,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai //vmenv := light.NewEnv(ctx, state, config, lc, msg, header, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxBig256) - ret, _, _ := core.ApplyMessage(vmenv, msg, gp) + ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp) if vmstate.Error() == nil { res = append(res, ret...) } diff --git a/light/odr_test.go b/light/odr_test.go index 576e3abc9a..2cdc014f34 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -179,7 +179,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain vmenv := vm.NewEVM(context, statedb, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxBig256) - ret, _, _ := core.ApplyMessage(vmenv, msg, gp) + ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp) res = append(res, ret...) } } else { @@ -194,7 +194,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain context := core.NewEVMContext(msg, header, lc, nil) vmenv := vm.NewEVM(context, vmstate, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxBig256) - ret, _, _ := core.ApplyMessage(vmenv, msg, gp) + ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp) if vmstate.Error() == nil { res = append(res, ret...) } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index c1892cdccf..c8e5c76b54 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -212,8 +212,7 @@ func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, env, tx m statedb.Reset(root) snapshot := statedb.Snapshot() - - ret, gasUsed, err := core.ApplyMessage(environment, msg, gaspool) + ret, gasUsed, _, err := core.ApplyMessage(environment, msg, gaspool) if err != nil { statedb.RevertToSnapshot(snapshot) } diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index d2ddee039e..2e6d7c9679 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -230,6 +230,6 @@ func RunVm(statedb *state.StateDB, env, exec map[string]string) ([]byte, []*type vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract) environment, _ := NewEVMEnvironment(true, chainConfig, statedb, env, exec) - ret, g, err := environment.Call(caller, to, data, gas.Uint64(), value) + ret, g, _, err := environment.Call(caller, to, data, gas.Uint64(), value) return ret, statedb.Logs(), new(big.Int).SetUint64(g), err }