github.com/cosmos/cosmos-sdk@v0.50.10/x/auth/ante/basic.go (about)

     1  package ante
     2  
     3  import (
     4  	errorsmod "cosmossdk.io/errors"
     5  	storetypes "cosmossdk.io/store/types"
     6  
     7  	"github.com/cosmos/cosmos-sdk/codec/legacy"
     8  	"github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
     9  	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
    10  	sdk "github.com/cosmos/cosmos-sdk/types"
    11  	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
    12  	"github.com/cosmos/cosmos-sdk/types/tx/signing"
    13  	"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
    14  	authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
    15  )
    16  
    17  // ValidateBasicDecorator will call tx.ValidateBasic and return any non-nil error.
    18  // If ValidateBasic passes, decorator calls next AnteHandler in chain. Note,
    19  // ValidateBasicDecorator decorator will not get executed on ReCheckTx since it
    20  // is not dependent on application state.
    21  type ValidateBasicDecorator struct{}
    22  
    23  func NewValidateBasicDecorator() ValidateBasicDecorator {
    24  	return ValidateBasicDecorator{}
    25  }
    26  
    27  func (vbd ValidateBasicDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
    28  	// no need to validate basic on recheck tx, call next antehandler
    29  	if ctx.IsReCheckTx() {
    30  		return next(ctx, tx, simulate)
    31  	}
    32  
    33  	if validateBasic, ok := tx.(sdk.HasValidateBasic); ok {
    34  		if err := validateBasic.ValidateBasic(); err != nil {
    35  			return ctx, err
    36  		}
    37  	}
    38  
    39  	return next(ctx, tx, simulate)
    40  }
    41  
    42  // ValidateMemoDecorator will validate memo given the parameters passed in
    43  // If memo is too large decorator returns with error, otherwise call next AnteHandler
    44  // CONTRACT: Tx must implement TxWithMemo interface
    45  type ValidateMemoDecorator struct {
    46  	ak AccountKeeper
    47  }
    48  
    49  func NewValidateMemoDecorator(ak AccountKeeper) ValidateMemoDecorator {
    50  	return ValidateMemoDecorator{
    51  		ak: ak,
    52  	}
    53  }
    54  
    55  func (vmd ValidateMemoDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
    56  	memoTx, ok := tx.(sdk.TxWithMemo)
    57  	if !ok {
    58  		return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type")
    59  	}
    60  
    61  	memoLength := len(memoTx.GetMemo())
    62  	if memoLength > 0 {
    63  		params := vmd.ak.GetParams(ctx)
    64  		if uint64(memoLength) > params.MaxMemoCharacters {
    65  			return ctx, errorsmod.Wrapf(sdkerrors.ErrMemoTooLarge,
    66  				"maximum number of characters is %d but received %d characters",
    67  				params.MaxMemoCharacters, memoLength,
    68  			)
    69  		}
    70  	}
    71  
    72  	return next(ctx, tx, simulate)
    73  }
    74  
    75  // ConsumeTxSizeGasDecorator will take in parameters and consume gas proportional
    76  // to the size of tx before calling next AnteHandler. Note, the gas costs will be
    77  // slightly over estimated due to the fact that any given signing account may need
    78  // to be retrieved from state.
    79  //
    80  // CONTRACT: If simulate=true, then signatures must either be completely filled
    81  // in or empty.
    82  // CONTRACT: To use this decorator, signatures of transaction must be represented
    83  // as legacytx.StdSignature otherwise simulate mode will incorrectly estimate gas cost.
    84  type ConsumeTxSizeGasDecorator struct {
    85  	ak AccountKeeper
    86  }
    87  
    88  func NewConsumeGasForTxSizeDecorator(ak AccountKeeper) ConsumeTxSizeGasDecorator {
    89  	return ConsumeTxSizeGasDecorator{
    90  		ak: ak,
    91  	}
    92  }
    93  
    94  func (cgts ConsumeTxSizeGasDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
    95  	sigTx, ok := tx.(authsigning.SigVerifiableTx)
    96  	if !ok {
    97  		return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "invalid tx type")
    98  	}
    99  	params := cgts.ak.GetParams(ctx)
   100  
   101  	ctx.GasMeter().ConsumeGas(params.TxSizeCostPerByte*storetypes.Gas(len(ctx.TxBytes())), "txSize")
   102  
   103  	// simulate gas cost for signatures in simulate mode
   104  	if simulate {
   105  		// in simulate mode, each element should be a nil signature
   106  		sigs, err := sigTx.GetSignaturesV2()
   107  		if err != nil {
   108  			return ctx, err
   109  		}
   110  		n := len(sigs)
   111  
   112  		signers, err := sigTx.GetSigners()
   113  		if err != nil {
   114  			return sdk.Context{}, err
   115  		}
   116  
   117  		for i, signer := range signers {
   118  			// if signature is already filled in, no need to simulate gas cost
   119  			if i < n && !isIncompleteSignature(sigs[i].Data) {
   120  				continue
   121  			}
   122  
   123  			var pubkey cryptotypes.PubKey
   124  
   125  			acc := cgts.ak.GetAccount(ctx, signer)
   126  
   127  			// use placeholder simSecp256k1Pubkey if sig is nil
   128  			if acc == nil || acc.GetPubKey() == nil {
   129  				pubkey = simSecp256k1Pubkey
   130  			} else {
   131  				pubkey = acc.GetPubKey()
   132  			}
   133  
   134  			// use stdsignature to mock the size of a full signature
   135  			simSig := legacytx.StdSignature{ //nolint:staticcheck // SA1019: legacytx.StdSignature is deprecated
   136  				Signature: simSecp256k1Sig[:],
   137  				PubKey:    pubkey,
   138  			}
   139  
   140  			sigBz := legacy.Cdc.MustMarshal(simSig)
   141  			cost := storetypes.Gas(len(sigBz) + 6)
   142  
   143  			// If the pubkey is a multi-signature pubkey, then we estimate for the maximum
   144  			// number of signers.
   145  			if _, ok := pubkey.(*multisig.LegacyAminoPubKey); ok {
   146  				cost *= params.TxSigLimit
   147  			}
   148  
   149  			ctx.GasMeter().ConsumeGas(params.TxSizeCostPerByte*cost, "txSize")
   150  		}
   151  	}
   152  
   153  	return next(ctx, tx, simulate)
   154  }
   155  
   156  // isIncompleteSignature tests whether SignatureData is fully filled in for simulation purposes
   157  func isIncompleteSignature(data signing.SignatureData) bool {
   158  	if data == nil {
   159  		return true
   160  	}
   161  
   162  	switch data := data.(type) {
   163  	case *signing.SingleSignatureData:
   164  		return len(data.Signature) == 0
   165  	case *signing.MultiSignatureData:
   166  		if len(data.Signatures) == 0 {
   167  			return true
   168  		}
   169  		for _, s := range data.Signatures {
   170  			if isIncompleteSignature(s) {
   171  				return true
   172  			}
   173  		}
   174  	}
   175  
   176  	return false
   177  }
   178  
   179  type (
   180  	// TxTimeoutHeightDecorator defines an AnteHandler decorator that checks for a
   181  	// tx height timeout.
   182  	TxTimeoutHeightDecorator struct{}
   183  
   184  	// TxWithTimeoutHeight defines the interface a tx must implement in order for
   185  	// TxHeightTimeoutDecorator to process the tx.
   186  	TxWithTimeoutHeight interface {
   187  		sdk.Tx
   188  
   189  		GetTimeoutHeight() uint64
   190  	}
   191  )
   192  
   193  // TxTimeoutHeightDecorator defines an AnteHandler decorator that checks for a
   194  // tx height timeout.
   195  func NewTxTimeoutHeightDecorator() TxTimeoutHeightDecorator {
   196  	return TxTimeoutHeightDecorator{}
   197  }
   198  
   199  // AnteHandle implements an AnteHandler decorator for the TxHeightTimeoutDecorator
   200  // type where the current block height is checked against the tx's height timeout.
   201  // If a height timeout is provided (non-zero) and is less than the current block
   202  // height, then an error is returned.
   203  func (txh TxTimeoutHeightDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
   204  	timeoutTx, ok := tx.(TxWithTimeoutHeight)
   205  	if !ok {
   206  		return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "expected tx to implement TxWithTimeoutHeight")
   207  	}
   208  
   209  	timeoutHeight := timeoutTx.GetTimeoutHeight()
   210  	if timeoutHeight > 0 && uint64(ctx.BlockHeight()) > timeoutHeight {
   211  		return ctx, errorsmod.Wrapf(
   212  			sdkerrors.ErrTxTimeoutHeight, "block height: %d, timeout height: %d", ctx.BlockHeight(), timeoutHeight,
   213  		)
   214  	}
   215  
   216  	return next(ctx, tx, simulate)
   217  }