Skip to content

Commit

Permalink
new changes and unit test fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
dhyaniarun1993 committed Nov 6, 2023
1 parent d61649b commit d9dba75
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 7 deletions.
130 changes: 130 additions & 0 deletions core/vm/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package vm
import (
"errors"
"fmt"
"math"
)

// List evm execution errors
Expand Down Expand Up @@ -75,3 +76,132 @@ type ErrInvalidOpCode struct {
}

func (e *ErrInvalidOpCode) Error() string { return fmt.Sprintf("invalid opcode: %s", e.opcode) }

// rpcError is the same interface as the one defined in rpc/errors.go
// but we do not want to depend on rpc package here so we redefine it.
//
// It's used to ensure that the VMError implements the RPC error interface.
type rpcError interface {
Error() string // returns the message
ErrorCode() int // returns the code
}

var _ rpcError = (*VMError)(nil)

// VMError wraps a VM error with an additional stable error code. The error
// field is the original error that caused the VM error and must be one of the
// VM error defined at the top of this file.
//
// If the error is not one of the known error above, the error code will be
// set to VMErrorCodeUnknown.
type VMError struct {
error
code int
}

func VMErrorFromErr(err error) error {
if err == nil {
return nil
}

return &VMError{
error: fmt.Errorf("%w", err),
code: vmErrorCodeFromErr(err),
}
}

func (e *VMError) Error() string {
return e.error.Error()
}

func (e *VMError) Unwrap() error {
return errors.Unwrap(e.error)
}

func (e *VMError) ErrorCode() int {
return e.code
}

const (
// We start the error code at 1 so that we can use 0 later for some possible extension. There
// is no unspecified value for the code today because it should always be set to a valid value
// that could be VMErrorCodeUnknown if the error is not mapped to a known error code.

VMErrorCodeOutOfGas = 1 + iota
VMErrorCodeCodeStoreOutOfGas
VMErrorCodeDepth
VMErrorCodeInsufficientBalance
VMErrorCodeContractAddressCollision
VMErrorCodeExecutionReverted
VMErrorCodeMaxInitCodeSizeExceeded
VMErrorCodeMaxCodeSizeExceeded
VMErrorCodeInvalidJump
VMErrorCodeWriteProtection
VMErrorCodeReturnDataOutOfBounds
VMErrorCodeGasUintOverflow
VMErrorCodeInvalidCode
VMErrorCodeNonceUintOverflow
VMErrorCodeStackUnderflow
VMErrorCodeStackOverflow
VMErrorCodeInvalidOpCode
VMErrorInvalidSubroutineEntry
VMErrorInvalidRetsub
VMErrorReturnStackExceeded

// VMErrorCodeUnknown explicitly marks an error as unknown, this is useful when error is converted
// from an actual `error` in which case if the mapping is not known, we can use this value to indicate that.
VMErrorCodeUnknown = math.MaxInt - 1
)

func vmErrorCodeFromErr(err error) int {
switch {
case errors.Is(err, ErrOutOfGas):
return VMErrorCodeOutOfGas
case errors.Is(err, ErrCodeStoreOutOfGas):
return VMErrorCodeCodeStoreOutOfGas
case errors.Is(err, ErrDepth):
return VMErrorCodeDepth
case errors.Is(err, ErrInsufficientBalance):
return VMErrorCodeInsufficientBalance
case errors.Is(err, ErrContractAddressCollision):
return VMErrorCodeContractAddressCollision
case errors.Is(err, ErrExecutionReverted):
return VMErrorCodeExecutionReverted
case errors.Is(err, ErrMaxCodeSizeExceeded):
return VMErrorCodeMaxCodeSizeExceeded
case errors.Is(err, ErrInvalidJump):
return VMErrorCodeInvalidJump
case errors.Is(err, ErrWriteProtection):
return VMErrorCodeWriteProtection
case errors.Is(err, ErrReturnDataOutOfBounds):
return VMErrorCodeReturnDataOutOfBounds
case errors.Is(err, ErrGasUintOverflow):
return VMErrorCodeGasUintOverflow
case errors.Is(err, ErrInvalidCode):
return VMErrorCodeInvalidCode
case errors.Is(err, ErrNonceUintOverflow):
return VMErrorCodeNonceUintOverflow
case errors.Is(err, ErrInvalidSubroutineEntry):
return VMErrorInvalidSubroutineEntry
case errors.Is(err, ErrInvalidRetsub):
return VMErrorInvalidRetsub
case errors.Is(err, ErrReturnStackExceeded):
return VMErrorReturnStackExceeded

default:
// Dynamic errors
if v := (*ErrStackUnderflow)(nil); errors.As(err, &v) {
return VMErrorCodeStackUnderflow
}

if v := (*ErrStackOverflow)(nil); errors.As(err, &v) {
return VMErrorCodeStackOverflow
}

if v := (*ErrInvalidOpCode)(nil); errors.As(err, &v) {
return VMErrorCodeInvalidOpCode
}

return VMErrorCodeUnknown
}
}
4 changes: 2 additions & 2 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,8 @@ func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas
}

if isRoot {
tracer.CaptureEnd(ret, startGas-leftOverGas, err)
tracer.CaptureEnd(ret, startGas-leftOverGas, VMErrorFromErr(err))
} else {
tracer.CaptureExit(ret, startGas-leftOverGas, err)
tracer.CaptureExit(ret, startGas-leftOverGas, VMErrorFromErr(err))
}
}
6 changes: 3 additions & 3 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// first: capture data/memory/state/depth/etc... then clenup them
if in.cfg.Debug && err != nil {
if !logged {
in.cfg.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.depth, err) //nolint:errcheck
in.cfg.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.depth, VMErrorFromErr(err)) //nolint:errcheck
} else {
in.cfg.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.depth, err)
in.cfg.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.depth, VMErrorFromErr(err))
}
}
// this function must execute _after_: the `CaptureState` needs the stacks before
Expand Down Expand Up @@ -299,7 +299,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}
if in.cfg.Debug {
in.cfg.Tracer.OnGasChange(gasCopy, gasCopy-cost, GasChangeCallOpCode)
in.cfg.Tracer.CaptureState(_pc, op, gasCopy, cost, callContext, in.returnData, in.depth, err) //nolint:errcheck
in.cfg.Tracer.CaptureState(_pc, op, gasCopy, cost, callContext, in.returnData, in.depth, VMErrorFromErr(err)) //nolint:errcheck
logged = true
}
// execute the operation
Expand Down
3 changes: 1 addition & 2 deletions eth/tracers/js/goja.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ type jsTracer struct {
activePrecompiles []libcommon.Address // List of active precompiles at current block
traceStep bool // True if tracer object exposes a `step()` method
traceFrame bool // True if tracer object exposes the `enter()` and `exit()` methods
gasLimit uint64 // Amount of gas bought for the whole tx
err error // Any error that should stop tracing
obj *goja.Object // Trace object

Expand Down Expand Up @@ -225,7 +224,7 @@ func (t *jsTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {
rules := env.ChainConfig().Rules(env.Context().BlockNumber, env.Context().Time)
t.activePrecompiles = vm.ActivePrecompiles(rules)
t.ctx["block"] = t.vm.ToValue(t.env.Context().BlockNumber)
t.ctx["gasPrice"] = t.vm.ToValue(t.env.TxContext().GasPrice)
t.ctx["gasPrice"] = t.vm.ToValue(t.env.TxContext().GasPrice.ToBig())
}

// CaptureTxEnd implements the Tracer interface and is invoked at the end of
Expand Down

0 comments on commit d9dba75

Please sign in to comment.