cosmos-sdk/x/auth/middleware/feegrant_test.go

238 lines
6.4 KiB
Go

package middleware_test
import (
"math/rand"
"testing"
"time"
"github.com/tendermint/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/simapp/helpers"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/simulation"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/cosmos/cosmos-sdk/x/auth/middleware"
authsign "github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/cosmos/cosmos-sdk/x/auth/tx"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/bank/testutil"
"github.com/cosmos/cosmos-sdk/x/feegrant"
)
func (s *MWTestSuite) TestDeductFeesNoDelegation() {
ctx := s.SetupTest(false) // setup
app := s.app
protoTxCfg := tx.NewTxConfig(codec.NewProtoCodec(app.InterfaceRegistry()), tx.DefaultSignModes)
txHandler := middleware.ComposeMiddlewares(
noopTxHandler,
middleware.DeductFeeMiddleware(
s.app.AccountKeeper,
s.app.BankKeeper,
s.app.FeeGrantKeeper,
),
)
// keys and addresses
priv1, _, addr1 := testdata.KeyTestPubAddr()
priv2, _, addr2 := testdata.KeyTestPubAddr()
priv3, _, addr3 := testdata.KeyTestPubAddr()
priv4, _, addr4 := testdata.KeyTestPubAddr()
priv5, _, addr5 := testdata.KeyTestPubAddr()
// Set addr1 with insufficient funds
err := testutil.FundAccount(s.app.BankKeeper, ctx, addr1, []sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(10))})
s.Require().NoError(err)
// Set addr2 with more funds
err = testutil.FundAccount(s.app.BankKeeper, ctx, addr2, []sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(99999))})
s.Require().NoError(err)
// grant fee allowance from `addr2` to `addr3` (plenty to pay)
err = app.FeeGrantKeeper.GrantAllowance(ctx, addr2, addr3, &feegrant.BasicAllowance{
SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 500)),
})
s.Require().NoError(err)
// grant low fee allowance (20atom), to check the tx requesting more than allowed.
err = app.FeeGrantKeeper.GrantAllowance(ctx, addr2, addr4, &feegrant.BasicAllowance{
SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 20)),
})
s.Require().NoError(err)
cases := map[string]struct {
signerKey cryptotypes.PrivKey
signer sdk.AccAddress
feeAccount sdk.AccAddress
fee int64
valid bool
}{
"paying with low funds": {
signerKey: priv1,
signer: addr1,
fee: 50,
valid: false,
},
"paying with good funds": {
signerKey: priv2,
signer: addr2,
fee: 50,
valid: true,
},
"paying with no account": {
signerKey: priv3,
signer: addr3,
fee: 1,
valid: false,
},
"no fee with real account": {
signerKey: priv1,
signer: addr1,
fee: 0,
valid: true,
},
"no fee with no account": {
signerKey: priv5,
signer: addr5,
fee: 0,
valid: false,
},
"valid fee grant without account": {
signerKey: priv3,
signer: addr3,
feeAccount: addr2,
fee: 50,
valid: true,
},
"no fee grant": {
signerKey: priv3,
signer: addr3,
feeAccount: addr1,
fee: 2,
valid: false,
},
"allowance smaller than requested fee": {
signerKey: priv4,
signer: addr4,
feeAccount: addr2,
fee: 50,
valid: false,
},
"granter cannot cover allowed fee grant": {
signerKey: priv4,
signer: addr4,
feeAccount: addr1,
fee: 50,
valid: false,
},
}
for name, stc := range cases {
tc := stc // to make scopelint happy
s.T().Run(name, func(t *testing.T) {
fee := sdk.NewCoins(sdk.NewInt64Coin("atom", tc.fee))
msgs := []sdk.Msg{testdata.NewTestMsg(tc.signer)}
acc := app.AccountKeeper.GetAccount(ctx, tc.signer)
privs, accNums, seqs := []cryptotypes.PrivKey{tc.signerKey}, []uint64{0}, []uint64{0}
if acc != nil {
accNums, seqs = []uint64{acc.GetAccountNumber()}, []uint64{acc.GetSequence()}
}
testTx, err := genTxWithFeeGranter(protoTxCfg, msgs, fee, helpers.DefaultGenTxGas, ctx.ChainID(), accNums, seqs, tc.feeAccount, privs...)
s.Require().NoError(err)
// tests only feegrant middleware
_, err = txHandler.DeliverTx(sdk.WrapSDKContext(ctx), txtypes.Request{Tx: testTx})
if tc.valid {
s.Require().NoError(err)
} else {
s.Require().Error(err)
}
// tests while stack
_, err = s.txHandler.DeliverTx(sdk.WrapSDKContext(ctx), txtypes.Request{Tx: testTx})
if tc.valid {
s.Require().NoError(err)
} else {
s.Require().Error(err)
}
})
}
}
// don't consume any gas
func SigGasNoConsumer(meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params authtypes.Params) error {
return nil
}
func genTxWithFeeGranter(gen client.TxConfig, msgs []sdk.Msg, feeAmt sdk.Coins, gas uint64, chainID string, accNums,
accSeqs []uint64, feeGranter sdk.AccAddress, priv ...cryptotypes.PrivKey) (sdk.Tx, error) {
sigs := make([]signing.SignatureV2, len(priv))
// create a random length memo
r := rand.New(rand.NewSource(time.Now().UnixNano()))
memo := simulation.RandStringOfLength(r, simulation.RandIntBetween(r, 0, 100))
signMode := gen.SignModeHandler().DefaultMode()
// 1st round: set SignatureV2 with empty signatures, to set correct
// signer infos.
for i, p := range priv {
sigs[i] = signing.SignatureV2{
PubKey: p.PubKey(),
Data: &signing.SingleSignatureData{
SignMode: signMode,
},
Sequence: accSeqs[i],
}
}
tx := gen.NewTxBuilder()
err := tx.SetMsgs(msgs...)
if err != nil {
return nil, err
}
err = tx.SetSignatures(sigs...)
if err != nil {
return nil, err
}
tx.SetMemo(memo)
tx.SetFeeAmount(feeAmt)
tx.SetGasLimit(gas)
tx.SetFeeGranter(feeGranter)
// 2nd round: once all signer infos are set, every signer can sign.
for i, p := range priv {
signerData := authsign.SignerData{
Address: sdk.AccAddress(p.PubKey().Address()).String(),
ChainID: chainID,
AccountNumber: accNums[i],
Sequence: accSeqs[i],
PubKey: p.PubKey(),
}
signBytes, err := gen.SignModeHandler().GetSignBytes(signMode, signerData, tx.GetTx())
if err != nil {
panic(err)
}
sig, err := p.Sign(signBytes)
if err != nil {
panic(err)
}
sigs[i].Data.(*signing.SingleSignatureData).Signature = sig
err = tx.SetSignatures(sigs...)
if err != nil {
panic(err)
}
}
return tx.GetTx(), nil
}