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 }