diff --git a/CHANGELOG.md b/CHANGELOG.md index de1abea73f..f8effcfb2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,66 @@ Ref: https://keepachangelog.com/en/1.0.0/ # Changelog +<<<<<<< HEAD +======= +## Unreleased + +## Improvements + +* (ci) [tharsis#784](https://github.com/tharsis/ethermint/pull/784) Enable automatic backport of PRs. +* (rpc) [tharsis#786](https://github.com/tharsis/ethermint/pull/786) Improve error message of `SendTransaction`/`SendRawTransaction` JSON-RPC APIs. + +### Bug Fixes + +* (feemarket) [tharsis#770](https://github.com/tharsis/ethermint/pull/770) Enable fee market (EIP1559) by default. +* (rpc) [tharsis#769](https://github.com/tharsis/ethermint/pull/769) Fix default Ethereum signer for JSON-RPC. +* (rpc) [tharsis#782](https://github.com/tharsis/ethermint/pull/782) Fix wrong block gas limit returned by JSON-RPC. + +## [v0.8.0] - 2021-11-17 + +### State Machine Breaking + +* (evm, ante) [tharsis#620](https://github.com/tharsis/ethermint/pull/620) Add fee market field to EVM `Keeper` and `AnteHandler`. +* (all) [tharsis#231](https://github.com/tharsis/ethermint/pull/231) Bump go-ethereum version to [`v1.10.9`](https://github.com/ethereum/go-ethereum/releases/tag/v1.10.9) +* (ante) [tharsis#703](https://github.com/tharsis/ethermint/pull/703) Fix some fields in transaction are not authenticated by signature. +* (evm) [tharsis#751](https://github.com/tharsis/ethermint/pull/751) don't revert gas refund logic when transaction reverted + +### Features + +* (rpc, evm) [tharsis#673](https://github.com/tharsis/ethermint/pull/673) Use tendermint events to store fee market basefee. +* (rpc) [tharsis#624](https://github.com/tharsis/ethermint/pull/624) Implement new JSON-RPC endpoints from latest geth version +* (evm) [tharsis#662](https://github.com/tharsis/ethermint/pull/662) Disable basefee for non london blocks +* (cmd) [tharsis#712](https://github.com/tharsis/ethermint/pull/712) add tx cli to build evm transaction +* (rpc) [tharsis#733](https://github.com/tharsis/ethermint/pull/733) add JSON_RPC endpoint `personal_unpair` +* (rpc) [tharsis#734](https://github.com/tharsis/ethermint/pull/734) add JSON_RPC endpoint `eth_feeHistory` +* (rpc) [tharsis#740](https://github.com/tharsis/ethermint/pull/740) add JSON_RPC endpoint `personal_initializeWallet` +* (rpc) [tharsis#743](https://github.com/tharsis/ethermint/pull/743) add JSON_RPC endpoint `debug_traceBlockByHash` +* (rpc) [tharsis#748](https://github.com/tharsis/ethermint/pull/748) add JSON_RPC endpoint `personal_listWallets` +* (rpc) [tharsis#754](https://github.com/tharsis/ethermint/pull/754) add JSON_RPC endpoint `debug_intermediateRoots` + +### Bug Fixes + +* (evm) [tharsis#746](https://github.com/tharsis/ethermint/pull/746) Set EVM debugging based on tracer configuration. +* (app,cli) [tharsis#725](https://github.com/tharsis/ethermint/pull/725) Fix cli-config for `keys` command. +* (rpc) [tharsis#727](https://github.com/tharsis/ethermint/pull/727) Decode raw transaction using RLP. +* (rpc) [tharsis#661](https://github.com/tharsis/ethermint/pull/661) Fix OOM bug when creating too many filters using JSON-RPC. +* (evm) [tharsis#660](https://github.com/tharsis/ethermint/pull/660) Fix `nil` pointer panic in `ApplyNativeMessage`. +* (evm, test) [tharsis#649](https://github.com/tharsis/ethermint/pull/649) Test DynamicFeeTx. +* (evm) [tharsis#702](https://github.com/tharsis/ethermint/pull/702) Fix panic in web3 RPC handlers +* (rpc) [tharsis#720](https://github.com/tharsis/ethermint/pull/720) Fix `debug_traceTransaction` failure +* (rpc) [tharsis#741](https://github.com/tharsis/ethermint/pull/741) Fix `eth_getBlockByNumberAndHash` return with non eth txs +* (rpc) [tharsis#743](https://github.com/tharsis/ethermint/pull/743) Fix debug JSON RPC handler crash on non-existing block + +### Improvements + +* (tests) [tharsis#704](https://github.com/tharsis/ethermint/pull/704) Introduce E2E testing framework for clients +* (deps) [tharsis#737](https://github.com/tharsis/ethermint/pull/737) Bump ibc-go to [`v2.0.0`](https://github.com/cosmos/ibc-go/releases/tag/v2.0.0) +* (rpc) [tharsis#671](https://github.com/tharsis/ethermint/pull/671) Don't pass base fee externally for `EthCall`/`EthEstimateGas` apis. +* (evm) [tharsis#674](https://github.com/tharsis/ethermint/pull/674) Refactor `ApplyMessage`, remove + `ApplyNativeMessage`. +* (rpc) [tharsis#714](https://github.com/tharsis/ethermint/pull/714) remove `MsgEthereumTx` support in `TxConfig` + +>>>>>>> c8d4d3f (fix: improve error message in `SendTransaction` json-rpc api (#786)) ## [v0.7.2] - 2021-10-24 ### Improvements diff --git a/app/ante/ante.go b/app/ante/ante.go index ff887892de..56afedb3f5 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -4,8 +4,6 @@ import ( "fmt" "runtime/debug" - "github.com/palantir/stacktrace" - tmlog "github.com/tendermint/tendermint/libs/log" sdk "github.com/cosmos/cosmos-sdk/types" @@ -68,9 +66,10 @@ func NewAnteHandler( ) default: - return ctx, stacktrace.Propagate( - sdkerrors.Wrap(sdkerrors.ErrUnknownExtensionOptions, typeURL), - "rejecting tx with unsupported extension option", + return ctx, sdkerrors.Wrapf( + sdkerrors.ErrUnknownExtensionOptions, + "rejecting tx with unsupported extension option: %s", + typeURL, ) } @@ -99,10 +98,7 @@ func NewAnteHandler( authante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator ) default: - return ctx, stacktrace.Propagate( - sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx), - "transaction is not an SDK tx", - ) + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx) } return anteHandler(ctx, tx, sim) diff --git a/app/ante/eth.go b/app/ante/eth.go index 1513c409e2..4672699c6a 100644 --- a/app/ante/eth.go +++ b/app/ante/eth.go @@ -54,10 +54,7 @@ func NewEthSigVerificationDecorator(ek EVMKeeper) EthSigVerificationDecorator { // won't see the error message. func (esvd EthSigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { if tx == nil || len(tx.GetMsgs()) != 1 { - return ctx, stacktrace.Propagate( - sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "only 1 ethereum msg supported per tx"), - "", - ) + return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "only 1 ethereum msg supported per tx") } chainID := esvd.evmKeeper.ChainID() @@ -71,18 +68,16 @@ func (esvd EthSigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, s msg := tx.GetMsgs()[0] msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) if !ok { - return ctx, stacktrace.Propagate( - sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)), - "failed to cast transaction", - ) + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)) } sender, err := signer.Sender(msgEthTx.AsTransaction()) if err != nil { - return ctx, stacktrace.Propagate( - sdkerrors.Wrap(sdkerrors.ErrorInvalidSigner, err.Error()), - "couldn't retrieve sender address ('%s') from the ethereum transaction", + return ctx, sdkerrors.Wrapf( + sdkerrors.ErrorInvalidSigner, + "couldn't retrieve sender address ('%s') from the ethereum transaction: %s", msgEthTx.From, + err.Error(), ) } @@ -125,32 +120,26 @@ func (avd EthAccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx for i, msg := range tx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) if !ok { - return ctx, stacktrace.Propagate( - sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)), - "failed to cast transaction %d", i, - ) + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)) } txData, err := evmtypes.UnpackTxData(msgEthTx.Data) if err != nil { - return ctx, stacktrace.Propagate(err, "failed to unpack tx data any for tx %d", i) + return ctx, sdkerrors.Wrapf(err, "failed to unpack tx data any for tx %d", i) } // sender address should be in the tx cache from the previous AnteHandle call from := msgEthTx.GetFrom() if from.Empty() { - return ctx, stacktrace.Propagate( - sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "from address cannot be empty"), - "sender address should have been in the tx field from the previous AnteHandle call", - ) + return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "from address cannot be empty") } // check whether the sender address is EOA fromAddr := common.BytesToAddress(from) codeHash := avd.evmKeeper.GetCodeHash(fromAddr) if codeHash != common.BytesToHash(evmtypes.EmptyCodeHash) { - return ctx, stacktrace.Propagate(sdkerrors.Wrapf(sdkerrors.ErrInvalidType, - "the sender is not EOA: address <%v>, codeHash <%s>", fromAddr, codeHash), "") + return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, + "the sender is not EOA: address <%v>, codeHash <%s>", fromAddr, codeHash) } acc := avd.ak.GetAccount(ctx, from) @@ -160,7 +149,7 @@ func (avd EthAccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx } if err := evmkeeper.CheckSenderBalance(ctx, avd.bankKeeper, from, txData, evmDenom); err != nil { - return ctx, stacktrace.Propagate(err, "failed to check sender balance") + return ctx, sdkerrors.Wrap(err, "failed to check sender balance") } } @@ -190,36 +179,30 @@ func (nvd EthNonceVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, return next(ctx, tx, simulate) } - for i, msg := range tx.GetMsgs() { + for _, msg := range tx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) if !ok { - return ctx, stacktrace.Propagate( - sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)), - "failed to cast transaction %d", i, - ) + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)) } // sender address should be in the tx cache from the previous AnteHandle call seq, err := nvd.ak.GetSequence(ctx, msgEthTx.GetFrom()) if err != nil { - return ctx, stacktrace.Propagate(err, "sequence not found for address %s", msgEthTx.From) + return ctx, sdkerrors.Wrapf(err, "sequence not found for address %s", msgEthTx.From) } txData, err := evmtypes.UnpackTxData(msgEthTx.Data) if err != nil { - return ctx, stacktrace.Propagate(err, "failed to unpack tx data") + return ctx, sdkerrors.Wrap(err, "failed to unpack tx data") } // if multiple transactions are submitted in succession with increasing nonces, // all will be rejected except the first, since the first needs to be included in a block // before the sequence increments if txData.GetNonce() != seq { - return ctx, stacktrace.Propagate( - sdkerrors.Wrapf( - sdkerrors.ErrInvalidSequence, - "invalid nonce; got %d, expected %d", txData.GetNonce(), seq, - ), - "", + return ctx, sdkerrors.Wrapf( + sdkerrors.ErrInvalidSequence, + "invalid nonce; got %d, expected %d", txData.GetNonce(), seq, ) } } @@ -269,18 +252,15 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula var events sdk.Events - for i, msg := range tx.GetMsgs() { + for _, msg := range tx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) if !ok { - return ctx, stacktrace.Propagate( - sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)), - "failed to cast transaction %d", i, - ) + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)) } txData, err := evmtypes.UnpackTxData(msgEthTx.Data) if err != nil { - return ctx, stacktrace.Propagate(err, "failed to unpack tx data") + return ctx, sdkerrors.Wrap(err, "failed to unpack tx data") } fees, err := egcd.evmKeeper.DeductTxCostsFromUserBalance( @@ -292,7 +272,7 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula istanbul, ) if err != nil { - return ctx, stacktrace.Propagate(err, "failed to deduct transaction costs from user balance") + return ctx, sdkerrors.Wrapf(err, "failed to deduct transaction costs from user balance") } events = append(events, sdk.NewEvent(sdk.EventTypeTx, sdk.NewAttribute(sdk.AttributeKeyFee, fees.String()))) @@ -341,18 +321,15 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate ethCfg := params.ChainConfig.EthereumConfig(ctd.evmKeeper.ChainID()) signer := ethtypes.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight())) - for i, msg := range tx.GetMsgs() { + for _, msg := range tx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) if !ok { - return ctx, stacktrace.Propagate( - sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)), - "failed to cast transaction %d", i, - ) + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)) } coreMsg, err := msgEthTx.AsMessage(signer) if err != nil { - return ctx, stacktrace.Propagate( + return ctx, sdkerrors.Wrapf( err, "failed to create an ethereum core.Message from signer %T", signer, ) @@ -364,11 +341,24 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate // check that caller has enough balance to cover asset transfer for **topmost** call // NOTE: here the gas consumed is from the context with the infinite gas meter if coreMsg.Value().Sign() > 0 && !evm.Context.CanTransfer(ctd.evmKeeper, coreMsg.From(), coreMsg.Value()) { - return ctx, stacktrace.Propagate( - sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "address %s", coreMsg.From()), - "failed to transfer %s using the EVM block context transfer function", coreMsg.Value(), + return ctx, sdkerrors.Wrapf( + sdkerrors.ErrInsufficientFunds, + "failed to transfer %s from address %s using the EVM block context transfer function", + coreMsg.Value(), + coreMsg.From(), ) } +<<<<<<< HEAD +======= + + if evmtypes.IsLondon(ethCfg, ctx.BlockHeight()) && !feeMktParams.NoBaseFee && baseFee == nil { + return ctx, sdkerrors.Wrap(evmtypes.ErrInvalidBaseFee, "base fee is supported but evm block context value is nil") + } + + if evmtypes.IsLondon(ethCfg, ctx.BlockHeight()) && !feeMktParams.NoBaseFee && baseFee != nil && coreMsg.GasFeeCap().Cmp(baseFee) < 0 { + return ctx, sdkerrors.Wrapf(evmtypes.ErrInvalidBaseFee, "max fee per gas less than block base fee (%s < %s)", coreMsg.GasFeeCap(), baseFee) + } +>>>>>>> c8d4d3f (fix: improve error message in `SendTransaction` json-rpc api (#786)) } ctd.evmKeeper.WithContext(ctx) @@ -413,20 +403,17 @@ func (ald AccessListDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b // setup the keeper context before setting the access list ald.evmKeeper.WithContext(ctx) - for i, msg := range tx.GetMsgs() { + for _, msg := range tx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) if !ok { - return ctx, stacktrace.Propagate( - sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)), - "failed to cast transaction %d", i, - ) + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)) } sender := common.BytesToAddress(msgEthTx.GetFrom()) txData, err := evmtypes.UnpackTxData(msgEthTx.Data) if err != nil { - return ctx, stacktrace.Propagate(err, "failed to unpack tx data") + return ctx, sdkerrors.Wrap(err, "failed to unpack tx data") } ald.evmKeeper.PrepareAccessList(sender, txData.GetTo(), vm.ActivePrecompiles(rules), txData.GetAccessList()) @@ -453,18 +440,15 @@ func NewEthIncrementSenderSequenceDecorator(ak evmtypes.AccountKeeper) EthIncrem // contract creation, the nonce will be incremented during the transaction execution and not within // this AnteHandler decorator. func (issd EthIncrementSenderSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { - for i, msg := range tx.GetMsgs() { + for _, msg := range tx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) if !ok { - return ctx, stacktrace.Propagate( - sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)), - "failed to cast transaction %d", i, - ) + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)) } txData, err := evmtypes.UnpackTxData(msgEthTx.Data) if err != nil { - return ctx, stacktrace.Propagate(err, "failed to unpack tx data") + return ctx, sdkerrors.Wrap(err, "failed to unpack tx data") } // NOTE: on contract creation, the nonce is incremented within the EVM Create function during tx execution @@ -480,17 +464,14 @@ func (issd EthIncrementSenderSequenceDecorator) AnteHandle(ctx sdk.Context, tx s acc := issd.ak.GetAccount(ctx, addr) if acc == nil { - return ctx, stacktrace.Propagate( - sdkerrors.Wrapf( - sdkerrors.ErrUnknownAddress, - "account %s (%s) is nil", common.BytesToAddress(addr.Bytes()), addr, - ), - "signer account not found", + return ctx, sdkerrors.Wrapf( + sdkerrors.ErrUnknownAddress, + "account %s (%s) is nil", common.BytesToAddress(addr.Bytes()), addr, ) } if err := acc.SetSequence(acc.GetSequence() + 1); err != nil { - return ctx, stacktrace.Propagate(err, "failed to set sequence to %d", acc.GetSequence()+1) + return ctx, sdkerrors.Wrapf(err, "failed to set sequence to %d", acc.GetSequence()+1) } issd.ak.SetAccount(ctx, acc) @@ -519,9 +500,66 @@ func (vbd EthValidateBasicDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu err := tx.ValidateBasic() // ErrNoSignatures is fine with eth tx if err != nil && !errors.Is(err, sdkerrors.ErrNoSignatures) { - return ctx, stacktrace.Propagate(err, "tx basic validation failed") + return ctx, sdkerrors.Wrap(err, "tx basic validation failed") + } + +<<<<<<< HEAD +======= + // For eth type cosmos tx, some fields should be veified as zero values, + // since we will only verify the signature against the hash of the MsgEthereumTx.Data + if wrapperTx, ok := tx.(protoTxProvider); ok { + protoTx := wrapperTx.GetProtoTx() + body := protoTx.Body + if body.Memo != "" || body.TimeoutHeight != uint64(0) || len(body.NonCriticalExtensionOptions) > 0 { + return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, + "for eth tx body Memo TimeoutHeight NonCriticalExtensionOptions should be empty") + } + + if len(body.ExtensionOptions) != 1 { + return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx length of ExtensionOptions should be 1") + } + + if len(protoTx.GetMsgs()) != 1 { + return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "only 1 ethereum msg supported per tx") + } + msg := protoTx.GetMsgs()[0] + msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) + if !ok { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, (*evmtypes.MsgEthereumTx)(nil)) + } + ethGasLimit := msgEthTx.GetGas() + + txData, err := evmtypes.UnpackTxData(msgEthTx.Data) + if err != nil { + return ctx, sdkerrors.Wrap(err, "failed to unpack MsgEthereumTx Data") + } + params := vbd.evmKeeper.GetParams(ctx) + ethFeeAmount := sdk.Coins{sdk.NewCoin(params.EvmDenom, sdk.NewIntFromBigInt(txData.Fee()))} + + authInfo := protoTx.AuthInfo + if len(authInfo.SignerInfos) > 0 { + return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx AuthInfo SignerInfos should be empty") + } + + if authInfo.Fee.Payer != "" || authInfo.Fee.Granter != "" { + return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx AuthInfo Fee payer and granter should be empty") + } + + if !authInfo.Fee.Amount.IsEqual(ethFeeAmount) { + return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "invalid eth tx AuthInfo Fee Amount") + } + + if authInfo.Fee.GasLimit != ethGasLimit { + return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "invalid eth tx AuthInfo Fee GasLimit") + } + + sigs := protoTx.Signatures + if len(sigs) > 0 { + return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx Signatures should be empty") + } } +>>>>>>> c8d4d3f (fix: improve error message in `SendTransaction` json-rpc api (#786)) return next(ctx, tx, simulate) } diff --git a/go.mod b/go.mod index efe402965c..1ce6e8731b 100644 --- a/go.mod +++ b/go.mod @@ -14,9 +14,17 @@ require ( github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.4.2 github.com/grpc-ecosystem/grpc-gateway v1.16.0 +<<<<<<< HEAD github.com/improbable-eng/grpc-web v0.14.1 github.com/miguelmota/go-ethereum-hdwallet v0.0.1 github.com/palantir/stacktrace v0.0.0-20161112013806-78658fd2d177 +======= + github.com/holiman/uint256 v1.2.0 + github.com/improbable-eng/grpc-web v0.15.0 + github.com/klauspost/compress v1.11.9 // indirect + github.com/miguelmota/go-ethereum-hdwallet v0.1.1 + github.com/onsi/ginkgo v1.16.5 +>>>>>>> c8d4d3f (fix: improve error message in `SendTransaction` json-rpc api (#786)) github.com/pkg/errors v0.9.1 github.com/rakyll/statik v0.1.7 github.com/regen-network/cosmos-proto v0.3.1 diff --git a/go.sum b/go.sum index 649395ac7e..21eb3af24a 100644 --- a/go.sum +++ b/go.sum @@ -942,8 +942,6 @@ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6 github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= -github.com/palantir/stacktrace v0.0.0-20161112013806-78658fd2d177 h1:nRlQD0u1871kaznCnn1EvYiMbum36v7hw1DLPEjds4o= -github.com/palantir/stacktrace v0.0.0-20161112013806-78658fd2d177/go.mod h1:ao5zGxj8Z4x60IOVYZUbDSmt3R8Ddo080vEgPosHpak= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= diff --git a/rpc/ethereum/backend/backend.go b/rpc/ethereum/backend/backend.go index dba7a82dd8..0ca763bbbd 100644 --- a/rpc/ethereum/backend/backend.go +++ b/rpc/ethereum/backend/backend.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/server" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/params" @@ -670,10 +671,10 @@ func (e *EVMBackend) SendTransaction(args types.SendTxArgs) (common.Hash, error) // NOTE: If error is encountered on the node, the broadcast will not return an error syncCtx := e.clientCtx.WithBroadcastMode(flags.BroadcastSync) rsp, err := syncCtx.BroadcastTx(txBytes) - if err != nil || rsp.Code != 0 { - if err == nil { - err = errors.New(rsp.RawLog) - } + if rsp != nil && rsp.Code != 0 { + err = sdkerrors.ABCIError(rsp.Codespace, rsp.Code, rsp.RawLog) + } + if err != nil { e.logger.Error("failed to broadcast tx", "error", err.Error()) return txHash, err } diff --git a/rpc/ethereum/namespaces/eth/api.go b/rpc/ethereum/namespaces/eth/api.go index 35c5095971..2d71e2d110 100644 --- a/rpc/ethereum/namespaces/eth/api.go +++ b/rpc/ethereum/namespaces/eth/api.go @@ -21,7 +21,11 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" +<<<<<<< HEAD authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" +======= + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +>>>>>>> c8d4d3f (fix: improve error message in `SendTransaction` json-rpc api (#786)) authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/ethereum/go-ethereum/accounts/keystore" @@ -423,10 +427,10 @@ func (e *PublicAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) syncCtx := e.clientCtx.WithBroadcastMode(flags.BroadcastSync) rsp, err := syncCtx.BroadcastTx(txBytes) - if err != nil || rsp.Code != 0 { - if err == nil { - err = errors.New(rsp.RawLog) - } + if rsp != nil && rsp.Code != 0 { + err = sdkerrors.ABCIError(rsp.Codespace, rsp.Code, rsp.RawLog) + } + if err != nil { e.logger.Error("failed to broadcast tx", "error", err.Error()) return txHash, err } diff --git a/rpc/ethereum/namespaces/miner/api.go b/rpc/ethereum/namespaces/miner/api.go index 0ae4e6e977..3b82473c3f 100644 --- a/rpc/ethereum/namespaces/miner/api.go +++ b/rpc/ethereum/namespaces/miner/api.go @@ -1,7 +1,6 @@ package miner import ( - "errors" "math/big" "github.com/cosmos/cosmos-sdk/client" @@ -11,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/server" sdkconfig "github.com/cosmos/cosmos-sdk/server/config" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" @@ -143,10 +143,10 @@ func (api *API) SetEtherbase(etherbase common.Address) bool { // NOTE: If error is encountered on the node, the broadcast will not return an error syncCtx := api.clientCtx.WithBroadcastMode(flags.BroadcastSync) rsp, err := syncCtx.BroadcastTx(txBytes) - if err != nil || rsp.Code != 0 { - if err == nil { - err = errors.New(rsp.RawLog) - } + if rsp != nil && rsp.Code != 0 { + err = sdkerrors.ABCIError(rsp.Codespace, rsp.Code, rsp.RawLog) + } + if err != nil { api.logger.Debug("failed to broadcast tx", "error", err.Error()) return false } diff --git a/x/evm/keeper/grpc_query.go b/x/evm/keeper/grpc_query.go index 8e76e13c00..662b5e1ce7 100644 --- a/x/evm/keeper/grpc_query.go +++ b/x/evm/keeper/grpc_query.go @@ -10,7 +10,6 @@ import ( "github.com/ethereum/go-ethereum/eth/tracers" - "github.com/palantir/stacktrace" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -316,7 +315,7 @@ func (k Keeper) EstimateGas(c context.Context, req *types.EthCallRequest) (*type k.ctxStack.RevertAll() if err != nil { - if errors.Is(stacktrace.RootCause(err), core.ErrIntrinsicGas) { + if errors.Is(err, core.ErrIntrinsicGas) { return true, nil, nil // Special case, raise gas limit } return true, nil, err // Bail out diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 9b74753beb..52730d52cf 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -7,10 +7,15 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" +<<<<<<< HEAD "github.com/palantir/stacktrace" +======= + "github.com/ethereum/go-ethereum/core/vm" +>>>>>>> c8d4d3f (fix: improve error message in `SendTransaction` json-rpc api (#786)) "github.com/tendermint/tendermint/libs/log" ethermint "github.com/tharsis/ethermint/types" @@ -338,11 +343,11 @@ func (k Keeper) ClearBalance(addr sdk.AccAddress) (prevBalance sdk.Coin, err err prevBalance = k.bankKeeper.GetBalance(k.Ctx(), addr, params.EvmDenom) if prevBalance.IsPositive() { if err := k.bankKeeper.SendCoinsFromAccountToModule(k.Ctx(), addr, types.ModuleName, sdk.Coins{prevBalance}); err != nil { - return sdk.Coin{}, stacktrace.Propagate(err, "failed to transfer to module account") + return sdk.Coin{}, sdkerrors.Wrap(err, "failed to transfer to module account") } if err := k.bankKeeper.BurnCoins(k.Ctx(), types.ModuleName, sdk.Coins{prevBalance}); err != nil { - return sdk.Coin{}, stacktrace.Propagate(err, "failed to burn coins from evm module account") + return sdk.Coin{}, sdkerrors.Wrap(err, "failed to burn coins from evm module account") } } diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go index 056b660a95..c12a877c84 100644 --- a/x/evm/keeper/msg_server.go +++ b/x/evm/keeper/msg_server.go @@ -5,12 +5,11 @@ import ( "encoding/json" "fmt" - "github.com/palantir/stacktrace" - tmbytes "github.com/tendermint/tendermint/libs/bytes" tmtypes "github.com/tendermint/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/tharsis/ethermint/x/evm/types" ) @@ -30,7 +29,7 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t response, err := k.ApplyTransaction(tx) if err != nil { - return nil, stacktrace.Propagate(err, "failed to apply transaction") + return nil, sdkerrors.Wrap(err, "failed to apply transaction") } attrs := []sdk.Attribute{ @@ -57,7 +56,7 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t for _, log := range response.Logs { value, err := json.Marshal(log) if err != nil { - return nil, stacktrace.Propagate(err, "failed to encode log") + return nil, sdkerrors.Wrap(err, "failed to encode log") } txLogAttrs = append(txLogAttrs, sdk.NewAttribute(types.AttributeKeyTxLog, string(value))) } diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 522f68f0aa..4d284b0e60 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -3,7 +3,6 @@ package keeper import ( "math/big" - "github.com/palantir/stacktrace" tmtypes "github.com/tendermint/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -21,6 +20,33 @@ import ( "github.com/ethereum/go-ethereum/params" ) +<<<<<<< HEAD +======= +// EVMConfig creates the EVMConfig based on current state +func (k *Keeper) EVMConfig(ctx sdk.Context) (*types.EVMConfig, error) { + params := k.GetParams(ctx) + ethCfg := params.ChainConfig.EthereumConfig(k.eip155ChainID) + + // get the coinbase address from the block proposer + coinbase, err := k.GetCoinbaseAddress(ctx) + if err != nil { + return nil, sdkerrors.Wrap(err, "failed to obtain coinbase address") + } + + var baseFee *big.Int + if types.IsLondon(ethCfg, ctx.BlockHeight()) { + baseFee = k.feeMarketKeeper.GetBaseFee(ctx) + } + + return &types.EVMConfig{ + Params: params, + ChainConfig: ethCfg, + CoinBase: coinbase, + BaseFee: baseFee, + }, nil +} + +>>>>>>> c8d4d3f (fix: improve error message in `SendTransaction` json-rpc api (#786)) // NewEVM generates a go-ethereum VM from the provided Message fields and the chain parameters // (ChainConfig and module Params). It additionally sets the validator operator address as the // coinbase address to make it available for the COINBASE opcode, even though there is no @@ -142,11 +168,17 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT // ensure keeper state error is cleared defer k.ClearStateError() +<<<<<<< HEAD // return error if contract creation or call are disabled through governance if !params.EnableCreate && tx.To() == nil { return nil, stacktrace.Propagate(types.ErrCreateDisabled, "failed to create new contract") } else if !params.EnableCall && tx.To() != nil { return nil, stacktrace.Propagate(types.ErrCallDisabled, "failed to call contract") +======= + cfg, err := k.EVMConfig(ctx) + if err != nil { + return nil, sdkerrors.Wrap(err, "failed to load evm config") +>>>>>>> c8d4d3f (fix: improve error message in `SendTransaction` json-rpc api (#786)) } ethCfg := params.ChainConfig.EthereumConfig(k.eip155ChainID) @@ -156,7 +188,7 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT msg, err := tx.AsMessage(signer) if err != nil { - return nil, stacktrace.Propagate(err, "failed to return ethereum transaction as core message") + return nil, sdkerrors.Wrap(err, "failed to return ethereum transaction as core message") } // get the coinbase address from the block proposer @@ -189,7 +221,7 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT // pass false to execute in real mode, which do actual gas refunding res, err := k.ApplyMessage(evm, msg, ethCfg, false) if err != nil { - return nil, stacktrace.Propagate(err, "failed to apply ethereum core message") + return nil, sdkerrors.Wrap(err, "failed to apply ethereum core message") } res.Hash = txHash.Hex() @@ -223,6 +255,17 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT } } +<<<<<<< HEAD +======= + // change to original context + k.WithContext(ctx) + + // refund gas according to Ethereum gas accounting rules. + if err := k.RefundGas(msg, msg.Gas()-res.GasUsed, cfg.Params.EvmDenom); err != nil { + return nil, sdkerrors.Wrapf(err, "failed to refund gas leftover gas to sender %s", msg.From()) + } + +>>>>>>> c8d4d3f (fix: improve error message in `SendTransaction` json-rpc api (#786)) if len(logs) > 0 { res.Logs = types.NewLogsFromEth(logs) // Update transient block bloom filter @@ -276,18 +319,28 @@ func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message, cfg *params.ChainCo // ensure keeper state error is cleared defer k.ClearStateError() +<<<<<<< HEAD +======= + // return error if contract creation or call are disabled through governance + if !cfg.Params.EnableCreate && msg.To() == nil { + return nil, sdkerrors.Wrap(types.ErrCreateDisabled, "failed to create new contract") + } else if !cfg.Params.EnableCall && msg.To() != nil { + return nil, sdkerrors.Wrap(types.ErrCallDisabled, "failed to call contract") + } + +>>>>>>> c8d4d3f (fix: improve error message in `SendTransaction` json-rpc api (#786)) sender := vm.AccountRef(msg.From()) contractCreation := msg.To() == nil intrinsicGas, err := k.GetEthIntrinsicGas(msg, cfg, contractCreation) if err != nil { // should have already been checked on Ante Handler - return nil, stacktrace.Propagate(err, "intrinsic gas failed") + return nil, sdkerrors.Wrap(err, "intrinsic gas failed") } // Should check again even if it is checked on Ante Handler, because eth_call don't go through Ante Handler. if msg.Gas() < intrinsicGas { // eth_estimateGas will check for this exact error - return nil, stacktrace.Propagate(core.ErrIntrinsicGas, "apply message") + return nil, sdkerrors.Wrap(core.ErrIntrinsicGas, "apply message") } leftoverGas := msg.Gas() - intrinsicGas @@ -319,6 +372,20 @@ func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message, cfg *params.ChainCo } } +<<<<<<< HEAD +======= + // calculate gas refund + if msg.Gas() < leftoverGas { + return nil, sdkerrors.Wrap(types.ErrGasOverflow, "apply message") + } + gasUsed := msg.Gas() - leftoverGas + refund := k.GasToRefund(gasUsed, refundQuotient) + if refund > gasUsed { + return nil, sdkerrors.Wrap(types.ErrGasOverflow, "apply message") + } + gasUsed -= refund + +>>>>>>> c8d4d3f (fix: improve error message in `SendTransaction` json-rpc api (#786)) // EVM execution error needs to be available for the JSON-RPC client var vmError string if vmErr != nil { @@ -333,6 +400,7 @@ func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message, cfg *params.ChainCo }, nil } +<<<<<<< HEAD // ApplyNativeMessage executes an ethereum message on the EVM. It is meant to be called from an internal // native Cosmos SDK module. func (k *Keeper) ApplyNativeMessage(msg core.Message) (*types.MsgEthereumTxResponse, error) { @@ -345,6 +413,13 @@ func (k *Keeper) ApplyNativeMessage(msg core.Message) (*types.MsgEthereumTxRespo return nil, stacktrace.Propagate(types.ErrCreateDisabled, "failed to create new contract") } else if !params.EnableCall && msg.To() != nil { return nil, stacktrace.Propagate(types.ErrCallDisabled, "failed to call contract") +======= +// ApplyMessage calls ApplyMessageWithConfig with default EVMConfig +func (k *Keeper) ApplyMessage(msg core.Message, tracer vm.Tracer, commit bool) (*types.MsgEthereumTxResponse, error) { + cfg, err := k.EVMConfig(k.Ctx()) + if err != nil { + return nil, sdkerrors.Wrap(err, "failed to load evm config") +>>>>>>> c8d4d3f (fix: improve error message in `SendTransaction` json-rpc api (#786)) } ethCfg := params.ChainConfig.EthereumConfig(k.eip155ChainID) @@ -430,7 +505,11 @@ func (k *Keeper) RefundGas(msg core.Message, leftoverGas, refundQuotient uint64) err := k.bankKeeper.SendCoinsFromModuleToAccount(k.Ctx(), authtypes.FeeCollectorName, msg.From().Bytes(), refundedCoins) if err != nil { err = sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "fee collector account failed to refund fees: %s", err.Error()) +<<<<<<< HEAD return leftoverGas, stacktrace.Propagate(err, "failed to refund %d leftover gas (%s)", leftoverGas, refundedCoins.String()) +======= + return sdkerrors.Wrapf(err, "failed to refund %d leftover gas (%s)", leftoverGas, refundedCoins.String()) +>>>>>>> c8d4d3f (fix: improve error message in `SendTransaction` json-rpc api (#786)) } default: // no refund, consume gas and update the tx gas meter @@ -453,9 +532,10 @@ func (k Keeper) GetCoinbaseAddress(ctx sdk.Context) (common.Address, error) { consAddr := sdk.ConsAddress(ctx.BlockHeader().ProposerAddress) validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, consAddr) if !found { - return common.Address{}, stacktrace.Propagate( - sdkerrors.Wrap(stakingtypes.ErrNoValidatorFound, consAddr.String()), - "failed to retrieve validator from block proposer address", + return common.Address{}, sdkerrors.Wrapf( + stakingtypes.ErrNoValidatorFound, + "failed to retrieve validator from block proposer address %s", + consAddr.String(), ) } diff --git a/x/evm/keeper/utils.go b/x/evm/keeper/utils.go index 2c9ef9bd20..f533cf2f3d 100644 --- a/x/evm/keeper/utils.go +++ b/x/evm/keeper/utils.go @@ -4,7 +4,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authante "github.com/cosmos/cosmos-sdk/x/auth/ante" - "github.com/palantir/stacktrace" evmtypes "github.com/tharsis/ethermint/x/evm/types" @@ -26,7 +25,7 @@ func (k Keeper) DeductTxCostsFromUserBalance( // fetch sender account from signature signerAcc, err := authante.GetSignerAcc(ctx, k.accountKeeper, msgEthTx.GetFrom()) if err != nil { - return nil, stacktrace.Propagate(err, "account not found for sender %s", msgEthTx.From) + return nil, sdkerrors.Wrapf(err, "account not found for sender %s", msgEthTx.From) } gasLimit := txData.GetGas() @@ -38,9 +37,9 @@ func (k Keeper) DeductTxCostsFromUserBalance( intrinsicGas, err := core.IntrinsicGas(txData.GetData(), accessList, isContractCreation, homestead, istanbul) if err != nil { - return nil, stacktrace.Propagate(sdkerrors.Wrap( + return nil, sdkerrors.Wrapf( err, - "failed to compute intrinsic gas cost"), "failed to retrieve intrinsic gas, contract creation = %t; homestead = %t, istanbul = %t", + "failed to retrieve intrinsic gas, contract creation = %t; homestead = %t, istanbul = %t", isContractCreation, homestead, istanbul, ) } @@ -60,7 +59,7 @@ func (k Keeper) DeductTxCostsFromUserBalance( // deduct the full gas cost from the user balance if err := authante.DeductFees(k.bankKeeper, ctx, signerAcc, fees); err != nil { - return nil, stacktrace.Propagate( + return nil, sdkerrors.Wrapf( err, "failed to deduct full gas cost %s from the user %s balance", fees, msgEthTx.From, @@ -82,22 +81,16 @@ func CheckSenderBalance( cost := txData.Cost() if cost.Sign() < 0 { - return stacktrace.Propagate( - sdkerrors.Wrapf( - sdkerrors.ErrInvalidCoins, - "tx cost (%s%s) is negative and invalid", cost, denom, - ), - "tx cost amount should never be negative") + return sdkerrors.Wrapf( + sdkerrors.ErrInvalidCoins, + "tx cost (%s%s) is negative and invalid", cost, denom, + ) } if balance.IsNegative() || balance.Amount.BigInt().Cmp(cost) < 0 { - return stacktrace.Propagate( - sdkerrors.Wrapf( - sdkerrors.ErrInsufficientFunds, - "sender balance < tx cost (%s < %s%s)", balance, txData.Cost(), denom, - ), - "sender should have had enough funds to pay for tx cost = fee + amount (%s = %s + %s)", - cost, txData.Fee(), txData.GetValue(), + return sdkerrors.Wrapf( + sdkerrors.ErrInsufficientFunds, + "sender balance < tx cost (%s < %s%s)", balance, txData.Cost(), denom, ) } return nil diff --git a/x/evm/types/chain_config.go b/x/evm/types/chain_config.go index 2cb2824097..980f1e2d67 100644 --- a/x/evm/types/chain_config.go +++ b/x/evm/types/chain_config.go @@ -131,7 +131,7 @@ func (cc ChainConfig) Validate() error { func validateHash(hex string) error { if hex != "" && strings.TrimSpace(hex) == "" { - return sdkerrors.Wrapf(ErrInvalidChainConfig, "hash cannot be blank") + return sdkerrors.Wrap(ErrInvalidChainConfig, "hash cannot be blank") } return nil