mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 23:26:44 +00:00
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.
This commit is contained in:
parent
66d8ff8983
commit
65a6cc2f1e
12 changed files with 123 additions and 136 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{}
|
||||
|
|
|
|||
Loading…
Reference in a new issue