From eb1af33649e7b2025c7633d017de58a4194386a2 Mon Sep 17 00:00:00 2001 From: Sai Kumar Date: Thu, 28 Apr 2022 13:13:39 +0530 Subject: [PATCH] fix: fix the rpc test cases --- app/app.go | 1 + app/middleware/eip792.go | 74 ++++---- app/middleware/eth.go | 290 ++++++++++++++++++------------ app/middleware/options.go | 24 +-- app/middleware/reject.go | 50 +++--- encoding/codec/codec.go | 2 - rpc/apis.go | 117 ++++++++----- scripts/integration-test-all.sh | 4 +- x/evm/module.go | 10 +- x/evm/simulation/decoder.go | 31 ++++ x/evm/simulation/genesis.go | 10 ++ x/evm/simulation/opereations.go | 302 ++++++++++++++++++++++++++++++++ x/evm/simulation/params.go | 28 +++ x/evm/types/msg.go | 8 +- 14 files changed, 714 insertions(+), 237 deletions(-) create mode 100644 x/evm/simulation/decoder.go create mode 100644 x/evm/simulation/opereations.go create mode 100644 x/evm/simulation/params.go diff --git a/app/app.go b/app/app.go index 04b48797..bd596376 100644 --- a/app/app.go +++ b/app/app.go @@ -683,6 +683,7 @@ func NewEthermintApp( // app.ScopedTransferKeeper = scopedTransferKeeper maxGasWanted := cast.ToUint64(appOpts.Get(srvflags.EVMMaxTxGasWanted)) options := middleware.HandlerOptions{ + Codec: app.appCodec, Debug: app.Trace(), LegacyRouter: app.legacyRouter, MsgServiceRouter: app.msgSvcRouter, diff --git a/app/middleware/eip792.go b/app/middleware/eip792.go index 98560c25..9dcd92cd 100644 --- a/app/middleware/eip792.go +++ b/app/middleware/eip792.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/types/tx" "github.com/cosmos/cosmos-sdk/types/tx/signing" @@ -18,6 +17,7 @@ import ( "github.com/tharsis/ethermint/ethereum/eip712" ethermint "github.com/tharsis/ethermint/types" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" evmtypes "github.com/tharsis/ethermint/x/evm/types" @@ -31,23 +31,25 @@ func init() { ethermintCodec = codec.NewProtoCodec(registry) } -// Eip712SigVerificationDecorator Verify all signatures for a tx and return an error if any are invalid. Note, -// the Eip712SigVerificationDecorator decorator will not get executed on ReCheck. +// Eip712SigVerificationMiddleware Verify all signatures for a tx and return an error if any are invalid. Note, +// the Eip712SigVerificationMiddleware middleware will not get executed on ReCheck. // -// CONTRACT: Pubkeys are set in context for all signers before this decorator runs +// CONTRACT: Pubkeys are set in context for all signers before this middleware runs // CONTRACT: Tx must implement SigVerifiableTx interface -type Eip712SigVerificationDecorator struct { +type Eip712SigVerificationMiddleware struct { + appCodec codec.Codec next tx.Handler ak evmtypes.AccountKeeper signModeHandler authsigning.SignModeHandler } -var _ tx.Handler = Eip712SigVerificationDecorator{} +var _ tx.Handler = Eip712SigVerificationMiddleware{} -// NewEip712SigVerificationDecorator creates a new Eip712SigVerificationDecorator -func NewEip712SigVerificationDecorator(ak evmtypes.AccountKeeper, signModeHandler authsigning.SignModeHandler) tx.Middleware { +// NewEip712SigVerificationMiddleware creates a new Eip712SigVerificationMiddleware +func NewEip712SigVerificationMiddleware(appCodec codec.Codec, ak evmtypes.AccountKeeper, signModeHandler authsigning.SignModeHandler) tx.Middleware { return func(h tx.Handler) tx.Handler { - return Eip712SigVerificationDecorator{ + return Eip712SigVerificationMiddleware{ + appCodec: appCodec, next: h, ak: ak, signModeHandler: signModeHandler, @@ -55,43 +57,37 @@ func NewEip712SigVerificationDecorator(ak evmtypes.AccountKeeper, signModeHandle } } -// CheckTx implements tx.Handler -func (svd Eip712SigVerificationDecorator) CheckTx(cx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { +func eipSigVerification(svd Eip712SigVerificationMiddleware, cx context.Context, req tx.Request) (tx.Response, error) { ctx := sdk.UnwrapSDKContext(cx) reqTx := req.Tx - // no need to verify signatures on recheck tx - if ctx.IsReCheckTx() { - return svd.next.CheckTx(ctx, req, checkReq) - } - sigTx, ok := reqTx.(authsigning.SigVerifiableTx) if !ok { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "tx %T doesn't implement authsigning.SigVerifiableTx", reqTx) + return tx.Response{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "tx %T doesn't implement authsigning.SigVerifiableTx", reqTx) } authSignTx, ok := reqTx.(authsigning.Tx) if !ok { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "tx %T doesn't implement the authsigning.Tx interface", reqTx) + return tx.Response{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "tx %T doesn't implement the authsigning.Tx interface", reqTx) } // stdSigs contains the sequence number, account number, and signatures. // When simulating, this would just be a 0-length slice. sigs, err := sigTx.GetSignaturesV2() if err != nil { - return tx.Response{}, tx.ResponseCheckTx{}, err + return tx.Response{}, err } signerAddrs := sigTx.GetSigners() // EIP712 allows just one signature if len(sigs) != 1 { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "invalid number of signers (%d); EIP712 signatures allows just one signature", len(sigs)) + return tx.Response{}, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "invalid number of signers (%d); EIP712 signatures allows just one signature", len(sigs)) } // check that signer length and signature length are the same if len(sigs) != len(signerAddrs) { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "invalid number of signer; expected: %d, got %d", len(signerAddrs), len(sigs)) + return tx.Response{}, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "invalid number of signer; expected: %d, got %d", len(signerAddrs), len(sigs)) } // EIP712 has just one signature, avoid looping here and only read index 0 @@ -100,18 +96,18 @@ func (svd Eip712SigVerificationDecorator) CheckTx(cx context.Context, req tx.Req acc, err := middleware.GetSignerAcc(ctx, svd.ak, signerAddrs[i]) if err != nil { - return tx.Response{}, tx.ResponseCheckTx{}, err + return tx.Response{}, err } // retrieve pubkey pubKey := acc.GetPubKey() if pubKey == nil { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrap(sdkerrors.ErrInvalidPubKey, "pubkey on account is not set") + return tx.Response{}, sdkerrors.Wrap(sdkerrors.ErrInvalidPubKey, "pubkey on account is not set") } // Check account sequence number. if sig.Sequence != acc.GetSequence() { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf( + return tx.Response{}, sdkerrors.Wrapf( sdkerrors.ErrWrongSequence, "account sequence mismatch, expected %d, got %d", acc.GetSequence(), sig.Sequence, ) @@ -132,27 +128,45 @@ func (svd Eip712SigVerificationDecorator) CheckTx(cx context.Context, req tx.Req Sequence: acc.GetSequence(), } - if err := VerifySignature(pubKey, signerData, sig.Data, svd.signModeHandler, authSignTx); err != nil { + if err := VerifySignature(svd.appCodec, pubKey, signerData, sig.Data, svd.signModeHandler, authSignTx); err != nil { errMsg := fmt.Errorf("signature verification failed; please verify account number (%d) and chain-id (%s): %w", accNum, chainID, err) - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, errMsg.Error()) + return tx.Response{}, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, errMsg.Error()) + } + + return tx.Response{}, nil +} + +// CheckTx implements tx.Handler +func (svd Eip712SigVerificationMiddleware) CheckTx(ctx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { + if _, err := eipSigVerification(svd, ctx, req); err != nil { + return tx.Response{}, tx.ResponseCheckTx{}, err } return svd.next.CheckTx(ctx, req, checkReq) } // DeliverTx implements tx.Handler -func (svd Eip712SigVerificationDecorator) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (svd Eip712SigVerificationMiddleware) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { + if _, err := eipSigVerification(svd, ctx, req); err != nil { + return tx.Response{}, err + } + return svd.next.DeliverTx(ctx, req) } // SimulateTx implements tx.Handler -func (svd Eip712SigVerificationDecorator) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (svd Eip712SigVerificationMiddleware) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { + if _, err := eipSigVerification(svd, ctx, req); err != nil { + return tx.Response{}, err + } + return svd.next.SimulateTx(ctx, req) } // VerifySignature verifies a transaction signature contained in SignatureData abstracting over different signing modes // and single vs multi-signatures. func VerifySignature( + appCodec codec.Codec, pubKey cryptotypes.PubKey, signerData authsigning.SignerData, sigData signing.SignatureData, @@ -206,7 +220,7 @@ func VerifySignature( var optIface ethermint.ExtensionOptionsWeb3TxI - if err := ethermintCodec.UnpackAny(opts[0], &optIface); err != nil { + if err := appCodec.UnpackAny(opts[0], &optIface); err != nil { return sdkerrors.Wrap(err, "failed to proto-unpack ExtensionOptionsWeb3Tx") } @@ -231,7 +245,7 @@ func VerifySignature( FeePayer: feePayer, } - typedData, err := eip712.WrapTxToTypedData(ethermintCodec, extOpt.TypedDataChainID, msgs[0], txBytes, feeDelegation) + typedData, err := eip712.WrapTxToTypedData(appCodec, extOpt.TypedDataChainID, msgs[0], txBytes, feeDelegation) if err != nil { return sdkerrors.Wrap(err, "failed to pack tx data in EIP712 object") } diff --git a/app/middleware/eth.go b/app/middleware/eth.go index 86199d61..ebca5f6d 100644 --- a/app/middleware/eth.go +++ b/app/middleware/eth.go @@ -17,26 +17,13 @@ import ( evmtypes "github.com/tharsis/ethermint/x/evm/types" ) -// EthSetupContextDecorator is adapted from SetUpContextDecorator from cosmos-sdk, it ignores gas consumption +// EthSetupContextMiddleware is adapted from SetUpContextMiddleware from cosmos-sdk, it ignores gas consumption // by setting the gas meter to infinite -type EthSetupContextDecorator struct { +type EthSetupContextMiddleware struct { next tx.Handler evmKeeper EVMKeeper } -// CheckTx implements tx.Handler -func (esc EthSetupContextDecorator) CheckTx(ctx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { - sdkCtx, err := gasContext(sdk.UnwrapSDKContext(ctx), req.Tx, false) - if err != nil { - return tx.Response{}, tx.ResponseCheckTx{}, err - } - - // Reset transient gas used to prepare the execution of current cosmos tx. - // Transient gas-used is necessary to sum the gas-used of cosmos tx, when it contains multiple eth msgs. - esc.evmKeeper.ResetTransientGasUsed(sdkCtx) - return esc.next.CheckTx(ctx, req, checkReq) -} - // gasContext returns a new context with a gas meter set from a given context. func gasContext(ctx sdk.Context, tx sdk.Tx, isSimulate bool) (sdk.Context, error) { // all transactions must implement GasTx @@ -62,16 +49,21 @@ func setGasMeter(ctx sdk.Context, gasLimit uint64, simulate bool) sdk.Context { return ctx.WithGasMeter(sdk.NewGasMeter(gasLimit)) } -// populateGas returns a new tx.Response with gas fields populated. -func populateGas(res tx.Response, sdkCtx sdk.Context) tx.Response { - res.GasWanted = sdkCtx.GasMeter().Limit() - res.GasUsed = sdkCtx.GasMeter().GasConsumed() +// CheckTx implements tx.Handler +func (esc EthSetupContextMiddleware) CheckTx(ctx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { + sdkCtx, err := gasContext(sdk.UnwrapSDKContext(ctx), req.Tx, false) + if err != nil { + return tx.Response{}, tx.ResponseCheckTx{}, err + } - return res + // Reset transient gas used to prepare the execution of current cosmos tx. + // Transient gas-used is necessary to sum the gas-used of cosmos tx, when it contains multiple eth msgs. + esc.evmKeeper.ResetTransientGasUsed(sdkCtx) + return esc.next.CheckTx(sdkCtx, req, checkReq) } // DeliverTx implements tx.Handler -func (esc EthSetupContextDecorator) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (esc EthSetupContextMiddleware) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { sdkCtx, err := gasContext(sdk.UnwrapSDKContext(ctx), req.Tx, false) if err != nil { return tx.Response{}, err @@ -80,11 +72,11 @@ func (esc EthSetupContextDecorator) DeliverTx(ctx context.Context, req tx.Reques // Reset transient gas used to prepare the execution of current cosmos tx. // Transient gas-used is necessary to sum the gas-used of cosmos tx, when it contains multiple eth msgs. esc.evmKeeper.ResetTransientGasUsed(sdkCtx) - return esc.next.DeliverTx(ctx, req) + return esc.next.DeliverTx(sdkCtx, req) } // SimulateTx implements tx.Handler -func (esc EthSetupContextDecorator) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (esc EthSetupContextMiddleware) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { sdkCtx, err := gasContext(sdk.UnwrapSDKContext(ctx), req.Tx, false) if err != nil { return tx.Response{}, err @@ -93,33 +85,33 @@ func (esc EthSetupContextDecorator) SimulateTx(ctx context.Context, req tx.Reque // Reset transient gas used to prepare the execution of current cosmos tx. // Transient gas-used is necessary to sum the gas-used of cosmos tx, when it contains multiple eth msgs. esc.evmKeeper.ResetTransientGasUsed(sdkCtx) - return esc.next.SimulateTx(ctx, req) + return esc.next.SimulateTx(sdkCtx, req) } -var _ tx.Handler = EthSetupContextDecorator{} +var _ tx.Handler = EthSetupContextMiddleware{} -func NewEthSetUpContextDecorator(evmKeeper EVMKeeper) tx.Middleware { +func NewEthSetUpContextMiddleware(evmKeeper EVMKeeper) tx.Middleware { return func(txh tx.Handler) tx.Handler { - return EthSetupContextDecorator{ + return EthSetupContextMiddleware{ next: txh, evmKeeper: evmKeeper, } } } -// EthMempoolFeeDecorator will check if the transaction's effective fee is at least as large +// EthMempoolFeeMiddleware will check if the transaction's effective fee is at least as large // as the local validator's minimum gasFee (defined in validator config). -// If fee is too low, decorator returns error and tx is rejected from mempool. +// If fee is too low, Middleware returns error and tx is rejected from mempool. // Note this only applies when ctx.CheckTx = true // If fee is high enough or not CheckTx, then call next AnteHandler -// CONTRACT: Tx must implement FeeTx to use MempoolFeeDecorator -type EthMempoolFeeDecorator struct { +// CONTRACT: Tx must implement FeeTx to use MempoolFeeMiddleware +type EthMempoolFeeMiddleware struct { next tx.Handler evmKeeper EVMKeeper } // CheckTx implements tx.Handler -func (mfd EthMempoolFeeDecorator) CheckTx(ctx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { +func (mfd EthMempoolFeeMiddleware) CheckTx(ctx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { sdkCtx := sdk.UnwrapSDKContext(ctx) params := mfd.evmKeeper.GetParams(sdkCtx) @@ -146,34 +138,34 @@ func (mfd EthMempoolFeeDecorator) CheckTx(ctx context.Context, req tx.Request, c } // DeliverTx implements tx.Handler -func (mfd EthMempoolFeeDecorator) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (mfd EthMempoolFeeMiddleware) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { return mfd.next.DeliverTx(ctx, req) } // SimulateTx implements tx.Handler -func (mfd EthMempoolFeeDecorator) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (mfd EthMempoolFeeMiddleware) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { return mfd.next.SimulateTx(ctx, req) } -var _ tx.Handler = EthMempoolFeeDecorator{} +var _ tx.Handler = EthMempoolFeeMiddleware{} -func NewEthMempoolFeeDecorator(ek EVMKeeper) tx.Middleware { +func NewEthMempoolFeeMiddleware(ek EVMKeeper) tx.Middleware { return func(txh tx.Handler) tx.Handler { - return EthMempoolFeeDecorator{ + return EthMempoolFeeMiddleware{ next: txh, evmKeeper: ek, } } } -// EthValidateBasicDecorator is adapted from ValidateBasicDecorator from cosmos-sdk, it ignores ErrNoSignatures -type EthValidateBasicDecorator struct { +// EthValidateBasicMiddleware is adapted from ValidateBasicMiddleware from cosmos-sdk, it ignores ErrNoSignatures +type EthValidateBasicMiddleware struct { next tx.Handler evmKeeper EVMKeeper } // CheckTx implements tx.Handler -func (vbd EthValidateBasicDecorator) CheckTx(cx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { +func (vbd EthValidateBasicMiddleware) CheckTx(cx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { ctx := sdk.UnwrapSDKContext(cx) reqTx := req.Tx @@ -255,21 +247,21 @@ func (vbd EthValidateBasicDecorator) CheckTx(cx context.Context, req tx.Request, } // DeliverTx implements tx.Handler -func (vbd EthValidateBasicDecorator) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (vbd EthValidateBasicMiddleware) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { return vbd.next.DeliverTx(ctx, req) } // SimulateTx implements tx.Handler -func (vbd EthValidateBasicDecorator) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (vbd EthValidateBasicMiddleware) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { return vbd.next.SimulateTx(ctx, req) } -var _ tx.Handler = EthValidateBasicDecorator{} +var _ tx.Handler = EthValidateBasicMiddleware{} -// NewEthValidateBasicDecorator creates a new EthValidateBasicDecorator -func NewEthValidateBasicDecorator(ek EVMKeeper) tx.Middleware { +// NewEthValidateBasicMiddleware creates a new EthValidateBasicMiddleware +func NewEthValidateBasicMiddleware(ek EVMKeeper) tx.Middleware { return func(h tx.Handler) tx.Handler { - return EthValidateBasicDecorator{ + return EthValidateBasicMiddleware{ next: h, evmKeeper: ek, } @@ -277,14 +269,13 @@ func NewEthValidateBasicDecorator(ek EVMKeeper) tx.Middleware { } -// EthSigVerificationDecorator validates an ethereum signatures -type EthSigVerificationDecorator struct { +// EthSigVerificationMiddleware validates an ethereum signatures +type EthSigVerificationMiddleware struct { next tx.Handler evmKeeper EVMKeeper } -// CheckTx implements tx.Handler -func (esvd EthSigVerificationDecorator) CheckTx(cx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { +func ethSigVerificationMiddleware(esvd EthSigVerificationMiddleware, cx context.Context, req tx.Request) (tx.Response, error) { chainID := esvd.evmKeeper.ChainID() ctx := sdk.UnwrapSDKContext(cx) reqTx := req.Tx @@ -298,12 +289,12 @@ func (esvd EthSigVerificationDecorator) CheckTx(cx context.Context, req tx.Reque for _, msg := range reqTx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) if !ok { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil)) + return tx.Response{}, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil)) } sender, err := signer.Sender(msgEthTx.AsTransaction()) if err != nil { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf( + return tx.Response{}, sdkerrors.Wrapf( sdkerrors.ErrorInvalidSigner, "couldn't retrieve sender address ('%s') from the ethereum transaction: %s", msgEthTx.From, @@ -315,33 +306,50 @@ func (esvd EthSigVerificationDecorator) CheckTx(cx context.Context, req tx.Reque msgEthTx.From = sender.Hex() } + return tx.Response{}, nil +} + +// CheckTx implements tx.Handler +func (esvd EthSigVerificationMiddleware) CheckTx(ctx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { + if _, err := ethSigVerificationMiddleware(esvd, ctx, req); err != nil { + return tx.Response{}, tx.ResponseCheckTx{}, err + } + return esvd.next.CheckTx(ctx, req, checkReq) } // DeliverTx implements tx.Handler -func (esvd EthSigVerificationDecorator) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (esvd EthSigVerificationMiddleware) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { + if _, err := ethSigVerificationMiddleware(esvd, ctx, req); err != nil { + return tx.Response{}, err + } + return esvd.next.DeliverTx(ctx, req) } // SimulateTx implements tx.Handler -func (esvd EthSigVerificationDecorator) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (esvd EthSigVerificationMiddleware) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { + if _, err := ethSigVerificationMiddleware(esvd, ctx, req); err != nil { + return tx.Response{}, err + } + return esvd.next.SimulateTx(ctx, req) } -var _ tx.Handler = EthSigVerificationDecorator{} +var _ tx.Handler = EthSigVerificationMiddleware{} -// NewEthSigVerificationDecorator creates a new EthSigVerificationDecorator -func NewEthSigVerificationDecorator(ek EVMKeeper) tx.Middleware { +// NewEthSigVerificationMiddleware creates a new EthSigVerificationMiddleware +func NewEthSigVerificationMiddleware(ek EVMKeeper) tx.Middleware { return func(h tx.Handler) tx.Handler { - return EthSigVerificationDecorator{ + return EthSigVerificationMiddleware{ next: h, evmKeeper: ek, } } } -// EthAccountVerificationDecorator validates an account balance checks -type EthAccountVerificationDecorator struct { +// EthAccountVerificationMiddleware validates an account balance checks +type EthAccountVerificationMiddleware struct { next tx.Handler ak evmtypes.AccountKeeper bankKeeper evmtypes.BankKeeper @@ -349,7 +357,7 @@ type EthAccountVerificationDecorator struct { } // CheckTx implements tx.Handler -func (avd EthAccountVerificationDecorator) CheckTx(cx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { +func (avd EthAccountVerificationMiddleware) CheckTx(cx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { reqTx := req.Tx ctx := sdk.UnwrapSDKContext(cx) if !ctx.IsCheckTx() { @@ -394,21 +402,21 @@ func (avd EthAccountVerificationDecorator) CheckTx(cx context.Context, req tx.Re } // DeliverTx implements tx.Handler -func (avd EthAccountVerificationDecorator) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (avd EthAccountVerificationMiddleware) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { return avd.next.DeliverTx(ctx, req) } // SimulateTx implements tx.Handler -func (avd EthAccountVerificationDecorator) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (avd EthAccountVerificationMiddleware) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { return avd.next.SimulateTx(ctx, req) } -var _ tx.Handler = EthAccountVerificationDecorator{} +var _ tx.Handler = EthAccountVerificationMiddleware{} -// NewEthAccountVerificationDecorator creates a new EthAccountVerificationDecorator -func NewEthAccountVerificationDecorator(ak evmtypes.AccountKeeper, bankKeeper evmtypes.BankKeeper, ek EVMKeeper) tx.Middleware { +// NewEthAccountVerificationMiddleware creates a new EthAccountVerificationMiddleware +func NewEthAccountVerificationMiddleware(ak evmtypes.AccountKeeper, bankKeeper evmtypes.BankKeeper, ek EVMKeeper) tx.Middleware { return func(h tx.Handler) tx.Handler { - return EthAccountVerificationDecorator{ + return EthAccountVerificationMiddleware{ next: h, ak: ak, bankKeeper: bankKeeper, @@ -417,16 +425,15 @@ func NewEthAccountVerificationDecorator(ak evmtypes.AccountKeeper, bankKeeper ev } } -// EthGasConsumeDecorator validates enough intrinsic gas for the transaction and +// EthGasConsumeMiddleware validates enough intrinsic gas for the transaction and // gas consumption. -type EthGasConsumeDecorator struct { +type EthGasConsumeMiddleware struct { next tx.Handler evmKeeper EVMKeeper maxGasWanted uint64 } -// CheckTx implements tx.Handler -func (egcd EthGasConsumeDecorator) CheckTx(cx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { +func ethGasMiddleware(egcd EthGasConsumeMiddleware, cx context.Context, req tx.Request) (context.Context, tx.Response, error) { ctx := sdk.UnwrapSDKContext(cx) reqTx := req.Tx @@ -445,12 +452,12 @@ func (egcd EthGasConsumeDecorator) CheckTx(cx context.Context, req tx.Request, c for _, msg := range reqTx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) if !ok { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil)) + return ctx, tx.Response{}, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil)) } txData, err := evmtypes.UnpackTxData(msgEthTx.Data) if err != nil { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrap(err, "failed to unpack tx data") + return ctx, tx.Response{}, sdkerrors.Wrap(err, "failed to unpack tx data") } if ctx.IsCheckTx() { @@ -474,7 +481,7 @@ func (egcd EthGasConsumeDecorator) CheckTx(cx context.Context, req tx.Request, c london, ) if err != nil { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf(err, "failed to deduct transaction costs from user balance") + return ctx, tx.Response{}, sdkerrors.Wrapf(err, "failed to deduct transaction costs from user balance") } events = append(events, sdk.NewEvent(sdk.EventTypeTx, sdk.NewAttribute(sdk.AttributeKeyFee, fees.String()))) @@ -498,28 +505,49 @@ func (egcd EthGasConsumeDecorator) CheckTx(cx context.Context, req tx.Request, c gasConsumed := ctx.GasMeter().GasConsumed() ctx = ctx.WithGasMeter(ethermint.NewInfiniteGasMeterWithLimit(gasWanted)) ctx.GasMeter().ConsumeGas(gasConsumed, "copy gas consumed") - return egcd.next.CheckTx(ctx, req, checkReq) + + return ctx, tx.Response{}, nil +} + +// CheckTx implements tx.Handler +func (egcd EthGasConsumeMiddleware) CheckTx(ctx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { + newCtx, _, err := ethGasMiddleware(egcd, ctx, req) + if err != nil { + return tx.Response{}, tx.ResponseCheckTx{}, err + } + + return egcd.next.CheckTx(newCtx, req, checkReq) } // DeliverTx implements tx.Handler -func (egcd EthGasConsumeDecorator) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { - return egcd.next.DeliverTx(ctx, req) +func (egcd EthGasConsumeMiddleware) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { + newCtx, _, err := ethGasMiddleware(egcd, ctx, req) + if err != nil { + return tx.Response{}, err + } + + return egcd.next.DeliverTx(newCtx, req) } // SimulateTx implements tx.Handler -func (egcd EthGasConsumeDecorator) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { - return egcd.next.SimulateTx(ctx, req) +func (egcd EthGasConsumeMiddleware) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { + newCtx, _, err := ethGasMiddleware(egcd, ctx, req) + if err != nil { + return tx.Response{}, err + } + + return egcd.next.SimulateTx(newCtx, req) } -var _ tx.Handler = EthGasConsumeDecorator{} +var _ tx.Handler = EthGasConsumeMiddleware{} -// NewEthGasConsumeDecorator creates a new EthGasConsumeDecorator -func NewEthGasConsumeDecorator( +// NewEthGasConsumeMiddleware creates a new EthGasConsumeMiddleware +func NewEthGasConsumeMiddleware( evmKeeper EVMKeeper, maxGasWanted uint64, ) tx.Middleware { return func(h tx.Handler) tx.Handler { - return EthGasConsumeDecorator{ + return EthGasConsumeMiddleware{ h, evmKeeper, maxGasWanted, @@ -527,15 +555,14 @@ func NewEthGasConsumeDecorator( } } -// CanTransferDecorator checks if the sender is allowed to transfer funds according to the EVM block +// CanTransferMiddleware checks if the sender is allowed to transfer funds according to the EVM block // context rules. -type CanTransferDecorator struct { +type CanTransferMiddleware struct { next tx.Handler evmKeeper EVMKeeper } -// CheckTx implements tx.Handler -func (ctd CanTransferDecorator) CheckTx(cx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { +func canTransfer(ctd CanTransferMiddleware, cx context.Context, req tx.Request) (tx.Response, error) { ctx := sdk.UnwrapSDKContext(cx) reqTx := req.Tx params := ctd.evmKeeper.GetParams(ctx) @@ -545,14 +572,14 @@ func (ctd CanTransferDecorator) CheckTx(cx context.Context, req tx.Request, chec for _, msg := range reqTx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) if !ok { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil)) + return tx.Response{}, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil)) } baseFee := ctd.evmKeeper.BaseFee(ctx, ethCfg) coreMsg, err := msgEthTx.AsMessage(signer, baseFee) if err != nil { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf( + return tx.Response{}, sdkerrors.Wrapf( err, "failed to create an ethereum core.Message from signer %T", signer, ) @@ -571,7 +598,7 @@ func (ctd CanTransferDecorator) CheckTx(cx context.Context, req tx.Request, chec // 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(stateDB, coreMsg.From(), coreMsg.Value()) { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf( + return tx.Response{}, sdkerrors.Wrapf( sdkerrors.ErrInsufficientFunds, "failed to transfer %s from address %s using the EVM block context transfer function", coreMsg.Value(), @@ -581,13 +608,13 @@ func (ctd CanTransferDecorator) CheckTx(cx context.Context, req tx.Request, chec if evmtypes.IsLondon(ethCfg, ctx.BlockHeight()) { if baseFee == nil { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrap( + return tx.Response{}, sdkerrors.Wrap( evmtypes.ErrInvalidBaseFee, "base fee is supported but evm block context value is nil", ) } if coreMsg.GasFeeCap().Cmp(baseFee) < 0 { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf( + return tx.Response{}, sdkerrors.Wrapf( sdkerrors.ErrInsufficientFee, "max fee per gas less than block base fee (%s < %s)", coreMsg.GasFeeCap(), baseFee, @@ -596,57 +623,75 @@ func (ctd CanTransferDecorator) CheckTx(cx context.Context, req tx.Request, chec } } + return tx.Response{}, nil +} + +// CheckTx implements tx.Handler +func (ctd CanTransferMiddleware) CheckTx(ctx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { + if _, err := canTransfer(ctd, ctx, req); err != nil { + return tx.Response{}, tx.ResponseCheckTx{}, err + } + return ctd.next.CheckTx(ctx, req, checkReq) } // DeliverTx implements tx.Handler -func (ctd CanTransferDecorator) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (ctd CanTransferMiddleware) DeliverTx(cx context.Context, req tx.Request) (tx.Response, error) { + ctx := sdk.UnwrapSDKContext(cx) + if _, err := canTransfer(ctd, ctx, req); err != nil { + return tx.Response{}, err + } + return ctd.next.DeliverTx(ctx, req) } // SimulateTx implements tx.Handler -func (ctd CanTransferDecorator) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (ctd CanTransferMiddleware) SimulateTx(cx context.Context, req tx.Request) (tx.Response, error) { + ctx := sdk.UnwrapSDKContext(cx) + if _, err := canTransfer(ctd, ctx, req); err != nil { + return tx.Response{}, err + } + return ctd.next.SimulateTx(ctx, req) } -var _ tx.Handler = CanTransferDecorator{} +var _ tx.Handler = CanTransferMiddleware{} -// NewCanTransferDecorator creates a new CanTransferDecorator instance. -func NewCanTransferDecorator(evmKeeper EVMKeeper) tx.Middleware { +// NewCanTransferMiddleware creates a new CanTransferMiddleware instance. +func NewCanTransferMiddleware(evmKeeper EVMKeeper) tx.Middleware { return func(h tx.Handler) tx.Handler { - return CanTransferDecorator{ + return CanTransferMiddleware{ next: h, evmKeeper: evmKeeper, } } } -// EthIncrementSenderSequenceDecorator increments the sequence of the signers. -type EthIncrementSenderSequenceDecorator struct { +// EthIncrementSenderSequenceMiddleware increments the sequence of the signers. +type EthIncrementSenderSequenceMiddleware struct { next tx.Handler ak evmtypes.AccountKeeper } -// CheckTx implements tx.Handler -func (issd EthIncrementSenderSequenceDecorator) CheckTx(cx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { +func ethIncrementSenderSequence(issd EthIncrementSenderSequenceMiddleware, cx context.Context, req tx.Request) (tx.Response, error) { ctx := sdk.UnwrapSDKContext(cx) reqTx := req.Tx for _, msg := range reqTx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) if !ok { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil)) + return tx.Response{}, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil)) } txData, err := evmtypes.UnpackTxData(msgEthTx.Data) if err != nil { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrap(err, "failed to unpack tx data") + return tx.Response{}, sdkerrors.Wrap(err, "failed to unpack tx data") } // increase sequence of sender acc := issd.ak.GetAccount(ctx, msgEthTx.GetFrom()) if acc == nil { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf( + return tx.Response{}, sdkerrors.Wrapf( sdkerrors.ErrUnknownAddress, "account %s is nil", common.BytesToAddress(msgEthTx.GetFrom().Bytes()), ) @@ -656,42 +701,57 @@ func (issd EthIncrementSenderSequenceDecorator) CheckTx(cx context.Context, req // we merged the nonce verification to nonce increment, so when tx includes multiple messages // with same sender, they'll be accepted. if txData.GetNonce() != nonce { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf( + return tx.Response{}, sdkerrors.Wrapf( sdkerrors.ErrInvalidSequence, "invalid nonce; got %d, expected %d", txData.GetNonce(), nonce, ) } if err := acc.SetSequence(nonce + 1); err != nil { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf(err, "failed to set sequence to %d", acc.GetSequence()+1) + return tx.Response{}, sdkerrors.Wrapf(err, "failed to set sequence to %d", acc.GetSequence()+1) } issd.ak.SetAccount(ctx, acc) } + return tx.Response{}, nil +} + +// CheckTx implements tx.Handler +func (issd EthIncrementSenderSequenceMiddleware) CheckTx(ctx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { + if _, err := ethIncrementSenderSequence(issd, ctx, req); err != nil { + return tx.Response{}, tx.ResponseCheckTx{}, err + } + return issd.next.CheckTx(ctx, req, checkReq) } // DeliverTx implements tx.Handler -func (issd EthIncrementSenderSequenceDecorator) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { - return issd.next.DeliverTx(ctx, req) +func (issd EthIncrementSenderSequenceMiddleware) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { + if _, err := ethIncrementSenderSequence(issd, ctx, req); err != nil { + return tx.Response{}, err + } + return issd.next.DeliverTx(ctx, req) } // SimulateTx implements tx.Handler -func (issd EthIncrementSenderSequenceDecorator) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (issd EthIncrementSenderSequenceMiddleware) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { + if _, err := ethIncrementSenderSequence(issd, ctx, req); err != nil { + return tx.Response{}, err + } + return issd.next.SimulateTx(ctx, req) } -var _ tx.Handler = EthIncrementSenderSequenceDecorator{} +var _ tx.Handler = EthIncrementSenderSequenceMiddleware{} -// NewEthIncrementSenderSequenceDecorator creates a new EthIncrementSenderSequenceDecorator. -func NewEthIncrementSenderSequenceDecorator(ak evmtypes.AccountKeeper) tx.Middleware { +// NewEthIncrementSenderSequenceMiddleware creates a new EthIncrementSenderSequenceMiddleware. +func NewEthIncrementSenderSequenceMiddleware(ak evmtypes.AccountKeeper) tx.Middleware { return func(h tx.Handler) tx.Handler { - return EthIncrementSenderSequenceDecorator{ + return EthIncrementSenderSequenceMiddleware{ next: h, ak: ak, } } - } diff --git a/app/middleware/options.go b/app/middleware/options.go index fd1ed2a4..fd64a789 100644 --- a/app/middleware/options.go +++ b/app/middleware/options.go @@ -1,6 +1,7 @@ package middleware import ( + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/types/tx" "github.com/cosmos/cosmos-sdk/types/tx/signing" evmtypes "github.com/tharsis/ethermint/x/evm/types" @@ -17,6 +18,7 @@ import ( type HandlerOptions struct { Debug bool + Codec codec.Codec // TxDecoder is used to decode the raw tx bytes into a sdk.Tx. TxDecoder sdk.TxDecoder @@ -68,14 +70,14 @@ func (options HandlerOptions) Validate() error { func newEthAuthMiddleware(options HandlerOptions) (tx.Handler, error) { return authmiddleware.ComposeMiddlewares( authmiddleware.NewRunMsgsTxHandler(options.MsgServiceRouter, options.LegacyRouter), - NewEthSetUpContextDecorator(options.EvmKeeper), - NewEthMempoolFeeDecorator(options.EvmKeeper), - NewEthValidateBasicDecorator(options.EvmKeeper), - NewEthSigVerificationDecorator(options.EvmKeeper), - NewEthAccountVerificationDecorator(options.AccountKeeper, options.BankKeeper, options.EvmKeeper), - NewEthGasConsumeDecorator(options.EvmKeeper, options.MaxTxGasWanted), - NewCanTransferDecorator(options.EvmKeeper), - NewEthIncrementSenderSequenceDecorator(options.AccountKeeper), + NewEthSetUpContextMiddleware(options.EvmKeeper), + NewEthMempoolFeeMiddleware(options.EvmKeeper), + NewEthValidateBasicMiddleware(options.EvmKeeper), + NewEthSigVerificationMiddleware(options.EvmKeeper), + NewEthAccountVerificationMiddleware(options.AccountKeeper, options.BankKeeper, options.EvmKeeper), + NewEthGasConsumeMiddleware(options.EvmKeeper, options.MaxTxGasWanted), + NewCanTransferMiddleware(options.EvmKeeper), + NewEthIncrementSenderSequenceMiddleware(options.AccountKeeper), ), nil } @@ -83,6 +85,7 @@ func newCosmosAuthMiddleware(options HandlerOptions) (tx.Handler, error) { return authmiddleware.ComposeMiddlewares( authmiddleware.NewRunMsgsTxHandler(options.MsgServiceRouter, options.LegacyRouter), authmiddleware.NewTxDecoderMiddleware(options.TxDecoder), + NewRejectMessagesMiddleware, // Set a new GasMeter on sdk.Context. // // Make sure the Gas middleware is outside of all other middlewares @@ -127,6 +130,7 @@ func newCosmosAnteHandlerEip712(options HandlerOptions) (tx.Handler, error) { return authmiddleware.ComposeMiddlewares( authmiddleware.NewRunMsgsTxHandler(options.MsgServiceRouter, options.LegacyRouter), + NewRejectMessagesMiddleware, authmiddleware.NewTxDecoderMiddleware(options.TxDecoder), // Set a new GasMeter on sdk.Context. // @@ -141,7 +145,7 @@ func newCosmosAnteHandlerEip712(options HandlerOptions) (tx.Handler, error) { // emitted outside of this middleware. authmiddleware.NewIndexEventsTxMiddleware(options.IndexEvents), // Reject all extension options other than the ones needed by the feemarket. - authmiddleware.NewExtensionOptionsMiddleware(options.ExtensionOptionChecker), + // authmiddleware.NewExtensionOptionsMiddleware(options.ExtensionOptionChecker), authmiddleware.ValidateBasicMiddleware, authmiddleware.TxTimeoutHeightMiddleware, authmiddleware.ValidateMemoMiddleware(options.AccountKeeper), @@ -155,7 +159,7 @@ func newCosmosAnteHandlerEip712(options HandlerOptions) (tx.Handler, error) { authmiddleware.ValidateSigCountMiddleware(options.AccountKeeper), authmiddleware.SigGasConsumeMiddleware(options.AccountKeeper, options.SigGasConsumer), // Note: signature verification uses EIP instead of the cosmos signature validator - NewEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), + NewEip712SigVerificationMiddleware(options.Codec, options.AccountKeeper, options.SignModeHandler), authmiddleware.IncrementSequenceMiddleware(options.AccountKeeper), // Creates a new MultiStore branch, discards downstream writes if the downstream returns error. // These kinds of middlewares should be put under this: diff --git a/app/middleware/reject.go b/app/middleware/reject.go index 4a57792f..55098f10 100644 --- a/app/middleware/reject.go +++ b/app/middleware/reject.go @@ -8,16 +8,14 @@ import ( evmtypes "github.com/tharsis/ethermint/x/evm/types" ) -// RejectMessagesDecorator prevents invalid msg types from being executed -type RejectMessagesDecorator struct { +// RejectMessagesMiddleware prevents invalid msg types from being executed +type RejectMessagesMiddleware struct { next tx.Handler } -func NewRejectMessagesDecorator() tx.Middleware { - return func(h tx.Handler) tx.Handler { - return RejectMessagesDecorator{ - next: h, - } +func NewRejectMessagesMiddleware(txh tx.Handler) tx.Handler { + return RejectMessagesMiddleware{ + next: txh, } } @@ -26,37 +24,33 @@ func NewRejectMessagesDecorator() tx.Middleware { // order to perform the refund. // CheckTx implements tx.Handler -func (rmd RejectMessagesDecorator) CheckTx(ctx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { - reqTx := req.Tx - for _, msg := range reqTx.GetMsgs() { - if _, ok := msg.(*evmtypes.MsgEthereumTx); ok { - return tx.Response{}, tx.ResponseCheckTx{}, sdkerrors.Wrapf( - sdkerrors.ErrInvalidType, - "MsgEthereumTx needs to be contained within a tx with 'ExtensionOptionsEthereumTx' option", - ) - } +func (rmd RejectMessagesMiddleware) CheckTx(ctx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { + if _, err := reject(req); err != nil { + return tx.Response{}, tx.ResponseCheckTx{}, err } return rmd.next.CheckTx(ctx, req, checkReq) } // DeliverTx implements tx.Handler -func (rmd RejectMessagesDecorator) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { - reqTx := req.Tx - for _, msg := range reqTx.GetMsgs() { - if _, ok := msg.(*evmtypes.MsgEthereumTx); ok { - return tx.Response{}, sdkerrors.Wrapf( - sdkerrors.ErrInvalidType, - "MsgEthereumTx needs to be contained within a tx with 'ExtensionOptionsEthereumTx' option", - ) - } +func (rmd RejectMessagesMiddleware) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { + if _, err := reject(req); err != nil { + return tx.Response{}, err } return rmd.next.DeliverTx(ctx, req) } // SimulateTx implements tx.Handler -func (rmd RejectMessagesDecorator) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { +func (rmd RejectMessagesMiddleware) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { + if _, err := reject(req); err != nil { + return tx.Response{}, err + } + + return rmd.next.SimulateTx(ctx, req) +} + +func reject(req tx.Request) (tx.Response, error) { reqTx := req.Tx for _, msg := range reqTx.GetMsgs() { if _, ok := msg.(*evmtypes.MsgEthereumTx); ok { @@ -67,7 +61,7 @@ func (rmd RejectMessagesDecorator) SimulateTx(ctx context.Context, req tx.Reques } } - return rmd.next.SimulateTx(ctx, req) + return tx.Response{}, nil } -var _ tx.Handler = RejectMessagesDecorator{} +var _ tx.Handler = RejectMessagesMiddleware{} diff --git a/encoding/codec/codec.go b/encoding/codec/codec.go index d600766f..957d00a1 100644 --- a/encoding/codec/codec.go +++ b/encoding/codec/codec.go @@ -6,7 +6,6 @@ import ( "github.com/cosmos/cosmos-sdk/std" sdk "github.com/cosmos/cosmos-sdk/types" - txtypes "github.com/cosmos/cosmos-sdk/types/tx" cryptocodec "github.com/tharsis/ethermint/crypto/codec" ethermint "github.com/tharsis/ethermint/types" ) @@ -21,7 +20,6 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { // RegisterInterfaces registers Interfaces from types, crypto, and SDK std. func RegisterInterfaces(interfaceRegistry codectypes.InterfaceRegistry) { std.RegisterInterfaces(interfaceRegistry) - txtypes.RegisterInterfaces(interfaceRegistry) cryptocodec.RegisterInterfaces(interfaceRegistry) ethermint.RegisterInterfaces(interfaceRegistry) } diff --git a/rpc/apis.go b/rpc/apis.go index 29c431a9..78f78a44 100644 --- a/rpc/apis.go +++ b/rpc/apis.go @@ -3,6 +3,8 @@ package rpc import ( + "fmt" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/server" @@ -35,92 +37,96 @@ const ( apiVersion = "1.0" ) -// GetRPCAPIs returns the list of all APIs -func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient, selectedAPIs []string) []rpc.API { - nonceLock := new(types.AddrLocker) - evmBackend := backend.NewEVMBackend(ctx, ctx.Logger, clientCtx) +// APICreator creates the json-rpc api implementations. +type APICreator = func(*server.Context, client.Context, *rpcclient.WSClient) []rpc.API - var apis []rpc.API - // remove duplicates - selectedAPIs = unique(selectedAPIs) +// apiCreators defines the json-rpc api namespaces. +var apiCreators map[string]APICreator - for index := range selectedAPIs { - switch selectedAPIs[index] { - case EthNamespace: - apis = append(apis, - rpc.API{ +func init() { + apiCreators = map[string]APICreator{ + EthNamespace: func(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient) []rpc.API { + nonceLock := new(types.AddrLocker) + evmBackend := backend.NewEVMBackend(ctx, ctx.Logger, clientCtx) + return []rpc.API{ + { Namespace: EthNamespace, Version: apiVersion, Service: eth.NewPublicAPI(ctx.Logger, clientCtx, evmBackend, nonceLock), Public: true, }, - rpc.API{ + { Namespace: EthNamespace, Version: apiVersion, Service: filters.NewPublicAPI(ctx.Logger, clientCtx, tmWSClient, evmBackend), Public: true, }, - ) - case Web3Namespace: - apis = append(apis, - rpc.API{ + } + }, + Web3Namespace: func(*server.Context, client.Context, *rpcclient.WSClient) []rpc.API { + return []rpc.API{ + { Namespace: Web3Namespace, Version: apiVersion, Service: web3.NewPublicAPI(), Public: true, }, - ) - case NetNamespace: - apis = append(apis, - rpc.API{ + } + }, + NetNamespace: func(_ *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API { + return []rpc.API{ + { Namespace: NetNamespace, Version: apiVersion, Service: net.NewPublicAPI(clientCtx), Public: true, }, - ) - case PersonalNamespace: - apis = append(apis, - rpc.API{ + } + }, + PersonalNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API { + evmBackend := backend.NewEVMBackend(ctx, ctx.Logger, clientCtx) + return []rpc.API{ + { Namespace: PersonalNamespace, Version: apiVersion, Service: personal.NewAPI(ctx.Logger, clientCtx, evmBackend), Public: false, }, - ) - case TxPoolNamespace: - apis = append(apis, - rpc.API{ + } + }, + TxPoolNamespace: func(ctx *server.Context, _ client.Context, _ *rpcclient.WSClient) []rpc.API { + return []rpc.API{ + { Namespace: TxPoolNamespace, Version: apiVersion, Service: txpool.NewPublicAPI(ctx.Logger), Public: true, }, - ) - case DebugNamespace: - apis = append(apis, - rpc.API{ + } + }, + DebugNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API { + evmBackend := backend.NewEVMBackend(ctx, ctx.Logger, clientCtx) + return []rpc.API{ + { Namespace: DebugNamespace, Version: apiVersion, Service: debug.NewAPI(ctx, evmBackend, clientCtx), Public: true, }, - ) - case MinerNamespace: - apis = append(apis, - rpc.API{ + } + }, + MinerNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API { + evmBackend := backend.NewEVMBackend(ctx, ctx.Logger, clientCtx) + return []rpc.API{ + { Namespace: MinerNamespace, Version: apiVersion, Service: miner.NewPrivateAPI(ctx, clientCtx, evmBackend), Public: false, }, - ) - default: - ctx.Logger.Error("invalid namespace value", "namespace", selectedAPIs[index]) - } + } + }, } - - return apis } func unique(intSlice []string) []string { @@ -134,3 +140,28 @@ func unique(intSlice []string) []string { } return list } + +// GetRPCAPIs returns the list of all APIs +func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient, selectedAPIs []string) []rpc.API { + var apis []rpc.API + + for _, ns := range selectedAPIs { + if creator, ok := apiCreators[ns]; ok { + apis = append(apis, creator(ctx, clientCtx, tmWSClient)...) + } else { + ctx.Logger.Error("invalid namespace value", "namespace", ns) + } + } + + return apis +} + +// RegisterAPINamespace registers a new API namespace with the API creator. +// This function fails if the namespace is already registered. +func RegisterAPINamespace(ns string, creator APICreator) error { + if _, ok := apiCreators[ns]; ok { + return fmt.Errorf("duplicated api namespace %s", ns) + } + apiCreators[ns] = creator + return nil +} diff --git a/scripts/integration-test-all.sh b/scripts/integration-test-all.sh index 11a0d00a..5e71ded1 100755 --- a/scripts/integration-test-all.sh +++ b/scripts/integration-test-all.sh @@ -144,7 +144,7 @@ if [[ -z $TEST || $TEST == "integration" ]] ; then for i in $(seq 1 "$TEST_QTD"); do HOST_RPC=http://$IP_ADDR:$RPC_PORT"$i" - echo "going to test ethermint node $HOST_RPC ..." + echo "going to test chibaclonk node $HOST_RPC ..." MODE=$MODE HOST=$HOST_RPC go test ./tests/e2e/... -timeout=$time_out -v -short TEST_FAIL=$? done @@ -158,7 +158,7 @@ if [[ -z $TEST || $TEST == "rpc" || $TEST == "pending" ]]; then for i in $(seq 1 "$TEST_QTD"); do HOST_RPC=http://$IP_ADDR:$RPC_PORT"$i" - echo "going to test ethermint node $HOST_RPC ..." + echo "going to test chibaclonk node $HOST_RPC ..." MODE=$MODE HOST=$HOST_RPC go test ./tests/rpc/... -timeout=$time_out -v -short TEST_FAIL=$? diff --git a/x/evm/module.go b/x/evm/module.go index ff07071e..1335b263 100644 --- a/x/evm/module.go +++ b/x/evm/module.go @@ -170,11 +170,13 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw // RandomizedParams creates randomized evm param changes for the simulator. func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { - return nil + return simulation.ParamChanges(r) } // RegisterStoreDecoder registers a decoder for evm module's types -func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) {} +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[types.StoreKey] = simulation.NewDecodeStore() +} // ProposalContents doesn't return any content functions for governance proposals. func (AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { @@ -188,5 +190,7 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) { // WeightedOperations returns the all the evm module operations with their respective weights. func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { - return nil + return simulation.WeightedOperations( + simState.AppParams, simState.Cdc, am.ak, am.keeper, + ) } diff --git a/x/evm/simulation/decoder.go b/x/evm/simulation/decoder.go new file mode 100644 index 00000000..e09e870c --- /dev/null +++ b/x/evm/simulation/decoder.go @@ -0,0 +1,31 @@ +package simulation + +import ( + "bytes" + "fmt" + + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/ethereum/go-ethereum/common" + "github.com/tharsis/ethermint/x/evm/types" +) + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding evm type. +func NewDecodeStore() func(kvA, kvB kv.Pair) string { + return func(kvA, kvB kv.Pair) string { + switch { + case bytes.Equal(kvA.Key[:1], types.KeyPrefixStorage): + storageHashA := common.BytesToHash(kvA.Value).Hex() + storageHashB := common.BytesToHash(kvB.Value).Hex() + + return fmt.Sprintf("%v\n%v", storageHashA, storageHashB) + case bytes.Equal(kvA.Key[:1], types.KeyPrefixCode): + codeHashA := common.BytesToHash(kvA.Value).Hex() + codeHashB := common.BytesToHash(kvB.Value).Hex() + + return fmt.Sprintf("%v\n%v", codeHashA, codeHashB) + default: + panic(fmt.Sprintf("invalid evm key prefix %X", kvA.Key[:1])) + } + } +} diff --git a/x/evm/simulation/genesis.go b/x/evm/simulation/genesis.go index 74c2eebb..1ee9c969 100644 --- a/x/evm/simulation/genesis.go +++ b/x/evm/simulation/genesis.go @@ -3,12 +3,22 @@ package simulation import ( "encoding/json" "fmt" + "math/rand" "github.com/cosmos/cosmos-sdk/types/module" "github.com/tharsis/ethermint/x/evm/types" ) +// GenExtraEIPs randomly generates specific extra eips or not. +func genExtraEIPs(r *rand.Rand) []int64 { + var extraEIPs []int64 + if r.Uint32()%2 == 0 { + extraEIPs = []int64{1344, 1884, 2200, 2929, 3198, 3529} + } + return extraEIPs +} + // RandomizedGenState generates a random GenesisState for nft func RandomizedGenState(simState *module.SimulationState) { params := types.NewParams(types.DefaultEVMDenom, true, true, types.DefaultChainConfig()) diff --git a/x/evm/simulation/opereations.go b/x/evm/simulation/opereations.go new file mode 100644 index 00000000..97252ca4 --- /dev/null +++ b/x/evm/simulation/opereations.go @@ -0,0 +1,302 @@ +package simulation + +import ( + "encoding/json" + "fmt" + "math/big" + "math/rand" + "time" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdktx "github.com/cosmos/cosmos-sdk/types/tx" + + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/auth/signing" + "github.com/cosmos/cosmos-sdk/x/auth/tx" + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + + "github.com/tharsis/ethermint/encoding" + "github.com/tharsis/ethermint/server/config" + "github.com/tharsis/ethermint/tests" + "github.com/tharsis/ethermint/x/evm/keeper" + "github.com/tharsis/ethermint/x/evm/types" +) + +const ( + /* #nosec */ + OpWeightMsgEthSimpleTransfer = "op_weight_msg_eth_simple_transfer" + /* #nosec */ + OpWeightMsgEthCreateContract = "op_weight_msg_eth_create_contract" + /* #nosec */ + OpWeightMsgEthCallContract = "op_weight_msg_eth_call_contract" +) + +const ( + WeightMsgEthSimpleTransfer = 50 + WeightMsgEthCreateContract = 50 +) + +var ErrNoEnoughBalance = fmt.Errorf("no enough balance") + +var maxWaitSeconds = 10 + +type simulateContext struct { + context sdk.Context + bapp *baseapp.BaseApp + rand *rand.Rand + keeper *keeper.Keeper +} + +// WeightedOperations generate Two kinds of operations: SimulateEthSimpleTransfer, SimulateEthCreateContract. +// Contract call operations work as the future operations of SimulateEthCreateContract. +func WeightedOperations( + appParams simtypes.AppParams, cdc codec.JSONCodec, ak types.AccountKeeper, k *keeper.Keeper, +) simulation.WeightedOperations { + var ( + weightMsgEthSimpleTransfer int + weightMsgEthCreateContract int + ) + + appParams.GetOrGenerate(cdc, OpWeightMsgEthSimpleTransfer, &weightMsgEthSimpleTransfer, nil, + func(_ *rand.Rand) { + weightMsgEthSimpleTransfer = WeightMsgEthSimpleTransfer + }, + ) + + appParams.GetOrGenerate(cdc, OpWeightMsgEthCreateContract, &weightMsgEthCreateContract, nil, + func(_ *rand.Rand) { + weightMsgEthCreateContract = WeightMsgEthCreateContract + }, + ) + + return simulation.WeightedOperations{ + simulation.NewWeightedOperation( + weightMsgEthSimpleTransfer, + SimulateEthSimpleTransfer(ak, k), + ), + simulation.NewWeightedOperation( + weightMsgEthCreateContract, + SimulateEthCreateContract(ak, k), + ), + } +} + +// SimulateEthSimpleTransfer simulate simple eth account transferring gas token. +// It randomly choose sender, recipient and transferable amount. +// Other tx details like nonce, gasprice, gaslimit are calculated to get valid value. +func SimulateEthSimpleTransfer(ak types.AccountKeeper, k *keeper.Keeper) simtypes.Operation { + return func( + r *rand.Rand, bapp *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + simAccount, _ := simtypes.RandomAcc(r, accs) + var recipient simtypes.Account + if r.Intn(2) == 1 { + recipient, _ = simtypes.RandomAcc(r, accs) + } else { + recipient = simtypes.RandomAccounts(r, 1)[0] + } + from := common.BytesToAddress(simAccount.Address) + to := common.BytesToAddress(recipient.Address) + + simulateContext := &simulateContext{ctx, bapp, r, k} + + return SimulateEthTx(simulateContext, &from, &to, nil, (*hexutil.Bytes)(&[]byte{}), simAccount.PrivKey, nil) + } +} + +// SimulateEthCreateContract simulate create an ERC20 contract. +// It makes operationSimulateEthCallContract the future operations of SimulateEthCreateContract to ensure valid contract call. +func SimulateEthCreateContract(ak types.AccountKeeper, k *keeper.Keeper) simtypes.Operation { + return func( + r *rand.Rand, bapp *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + simAccount, _ := simtypes.RandomAcc(r, accs) + + from := common.BytesToAddress(simAccount.Address) + nonce := k.GetNonce(ctx, from) + + ctorArgs, err := types.ERC20Contract.ABI.Pack("", from, sdk.NewIntWithDecimal(1000, 18).BigInt()) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEthereumTx, "can not pack owner and supply"), nil, err + } + data := types.ERC20Contract.Bin + data = append(data, ctorArgs...) + + simulateContext := &simulateContext{ctx, bapp, r, k} + + fops := make([]simtypes.FutureOperation, 1) + whenCall := ctx.BlockHeader().Time.Add(time.Duration(r.Intn(maxWaitSeconds)+1) * time.Second) + contractAddr := crypto.CreateAddress(from, nonce) + var tokenReceipient simtypes.Account + if r.Intn(2) == 1 { + tokenReceipient, _ = simtypes.RandomAcc(r, accs) + } else { + tokenReceipient = simtypes.RandomAccounts(r, 1)[0] + } + receipientAddr := common.BytesToAddress(tokenReceipient.Address) + fops[0] = simtypes.FutureOperation{ + BlockTime: whenCall, + Op: operationSimulateEthCallContract(k, &contractAddr, &receipientAddr, nil), + } + return SimulateEthTx(simulateContext, &from, nil, nil, (*hexutil.Bytes)(&data), simAccount.PrivKey, fops) + } +} + +// operationSimulateEthCallContract simulate calling an contract. +// It is always calling an ERC20 contract. +func operationSimulateEthCallContract(k *keeper.Keeper, contractAddr, to *common.Address, amount *big.Int) simtypes.Operation { + return func( + r *rand.Rand, bapp *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + simAccount, _ := simtypes.RandomAcc(r, accs) + + from := common.BytesToAddress(simAccount.Address) + + ctorArgs, err := types.ERC20Contract.ABI.Pack("transfer", to, amount) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEthereumTx, "can not pack method and args"), nil, err + } + data := types.ERC20Contract.Bin + data = append(data, ctorArgs...) + + simulateContext := &simulateContext{ctx, bapp, r, k} + + return SimulateEthTx(simulateContext, &from, contractAddr, nil, (*hexutil.Bytes)(&data), simAccount.PrivKey, nil) + } +} + +// SimulateEthTx creates valid ethereum tx and pack it as cosmos tx, and deliver it. +func SimulateEthTx( + ctx *simulateContext, from, to *common.Address, amount *big.Int, data *hexutil.Bytes, prv cryptotypes.PrivKey, fops []simtypes.FutureOperation, +) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + ethTx, err := CreateRandomValidEthTx(ctx, from, nil, nil, data) + if err == ErrNoEnoughBalance { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEthereumTx, "no enough balance"), nil, nil + } + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEthereumTx, "can not create valid eth tx"), nil, err + } + + txConfig := encoding.MakeConfig(module.NewBasicManager()).TxConfig + txBuilder := txConfig.NewTxBuilder() + signedTx, err := GetSignedTx(ctx, txBuilder, ethTx, prv) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEthereumTx, "can not sign ethereum tx"), nil, err + } + + _, _, err = ctx.bapp.SimDeliver(txConfig.TxEncoder(), signedTx) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEthereumTx, "failed to deliver tx"), nil, err + } + + return simtypes.OperationMsg{}, fops, nil +} + +// CreateRandomValidEthTx create the ethereum tx with valid random values +func CreateRandomValidEthTx(ctx *simulateContext, from, to *common.Address, amount *big.Int, data *hexutil.Bytes) (ethTx *types.MsgEthereumTx, err error) { + estimateGas, err := EstimateGas(ctx, from, to, data) + if err != nil { + return nil, err + } + gasLimit := estimateGas + uint64(ctx.rand.Intn(int(sdktx.MaxGasWanted-estimateGas))) + ethChainID := ctx.keeper.ChainID() + chainConfig := ctx.keeper.GetParams(ctx.context).ChainConfig.EthereumConfig(ethChainID) + gasPrice := ctx.keeper.BaseFee(ctx.context, chainConfig) + gasFeeCap := new(big.Int).Add(gasPrice, big.NewInt(int64(ctx.rand.Int()))) + gasTipCap := big.NewInt(int64(ctx.rand.Int())) + nonce := ctx.keeper.GetNonce(ctx.context, *from) + + if amount == nil { + amount, err = RandomTransferableAmount(ctx, *from, gasLimit, gasFeeCap) + if err != nil { + return nil, err + } + } + + ethTx = types.NewTx(ethChainID, nonce, to, amount, gasLimit, gasPrice, gasFeeCap, gasTipCap, *data, nil) + ethTx.From = from.String() + return ethTx, nil +} + +// GetSignedTx sign the ethereum tx and packs it as a signing.Tx . +func GetSignedTx(ctx *simulateContext, txBuilder client.TxBuilder, msg *types.MsgEthereumTx, prv cryptotypes.PrivKey) (signedTx signing.Tx, err error) { + builder, ok := txBuilder.(tx.ExtensionOptionsTxBuilder) + if !ok { + return nil, fmt.Errorf("can not initiate ExtensionOptionsTxBuilder") + } + option, err := codectypes.NewAnyWithValue(&types.ExtensionOptionsEthereumTx{}) + if err != nil { + return nil, err + } + builder.SetExtensionOptions(option) + + if err := msg.Sign(ethtypes.LatestSignerForChainID(ctx.keeper.ChainID()), tests.NewSigner(prv)); err != nil { + return nil, err + } + + if err = builder.SetMsgs(msg); err != nil { + return nil, err + } + + txData, err := types.UnpackTxData(msg.Data) + if err != nil { + return nil, err + } + + fees := sdk.NewCoins(sdk.NewCoin(ctx.keeper.GetParams(ctx.context).EvmDenom, sdk.NewIntFromBigInt(txData.Fee()))) + builder.SetFeeAmount(fees) + builder.SetGasLimit(msg.GetGas()) + + signedTx = builder.GetTx() + return signedTx, nil +} + +// RandomTransferableAmount generates a random valid transferable amount. +// Transferable amount is between the range [0, spendable), spendable = balance - gasFeeCap * GasLimit. +func RandomTransferableAmount(ctx *simulateContext, address common.Address, gasLimit uint64, gasFeeCap *big.Int) (amount *big.Int, err error) { + balance := ctx.keeper.GetBalance(ctx.context, address) + feeLimit := new(big.Int).Mul(gasFeeCap, big.NewInt(int64(gasLimit))) + if (feeLimit.Cmp(balance)) > 0 { + return nil, ErrNoEnoughBalance + } + spendable := new(big.Int).Sub(balance, feeLimit) + if spendable.Cmp(big.NewInt(0)) == 0 { + amount = new(big.Int).Set(spendable) + return amount, nil + } + simAmount, err := simtypes.RandPositiveInt(ctx.rand, sdk.NewIntFromBigInt(spendable)) + if err != nil { + return nil, err + } + amount = simAmount.BigInt() + return amount, nil +} + +// EstimateGas estimates the gas used by quering the keeper. +func EstimateGas(ctx *simulateContext, from, to *common.Address, data *hexutil.Bytes) (gas uint64, err error) { + args, err := json.Marshal(&types.TransactionArgs{To: to, From: from, Data: data}) + if err != nil { + return 0, err + } + + res, err := ctx.keeper.EstimateGas(sdk.WrapSDKContext(ctx.context), &types.EthCallRequest{ + Args: args, + GasCap: config.DefaultGasCap, + }) + if err != nil { + return 0, err + } + return res.Gas, nil +} diff --git a/x/evm/simulation/params.go b/x/evm/simulation/params.go new file mode 100644 index 00000000..94295a5c --- /dev/null +++ b/x/evm/simulation/params.go @@ -0,0 +1,28 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/tharsis/ethermint/x/evm/types" +) + +const ( + keyExtraEIPs = "ExtraEIPs" +) + +// ParamChanges defines the parameters that can be modified by param change proposals +// on the simulation. +func ParamChanges(r *rand.Rand) []simtypes.ParamChange { + return []simtypes.ParamChange{ + simulation.NewSimParamChange(types.ModuleName, keyExtraEIPs, + func(r *rand.Rand) string { + return fmt.Sprintf("\"%d\"", genExtraEIPs(r)) + }, + ), + } +} diff --git a/x/evm/types/msg.go b/x/evm/types/msg.go index 6ee739a5..204a926f 100644 --- a/x/evm/types/msg.go +++ b/x/evm/types/msg.go @@ -11,7 +11,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - // "github.com/cosmos/cosmos-sdk/x/auth/ante" + authmiddleware "github.com/cosmos/cosmos-sdk/x/auth/middleware" "github.com/cosmos/cosmos-sdk/x/auth/signing" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" @@ -23,9 +23,9 @@ import ( ) var ( - _ sdk.Msg = &MsgEthereumTx{} - _ sdk.Tx = &MsgEthereumTx{} - // _ ante.GasTx = &MsgEthereumTx{} + _ sdk.Msg = &MsgEthereumTx{} + _ sdk.Tx = &MsgEthereumTx{} + _ authmiddleware.GasTx = &MsgEthereumTx{} _ codectypes.UnpackInterfacesMessage = MsgEthereumTx{} )