github.com/cosmos/cosmos-sdk@v0.50.10/x/auth/ante/feegrant_test.go (about) 1 package ante_test 2 3 import ( 4 "context" 5 "errors" 6 "math/rand" 7 "testing" 8 "time" 9 10 "github.com/cometbft/cometbft/crypto" 11 "github.com/golang/mock/gomock" 12 "github.com/stretchr/testify/require" 13 14 storetypes "cosmossdk.io/store/types" 15 16 "github.com/cosmos/cosmos-sdk/client" 17 "github.com/cosmos/cosmos-sdk/codec" 18 cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" 19 "github.com/cosmos/cosmos-sdk/testutil/testdata" 20 sdk "github.com/cosmos/cosmos-sdk/types" 21 sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 22 "github.com/cosmos/cosmos-sdk/types/simulation" 23 "github.com/cosmos/cosmos-sdk/types/tx/signing" 24 "github.com/cosmos/cosmos-sdk/x/auth/ante" 25 authsign "github.com/cosmos/cosmos-sdk/x/auth/signing" 26 "github.com/cosmos/cosmos-sdk/x/auth/testutil" 27 "github.com/cosmos/cosmos-sdk/x/auth/tx" 28 authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 29 ) 30 31 func TestDeductFeesNoDelegation(t *testing.T) { 32 cases := map[string]struct { 33 fee int64 34 valid bool 35 err error 36 errMsg string 37 malleate func(*AnteTestSuite) (signer TestAccount, feeAcc sdk.AccAddress) 38 }{ 39 "paying with low funds": { 40 fee: 50, 41 valid: false, 42 err: sdkerrors.ErrInsufficientFunds, 43 malleate: func(suite *AnteTestSuite) (TestAccount, sdk.AccAddress) { 44 accs := suite.CreateTestAccounts(1) 45 // 2 calls are needed because we run the ante twice 46 suite.bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), accs[0].acc.GetAddress(), authtypes.FeeCollectorName, gomock.Any()).Return(sdkerrors.ErrInsufficientFunds).Times(2) 47 return accs[0], nil 48 }, 49 }, 50 "paying with good funds": { 51 fee: 50, 52 valid: true, 53 malleate: func(suite *AnteTestSuite) (TestAccount, sdk.AccAddress) { 54 accs := suite.CreateTestAccounts(1) 55 suite.bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), accs[0].acc.GetAddress(), authtypes.FeeCollectorName, gomock.Any()).Return(nil).Times(2) 56 return accs[0], nil 57 }, 58 }, 59 "paying with no account": { 60 fee: 1, 61 valid: false, 62 err: sdkerrors.ErrUnknownAddress, 63 malleate: func(suite *AnteTestSuite) (TestAccount, sdk.AccAddress) { 64 // Do not register the account 65 priv, _, addr := testdata.KeyTestPubAddr() 66 return TestAccount{ 67 acc: authtypes.NewBaseAccountWithAddress(addr), 68 priv: priv, 69 }, nil 70 }, 71 }, 72 "no fee with real account": { 73 fee: 0, 74 valid: true, 75 malleate: func(suite *AnteTestSuite) (TestAccount, sdk.AccAddress) { 76 accs := suite.CreateTestAccounts(1) 77 return accs[0], nil 78 }, 79 }, 80 "no fee with no account": { 81 fee: 0, 82 valid: false, 83 err: sdkerrors.ErrUnknownAddress, 84 malleate: func(suite *AnteTestSuite) (TestAccount, sdk.AccAddress) { 85 // Do not register the account 86 priv, _, addr := testdata.KeyTestPubAddr() 87 return TestAccount{ 88 acc: authtypes.NewBaseAccountWithAddress(addr), 89 priv: priv, 90 }, nil 91 }, 92 }, 93 "valid fee grant": { 94 // note: the original test said "valid fee grant with no account". 95 // this is impossible given that feegrant.GrantAllowance calls 96 // SetAccount for the grantee. 97 fee: 50, 98 valid: true, 99 malleate: func(suite *AnteTestSuite) (TestAccount, sdk.AccAddress) { 100 accs := suite.CreateTestAccounts(2) 101 102 suite.feeGrantKeeper.EXPECT().UseGrantedFees(gomock.Any(), accs[1].acc.GetAddress(), accs[0].acc.GetAddress(), gomock.Any(), gomock.Any()).Return(nil).Times(2) 103 suite.bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), accs[1].acc.GetAddress(), authtypes.FeeCollectorName, gomock.Any()).Return(nil).Times(2) 104 return accs[0], accs[1].acc.GetAddress() 105 }, 106 }, 107 "no fee grant": { 108 fee: 2, 109 valid: false, 110 err: sdkerrors.ErrNotFound, 111 malleate: func(suite *AnteTestSuite) (TestAccount, sdk.AccAddress) { 112 accs := suite.CreateTestAccounts(2) 113 suite.feeGrantKeeper.EXPECT(). 114 UseGrantedFees(gomock.Any(), accs[1].acc.GetAddress(), accs[0].acc.GetAddress(), gomock.Any(), gomock.Any()). 115 Return(sdkerrors.ErrNotFound.Wrap("fee-grant not found")). 116 Times(2) 117 return accs[0], accs[1].acc.GetAddress() 118 }, 119 }, 120 "allowance smaller than requested fee": { 121 fee: 50, 122 valid: false, 123 errMsg: "fee limit exceeded", 124 malleate: func(suite *AnteTestSuite) (TestAccount, sdk.AccAddress) { 125 accs := suite.CreateTestAccounts(2) 126 suite.feeGrantKeeper.EXPECT(). 127 UseGrantedFees(gomock.Any(), accs[1].acc.GetAddress(), accs[0].acc.GetAddress(), gomock.Any(), gomock.Any()). 128 Return(errors.New("fee limit exceeded")). 129 Times(2) 130 return accs[0], accs[1].acc.GetAddress() 131 }, 132 }, 133 "granter cannot cover allowed fee grant": { 134 fee: 50, 135 valid: false, 136 err: sdkerrors.ErrInsufficientFunds, 137 malleate: func(suite *AnteTestSuite) (TestAccount, sdk.AccAddress) { 138 accs := suite.CreateTestAccounts(2) 139 suite.feeGrantKeeper.EXPECT().UseGrantedFees(gomock.Any(), accs[1].acc.GetAddress(), accs[0].acc.GetAddress(), gomock.Any(), gomock.Any()).Return(nil).Times(2) 140 suite.bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), accs[1].acc.GetAddress(), authtypes.FeeCollectorName, gomock.Any()).Return(sdkerrors.ErrInsufficientFunds).Times(2) 141 return accs[0], accs[1].acc.GetAddress() 142 }, 143 }, 144 } 145 146 for name, stc := range cases { 147 tc := stc // to make scopelint happy 148 t.Run(name, func(t *testing.T) { 149 suite := SetupTestSuite(t, false) 150 protoTxCfg := tx.NewTxConfig(codec.NewProtoCodec(suite.encCfg.InterfaceRegistry), tx.DefaultSignModes) 151 // this just tests our handler 152 dfd := ante.NewDeductFeeDecorator(suite.accountKeeper, suite.bankKeeper, suite.feeGrantKeeper, nil) 153 feeAnteHandler := sdk.ChainAnteDecorators(dfd) 154 155 // this tests the whole stack 156 anteHandlerStack := suite.anteHandler 157 158 signer, feeAcc := stc.malleate(suite) 159 160 fee := sdk.NewCoins(sdk.NewInt64Coin("atom", tc.fee)) 161 msgs := []sdk.Msg{testdata.NewTestMsg(signer.acc.GetAddress())} 162 163 acc := suite.accountKeeper.GetAccount(suite.ctx, signer.acc.GetAddress()) 164 privs, accNums, seqs := []cryptotypes.PrivKey{signer.priv}, []uint64{0}, []uint64{0} 165 if acc != nil { 166 accNums, seqs = []uint64{acc.GetAccountNumber()}, []uint64{acc.GetSequence()} 167 } 168 169 var defaultGenTxGas uint64 = 10000000 170 tx, err := genTxWithFeeGranter(protoTxCfg, msgs, fee, defaultGenTxGas, suite.ctx.ChainID(), accNums, seqs, feeAcc, privs...) 171 require.NoError(t, err) 172 txBytes, err := protoTxCfg.TxEncoder()(tx) 173 require.NoError(t, err) 174 bytesCtx := suite.ctx.WithTxBytes(txBytes) 175 require.NoError(t, err) 176 _, err = feeAnteHandler(bytesCtx, tx, false) // tests only feegrant ante 177 if tc.valid { 178 require.NoError(t, err) 179 } else { 180 testutil.AssertError(t, err, tc.err, tc.errMsg) 181 } 182 183 _, err = anteHandlerStack(bytesCtx, tx, false) // tests whole stack 184 if tc.valid { 185 require.NoError(t, err) 186 } else { 187 testutil.AssertError(t, err, tc.err, tc.errMsg) 188 } 189 }) 190 } 191 } 192 193 // don't consume any gas 194 func SigGasNoConsumer(meter storetypes.GasMeter, sig []byte, pubkey crypto.PubKey, params authtypes.Params) error { 195 return nil 196 } 197 198 func genTxWithFeeGranter(gen client.TxConfig, msgs []sdk.Msg, feeAmt sdk.Coins, gas uint64, chainID string, accNums, 199 accSeqs []uint64, feeGranter sdk.AccAddress, priv ...cryptotypes.PrivKey, 200 ) (sdk.Tx, error) { 201 sigs := make([]signing.SignatureV2, len(priv)) 202 203 // create a random length memo 204 r := rand.New(rand.NewSource(time.Now().UnixNano())) 205 206 memo := simulation.RandStringOfLength(r, simulation.RandIntBetween(r, 0, 100)) 207 208 signMode := signing.SignMode_SIGN_MODE_DIRECT 209 210 // 1st round: set SignatureV2 with empty signatures, to set correct 211 // signer infos. 212 for i, p := range priv { 213 sigs[i] = signing.SignatureV2{ 214 PubKey: p.PubKey(), 215 Data: &signing.SingleSignatureData{ 216 SignMode: signMode, 217 }, 218 Sequence: accSeqs[i], 219 } 220 } 221 222 tx := gen.NewTxBuilder() 223 err := tx.SetMsgs(msgs...) 224 if err != nil { 225 return nil, err 226 } 227 err = tx.SetSignatures(sigs...) 228 if err != nil { 229 return nil, err 230 } 231 tx.SetMemo(memo) 232 tx.SetFeeAmount(feeAmt) 233 tx.SetGasLimit(gas) 234 tx.SetFeeGranter(feeGranter) 235 236 // 2nd round: once all signer infos are set, every signer can sign. 237 for i, p := range priv { 238 signerData := authsign.SignerData{ 239 ChainID: chainID, 240 AccountNumber: accNums[i], 241 Sequence: accSeqs[i], 242 PubKey: p.PubKey(), 243 } 244 signBytes, err := authsign.GetSignBytesAdapter( 245 context.Background(), gen.SignModeHandler(), signMode, signerData, tx.GetTx()) 246 if err != nil { 247 panic(err) 248 } 249 sig, err := p.Sign(signBytes) 250 if err != nil { 251 panic(err) 252 } 253 sigs[i].Data.(*signing.SingleSignatureData).Signature = sig 254 err = tx.SetSignatures(sigs...) 255 if err != nil { 256 panic(err) 257 } 258 } 259 260 return tx.GetTx(), nil 261 }