Skip to content

Commit e9393df

Browse files
committed
fix(eth): present revert "data" as plain bytes
decode the cbor return value for reverts and present that, as is expected by Ethereum tooling
1 parent f0f5c76 commit e9393df

File tree

4 files changed

+42
-44
lines changed

4 files changed

+42
-44
lines changed

api/api_errors.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/filecoin-project/go-jsonrpc"
1111
"github.com/filecoin-project/go-state-types/abi"
12+
"github.com/filecoin-project/go-state-types/exitcode"
1213
)
1314

1415
var invalidExecutionRevertedMsg = xerrors.New("invalid execution reverted error")
@@ -160,10 +161,10 @@ func (e *ErrExecutionReverted) ToJSONRPCError() (jsonrpc.JSONRPCError, error) {
160161
}
161162

162163
// NewErrExecutionReverted creates a new ErrExecutionReverted with the given reason.
163-
func NewErrExecutionReverted(reason string) *ErrExecutionReverted {
164+
func NewErrExecutionReverted(exitCode exitcode.ExitCode, error, reason string, data []byte) *ErrExecutionReverted {
164165
return &ErrExecutionReverted{
165-
Message: "execution reverted",
166-
Data: reason,
166+
Message: fmt.Sprintf("message execution failed (exit=[%s], revert reason=[%s], vm error=[%s])", exitCode, reason, error),
167+
Data: fmt.Sprintf("0x%x", data),
167168
}
168169
}
169170

itests/fevm_test.go

+13-13
Original file line numberDiff line numberDiff line change
@@ -1031,10 +1031,10 @@ func TestFEVMErrorParsing(t *testing.T) {
10311031
require.NoError(t, err)
10321032
customError := ethtypes.EthBytes(kit.CalcFuncSignature("CustomError()")).String()
10331033
for sig, expected := range map[string]string{
1034-
"failRevertEmpty()": "none",
1035-
"failRevertReason()": "Error(my reason)",
1036-
"failAssert()": "Assert()",
1037-
"failDivZero()": "DivideByZero()",
1034+
"failRevertEmpty()": "0x",
1035+
"failRevertReason()": fmt.Sprintf("%x", []byte("my reason")),
1036+
"failAssert()": "0x4e487b710000000000000000000000000000000000000000000000000000000000000001", // Assert()
1037+
"failDivZero()": "0x4e487b710000000000000000000000000000000000000000000000000000000000000012", // DivideByZero()
10381038
"failCustom()": customError,
10391039
} {
10401040
sig := sig
@@ -1602,10 +1602,10 @@ func TestEthCall(t *testing.T) {
16021602

16031603
var dataErr *api.ErrExecutionReverted
16041604
require.ErrorAs(t, err, &dataErr, "Expected error to be ErrExecutionReverted")
1605-
require.Contains(t, dataErr.Message, "execution reverted", "Expected 'execution reverted' message")
1605+
require.Regexp(t, `message execution failed [\s\S]+\[DivideByZero\(\)\]`, dataErr.Message)
16061606

16071607
// Get the error data
1608-
require.Equal(t, dataErr.Data, "DivideByZero()", "Expected error data to contain 'DivideByZero()'")
1608+
require.Equal(t, dataErr.Data, "0x4e487b710000000000000000000000000000000000000000000000000000000000000012", "Expected error data to contain 'DivideByZero()'")
16091609
})
16101610
}
16111611

@@ -1627,12 +1627,12 @@ func TestEthEstimateGas(t *testing.T) {
16271627
name string
16281628
function string
16291629
expectedError string
1630-
expectedErrMsg string
1630+
expectedErrMsg interface{}
16311631
}{
1632-
{"DivideByZero", "failDivZero()", "DivideByZero()", "execution reverted"},
1633-
{"Assert", "failAssert()", "Assert()", "execution reverted"},
1634-
{"RevertWithReason", "failRevertReason()", "Error(my reason)", "execution reverted"},
1635-
{"RevertEmpty", "failRevertEmpty()", "", "execution reverted"},
1632+
{"DivideByZero", "failDivZero()", "0x4e487b710000000000000000000000000000000000000000000000000000000000000012", `message execution failed [\s\S]+\[DivideByZero\(\)\]`},
1633+
{"Assert", "failAssert()", "0x4e487b710000000000000000000000000000000000000000000000000000000000000001", `message execution failed [\s\S]+\[Assert\(\)\]`},
1634+
{"RevertWithReason", "failRevertReason()", fmt.Sprintf("%x", []byte("my reason")), `message execution failed [\s\S]+\[Error\(my reason\)\]`},
1635+
{"RevertEmpty", "failRevertEmpty()", "0x", `message execution failed [\s\S]+\[none\]`},
16361636
}
16371637

16381638
for _, tc := range testCases {
@@ -1654,8 +1654,8 @@ func TestEthEstimateGas(t *testing.T) {
16541654
require.Error(t, err)
16551655
var dataErr *api.ErrExecutionReverted
16561656
require.ErrorAs(t, err, &dataErr, "Expected error to be ErrExecutionReverted")
1657-
require.Equal(t, tc.expectedErrMsg, dataErr.Message)
1658-
require.Contains(t, tc.expectedError, dataErr.Data)
1657+
require.Regexp(t, tc.expectedErrMsg, dataErr.Message)
1658+
require.Contains(t, dataErr.Data, tc.expectedError)
16591659
}
16601660
})
16611661
}

