github.com/Finschia/finschia-sdk@v0.49.1/x/auth/ante/basic.go (about)

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