From 40d36e15a48ed7c78d0b52a2fe2c97513af3352c Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Thu, 15 Jun 2017 21:46:57 +0800 Subject: [PATCH 1/9] core/types: add a `reverted` field in receipt --- .idea/vcs.xml | 6 ++++++ core/types/gen_receipt_json.go | 6 ++++++ core/types/receipt.go | 9 +++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 .idea/vcs.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000..35eb1ddfbb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go index edbd64ba4e..ce6cc0e7a1 100644 --- a/core/types/gen_receipt_json.go +++ b/core/types/gen_receipt_json.go @@ -20,6 +20,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) { TxHash common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress common.Address `json:"contractAddress"` GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"` + Reverted bool `json:"reverted" gencode:"required"` } var enc Receipt enc.PostState = r.PostState @@ -29,6 +30,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) { enc.TxHash = r.TxHash enc.ContractAddress = r.ContractAddress enc.GasUsed = (*hexutil.Big)(r.GasUsed) + enc.Reverted = r.Reverted return json.Marshal(&enc) } @@ -41,6 +43,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { TxHash *common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress *common.Address `json:"contractAddress"` GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"` + Reverted *bool `json:"reverted" gencode:"required"` } var dec Receipt if err := json.Unmarshal(input, &dec); err != nil { @@ -73,5 +76,8 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'gasUsed' for Receipt") } r.GasUsed = (*big.Int)(dec.GasUsed) + if dec.Reverted != nil { + r.Reverted = *dec.Reverted + } return nil } diff --git a/core/types/receipt.go b/core/types/receipt.go index ef6f6a2bb2..598a37bdd9 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -40,6 +40,10 @@ type Receipt struct { TxHash common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress common.Address `json:"contractAddress"` GasUsed *big.Int `json:"gasUsed" gencodec:"required"` + // The Reverted field is true if transaction is reverted during state transition, + // As the transaction revert will cause all the remaining gas to be consumed, + // This field could help to gas estimation. + Reverted bool `json:"reverted" gencode:"required"` } type receiptMarshaling struct { @@ -91,7 +95,7 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error { for i, log := range r.Logs { logs[i] = (*LogForStorage)(log) } - return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed}) + return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed, r.Reverted}) } // DecodeRLP implements rlp.Decoder, and loads both consensus and implementation @@ -105,6 +109,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { ContractAddress common.Address Logs []*LogForStorage GasUsed *big.Int + Reverted bool } if err := s.Decode(&receipt); err != nil { return err @@ -116,7 +121,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { r.Logs[i] = (*Log)(log) } // Assign the implementation fields - r.TxHash, r.ContractAddress, r.GasUsed = receipt.TxHash, receipt.ContractAddress, receipt.GasUsed + r.TxHash, r.ContractAddress, r.GasUsed, r.Reverted = receipt.TxHash, receipt.ContractAddress, receipt.GasUsed, receipt.Reverted return nil } From 29f803892d5f978abe5be1ad27860aa514b1e41f Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Thu, 15 Jun 2017 21:49:12 +0800 Subject: [PATCH 2/9] core/vm: add a reverted flag in vm contract which is used to indicate whether state revert happen during state transition. if child's revert flag been set, it will also affect its parent recursively --- core/vm/contract.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/vm/contract.go b/core/vm/contract.go index 66748e8215..810a1a8b46 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -62,6 +62,8 @@ type Contract struct { Args []byte DelegateCall bool + Reverted bool // use to represent whether transaction is reverted during state transition + // child's reverted field changed will also affect parent. } // NewContract returns a new contract environment for the execution of EVM. @@ -151,3 +153,12 @@ func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code [ self.CodeHash = hash self.CodeAddr = addr } + +// MarkReverted mark current transaction's execution has meet revert. +func (self *Contract) MarkReverted() { + self.Reverted = true + // affect parent recursively + if parent, ok := self.caller.(*Contract); ok { + parent.MarkReverted() + } +} From b7c7166202bd9c04203d999d964f4218bf7c0dca Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Thu, 15 Jun 2017 21:52:17 +0800 Subject: [PATCH 3/9] core/vm: set revert flag in contract when state revert happen. --- core/vm/evm.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index 9296cc7cac..de1ee1240e 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -163,7 +163,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // when we're in homestead this also counts for code storage gas errors. if err != nil { contract.UseGas(contract.Gas) - evm.StateDB.RevertToSnapshot(snapshot) + evm.Revert(snapshot, contract) } return ret, contract.Gas, err } @@ -200,7 +200,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, ret, err = run(evm, snapshot, contract, input) if err != nil { contract.UseGas(contract.Gas) - evm.StateDB.RevertToSnapshot(snapshot) + evm.Revert(snapshot, contract) } return ret, contract.Gas, err @@ -234,7 +234,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by ret, err = run(evm, snapshot, contract, input) if err != nil { contract.UseGas(contract.Gas) - evm.StateDB.RevertToSnapshot(snapshot) + evm.Revert(snapshot, contract) } return ret, contract.Gas, err @@ -295,7 +295,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) { contract.UseGas(contract.Gas) - evm.StateDB.RevertToSnapshot(snapshot) + evm.Revert(snapshot, contract) } // 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 @@ -312,3 +312,10 @@ func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } // Interpreter returns the EVM interpreter func (evm *EVM) Interpreter() *Interpreter { return evm.interpreter } + +// Revert revert all state changes made since the given revision. +// set `reverted` flag recursively. +func (evm *EVM) Revert(snapshot int, contract *Contract) { + evm.StateDB.RevertToSnapshot(snapshot) + contract.MarkReverted() +} From 6bca2216db095effa0f7b79ebd354f5137a8bf5e Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Thu, 15 Jun 2017 19:27:21 +0800 Subject: [PATCH 4/9] core: trace whether tx is reverted detect whether tx is reverted during state transition and save to receipt if so. --- accounts/abi/bind/backends/simulated.go | 2 +- core/state_processor.go | 3 ++- core/state_transition.go | 20 ++++++++++---------- core/vm/evm.go | 22 +++++++++++----------- core/vm/instructions.go | 7 ++++--- core/vm/runtime/runtime.go | 6 +++--- eth/api.go | 5 +++-- internal/ethapi/api.go | 4 +++- les/odr_test.go | 4 ++-- light/odr_test.go | 2 +- tests/state_test_util.go | 2 +- tests/vm_test_util.go | 2 +- 12 files changed, 42 insertions(+), 37 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 7ac8b58204..de238be6c2 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 44cde4f70a..04152fe59b 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 0d90759b6c..d146e04335 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 c22c56dfb3..16347f35fd 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -637,7 +637,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 } @@ -690,6 +690,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 @@ -1080,6 +1081,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 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 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 } From 2b73e775ed61e3d241e01e73baa0900f66154849 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Thu, 15 Jun 2017 21:57:36 +0800 Subject: [PATCH 5/9] dep: remove useless --- .idea/vcs.xml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .idea/vcs.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddfbb..0000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 90da06cccb79a6087c2c92b0ef909f7154059c45 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 16 Jun 2017 16:19:00 +0800 Subject: [PATCH 6/9] core/vm/runtime: return `tx reverted` back --- cmd/evm/runner.go | 4 ++-- core/vm/runtime/fuzz.go | 2 +- core/vm/runtime/runtime.go | 18 +++++++++--------- core/vm/runtime/runtime_example_test.go | 2 +- core/vm/runtime/runtime_test.go | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) 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/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 04152fe59b..64409f53e7 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, _, reverted, 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, reverted, 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, reverted, err := vmenv.Create( sender, input, cfg.GasLimit, cfg.Value, ) - return code, address, leftOverGas, err + return code, address, leftOverGas, reverted, 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, reverted, 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, reverted, 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..461d8708d4 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) } From 66d8ff89832473ba4270b8c9c9d86a1960fea6a9 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 16 Jun 2017 17:06:40 +0800 Subject: [PATCH 7/9] core/vm/runtime: add tx revert related unit test --- core/vm/runtime/runtime_test.go | 137 ++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 461d8708d4..dd6b658564 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -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 reverted bool + // deploy + cfg := &Config{ + Origin: common.HexToAddress("sender"), + } + code, _, _, _, err := Create(rawcode, cfg) + if err != nil { + t.Fatal(err) + } + _, _, reverted, _ = Execute(code, legalDivision, cfg) + if reverted != false { + t.Fatal("Expect false, got true") + } + _, _, reverted, _ = Execute(code, illegalDivision, cfg) + if reverted != 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 reverted 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) + } + _, _, reverted, _ = Call(addr2, legalDivision, cfg) + if reverted != false { + t.Fatal("Expect false, got true") + } + _, _, reverted, _ = Call(addr2, illegalDivision, cfg) + if reverted != 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"}]` From 65a6cc2f1e87304cce49c1ab08dee2b9eb87adef Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Wed, 28 Jun 2017 10:32:31 +0800 Subject: [PATCH 8/9] core/types, core/vm: mark tx execution as failed if error return Motivation: Current problem with gas estimation is not all cases is correct using gas metrics, e.g. in the frontier contract deploys can fail without consuming all gas due to have not enough gas to store contract code. So a `failed` flag is useful which can detect the vm execution status. In Addition: the `Reverted` flag has been removed, since it is too sensitive to insert addtional field to it. --- accounts/abi/bind/backends/simulated.go | 16 +-- core/state_processor.go | 3 +- core/state_transition.go | 12 +-- core/types/gen_receipt_json.go | 6 -- core/types/receipt.go | 9 +- core/vm/contract.go | 11 +-- core/vm/evm.go | 31 +++--- core/vm/instructions.go | 4 +- core/vm/runtime/runtime.go | 12 +-- core/vm/runtime/runtime_test.go | 126 ++++++++++++------------ eth/api.go | 6 +- internal/ethapi/api.go | 23 +++-- 12 files changed, 123 insertions(+), 136 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index de238be6c2..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/core/state_processor.go b/core/state_processor.go index e09771b8f7..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, reverted, err := ApplyMessage(vmenv, msg, gp) + _, gas, _, err := ApplyMessage(vmenv, msg, gp) if err != nil { return nil, nil, err } @@ -111,7 +111,6 @@ 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 f64a0ca014..d8ed410403 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -130,8 +130,8 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, bool, error) { st := NewStateTransition(evm, msg, gp) - ret, _, gasUsed, reverted, err := st.TransitionDb() - return ret, gasUsed, reverted, 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, reverted bool, err error) { +func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, failed bool, err error) { if err = st.preCheck(); err != nil { return } @@ -236,11 +236,11 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big vmerr error ) if contractCreation { - ret, _, st.gas, reverted, 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, reverted, 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) @@ -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(), reverted, err + return ret, requiredGas, st.gasUsed(), failed, err } func (st *StateTransition) refundGas() { diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go index ce6cc0e7a1..edbd64ba4e 100644 --- a/core/types/gen_receipt_json.go +++ b/core/types/gen_receipt_json.go @@ -20,7 +20,6 @@ func (r Receipt) MarshalJSON() ([]byte, error) { TxHash common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress common.Address `json:"contractAddress"` GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"` - Reverted bool `json:"reverted" gencode:"required"` } var enc Receipt enc.PostState = r.PostState @@ -30,7 +29,6 @@ func (r Receipt) MarshalJSON() ([]byte, error) { enc.TxHash = r.TxHash enc.ContractAddress = r.ContractAddress enc.GasUsed = (*hexutil.Big)(r.GasUsed) - enc.Reverted = r.Reverted return json.Marshal(&enc) } @@ -43,7 +41,6 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { TxHash *common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress *common.Address `json:"contractAddress"` GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"` - Reverted *bool `json:"reverted" gencode:"required"` } var dec Receipt if err := json.Unmarshal(input, &dec); err != nil { @@ -76,8 +73,5 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'gasUsed' for Receipt") } r.GasUsed = (*big.Int)(dec.GasUsed) - if dec.Reverted != nil { - r.Reverted = *dec.Reverted - } return nil } diff --git a/core/types/receipt.go b/core/types/receipt.go index 598a37bdd9..ef6f6a2bb2 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -40,10 +40,6 @@ type Receipt struct { TxHash common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress common.Address `json:"contractAddress"` GasUsed *big.Int `json:"gasUsed" gencodec:"required"` - // The Reverted field is true if transaction is reverted during state transition, - // As the transaction revert will cause all the remaining gas to be consumed, - // This field could help to gas estimation. - Reverted bool `json:"reverted" gencode:"required"` } type receiptMarshaling struct { @@ -95,7 +91,7 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error { for i, log := range r.Logs { logs[i] = (*LogForStorage)(log) } - return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed, r.Reverted}) + return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed}) } // DecodeRLP implements rlp.Decoder, and loads both consensus and implementation @@ -109,7 +105,6 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { ContractAddress common.Address Logs []*LogForStorage GasUsed *big.Int - Reverted bool } if err := s.Decode(&receipt); err != nil { return err @@ -121,7 +116,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { r.Logs[i] = (*Log)(log) } // Assign the implementation fields - r.TxHash, r.ContractAddress, r.GasUsed, r.Reverted = receipt.TxHash, receipt.ContractAddress, receipt.GasUsed, receipt.Reverted + r.TxHash, r.ContractAddress, r.GasUsed = receipt.TxHash, receipt.ContractAddress, receipt.GasUsed return nil } diff --git a/core/vm/contract.go b/core/vm/contract.go index 810a1a8b46..bfd62fa751 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -62,8 +62,7 @@ type Contract struct { Args []byte DelegateCall bool - Reverted bool // use to represent whether transaction is reverted during state transition - // child's reverted field changed will also affect parent. + Failed bool // Used to identify the execution status of the current transaction } // NewContract returns a new contract environment for the execution of EVM. @@ -154,11 +153,11 @@ func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code [ self.CodeAddr = addr } -// MarkReverted mark current transaction's execution has meet revert. -func (self *Contract) MarkReverted() { - self.Reverted = true +// 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.MarkReverted() + parent.MarkFailed() } } diff --git a/core/vm/evm.go b/core/vm/evm.go index cdd916c1f1..3873d2d29c 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -124,7 +124,7 @@ 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, reverted bool, 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, false, nil } @@ -163,9 +163,10 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // when we're in homestead this also counts for code storage gas errors. if err != nil { contract.UseGas(contract.Gas) - evm.Revert(snapshot, contract) + evm.StateDB.RevertToSnapshot(snapshot) + contract.MarkFailed() } - return ret, contract.Gas, contract.Reverted, 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 @@ -200,7 +201,8 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, ret, err = run(evm, snapshot, contract, input) if err != nil { contract.UseGas(contract.Gas) - evm.Revert(snapshot, contract) + evm.StateDB.RevertToSnapshot(snapshot) + contract.MarkFailed() } return ret, contract.Gas, err @@ -234,14 +236,15 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by ret, err = run(evm, snapshot, contract, input) if err != nil { contract.UseGas(contract.Gas) - evm.Revert(snapshot, contract) + 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, reverted bool, 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, false, nil } @@ -295,7 +298,12 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) { contract.UseGas(contract.Gas) - evm.Revert(snapshot, contract) + 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 @@ -304,7 +312,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I ret = nil } - return ret, contractAddr, contract.Gas, contract.Reverted, err + return ret, contractAddr, contract.Gas, contract.Failed, err } // ChainConfig returns the evmironment's chain configuration @@ -312,10 +320,3 @@ func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } // Interpreter returns the EVM interpreter func (evm *EVM) Interpreter() *Interpreter { return evm.interpreter } - -// Revert revert all state changes made since the given revision. -// set `reverted` flag recursively. -func (evm *EVM) Revert(snapshot int, contract *Contract) { - evm.StateDB.RevertToSnapshot(snapshot) - contract.MarkReverted() -} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 12e43e83d0..6babf600c4 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -551,7 +551,7 @@ func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S } contract.UseGas(gas) - // ignore reverted here since it is only useful in vm entry. + // 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 @@ -589,7 +589,7 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta if value.Sign() != 0 { gas += params.CallStipend } - // ignore revert here, same with opCreate. + // 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)) diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 64409f53e7..b69df4d617 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, bool, err // set the receiver's (the executing contract) code for execution. cfg.State.SetCode(address, code) // Call the code with the given configuration. - ret, _, reverted, err := vmenv.Call( + ret, _, failed, err := vmenv.Call( sender, common.StringToAddress("contract"), input, @@ -121,7 +121,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, bool, err cfg.Value, ) - return ret, cfg.State, reverted, err + return ret, cfg.State, failed, err } // Create executes the code using the EVM create method @@ -141,13 +141,13 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, bool, er ) // Call the code with the given configuration. - code, address, leftOverGas, reverted, err := vmenv.Create( + code, address, leftOverGas, failed, err := vmenv.Create( sender, input, cfg.GasLimit, cfg.Value, ) - return code, address, leftOverGas, reverted, err + return code, address, leftOverGas, failed, err } // Call executes the code given by the contract's address. It will return the @@ -162,7 +162,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, bo sender := cfg.State.GetOrNewStateObject(cfg.Origin) // Call the code with the given configuration. - ret, leftOverGas, reverted, 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, bo cfg.Value, ) - return ret, leftOverGas, reverted, err + return ret, leftOverGas, failed, err } diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index dd6b658564..fbaad6c7cb 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -119,21 +119,21 @@ 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; - } - } - ``` - */ + 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") @@ -151,7 +151,7 @@ func TestTransactionReverted(t *testing.T) { if err != nil { t.Fatal(err) } - var reverted bool + var failed bool // deploy cfg := &Config{ Origin: common.HexToAddress("sender"), @@ -160,58 +160,58 @@ func TestTransactionReverted(t *testing.T) { if err != nil { t.Fatal(err) } - _, _, reverted, _ = Execute(code, legalDivision, cfg) - if reverted != false { + _, _, failed, _ = Execute(code, legalDivision, cfg) + if failed != false { t.Fatal("Expect false, got true") } - _, _, reverted, _ = Execute(code, illegalDivision, cfg) - if reverted != 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; + 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; - } - } + 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; - } - } - ``` - */ + 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") @@ -230,7 +230,7 @@ func TestDelegateReverted(t *testing.T) { if err != nil { t.Fatal(err) } - var reverted bool + var failed bool // deploy cfg := &Config{ Origin: common.HexToAddress("sender"), @@ -244,12 +244,12 @@ func TestDelegateReverted(t *testing.T) { if err != nil { t.Fatal(err) } - _, _, reverted, _ = Call(addr2, legalDivision, cfg) - if reverted != false { + _, _, failed, _ = Call(addr2, legalDivision, cfg) + if failed != false { t.Fatal("Expect false, got true") } - _, _, reverted, _ = Call(addr2, illegalDivision, cfg) - if reverted != true { + _, _, failed, _ = Call(addr2, illegalDivision, cfg) + if failed != true { t.Fatal("Expect true, got false") } } diff --git a/eth/api.go b/eth/api.go index d146e04335..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, reverted, 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,7 +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, + Failed: failed, }, nil case *ethapi.JavascriptTracer: return tracer.GetResult() @@ -591,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 16347f35fd..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,7 +690,7 @@ type ExecutionResult struct { Gas *big.Int `json:"gas"` ReturnValue string `json:"returnValue"` StructLogs []StructLogRes `json:"structLogs"` - Reverted bool `json:"reverted"` + Failed bool `json:"failed"` } // StructLogRes stores a structured log emitted by the EVM while replaying a @@ -1081,7 +1081,6 @@ 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{} From 2320af4b9d7430f0292f6d3ff671a66c4969cec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Wed, 28 Jun 2017 11:25:50 +0300 Subject: [PATCH 9/9] core/vm/runtime: gofmt the tests --- core/vm/runtime/runtime_test.go | 74 ++++++++++++++++----------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index fbaad6c7cb..0a72853812 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -172,45 +172,45 @@ func TestTransactionReverted(t *testing.T) { func TestDelegateReverted(t *testing.T) { /* - Contract Source Code - ``` - contract Relay { - address public currentVersion; - address public owner; + 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; - } - } + 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; - } - } - ``` + 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")