node/impl/full/eth.go

+10-3
Original file line numberDiff line numberDiff line change
@@ -1496,9 +1496,16 @@ func (a *EthModule) applyMessage(ctx context.Context, msg *types.Message, tsk ty
14961496
}
14971497

14981498
if res.MsgRct.ExitCode.IsError() {
1499-
return nil, api.NewErrExecutionReverted(
1500-
parseEthRevert(res.MsgRct.Return),
1501-
)
1499+
reason := "none"
1500+
var cbytes abi.CborBytes
1501+
if err := cbytes.UnmarshalCBOR(bytes.NewReader(res.MsgRct.Return)); err != nil {
1502+
log.Warnw("failed to unmarshal cbor bytes from message receipt return", "error", err)
1503+
reason = "ERROR: revert reason is not cbor encoded bytes"
1504+
} // else leave as empty bytes
1505+
if len(cbytes) > 0 {
1506+
reason = parseEthRevert(cbytes)
1507+
}
1508+
return nil, api.NewErrExecutionReverted(res.MsgRct.ExitCode, reason, res.Error, cbytes)
15021509
}
15031510

15041511
return res, nil

node/impl/full/eth_utils.go

+15-25
Original file line numberDiff line numberDiff line change
@@ -331,62 +331,52 @@ var panicErrorCodes = map[uint64]string{
331331
//
332332
// See https://docs.soliditylang.org/en/latest/control-structures.html#panic-via-assert-and-error-via-require
333333
func parseEthRevert(ret []byte) string {
334-
if len(ret) == 0 {
335-
return "none"
336-
}
337-
var cbytes abi.CborBytes
338-
if err := cbytes.UnmarshalCBOR(bytes.NewReader(ret)); err != nil {
339-
return "ERROR: revert reason is not cbor encoded bytes"
340-
}
341-
if len(cbytes) == 0 {
342-
return "none"
343-
}
344334
// If it's not long enough to contain an ABI encoded response, return immediately.
345-
if len(cbytes) < 4+32 {
346-
return ethtypes.EthBytes(cbytes).String()
335+
if len(ret) < 4+32 {
336+
return ethtypes.EthBytes(ret).String()
347337
}
348-
switch string(cbytes[:4]) {
338+
switch string(ret[:4]) {
349339
case panicFunctionSelector:
350-
cbytes := cbytes[4 : 4+32]
340+
ret := ret[4 : 4+32]
351341
// Read the and check the code.
352-
code, err := ethtypes.EthUint64FromBytes(cbytes)
342+
code, err := ethtypes.EthUint64FromBytes(ret)
353343
if err != nil {
354344
// If it's too big, just return the raw value.
355-
codeInt := big.PositiveFromUnsignedBytes(cbytes)
345+
codeInt := big.PositiveFromUnsignedBytes(ret)
356346
return fmt.Sprintf("Panic(%s)", ethtypes.EthBigInt(codeInt).String())
357347
}
358348
if s, ok := panicErrorCodes[uint64(code)]; ok {
359349
return s
360350
}
361351
return fmt.Sprintf("Panic(0x%x)", code)
362352
case errorFunctionSelector:
363-
cbytes := cbytes[4:]
364-
cbytesLen := ethtypes.EthUint64(len(cbytes))
353+
ret := ret[4:]
354+
retLen := ethtypes.EthUint64(len(ret))
365355
// Read the and check the offset.
366-
offset, err := ethtypes.EthUint64FromBytes(cbytes[:32])
356+
offset, err := ethtypes.EthUint64FromBytes(ret[:32])
367357
if err != nil {
368358
break
369359
}
370-
if cbytesLen < offset {
360+
if retLen < offset {
371361
break
372362
}
373363

374364
// Read and check the length.
375-
if cbytesLen-offset < 32 {
365+
if retLen-offset < 32 {
376366
break
377367
}
378368
start := offset + 32
379-
length, err := ethtypes.EthUint64FromBytes(cbytes[offset : offset+32])
369+
length, err := ethtypes.EthUint64FromBytes(ret[offset : offset+32])
380370
if err != nil {
381371
break
382372
}
383-
if cbytesLen-start < length {
373+
if retLen-start < length {
384374
break
385375
}
386376
// Slice the error message.
387-
return fmt.Sprintf("Error(%s)", cbytes[start:start+length])
377+
return fmt.Sprintf("Error(%s)", ret[start:start+length])
388378
}
389-
return ethtypes.EthBytes(cbytes).String()
379+
return ethtypes.EthBytes(ret).String()
390380
}
391381

392382
// lookupEthAddress makes its best effort at finding the Ethereum address for a

0 commit comments

Comments
 (0)