evm: update error format (#350)
* return geth error format * fix format in gasestimate * deal with other evm errors * fix import * fix lint * add test Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
parent
695027cb2a
commit
63aa0de1e8
@ -8,6 +8,10 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
@ -26,7 +30,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
||||||
"github.com/tharsis/ethermint/crypto/hd"
|
"github.com/tharsis/ethermint/crypto/hd"
|
||||||
@ -559,18 +562,12 @@ func (e *PublicAPI) doCall(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(res.VmError) > 0 {
|
|
||||||
if res.VmError == vm.ErrExecutionReverted.Error() {
|
|
||||||
return nil, evmtypes.NewExecErrorWithReason(res.Ret)
|
|
||||||
}
|
|
||||||
return nil, errors.New(res.VmError)
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.Failed() {
|
if res.Failed() {
|
||||||
if res.VmError == vm.ErrExecutionReverted.Error() {
|
if res.VmError != vm.ErrExecutionReverted.Error() {
|
||||||
return nil, evmtypes.NewExecErrorWithReason(res.Ret)
|
return nil, status.Error(codes.Internal, res.VmError)
|
||||||
}
|
}
|
||||||
return nil, errors.New(res.VmError)
|
return nil, evmtypes.NewExecErrorWithReason(res.Ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
|
@ -405,7 +405,7 @@ func (k Keeper) EstimateGas(c context.Context, req *types.EthCallRequest) (*type
|
|||||||
|
|
||||||
// Binary search the gas requirement, as it may be higher than the amount used
|
// Binary search the gas requirement, as it may be higher than the amount used
|
||||||
var (
|
var (
|
||||||
lo uint64 = ethparams.TxGas - 1
|
lo = ethparams.TxGas - 1
|
||||||
hi uint64
|
hi uint64
|
||||||
cap uint64
|
cap uint64
|
||||||
)
|
)
|
||||||
@ -478,7 +478,7 @@ func (k Keeper) EstimateGas(c context.Context, req *types.EthCallRequest) (*type
|
|||||||
if failed {
|
if failed {
|
||||||
if result != nil && result.VmError != vm.ErrOutOfGas.Error() {
|
if result != nil && result.VmError != vm.ErrOutOfGas.Error() {
|
||||||
if result.VmError == vm.ErrExecutionReverted.Error() {
|
if result.VmError == vm.ErrExecutionReverted.Error() {
|
||||||
return nil, status.Error(codes.Internal, types.NewExecErrorWithReason(result.Ret).Error())
|
return nil, types.NewExecErrorWithReason(result.Ret)
|
||||||
}
|
}
|
||||||
return nil, status.Error(codes.Internal, result.VmError)
|
return nil, status.Error(codes.Internal, result.VmError)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
@ -75,12 +79,33 @@ var (
|
|||||||
|
|
||||||
// NewExecErrorWithReason unpacks the revert return bytes and returns a wrapped error
|
// NewExecErrorWithReason unpacks the revert return bytes and returns a wrapped error
|
||||||
// with the return reason.
|
// with the return reason.
|
||||||
func NewExecErrorWithReason(revertReason []byte) error {
|
func NewExecErrorWithReason(revertReason []byte) *RevertError {
|
||||||
hexValue := hexutil.Encode(revertReason)
|
var result = common.CopyBytes(revertReason)
|
||||||
reason, errUnpack := abi.UnpackRevert(revertReason)
|
reason, errUnpack := abi.UnpackRevert(result)
|
||||||
|
err := errors.New("execution reverted")
|
||||||
if errUnpack == nil {
|
if errUnpack == nil {
|
||||||
return sdkerrors.Wrapf(ErrExecutionReverted, "%s: %s", reason, hexValue)
|
err = fmt.Errorf("execution reverted: %v", reason)
|
||||||
|
}
|
||||||
|
return &RevertError{
|
||||||
|
error: err,
|
||||||
|
reason: hexutil.Encode(result),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return sdkerrors.Wrapf(ErrExecutionReverted, "%s", hexValue)
|
|
||||||
|
// RevertError is an API error that encompass an EVM revert with JSON error
|
||||||
|
// code and a binary data blob.
|
||||||
|
type RevertError struct {
|
||||||
|
error
|
||||||
|
reason string // revert reason hex encoded
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorCode returns the JSON error code for a revert.
|
||||||
|
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal
|
||||||
|
func (e *RevertError) ErrorCode() int {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorData returns the hex encoded revert reason.
|
||||||
|
func (e *RevertError) ErrorData() interface{} {
|
||||||
|
return e.reason
|
||||||
}
|
}
|
||||||
|
53
x/evm/types/errors_test.go
Normal file
53
x/evm/types/errors_test.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/status-im/keycard-go/hexutils"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
|
||||||
|
|
||||||
|
func TestNewExecErrorWithReason(t *testing.T) {
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
errorMessage string
|
||||||
|
revertReason []byte
|
||||||
|
data string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Empty reason",
|
||||||
|
"execution reverted",
|
||||||
|
nil,
|
||||||
|
"0x",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"With unpackable reason",
|
||||||
|
"execution reverted",
|
||||||
|
[]byte("a"),
|
||||||
|
"0x61",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"With packable reason but empty reason",
|
||||||
|
"execution reverted",
|
||||||
|
revertSelector,
|
||||||
|
"0x08c379a0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"With packable reason with reason",
|
||||||
|
"execution reverted: COUNTER_TOO_LOW",
|
||||||
|
hexutils.HexToBytes("08C379A00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000F434F554E5445525F544F4F5F4C4F570000000000000000000000000000000000"),
|
||||||
|
"0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000f434f554e5445525f544f4f5f4c4f570000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
|
errWithReason := NewExecErrorWithReason(tc.revertReason)
|
||||||
|
require.Equal(t, tc.errorMessage, errWithReason.Error())
|
||||||
|
require.Equal(t, tc.data, errWithReason.ErrorData())
|
||||||
|
require.Equal(t, 3, errWithReason.ErrorCode())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user