diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 7ac8b58204..f0b5920420 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -167,7 +167,7 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM if err != nil { return nil, err } - rval, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state) + rval, _, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state) return rval, err } @@ -177,7 +177,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu defer b.mu.Unlock() defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) - rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) + rval, _, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) return rval, err } @@ -215,11 +215,11 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs call.Gas = new(big.Int).SetUint64(mid) snapshot := b.pendingState.Snapshot() - _, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) + _, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) b.pendingState.RevertToSnapshot(snapshot) - // If the transaction became invalid or used all the gas (failed), raise the gas limit - if err != nil || gas.Cmp(call.Gas) == 0 { + // If the transaction became invalid or failed, raise the gas limit + if err != nil || failed { lo = mid continue } @@ -231,7 +231,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs // callContract implemens common code between normal and pending contract calls. // state is modified during execution, make sure to copy it if necessary. -func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, error) { +func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, bool, error) { // Ensure message is initialized properly. if call.GasPrice == nil { call.GasPrice = big.NewInt(1) @@ -253,8 +253,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, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() - return ret, gasUsed, err + ret, gasUsed, _, failed, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() + return ret, gasUsed, failed, err } // SendTransaction updates the pending block to include the given transaction. diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 3f95a0c93a..a9b7459a4a 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -178,12 +178,12 @@ func runCmd(ctx *cli.Context) error { var leftOverGas uint64 if ctx.GlobalBool(CreateFlag.Name) { input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...) - ret, _, leftOverGas, err = runtime.Create(input, &runtimeConfig) + ret, _, leftOverGas, _, err = runtime.Create(input, &runtimeConfig) } else { receiver := common.StringToAddress("receiver") statedb.SetCode(receiver, code) - ret, leftOverGas, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig) + ret, leftOverGas, _, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig) } execTime := time.Since(tstart) diff --git a/core/state_processor.go b/core/state_processor.go index 90f5a4f60c..9c6b69adf7 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, _, err := ApplyMessage(vmenv, msg, gp) if err != nil { return nil, nil, err } diff --git a/core/state_transition.go b/core/state_transition.go index 0ae9d7fcbf..d8ed410403 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, failed, err := st.TransitionDb() + return ret, gasUsed, failed, 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, failed 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, failed, 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, failed, 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(), failed, err } func (st *StateTransition) refundGas() { diff --git a/core/vm/contract.go b/core/vm/contract.go index 66748e8215..bfd62fa751 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -62,6 +62,7 @@ type Contract struct { Args []byte DelegateCall bool + Failed bool // Used to identify the execution status of the current transaction } // NewContract returns a new contract environment for the execution of EVM. @@ -151,3 +152,12 @@ func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code [ self.CodeHash = hash self.CodeAddr = addr } + +// MarkFailed mark current transaction's execution failed. +func (self *Contract) MarkFailed() { + self.Failed = true + // affect parent recursively + if parent, ok := self.caller.(*Contract); ok { + parent.MarkFailed() + } +} diff --git a/core/vm/evm.go b/core/vm/evm.go index 9296cc7cac..3873d2d29c 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, failed 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) @@ -164,8 +164,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas if err != nil { contract.UseGas(contract.Gas) evm.StateDB.RevertToSnapshot(snapshot) + contract.MarkFailed() } - return ret, contract.Gas, err + return ret, contract.Gas, contract.Failed, err } // CallCode executes the contract associated with the addr with the given input as parameters. It also handles any @@ -201,6 +202,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, if err != nil { contract.UseGas(contract.Gas) evm.StateDB.RevertToSnapshot(snapshot) + contract.MarkFailed() } return ret, contract.Gas, err @@ -235,24 +237,25 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by if err != nil { contract.UseGas(contract.Gas) evm.StateDB.RevertToSnapshot(snapshot) + contract.MarkFailed() } return ret, contract.Gas, err } // 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, failed 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 @@ -297,6 +300,11 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I contract.UseGas(contract.Gas) evm.StateDB.RevertToSnapshot(snapshot) } + // When the virtual machine returns an error or stores an excessive contract code, + // the execution of the transaction is marked as failed. + if maxCodeSizeExceeded || err != nil { + contract.MarkFailed() + } // If the vm returned with an error the return value should be set to nil. // This isn't consensus critical but merely to for behaviour reasons such as // tests, RPC calls, etc. @@ -304,7 +312,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.Failed, err } // ChainConfig returns the evmironment's chain configuration diff --git a/core/vm/instructions.go b/core/vm/instructions.go index f5164fcdd4..6babf600c4 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 failed flag here since it is only useful in vm entrance. + _, 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 failed flag 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/fuzz.go b/core/vm/runtime/fuzz.go index cb9ff08b5b..2b3eddb964 100644 --- a/core/vm/runtime/fuzz.go +++ b/core/vm/runtime/fuzz.go @@ -23,7 +23,7 @@ package runtime // This returns 1 for valid parsable/runable code, 0 // for invalid opcode. func Fuzz(input []byte) int { - _, _, err := Execute(input, input, &Config{ + _, _, _, err := Execute(input, input, &Config{ GasLimit: 3000000, }) diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 44cde4f70a..b69df4d617 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -94,7 +94,7 @@ func setDefaults(cfg *Config) { // Executes sets up a in memory, temporarily, environment for the execution of // the given code. It enabled the JIT by default and make sure that it's restored // to it's original state afterwards. -func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { +func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, bool, error) { if cfg == nil { cfg = new(Config) } @@ -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, _, failed, err := vmenv.Call( sender, common.StringToAddress("contract"), input, @@ -121,11 +121,11 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { cfg.Value, ) - return ret, cfg.State, err + return ret, cfg.State, failed, err } // Create executes the code using the EVM create method -func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { +func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, bool, error) { if cfg == nil { cfg = new(Config) } @@ -141,13 +141,13 @@ 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, failed, err := vmenv.Create( sender, input, cfg.GasLimit, cfg.Value, ) - return code, address, leftOverGas, err + return code, address, leftOverGas, failed, err } // Call executes the code given by the contract's address. It will return the @@ -155,14 +155,14 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { // // Call, unlike Execute, requires a config and also requires the State field to // be set. -func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, error) { +func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, bool, error) { setDefaults(cfg) vmenv := NewEnv(cfg, cfg.State) sender := cfg.State.GetOrNewStateObject(cfg.Origin) // Call the code with the given configuration. - ret, leftOverGas, err := vmenv.Call( + ret, leftOverGas, failed, err := vmenv.Call( sender, address, input, @@ -170,5 +170,5 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er cfg.Value, ) - return ret, leftOverGas, err + return ret, leftOverGas, failed, err } diff --git a/core/vm/runtime/runtime_example_test.go b/core/vm/runtime/runtime_example_test.go index b7d0ddc384..d183b3c22f 100644 --- a/core/vm/runtime/runtime_example_test.go +++ b/core/vm/runtime/runtime_example_test.go @@ -24,7 +24,7 @@ import ( ) func ExampleExecute() { - ret, _, err := runtime.Execute(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00"), nil, nil) + ret, _, _, err := runtime.Execute(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00"), nil, nil) if err != nil { fmt.Println(err) } diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 2c4dc50265..0a72853812 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -75,7 +75,7 @@ func TestEVM(t *testing.T) { } func TestExecute(t *testing.T) { - ret, _, err := Execute([]byte{ + ret, _, _, err := Execute([]byte{ byte(vm.PUSH1), 10, byte(vm.PUSH1), 0, byte(vm.MSTORE), @@ -106,7 +106,7 @@ func TestCall(t *testing.T) { byte(vm.RETURN), }) - ret, _, err := Call(address, nil, &Config{State: state}) + ret, _, _, err := Call(address, nil, &Config{State: state}) if err != nil { t.Fatal("didn't expect error", err) } @@ -117,6 +117,143 @@ func TestCall(t *testing.T) { } } +func TestTransactionReverted(t *testing.T) { + /* + Contract Source Code + ``` + contract Demo { + function Demo() {} + function IllegalDivision() returns(int) { + var dividend = 0; + return 1 / dividend; + } + function LegalDivision() returns(int) { + var dividend = 1; + return 1 / dividend; + } + } + ``` + */ + var definition = `[{"constant":false,"inputs":[],"name":"LegalDivision","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"IllegalDivision","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"}]` + var rawcode = common.Hex2Bytes("6060604052341561000c57fe5b5b5b5b60f88061001d6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632d256662146044578063522de105146067575bfe5b3415604b57fe5b6051608a565b6040518082815260200191505060405180910390f35b3415606e57fe5b607460ab565b6040518082815260200191505060405180910390f35b60006000600190508060ff16600181151560a057fe5b0460ff1691505b5090565b60006000600090508060ff16600181151560c157fe5b0460ff1691505b50905600a165627a7a7230582091585859014c4644d0427bf34abc65433b5874993ddea00cac57bcb87ee4cb6b0029") + + abi, err := abi.JSON(strings.NewReader(definition)) + if err != nil { + t.Fatal(err) + } + + legalDivision, err := abi.Pack("LegalDivision") + if err != nil { + t.Fatal(err) + } + + illegalDivision, err := abi.Pack("IllegalDivision") + if err != nil { + t.Fatal(err) + } + var failed bool + // deploy + cfg := &Config{ + Origin: common.HexToAddress("sender"), + } + code, _, _, _, err := Create(rawcode, cfg) + if err != nil { + t.Fatal(err) + } + _, _, failed, _ = Execute(code, legalDivision, cfg) + if failed != false { + t.Fatal("Expect false, got true") + } + _, _, failed, _ = Execute(code, illegalDivision, cfg) + if failed != true { + t.Fatal("Expect true, got false") + } +} + +func TestDelegateReverted(t *testing.T) { + /* + Contract Source Code + ``` + contract Relay { + address public currentVersion; + address public owner; + + modifier onlyOwner() { + if (msg.sender != owner) { + throw; + } + _; + } + function Relay(address _address) { + currentVersion = _address; + owner = msg.sender; + } + function changeContract(address newVersion) public + onlyOwner() + { + currentVersion = newVersion; + } + function() { + if(!currentVersion.delegatecall(msg.data)) throw; + } + } + + contract Demo { + function Demo() { + } + function IllegalDivision() returns(int) { + var dividend = 0; + return 1 / dividend; + } + function LegalDivision() returns(int) { + var dividend = 1; + return 1 / dividend; + } + } + ``` + */ + var definition = `[{"constant":false,"inputs":[],"name":"LegalDivision","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"IllegalDivision","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"}]` + var rawcode1 = common.Hex2Bytes("6060604052341561000c57fe5b5b5b5b60f88061001d6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632d256662146044578063522de105146067575bfe5b3415604b57fe5b6051608a565b6040518082815260200191505060405180910390f35b3415606e57fe5b607460ab565b6040518082815260200191505060405180910390f35b60006000600190508060ff16600181151560a057fe5b0460ff1691505b5090565b60006000600090508060ff16600181151560c157fe5b0460ff1691505b50905600a165627a7a7230582091585859014c4644d0427bf34abc65433b5874993ddea00cac57bcb87ee4cb6b0029") + var rawcode2 = common.Hex2Bytes("6060604052341561000c57fe5b60405160208061039c833981016040528080519060200190919050505b80600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b505b6102df806100bd6000396000f30060606040523615610055576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633d71c3af146100ea5780638da5cb5b146101205780639d888e8614610172575b341561005d57fe5b6100e85b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600036600060405160200152604051808383808284378201915050925050506020604051808303818560325a03f415156100d057fe5b50506040518051905015156100e55760006000fd5b5b565b005b34156100f257fe5b61011e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101c4565b005b341561012857fe5b610130610267565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561017a57fe5b61018261028d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156102215760006000fd5b80600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b50565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a723058208496aa0ef6d67e2e0423b76e8fd61b92e0047fe2e20ad20f887caf2b378dfa300029") + + abi, err := abi.JSON(strings.NewReader(definition)) + if err != nil { + t.Fatal(err) + } + + legalDivision, err := abi.Pack("LegalDivision") + if err != nil { + t.Fatal(err) + } + + illegalDivision, err := abi.Pack("IllegalDivision") + if err != nil { + t.Fatal(err) + } + var failed bool + // deploy + cfg := &Config{ + Origin: common.HexToAddress("sender"), + } + _, addr, _, _, err := Create(rawcode1, cfg) + if err != nil { + t.Fatal(err) + } + + _, addr2, _, _, err := Create(append(rawcode2, common.LeftPadBytes(addr.Bytes(), 32)...), cfg) + if err != nil { + t.Fatal(err) + } + _, _, failed, _ = Call(addr2, legalDivision, cfg) + if failed != false { + t.Fatal("Expect false, got true") + } + _, _, failed, _ = Call(addr2, illegalDivision, cfg) + if failed != true { + t.Fatal("Expect true, got false") + } +} + func BenchmarkCall(b *testing.B) { var definition = `[{"constant":true,"inputs":[],"name":"seller","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"abort","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"refund","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"buyer","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmReceived","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmPurchase","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"},{"anonymous":false,"inputs":[],"name":"Aborted","type":"event"},{"anonymous":false,"inputs":[],"name":"PurchaseConfirmed","type":"event"},{"anonymous":false,"inputs":[],"name":"ItemReceived","type":"event"},{"anonymous":false,"inputs":[],"name":"Refunded","type":"event"}]` diff --git a/eth/api.go b/eth/api.go index 0d90759b6c..145a4f4823 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, failed, 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()), + Failed: failed, }, 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 c22c56dfb3..c4de6d8b1d 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, error) { +func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, *big.Int, bool, 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, err + return nil, common.Big0, false, 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, err + return nil, common.Big0, false, err } // Wait for the context to be done and cancel the evm. Even if the // EVM has finished, cancelling may be done (repeatedly) @@ -637,17 +637,17 @@ 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, failed, err := core.ApplyMessage(evm, msg, gp) if err := vmError(); err != nil { - return nil, common.Big0, err + return nil, common.Big0, false, err } - return res, gas, err + return res, gas, failed, err } // Call executes the given transaction on the state for the given block number. // It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values. func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) { - result, _, err := s.doCall(ctx, args, blockNr, vm.Config{DisableGasMetering: true}) + result, _, _, err := s.doCall(ctx, args, blockNr, vm.Config{DisableGasMetering: true}) return (hexutil.Bytes)(result), err } @@ -670,10 +670,10 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (* mid := (hi + lo) / 2 (*big.Int)(&args.Gas).SetUint64(mid) - _, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{}) + _, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{}) - // If the transaction became invalid or used all the gas (failed), raise the gas limit - if err != nil || gas.Cmp((*big.Int)(&args.Gas)) == 0 { + // If the transaction became invalid or failed, raise the gas limit + if err != nil || failed { lo = mid continue } @@ -690,6 +690,7 @@ type ExecutionResult struct { Gas *big.Int `json:"gas"` ReturnValue string `json:"returnValue"` StructLogs []StructLogRes `json:"structLogs"` + Failed bool `json:"failed"` } // StructLogRes stores a structured log emitted by the EVM while replaying a diff --git a/les/odr_test.go b/les/odr_test.go index 3a0fd6738f..f56c4036d4 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -127,7 +127,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 { @@ -138,7 +138,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai context := core.NewEVMContext(msg, header, lc, nil) vmenv := vm.NewEVM(context, state, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxBig256) - ret, _, _ := core.ApplyMessage(vmenv, msg, gp) + ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp) if state.Error() == nil { res = append(res, ret...) } diff --git a/light/odr_test.go b/light/odr_test.go index 544b64effc..9b5168fbc9 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -181,7 +181,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain context := core.NewEVMContext(msg, header, chain, nil) vmenv := vm.NewEVM(context, st, 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...) if st.Error() != nil { return res, st.Error() diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 58acdd488b..64cb9bd4a0 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -188,7 +188,7 @@ func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, db ethdb. gaspool := new(core.GasPool).AddGas(math.MustParseBig256(env["currentGasLimit"])) snapshot := statedb.Snapshot() - ret, _, err := core.ApplyMessage(environment, msg, gaspool) + ret, _, _, 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 e7fe74f49b..369b17d415 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 }