Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[R4R]-[l2geth]feat: add data in eth_call's response when revert #1373

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion l2geth/accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,13 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
log.Error("new state transition fail", "err", err)
return nil, 0, false, err
}
return stateTransition.TransitionDb()
ret, usedGas, vmerr, err := stateTransition.TransitionDb()
failed := false
if vmerr != nil {
failed = true
}

return ret, usedGas, failed, err
}

// SendTransaction updates the pending block to include the given transaction.
Expand Down
6 changes: 5 additions & 1 deletion l2geth/core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,14 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
}

// Apply the transaction to the current state (included in the env)
_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
_, gas, vmerr, err := ApplyMessage(vmenv, msg, gp)
if err != nil {
return nil, err
}
failed := false
if vmerr != nil {
failed = true
}

// Update the state with pending changes
var root []byte
Expand Down
51 changes: 43 additions & 8 deletions l2geth/core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,41 @@ type Message interface {
QueueOrigin() types.QueueOrigin
}

// ExecutionResult includes all output after executing given evm
// message no matter the execution itself is successful or not.
type ExecutionResult struct {
UsedGas uint64 // Total used gas but include the refunded gas
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
}

// Unwrap returns the internal evm error which allows us for further
// analysis outside.
func (result *ExecutionResult) Unwrap() error {
return result.Err
}

// Failed returns the indicator whether the execution is successful or not
func (result *ExecutionResult) Failed() bool { return result.Err != nil }

// Return is a helper function to help caller distinguish between revert reason
// and function return. Return returns the data after execution if no error occurs.
func (result *ExecutionResult) Return() []byte {
if result.Err != nil {
return nil
}
return common.CopyBytes(result.ReturnData)
}

// Revert returns the concrete revert reason if the execution is aborted by `REVERT`
// opcode. Note the reason can be nil if no data supplied with revert opcode.
func (result *ExecutionResult) Revert() []byte {
if result.Err != vm.ErrExecutionReverted {
return nil
}
return common.CopyBytes(result.ReturnData)
}

// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
func IntrinsicGas(data []byte, contractCreation, isHomestead bool, isEIP2028 bool) (uint64, error) {
// Set the starting gas for the raw transaction
Expand Down Expand Up @@ -176,11 +211,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, uint64, bool, error) {
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, error, error) {
stateTransition, err := NewStateTransition(evm, msg, gp)
if err != nil {
log.Error("apply message fall", "err", err)
return nil, 0, false, err
return nil, 0, err, err
}
return stateTransition.TransitionDb()
}
Expand Down Expand Up @@ -249,7 +284,7 @@ func (st *StateTransition) preCheck() error {
// TransitionDb will transition the state by applying the current message and
// returning the result including the used gas. It returns an error if failed.
// An error indicates a consensus issue.
func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) {
func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, vmerr error, err error) {
if err = st.preCheck(); err != nil {
return
}
Expand All @@ -262,18 +297,18 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
// Pay intrinsic gas
gas, err := IntrinsicGas(st.data, contractCreation, homestead, istanbul)
if err != nil {
return nil, 0, false, err
return nil, 0, err, err
}
if err = st.useGas(gas); err != nil {
return nil, 0, false, err
return nil, 0, err, err
}

var (
evm = st.evm
// vm errors do not effect consensus and are therefore
// not assigned to err, except for insufficient balance
// error.
vmerr error
// vmerr error
)

// The access list gets created here
Expand All @@ -295,7 +330,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
// sufficient balance to make the transfer happen. The first
// balance transfer may never fail.
if vmerr == vm.ErrInsufficientBalance {
return nil, 0, false, vmerr
return nil, 0, vmerr, vmerr
}
}
st.refundGas()
Expand All @@ -311,7 +346,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
st.state.AddBalance(evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
}

return ret, st.gasUsed(), vmerr != nil, err
return ret, st.gasUsed(), vmerr, err
}

