Skip to content

Commit

Permalink
fix: revert reason handling fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ptrus committed Oct 24, 2023
1 parent ec2814a commit 1c3cfa6
Showing 1 changed file with 54 additions and 27 deletions.
81 changes: 54 additions & 27 deletions rpc/eth/revert_errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package eth
import (
"context"
"encoding/base64"
"errors"
"fmt"
"strings"

"github.com/ethereum/go-ethereum/accounts/abi"
Expand All @@ -17,51 +19,76 @@ const (
revertErrorPrefix = "reverted: "
)

type RevertError struct {
type revertError struct {
error
Reason string `json:"reason"`
// Revert reason hex encoded.
reason string
}

// ErrorData returns the ABI encoded error reason.
func (e *RevertError) ErrorData() interface{} {
return e.Reason
// ErrorData returns the hex encoded error reason.
func (e *revertError) ErrorData() interface{} {
return e.reason

Check warning on line 30 in rpc/eth/revert_errors.go

View check run for this annotation

Codecov / codecov/patch

rpc/eth/revert_errors.go#L29-L30

Added lines #L29 - L30 were not covered by tests
}

func (api *publicAPI) newRevertErrorV1(revertErr error) *RevertError {
// ABI encoded function.
abiReason := []byte{0x08, 0xc3, 0x79, 0xa0} // Keccak256("Error(string)")
// ErrorCode is the error code for revert errors.
func (e *revertError) ErrorCode() int {
return 3

Check warning on line 35 in rpc/eth/revert_errors.go

View check run for this annotation

Codecov / codecov/patch

rpc/eth/revert_errors.go#L34-L35

Added lines #L34 - L35 were not covered by tests
}

// ABI encode the revert Reason string.
func (api *publicAPI) newRevertErrorV1(revertErr error) *revertError {

Check warning on line 38 in rpc/eth/revert_errors.go

View check run for this annotation

Codecov / codecov/patch

rpc/eth/revert_errors.go#L38

Added line #L38 was not covered by tests
revertReason := strings.TrimPrefix(revertErr.Error(), revertErrorPrefix)
typ, _ := abi.NewType("string", "", nil)
unpacked, err := (abi.Arguments{{Type: typ}}).Pack(revertReason)
if err != nil {
api.Logger.Error("failed to encode V1 revert error", "revert_reason", revertReason, "err", err)
return &RevertError{
error: revertErr,

var reason string
var err error
switch revertReason {
case "":
err = errors.New("execution reverted")
default:
err = fmt.Errorf("execution reverted: %v", revertReason)

// ABI encode the revert Reason string.
abiReason := []byte{0x08, 0xc3, 0x79, 0xa0} // Keccak256("Error(string)")
typ, _ := abi.NewType("string", "", nil)
packed, packErr := (abi.Arguments{{Type: typ}}).Pack(revertReason)
if packErr != nil {
api.Logger.Error("failed to encode V1 revert error", "revert_reason", revertReason, "err", packErr)
err = revertErr
break

Check warning on line 56 in rpc/eth/revert_errors.go

View check run for this annotation

Codecov / codecov/patch

rpc/eth/revert_errors.go#L40-L56

Added lines #L40 - L56 were not covered by tests
}
abiReason = append(abiReason, packed...)
reason = hexutil.Encode(abiReason)

Check warning on line 59 in rpc/eth/revert_errors.go

View check run for this annotation

Codecov / codecov/patch

rpc/eth/revert_errors.go#L58-L59

Added lines #L58 - L59 were not covered by tests
}
abiReason = append(abiReason, unpacked...)

return &RevertError{
error: revertErr,
Reason: hexutil.Encode(abiReason),
return &revertError{
error: err,
reason: reason,

Check warning on line 64 in rpc/eth/revert_errors.go

View check run for this annotation

Codecov / codecov/patch

rpc/eth/revert_errors.go#L62-L64

Added lines #L62 - L64 were not covered by tests
}
}

func (api *publicAPI) newRevertErrorV2(revertErr error) *RevertError {
func (api *publicAPI) newRevertErrorV2(revertErr error) *revertError {
// V2: 'reverted: <base64 encoded revert error>'.

// Strip the 'reverted:' prefix and decode the error.

Check warning on line 71 in rpc/eth/revert_errors.go

View check run for this annotation

Codecov / codecov/patch

rpc/eth/revert_errors.go#L68-L71

Added lines #L68 - L71 were not covered by tests
b64Reason := strings.TrimPrefix(revertErr.Error(), revertErrorPrefix)
reason, err := base64.RawStdEncoding.DecodeString(b64Reason)
rawReason, err := base64.RawStdEncoding.DecodeString(b64Reason)

Check warning on line 73 in rpc/eth/revert_errors.go

View check run for this annotation

Codecov / codecov/patch

rpc/eth/revert_errors.go#L73

Added line #L73 was not covered by tests
if err != nil {
api.Logger.Error("failed to encode V2 revert error", "revert_reason", b64Reason, "err", err)
return &RevertError{
return &revertError{

Check warning on line 76 in rpc/eth/revert_errors.go

View check run for this annotation

Codecov / codecov/patch

rpc/eth/revert_errors.go#L76

Added line #L76 was not covered by tests
error: revertErr,
}
}

return &RevertError{
error: revertErr,
Reason: hexutil.Encode(reason),
// Try to parse the reason from the revert error.
reason, unpackErr := abi.UnpackRevert(rawReason)
switch unpackErr {
case nil:
err = fmt.Errorf("execution reverted: %v", reason)
default:
err = errors.New("execution reverted")

Check warning on line 87 in rpc/eth/revert_errors.go

View check run for this annotation

Codecov / codecov/patch

rpc/eth/revert_errors.go#L82-L87

Added lines #L82 - L87 were not covered by tests
}
return &revertError{
error: err,
reason: hexutil.Encode(rawReason),

Check warning on line 91 in rpc/eth/revert_errors.go

View check run for this annotation

Codecov / codecov/patch

rpc/eth/revert_errors.go#L89-L91

Added lines #L89 - L91 were not covered by tests
}
}

Expand Down Expand Up @@ -91,7 +118,7 @@ func (api *publicAPI) handleCallFailure(ctx context.Context, logger *logging.Log
// In EVM module version 1 the SDK decoded and returned readable revert reasons.
// Since EVM version 2 the raw data is just base64 encoded and returned.
// Changed in https://github.com/oasisprotocol/oasis-sdk/pull/1479
var revertErr *RevertError
var revertErr *revertError

Check warning on line 121 in rpc/eth/revert_errors.go

View check run for this annotation

Codecov / codecov/patch

rpc/eth/revert_errors.go#L121

Added line #L121 was not covered by tests
switch {
case rtInfo == nil || rtInfo.Modules[evm.ModuleName].Version > 1:
// EVM version 2 onward (if no runtime info assume >1).
Expand All @@ -100,6 +127,6 @@ func (api *publicAPI) handleCallFailure(ctx context.Context, logger *logging.Log
// EVM version 1 (deprecated).
revertErr = api.newRevertErrorV1(err)
}
logger.Debug("failed to execute SimulateCall, reverted", "err", err, "reason", revertErr.Reason)
logger.Debug("failed to execute SimulateCall, reverted", "err", err, "reason", revertErr.reason)

Check warning on line 130 in rpc/eth/revert_errors.go

View check run for this annotation

Codecov / codecov/patch

rpc/eth/revert_errors.go#L130

Added line #L130 was not covered by tests
return revertErr
}

0 comments on commit 1c3cfa6

Please sign in to comment.