Skip to content

Commit

Permalink
[R4R]-[l2geth]feat: add data in eth_calls response when revert
Browse files Browse the repository at this point in the history
  • Loading branch information
Tri-stone committed Oct 31, 2023
1 parent a18ec93 commit d5273c9
Show file tree
Hide file tree
Showing 13 changed files with 197 additions and 76 deletions.
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
52 changes: 44 additions & 8 deletions l2geth/core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,42 @@ 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 {
log.Info("ExecutionResult", "result.Err", result.Err)
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 +212,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 +285,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 +298,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 +331,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 +347,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

0 comments on commit d5273c9

Please sign in to comment.