From c30205cae9017bec37ed646af789cbfae02755d8 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Thu, 16 Apr 2020 11:47:39 -0400 Subject: [PATCH] error migration (#254) * migrate errors * ante handler errors * return err instead of panic --- app/ante/ante.go | 65 +++++++++++++------------- types/errors.go | 61 +++++-------------------- x/evm/handler.go | 14 +++--- x/evm/keeper/keeper.go | 16 ++----- x/evm/keeper/querier.go | 81 ++++++++++++++++++--------------- x/evm/module.go | 4 +- x/evm/types/codec.go | 17 +++---- x/evm/types/emint_msg.go | 11 +++-- x/evm/types/msg.go | 19 ++++++-- x/evm/types/state_transition.go | 24 ++++++---- x/evm/types/statedb.go | 14 +++--- 11 files changed, 156 insertions(+), 170 deletions(-) diff --git a/app/ante/ante.go b/app/ante/ante.go index 74015725..2a407fb8 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -7,7 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/auth/ante" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/ethermint/crypto" @@ -38,17 +38,17 @@ func NewAnteHandler(ak auth.AccountKeeper, sk types.SupplyKeeper) sdk.AnteHandle switch castTx := tx.(type) { case auth.StdTx: stdAnte := sdk.ChainAnteDecorators( - ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first - ante.NewMempoolFeeDecorator(), - ante.NewValidateBasicDecorator(), - ante.NewValidateMemoDecorator(ak), - ante.NewConsumeGasForTxSizeDecorator(ak), - ante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators - ante.NewValidateSigCountDecorator(ak), - ante.NewDeductFeeDecorator(ak, sk), - ante.NewSigGasConsumeDecorator(ak, sigGasConsumer), - ante.NewSigVerificationDecorator(ak), - ante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator + authante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first + authante.NewMempoolFeeDecorator(), + authante.NewValidateBasicDecorator(), + authante.NewValidateMemoDecorator(ak), + authante.NewConsumeGasForTxSizeDecorator(ak), + authante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators + authante.NewValidateSigCountDecorator(ak), + authante.NewDeductFeeDecorator(ak, sk), + authante.NewSigGasConsumeDecorator(ak, sigGasConsumer), + authante.NewSigVerificationDecorator(ak), + authante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator ) return stdAnte(ctx, tx, sim) @@ -57,7 +57,7 @@ func NewAnteHandler(ak auth.AccountKeeper, sk types.SupplyKeeper) sdk.AnteHandle return ethAnteHandler(ctx, ak, sk, &castTx, sim) default: - return ctx, sdk.ErrInternal(fmt.Sprintf("transaction type invalid: %T", tx)) + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx) } } } @@ -122,10 +122,11 @@ func ethAnteHandler( if r := recover(); r != nil { switch rType := r.(type) { case sdk.ErrorOutOfGas: - log := fmt.Sprintf("out of gas in location: %v; gasUsed: %d", + err = sdkerrors.Wrapf( + sdkerrors.ErrOutOfGas, + "out of gas in location: %v; gasUsed: %d", rType.Descriptor, ctx.GasMeter().GasConsumed(), ) - err = sdk.ErrOutOfGas(log) default: panic(r) } @@ -201,13 +202,13 @@ func validateSignature(ctx sdk.Context, ethTxMsg *evmtypes.MsgEthereumTx) (sdk.A // parse the chainID from a string to a base-10 integer chainID, ok := new(big.Int).SetString(ctx.ChainID(), 10) if !ok { - return nil, emint.ErrInvalidChainID(fmt.Sprintf("invalid chainID: %s", ctx.ChainID())) + return nil, sdkerrors.Wrap(emint.ErrInvalidChainID, ctx.ChainID()) } // validate sender/signature signer, err := ethTxMsg.VerifySig(chainID) if err != nil { - return nil, sdk.ErrUnauthorized(fmt.Sprintf("signature verification failed: %s", err)) + return nil, sdkerrors.Wrap(err, "signature verification failed") } return sdk.AccAddress(signer.Bytes()), nil @@ -221,12 +222,12 @@ func validateSignature(ctx sdk.Context, ethTxMsg *evmtypes.MsgEthereumTx) (sdk.A func validateIntrinsicGas(ethTxMsg *evmtypes.MsgEthereumTx) error { gas, err := ethcore.IntrinsicGas(ethTxMsg.Data.Payload, ethTxMsg.To() == nil, true) if err != nil { - return sdk.ErrInternal(fmt.Sprintf("failed to compute intrinsic gas cost: %s", err)) + return sdkerrors.Wrap(err, "failed to compute intrinsic gas cost") } if ethTxMsg.Data.GasLimit < gas { - return sdk.ErrInternal( - fmt.Sprintf("intrinsic gas too low: %d < %d", ethTxMsg.Data.GasLimit, gas), + return fmt.Errorf( + "intrinsic gas too low: %d < %d", ethTxMsg.Data.GasLimit, gas, ) } @@ -243,10 +244,9 @@ func validateAccount( // on InitChain make sure account number == 0 if ctx.BlockHeight() == 0 && acc.GetAccountNumber() != 0 { - return sdk.ErrInternal( - fmt.Sprintf( - "invalid account number for height zero (got %d)", acc.GetAccountNumber(), - ), + return sdkerrors.Wrapf( + sdkerrors.ErrInvalidSequence, + "invalid account number for height zero (got %d)", acc.GetAccountNumber(), ) } @@ -258,8 +258,9 @@ func validateAccount( // validate sender has enough funds balance := acc.GetCoins().AmountOf(emint.DenomDefault) if balance.BigInt().Cmp(ethTxMsg.Cost()) < 0 { - return sdk.ErrInsufficientFunds( - fmt.Sprintf("insufficient funds: %s < %s", balance, ethTxMsg.Cost()), + return sdkerrors.Wrapf( + sdkerrors.ErrInsufficientFunds, + "%s < %s%s", balance.String(), ethTxMsg.Cost().String(), emint.DenomDefault, ) } @@ -274,8 +275,9 @@ func checkNonce( // current nonce). seq := acc.GetSequence() if ethTxMsg.Data.AccountNonce != seq { - return sdk.ErrInvalidSequence( - fmt.Sprintf("invalid nonce; got %d, expected %d", ethTxMsg.Data.AccountNonce, seq), + return sdkerrors.Wrapf( + sdkerrors.ErrInvalidSequence, + "got nonce %d, expected %d", ethTxMsg.Data.AccountNonce, seq, ) } @@ -302,10 +304,9 @@ func ensureSufficientMempoolFees(ctx sdk.Context, ethTxMsg *evmtypes.MsgEthereum // it is assumed that the minimum fees will only include the single valid denom if !ctx.MinGasPrices().IsZero() && !allGTE { // reject the transaction that does not meet the minimum fee - return sdk.ErrInsufficientFee( - fmt.Sprintf( - "insufficient fee, got: %q required: %q", fee, ctx.MinGasPrices(), - ), + return sdkerrors.Wrapf( + sdkerrors.ErrInsufficientFee, + "got: %q required: %q", fee, ctx.MinGasPrices(), ) } diff --git a/types/errors.go b/types/errors.go index 7ad47505..a968cff5 100644 --- a/types/errors.go +++ b/types/errors.go @@ -1,60 +1,21 @@ package types import ( - sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -// Ethermint error codes const ( - // DefaultCodespace reserves a Codespace for Ethermint. - DefaultCodespace sdk.CodespaceType = "ethermint" - - CodeInvalidValue sdk.CodeType = 1 - CodeInvalidChainID sdk.CodeType = 2 - CodeInvalidSender sdk.CodeType = 3 - CodeVMExecution sdk.CodeType = 4 - CodeInvalidNonce sdk.CodeType = 5 + // RootCodespace is the codespace for all errors defined in this package + RootCodespace = "ethermint" ) -// CodeToDefaultMsg takes the CodeType variable and returns the error string -func CodeToDefaultMsg(code sdk.CodeType) string { - switch code { - case CodeInvalidValue: - return "invalid value" - case CodeInvalidChainID: - return "invalid chain ID" - case CodeInvalidSender: - return "could not derive sender from transaction" - case CodeVMExecution: - return "error while executing evm transaction" - case CodeInvalidNonce: - return "invalid nonce" - default: - return sdk.CodeToDefaultMsg(code) - } -} +var ( + // ErrInvalidValue returns an error resulting from an invalid value. + ErrInvalidValue = sdkerrors.Register(RootCodespace, 1, "invalid value") -// ErrInvalidValue returns a standardized SDK error resulting from an invalid value. -func ErrInvalidValue(msg string) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeInvalidValue, msg) -} + // ErrInvalidChainID returns an error resulting from an invalid chain ID. + ErrInvalidChainID = sdkerrors.Register(RootCodespace, 2, "invalid chain ID") -// ErrInvalidChainID returns a standardized SDK error resulting from an invalid chain ID. -func ErrInvalidChainID(msg string) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeInvalidChainID, msg) -} - -// ErrInvalidSender returns a standardized SDK error resulting from an invalid transaction sender. -func ErrInvalidSender(msg string) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeInvalidSender, msg) -} - -// ErrVMExecution returns a standardized SDK error resulting from an error in EVM execution. -func ErrVMExecution(msg string) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeVMExecution, msg) -} - -// ErrVMExecution returns a standardized SDK error resulting from an error in EVM execution. -func ErrInvalidNonce(msg string) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeInvalidNonce, msg) -} + // ErrVMExecution returns an error resulting from an error in EVM execution. + ErrVMExecution = sdkerrors.Register(RootCodespace, 3, "error while executing evm transaction") +) diff --git a/x/evm/handler.go b/x/evm/handler.go index 69cea3ad..ac0a7c07 100644 --- a/x/evm/handler.go +++ b/x/evm/handler.go @@ -1,12 +1,12 @@ package evm import ( - "fmt" "math/big" "github.com/ethereum/go-ethereum/common" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" emint "github.com/cosmos/ethermint/types" "github.com/cosmos/ethermint/x/evm/types" @@ -16,25 +16,26 @@ import ( // NewHandler returns a handler for Ethermint type messages. func NewHandler(k Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { case types.MsgEthereumTx: return HandleMsgEthereumTx(ctx, k, msg) case types.MsgEthermint: return HandleMsgEthermint(ctx, k, msg) default: - errMsg := fmt.Sprintf("unrecognized ethermint msg type: %v", msg.Type()) - return sdk.ErrUnknownRequest(errMsg).Result() + return sdk.ResultFromError( + sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg), + ) } } } // HandleMsgEthereumTx handles an Ethereum specific tx func HandleMsgEthereumTx(ctx sdk.Context, k Keeper, msg types.MsgEthereumTx) sdk.Result { - ctx = ctx.WithEventManager(sdk.NewEventManager()) // parse the chainID from a string to a base-10 integer intChainID, ok := new(big.Int).SetString(ctx.ChainID(), 10) if !ok { - return emint.ErrInvalidChainID(fmt.Sprintf("invalid chainID: %s", ctx.ChainID())).Result() + return sdk.ResultFromError(sdkerrors.Wrap(emint.ErrInvalidChainID, ctx.ChainID())) } // Verify signature and retrieve sender address @@ -108,11 +109,10 @@ func HandleMsgEthereumTx(ctx sdk.Context, k Keeper, msg types.MsgEthereumTx) sdk // HandleMsgEthermint handles a MsgEthermint func HandleMsgEthermint(ctx sdk.Context, k Keeper, msg types.MsgEthermint) sdk.Result { - ctx = ctx.WithEventManager(sdk.NewEventManager()) // parse the chainID from a string to a base-10 integer intChainID, ok := new(big.Int).SetString(ctx.ChainID(), 10) if !ok { - return emint.ErrInvalidChainID(fmt.Sprintf("invalid chainID: %s", ctx.ChainID())).Result() + return sdk.ResultFromError(sdkerrors.Wrap(emint.ErrInvalidChainID, ctx.ChainID())) } txHash := tmtypes.Tx(ctx.TxBytes()).Hash() diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 234c946c..28e057b3 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -100,7 +100,7 @@ func (k *Keeper) GetBlockBloomMapping(ctx sdk.Context, height int64) (ethtypes.B return ethtypes.BytesToBloom(bloom), nil } -// SetBlockLogs sets the transaction's logs in the KVStore +// SetTransactionLogs sets the transaction's logs in the KVStore func (k *Keeper) SetTransactionLogs(ctx sdk.Context, logs []*ethtypes.Log, hash []byte) error { store := ctx.KVStore(k.blockKey) encLogs, err := types.EncodeLogs(logs) @@ -112,7 +112,7 @@ func (k *Keeper) SetTransactionLogs(ctx sdk.Context, logs []*ethtypes.Log, hash return nil } -// GetBlockLogs gets the logs for a transaction from the KVStore +// GetTransactionLogs gets the logs for a transaction from the KVStore func (k *Keeper) GetTransactionLogs(ctx sdk.Context, hash []byte) ([]*ethtypes.Log, error) { store := ctx.KVStore(k.blockKey) encLogs := store.Get(types.LogsKey(hash)) @@ -243,12 +243,7 @@ func (k *Keeper) GetCommittedState(ctx sdk.Context, addr ethcmn.Address, hash et // GetLogs calls CommitStateDB.GetLogs using the passed in context func (k *Keeper) GetLogs(ctx sdk.Context, hash ethcmn.Hash) ([]*ethtypes.Log, error) { - logs, err := k.CommitStateDB.WithContext(ctx).GetLogs(hash) - if err != nil { - return nil, err - } - - return logs, nil + return k.CommitStateDB.WithContext(ctx).GetLogs(hash) } // AllLogs calls CommitStateDB.AllLogs using the passed in context @@ -293,10 +288,7 @@ func (k *Keeper) Finalise(ctx sdk.Context, deleteEmptyObjects bool) error { // IntermediateRoot calls CommitStateDB.IntermediateRoot using the passed in context func (k *Keeper) IntermediateRoot(ctx sdk.Context, deleteEmptyObjects bool) error { _, err := k.CommitStateDB.WithContext(ctx).IntermediateRoot(deleteEmptyObjects) - if err != nil { - return err - } - return nil + return err } // ---------------------------------------------------------------------------- diff --git a/x/evm/keeper/querier.go b/x/evm/keeper/querier.go index 8509e9dc..0b1dd4d5 100644 --- a/x/evm/keeper/querier.go +++ b/x/evm/keeper/querier.go @@ -6,6 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/ethermint/utils" "github.com/cosmos/ethermint/version" "github.com/cosmos/ethermint/x/evm/types" @@ -17,167 +18,173 @@ import ( // NewQuerier is the module level router for state queries func NewQuerier(keeper Keeper) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) { + var ( + bz []byte + err error + ) switch path[0] { case types.QueryProtocolVersion: - return queryProtocolVersion(keeper) + bz, err = queryProtocolVersion(keeper) case types.QueryBalance: - return queryBalance(ctx, path, keeper) + bz, err = queryBalance(ctx, path, keeper) case types.QueryBlockNumber: - return queryBlockNumber(ctx, keeper) + bz, err = queryBlockNumber(ctx, keeper) case types.QueryStorage: - return queryStorage(ctx, path, keeper) + bz, err = queryStorage(ctx, path, keeper) case types.QueryCode: - return queryCode(ctx, path, keeper) + bz, err = queryCode(ctx, path, keeper) case types.QueryNonce: - return queryNonce(ctx, path, keeper) + bz, err = queryNonce(ctx, path, keeper) case types.QueryHashToHeight: - return queryHashToHeight(ctx, path, keeper) + bz, err = queryHashToHeight(ctx, path, keeper) case types.QueryTxLogs: - return queryTxLogs(ctx, path, keeper) + bz, err = queryTxLogs(ctx, path, keeper) case types.QueryLogsBloom: - return queryBlockLogsBloom(ctx, path, keeper) + bz, err = queryBlockLogsBloom(ctx, path, keeper) case types.QueryLogs: - return queryLogs(ctx, keeper) + bz, err = queryLogs(ctx, keeper) case types.QueryAccount: - return queryAccount(ctx, path, keeper) + bz, err = queryAccount(ctx, path, keeper) default: - return nil, sdk.ErrUnknownRequest("unknown query endpoint") + bz, err = nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown query endpoint") } + + return bz, sdk.ConvertError(err) } } -func queryProtocolVersion(keeper Keeper) ([]byte, sdk.Error) { +func queryProtocolVersion(keeper Keeper) ([]byte, error) { vers := version.ProtocolVersion bz, err := codec.MarshalJSONIndent(keeper.cdc, hexutil.Uint(vers)) if err != nil { - return nil, sdk.ErrInternal(err.Error()) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryBalance(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) { +func queryBalance(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { addr := ethcmn.HexToAddress(path[1]) balance := keeper.GetBalance(ctx, addr) res := types.QueryResBalance{Balance: utils.MarshalBigInt(balance)} bz, err := codec.MarshalJSONIndent(keeper.cdc, res) if err != nil { - return nil, sdk.ErrInternal(err.Error()) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryBlockNumber(ctx sdk.Context, keeper Keeper) ([]byte, sdk.Error) { +func queryBlockNumber(ctx sdk.Context, keeper Keeper) ([]byte, error) { num := ctx.BlockHeight() bnRes := types.QueryResBlockNumber{Number: num} bz, err := codec.MarshalJSONIndent(keeper.cdc, bnRes) if err != nil { - return nil, sdk.ErrInternal(err.Error()) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryStorage(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) { +func queryStorage(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { addr := ethcmn.HexToAddress(path[1]) key := ethcmn.HexToHash(path[2]) val := keeper.GetState(ctx, addr, key) res := types.QueryResStorage{Value: val.Bytes()} bz, err := codec.MarshalJSONIndent(keeper.cdc, res) if err != nil { - return nil, sdk.ErrInternal(err.Error()) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryCode(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) { +func queryCode(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { addr := ethcmn.HexToAddress(path[1]) code := keeper.GetCode(ctx, addr) res := types.QueryResCode{Code: code} bz, err := codec.MarshalJSONIndent(keeper.cdc, res) if err != nil { - return nil, sdk.ErrInternal(err.Error()) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryNonce(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) { +func queryNonce(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { addr := ethcmn.HexToAddress(path[1]) nonce := keeper.GetNonce(ctx, addr) nRes := types.QueryResNonce{Nonce: nonce} bz, err := codec.MarshalJSONIndent(keeper.cdc, nRes) if err != nil { - return nil, sdk.ErrInternal(err.Error()) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryHashToHeight(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) { +func queryHashToHeight(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { blockHash := ethcmn.FromHex(path[1]) blockNumber := keeper.GetBlockHashMapping(ctx, blockHash) res := types.QueryResBlockNumber{Number: blockNumber} bz, err := codec.MarshalJSONIndent(keeper.cdc, res) if err != nil { - return nil, sdk.ErrInternal(err.Error()) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryBlockLogsBloom(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) { +func queryBlockLogsBloom(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { num, err := strconv.ParseInt(path[1], 10, 64) if err != nil { - return nil, sdk.ErrInternal(fmt.Sprintf("could not unmarshall block number: %s", err.Error())) + return nil, fmt.Errorf("could not unmarshal block number: %w", err) } bloom, err := keeper.GetBlockBloomMapping(ctx, num) if err != nil { - return nil, sdk.ErrInternal(fmt.Sprintf("failed to get block bloom mapping: %s", err.Error())) + return nil, fmt.Errorf("failed to get block bloom mapping: %w", err) } res := types.QueryBloomFilter{Bloom: bloom} bz, err := codec.MarshalJSONIndent(keeper.cdc, res) if err != nil { - return nil, sdk.ErrInternal(err.Error()) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryTxLogs(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) { +func queryTxLogs(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { txHash := ethcmn.HexToHash(path[1]) logs, err := keeper.GetLogs(ctx, txHash) if err != nil { - return nil, sdk.ErrInternal(err.Error()) + return nil, err } res := types.QueryETHLogs{Logs: logs} bz, err := codec.MarshalJSONIndent(keeper.cdc, res) if err != nil { - return nil, sdk.ErrInternal(err.Error()) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryLogs(ctx sdk.Context, keeper Keeper) ([]byte, sdk.Error) { +func queryLogs(ctx sdk.Context, keeper Keeper) ([]byte, error) { logs := keeper.AllLogs(ctx) res := types.QueryETHLogs{Logs: logs} bz, err := codec.MarshalJSONIndent(keeper.cdc, res) if err != nil { - return nil, sdk.ErrInternal(err.Error()) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryAccount(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) { +func queryAccount(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { addr := ethcmn.HexToAddress(path[1]) so := keeper.GetOrNewStateObject(ctx, addr) @@ -188,7 +195,7 @@ func queryAccount(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Er } bz, err := codec.MarshalJSONIndent(keeper.cdc, res) if err != nil { - return nil, sdk.ErrInternal(err.Error()) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } diff --git a/x/evm/module.go b/x/evm/module.go index 7b24e01b..2abfbd00 100644 --- a/x/evm/module.go +++ b/x/evm/module.go @@ -65,7 +65,9 @@ func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { return cli.GetTxCmd(types.ModuleName, cdc) } -// AppModule is struct that defines variables used within module +//____________________________________________________________________________ + +// AppModule implements an application module for the evm module. type AppModule struct { AppModuleBasic keeper Keeper diff --git a/x/evm/types/codec.go b/x/evm/types/codec.go index 39efba39..bdfe5121 100644 --- a/x/evm/types/codec.go +++ b/x/evm/types/codec.go @@ -4,18 +4,19 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) -// ModuleCdc defines the codec to be used by evm module +// ModuleCdc defines the evm module's codec var ModuleCdc = codec.New() -func init() { - cdc := codec.New() - codec.RegisterCrypto(cdc) - ModuleCdc = cdc.Seal() -} - -// RegisterCodec registers concrete types and interfaces on the given codec. +// RegisterCodec registers all the necessary types and interfaces for the +// evm module func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgEthereumTx{}, "ethermint/MsgEthereumTx", nil) cdc.RegisterConcrete(MsgEthermint{}, "ethermint/MsgEthermint", nil) cdc.RegisterConcrete(EncodableTxData{}, "ethermint/EncodableTxData", nil) } + +func init() { + RegisterCodec(ModuleCdc) + codec.RegisterCrypto(ModuleCdc) + ModuleCdc.Seal() +} diff --git a/x/evm/types/emint_msg.go b/x/evm/types/emint_msg.go index 616de8cf..7b19d3bf 100644 --- a/x/evm/types/emint_msg.go +++ b/x/evm/types/emint_msg.go @@ -1,9 +1,8 @@ package types import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/ethermint/types" ethcmn "github.com/ethereum/go-ethereum/common" @@ -61,12 +60,16 @@ func (msg MsgEthermint) GetSignBytes() []byte { // ValidateBasic runs stateless checks on the message func (msg MsgEthermint) ValidateBasic() sdk.Error { if msg.Price.Sign() != 1 { - return types.ErrInvalidValue(fmt.Sprintf("Price must be positive: %x", msg.Price)) + return sdk.ConvertError( + sdkerrors.Wrapf(types.ErrInvalidValue, "price must be positive %s", msg.Price), + ) } // Amount can be 0 if msg.Amount.Sign() == -1 { - return types.ErrInvalidValue(fmt.Sprintf("amount cannot be negative: %x", msg.Amount)) + return sdk.ConvertError( + sdkerrors.Wrapf(types.ErrInvalidValue, "amount cannot be negative %s", msg.Amount), + ) } return nil diff --git a/x/evm/types/msg.go b/x/evm/types/msg.go index ca7a0a6a..6aa7d5c8 100644 --- a/x/evm/types/msg.go +++ b/x/evm/types/msg.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/ethermint/types" ethcmn "github.com/ethereum/go-ethereum/common" @@ -127,12 +128,16 @@ func (msg MsgEthereumTx) Type() string { return TypeMsgEthereumTx } // checks of a Transaction. If returns an error if validation fails. func (msg MsgEthereumTx) ValidateBasic() sdk.Error { if msg.Data.Price.Sign() != 1 { - return types.ErrInvalidValue(fmt.Sprintf("price must be positive: %x", msg.Data.Price)) + return sdk.ConvertError( + sdkerrors.Wrapf(types.ErrInvalidValue, "price must be positive %s", msg.Data.Price), + ) } // Amount can be 0 if msg.Data.Amount.Sign() == -1 { - return types.ErrInvalidValue(fmt.Sprintf("amount must be positive: %x", msg.Data.Amount)) + return sdk.ConvertError( + sdkerrors.Wrapf(types.ErrInvalidValue, "amount cannot be negative %s", msg.Data.Amount), + ) } return nil @@ -306,12 +311,18 @@ func TxDecoder(cdc *codec.Codec) sdk.TxDecoder { var tx sdk.Tx if len(txBytes) == 0 { - return nil, sdk.ErrTxDecode("txBytes are empty") + return nil, sdk.ConvertError( + sdkerrors.Wrap(sdkerrors.ErrTxDecode, "tx bytes are empty"), + ) } + // sdk.Tx is an interface. The concrete message types + // are registered by MakeTxCodec err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) if err != nil { - return nil, sdk.ErrTxDecode("failed to decode tx").TraceSDK(err.Error()) + return nil, sdk.ConvertError( + sdkerrors.Wrap(sdkerrors.ErrTxDecode, err.Error()), + ) } return tx, nil diff --git a/x/evm/types/state_transition.go b/x/evm/types/state_transition.go index 575d2a0e..9bd839bd 100644 --- a/x/evm/types/state_transition.go +++ b/x/evm/types/state_transition.go @@ -1,7 +1,7 @@ package types import ( - "fmt" + "errors" "math/big" "github.com/ethereum/go-ethereum/common" @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" emint "github.com/cosmos/ethermint/types" ) @@ -39,12 +40,11 @@ type ReturnData struct { // TransitionCSDB performs an evm state transition from a transaction // TODO: update godoc, it doesn't explain what it does in depth. func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*ReturnData, error) { - returnData := new(ReturnData) contractCreation := st.Recipient == nil cost, err := core.IntrinsicGas(st.Payload, contractCreation, true) if err != nil { - return nil, fmt.Errorf("invalid intrinsic gas for transaction: %s", err.Error()) + return nil, sdkerrors.Wrap(err, "invalid intrinsic gas for transaction") } // This gas limit the the transaction gas limit with intrinsic gas subtracted @@ -73,6 +73,11 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*ReturnData, error) { // Clear cache of accounts to handle changes outside of the EVM csdb.UpdateAccounts() + gasPrice := ctx.MinGasPrices().AmountOf(emint.DenomDefault) + if gasPrice.IsNil() { + return nil, errors.New("gas price cannot be nil") + } + // Create context for evm context := vm.Context{ CanTransfer: core.CanTransfer, @@ -83,7 +88,7 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*ReturnData, error) { Time: big.NewInt(ctx.BlockHeader().Time.Unix()), Difficulty: big.NewInt(0), // unused. Only required in PoW context GasLimit: gasLimit, - GasPrice: ctx.MinGasPrices().AmountOf(emint.DenomDefault).Int, + GasPrice: gasPrice.Int, } evm := vm.NewEVM(context, csdb, GenerateChainConfig(st.ChainID), vm.Config{}) @@ -150,7 +155,7 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*ReturnData, error) { // handle errors if err != nil { if err == vm.ErrOutOfGas || err == vm.ErrCodeStoreOutOfGas { - return nil, fmt.Errorf("evm execution went out of gas: %s", err.Error()) + return nil, sdkerrors.Wrap(err, "evm execution went out of gas") } // Consume gas before returning @@ -177,8 +182,11 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*ReturnData, error) { return nil, err } - returnData.Logs = logs - returnData.Bloom = bloomInt - returnData.Result = &sdk.Result{Data: resultData, GasUsed: gasConsumed} + returnData := &ReturnData{ + Logs: logs, + Bloom: bloomInt, + Result: &sdk.Result{Data: resultData}, + } + return returnData, nil } diff --git a/x/evm/types/statedb.go b/x/evm/types/statedb.go index 7d5f657c..de94a9ba 100644 --- a/x/evm/types/statedb.go +++ b/x/evm/types/statedb.go @@ -165,6 +165,10 @@ func (csdb *CommitStateDB) SetLogs(hash ethcmn.Hash, logs []*ethtypes.Log) error return err } + if len(enc) == 0 { + return nil + } + store.Set(LogsKey(hash[:]), enc) return nil } @@ -310,18 +314,14 @@ func (csdb *CommitStateDB) GetLogs(hash ethcmn.Hash) ([]*ethtypes.Log, error) { encLogs := store.Get(LogsKey(hash[:])) if len(encLogs) == 0 { + // return nil if logs are not found return []*ethtypes.Log{}, nil } - logs, err := DecodeLogs(encLogs) - if err != nil { - return nil, err - } - - return logs, nil + return DecodeLogs(encLogs) } -// Logs returns all the current logs in the state. +// AllLogs returns all the current logs in the state. func (csdb *CommitStateDB) AllLogs() []*ethtypes.Log { // nolint: prealloc var logs []*ethtypes.Log