func (st *StateTransition) refundGas() {
Expand Down
10 changes: 5 additions & 5 deletions l2geth/core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,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 {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas)
}
}
Expand Down Expand Up @@ -339,7 +339,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
ret, err = run(evm, contract, input, false)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas)
}
}
Expand Down Expand Up @@ -372,7 +372,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
ret, err = run(evm, contract, input, false)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas)
}
}
Expand Down Expand Up @@ -413,7 +413,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
ret, err = run(evm, contract, input, true)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas)
}
}
Expand Down Expand Up @@ -501,7 +501,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// when we're in homestead this also counts for code storage gas errors.
if maxCodeSizeExceeded || (err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas)) {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas)
}
}
Expand Down
14 changes: 7 additions & 7 deletions l2geth/core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var (
ErrWriteProtection = errors.New("evm: write protection")
ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
errExecutionReverted = errors.New("evm: execution reverted")
ErrExecutionReverted = errors.New("evm: execution reverted")
errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded")
errInvalidJump = errors.New("evm: invalid jump destination")
)
Expand Down Expand Up @@ -721,7 +721,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
contract.Gas += returnGas
interpreter.intPool.put(value, offset, size)

if suberr == errExecutionReverted {
if suberr == ErrExecutionReverted {
return res, nil
}
return nil, nil
Expand Down Expand Up @@ -749,7 +749,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
contract.Gas += returnGas
interpreter.intPool.put(endowment, offset, size, salt)

if suberr == errExecutionReverted {
if suberr == ErrExecutionReverted {
return res, nil
}
return nil, nil
Expand All @@ -775,7 +775,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
} else {
stack.push(interpreter.intPool.get().SetUint64(1))
}
if err == nil || err == errExecutionReverted {
if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret)
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
Expand Down Expand Up @@ -805,7 +805,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, contract *Contract, mem
} else {
stack.push(interpreter.intPool.get().SetUint64(1))
}
if err == nil || err == errExecutionReverted {
if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret)
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
Expand All @@ -831,7 +831,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract,
} else {
stack.push(interpreter.intPool.get().SetUint64(1))
}
if err == nil || err == errExecutionReverted {
if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret)
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
Expand All @@ -857,7 +857,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, m
} else {
stack.push(interpreter.intPool.get().SetUint64(1))
}
if err == nil || err == errExecutionReverted {
if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret)
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
Expand Down
4 changes: 2 additions & 2 deletions l2geth/core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
//
// It's important to note that any errors returned by the interpreter should be
// considered a revert-and-consume-all-gas operation except for
// errExecutionReverted which means revert-and-keep-gas-left.
// ErrExecutionReverted which means revert-and-keep-gas-left.
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
if in.intPool == nil {
in.intPool = poolOfIntPools.get()
Expand Down Expand Up @@ -289,7 +289,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
case err != nil:
return nil, err
case operation.reverts:
return res, errExecutionReverted
return res, ErrExecutionReverted
case operation.halts:
return res, nil
case !operation.jumps:
Expand Down
6 changes: 5 additions & 1 deletion l2geth/eth/api_tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -931,10 +931,14 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
// Run the transaction with tracing enabled.
vmenv := vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{Debug: true, Tracer: tracer})

ret, gas, failed, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
ret, gas, vmerr, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
if err != nil {
return nil, fmt.Errorf("tracing failed: %v", err)
}
failed := false
if vmerr != nil {
failed = true
}
// Depending on the tracer type, format and return the output
switch tracer := tracer.(type) {
case *vm.StructLogger:
Expand Down
16 changes: 8 additions & 8 deletions l2geth/graphql/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -776,14 +776,14 @@ func (b *Block) Call(ctx context.Context, args struct {
return nil, err
}
}
result, gas, failed, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, &vm.Config{}, 5*time.Second, b.backend.RPCGasCap())
result, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, &vm.Config{}, 5*time.Second, b.backend.RPCGasCap())
status := hexutil.Uint64(1)
if failed {
if result.Failed() {
status = 0
}
return &CallResult{
data: hexutil.Bytes(result),
gasUsed: hexutil.Uint64(gas),
data: result.ReturnData,
gasUsed: hexutil.Uint64(result.UsedGas),
status: status,
}, err
}
Expand Down Expand Up @@ -842,14 +842,14 @@ func (p *Pending) Call(ctx context.Context, args struct {
Data ethapi.CallArgs
}) (*CallResult, error) {
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
result, gas, failed, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, &vm.Config{}, 5*time.Second, p.backend.RPCGasCap())
result, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, &vm.Config{}, 5*time.Second, p.backend.RPCGasCap())
status := hexutil.Uint64(1)
if failed {
if result.Failed() {
status = 0
}
return &CallResult{
data: hexutil.Bytes(result),
gasUsed: hexutil.Uint64(gas),
data: result.ReturnData,
gasUsed: hexutil.Uint64(result.UsedGas),
status: status,
}, err
}
Expand Down
Loading
Loading