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

     1  package ante_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	abci "github.com/cometbft/cometbft/abci/types"
     7  	"github.com/golang/mock/gomock"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	// TODO We don't need to import these API types if we use gogo's registry
    11  	// ref: https://github.com/cosmos/cosmos-sdk/issues/14647
    12  	_ "cosmossdk.io/api/cosmos/bank/v1beta1"
    13  	_ "cosmossdk.io/api/cosmos/crypto/secp256k1"
    14  	storetypes "cosmossdk.io/store/types"
    15  
    16  	"github.com/cosmos/cosmos-sdk/client"
    17  	"github.com/cosmos/cosmos-sdk/client/tx"
    18  	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
    19  	"github.com/cosmos/cosmos-sdk/runtime"
    20  	"github.com/cosmos/cosmos-sdk/testutil"
    21  	clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
    22  	"github.com/cosmos/cosmos-sdk/testutil/testdata"
    23  	_ "github.com/cosmos/cosmos-sdk/testutil/testdata/testpb"
    24  	sdk "github.com/cosmos/cosmos-sdk/types"
    25  	moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
    26  	"github.com/cosmos/cosmos-sdk/types/tx/signing"
    27  	"github.com/cosmos/cosmos-sdk/x/auth"
    28  	"github.com/cosmos/cosmos-sdk/x/auth/ante"
    29  	antetestutil "github.com/cosmos/cosmos-sdk/x/auth/ante/testutil"
    30  	authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec"
    31  	"github.com/cosmos/cosmos-sdk/x/auth/keeper"
    32  	xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
    33  	authtestutil "github.com/cosmos/cosmos-sdk/x/auth/testutil"
    34  	txtestutil "github.com/cosmos/cosmos-sdk/x/auth/tx/testutil"
    35  	"github.com/cosmos/cosmos-sdk/x/auth/types"
    36  	"github.com/cosmos/cosmos-sdk/x/bank"
    37  )
    38  
    39  // TestAccount represents an account used in the tests in x/auth/ante.
    40  type TestAccount struct {
    41  	acc  sdk.AccountI
    42  	priv cryptotypes.PrivKey
    43  }
    44  
    45  // AnteTestSuite is a test suite to be used with ante handler tests.
    46  type AnteTestSuite struct {
    47  	anteHandler    sdk.AnteHandler
    48  	ctx            sdk.Context
    49  	clientCtx      client.Context
    50  	txBuilder      client.TxBuilder
    51  	accountKeeper  keeper.AccountKeeper
    52  	bankKeeper     *authtestutil.MockBankKeeper
    53  	txBankKeeper   *txtestutil.MockBankKeeper
    54  	feeGrantKeeper *antetestutil.MockFeegrantKeeper
    55  	encCfg         moduletestutil.TestEncodingConfig
    56  }
    57  
    58  // SetupTest setups a new test, with new app, context, and anteHandler.
    59  func SetupTestSuite(t *testing.T, isCheckTx bool) *AnteTestSuite {
    60  	suite := &AnteTestSuite{}
    61  	ctrl := gomock.NewController(t)
    62  	suite.bankKeeper = authtestutil.NewMockBankKeeper(ctrl)
    63  	suite.txBankKeeper = txtestutil.NewMockBankKeeper(ctrl)
    64  	suite.feeGrantKeeper = antetestutil.NewMockFeegrantKeeper(ctrl)
    65  
    66  	key := storetypes.NewKVStoreKey(types.StoreKey)
    67  	testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
    68  	suite.ctx = testCtx.Ctx.WithIsCheckTx(isCheckTx).WithBlockHeight(1) // app.BaseApp.NewContext(isCheckTx, cmtproto.Header{}).WithBlockHeight(1)
    69  	suite.encCfg = moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, bank.AppModuleBasic{})
    70  
    71  	maccPerms := map[string][]string{
    72  		"fee_collector":          nil,
    73  		"mint":                   {"minter"},
    74  		"bonded_tokens_pool":     {"burner", "staking"},
    75  		"not_bonded_tokens_pool": {"burner", "staking"},
    76  		"multiPerm":              {"burner", "minter", "staking"},
    77  		"random":                 {"random"},
    78  	}
    79  
    80  	suite.accountKeeper = keeper.NewAccountKeeper(
    81  		suite.encCfg.Codec, runtime.NewKVStoreService(key), types.ProtoBaseAccount, maccPerms, authcodec.NewBech32Codec("cosmos"),
    82  		sdk.Bech32MainPrefix, types.NewModuleAddress("gov").String(),
    83  	)
    84  	suite.accountKeeper.GetModuleAccount(suite.ctx, types.FeeCollectorName)
    85  	err := suite.accountKeeper.Params.Set(suite.ctx, types.DefaultParams())
    86  	require.NoError(t, err)
    87  
    88  	// We're using TestMsg encoding in some tests, so register it here.
    89  	suite.encCfg.Amino.RegisterConcrete(&testdata.TestMsg{}, "testdata.TestMsg", nil)
    90  	testdata.RegisterInterfaces(suite.encCfg.InterfaceRegistry)
    91  
    92  	suite.clientCtx = client.Context{}.
    93  		WithTxConfig(suite.encCfg.TxConfig).
    94  		WithClient(clitestutil.NewMockCometRPC(abci.ResponseQuery{}))
    95  
    96  	anteHandler, err := ante.NewAnteHandler(
    97  		ante.HandlerOptions{
    98  			AccountKeeper:   suite.accountKeeper,
    99  			BankKeeper:      suite.bankKeeper,
   100  			FeegrantKeeper:  suite.feeGrantKeeper,
   101  			SignModeHandler: suite.encCfg.TxConfig.SignModeHandler(),
   102  			SigGasConsumer:  ante.DefaultSigVerificationGasConsumer,
   103  		},
   104  	)
   105  
   106  	require.NoError(t, err)
   107  	suite.anteHandler = anteHandler
   108  
   109  	suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder()
   110  
   111  	return suite
   112  }
   113  
   114  func (suite *AnteTestSuite) CreateTestAccounts(numAccs int) []TestAccount {
   115  	var accounts []TestAccount
   116  
   117  	for i := 0; i < numAccs; i++ {
   118  		priv, _, addr := testdata.KeyTestPubAddr()
   119  		acc := suite.accountKeeper.NewAccountWithAddress(suite.ctx, addr)
   120  		acc.SetAccountNumber(uint64(i + 1000))
   121  		suite.accountKeeper.SetAccount(suite.ctx, acc)
   122  		accounts = append(accounts, TestAccount{acc, priv})
   123  	}
   124  
   125  	return accounts
   126  }
   127  
   128  // TestCase represents a test case used in test tables.
   129  type TestCase struct {
   130  	desc     string
   131  	malleate func(*AnteTestSuite) TestCaseArgs
   132  	simulate bool
   133  	expPass  bool
   134  	expErr   error
   135  }
   136  
   137  type TestCaseArgs struct {
   138  	chainID   string
   139  	accNums   []uint64
   140  	accSeqs   []uint64
   141  	feeAmount sdk.Coins
   142  	gasLimit  uint64
   143  	msgs      []sdk.Msg
   144  	privs     []cryptotypes.PrivKey
   145  }
   146  
   147  func (t TestCaseArgs) WithAccountsInfo(accs []TestAccount) TestCaseArgs {
   148  	newT := t
   149  	for _, acc := range accs {
   150  		newT.accNums = append(newT.accNums, acc.acc.GetAccountNumber())
   151  		newT.accSeqs = append(newT.accSeqs, acc.acc.GetSequence())
   152  		newT.privs = append(newT.privs, acc.priv)
   153  	}
   154  	return newT
   155  }
   156  
   157  // DeliverMsgs constructs a tx and runs it through the ante handler. This is used to set the context for a test case, for
   158  // example to test for replay protection.
   159  func (suite *AnteTestSuite) DeliverMsgs(t *testing.T, privs []cryptotypes.PrivKey, msgs []sdk.Msg, feeAmount sdk.Coins, gasLimit uint64, accNums, accSeqs []uint64, chainID string, simulate bool) (sdk.Context, error) {
   160  	require.NoError(t, suite.txBuilder.SetMsgs(msgs...))
   161  	suite.txBuilder.SetFeeAmount(feeAmount)
   162  	suite.txBuilder.SetGasLimit(gasLimit)
   163  
   164  	tx, txErr := suite.CreateTestTx(suite.ctx, privs, accNums, accSeqs, chainID, signing.SignMode_SIGN_MODE_DIRECT)
   165  	require.NoError(t, txErr)
   166  	txBytes, err := suite.clientCtx.TxConfig.TxEncoder()(tx)
   167  	bytesCtx := suite.ctx.WithTxBytes(txBytes)
   168  	require.NoError(t, err)
   169  	return suite.anteHandler(bytesCtx, tx, simulate)
   170  }
   171  
   172  func (suite *AnteTestSuite) RunTestCase(t *testing.T, tc TestCase, args TestCaseArgs) {
   173  	require.NoError(t, suite.txBuilder.SetMsgs(args.msgs...))
   174  	suite.txBuilder.SetFeeAmount(args.feeAmount)
   175  	suite.txBuilder.SetGasLimit(args.gasLimit)
   176  
   177  	// Theoretically speaking, ante handler unit tests should only test
   178  	// ante handlers, but here we sometimes also test the tx creation
   179  	// process.
   180  	tx, txErr := suite.CreateTestTx(suite.ctx, args.privs, args.accNums, args.accSeqs, args.chainID, signing.SignMode_SIGN_MODE_DIRECT)
   181  	txBytes, err := suite.clientCtx.TxConfig.TxEncoder()(tx)
   182  	require.NoError(t, err)
   183  	bytesCtx := suite.ctx.WithTxBytes(txBytes)
   184  	newCtx, anteErr := suite.anteHandler(bytesCtx, tx, tc.simulate)
   185  
   186  	if tc.expPass {
   187  		require.NoError(t, txErr)
   188  		require.NoError(t, anteErr)
   189  		require.NotNil(t, newCtx)
   190  
   191  		suite.ctx = newCtx
   192  	} else {
   193  		switch {
   194  		case txErr != nil:
   195  			require.Error(t, txErr)
   196  			require.ErrorIs(t, txErr, tc.expErr)
   197  
   198  		case anteErr != nil:
   199  			require.Error(t, anteErr)
   200  			require.ErrorIs(t, anteErr, tc.expErr)
   201  
   202  		default:
   203  			t.Fatal("expected one of txErr, anteErr to be an error")
   204  		}
   205  	}
   206  }
   207  
   208  // CreateTestTx is a helper function to create a tx given multiple inputs.
   209  func (suite *AnteTestSuite) CreateTestTx(
   210  	ctx sdk.Context, privs []cryptotypes.PrivKey,
   211  	accNums, accSeqs []uint64,
   212  	chainID string, signMode signing.SignMode,
   213  ) (xauthsigning.Tx, error) {
   214  	// First round: we gather all the signer infos. We use the "set empty
   215  	// signature" hack to do that.
   216  	var sigsV2 []signing.SignatureV2
   217  	for i, priv := range privs {
   218  		sigV2 := signing.SignatureV2{
   219  			PubKey: priv.PubKey(),
   220  			Data: &signing.SingleSignatureData{
   221  				SignMode:  signMode,
   222  				Signature: nil,
   223  			},
   224  			Sequence: accSeqs[i],
   225  		}
   226  
   227  		sigsV2 = append(sigsV2, sigV2)
   228  	}
   229  	err := suite.txBuilder.SetSignatures(sigsV2...)
   230  	if err != nil {
   231  		return nil, err
   232  	}
   233  
   234  	// Second round: all signer infos are set, so each signer can sign.
   235  	sigsV2 = []signing.SignatureV2{}
   236  	for i, priv := range privs {
   237  		signerData := xauthsigning.SignerData{
   238  			Address:       sdk.AccAddress(priv.PubKey().Address()).String(),
   239  			ChainID:       chainID,
   240  			AccountNumber: accNums[i],
   241  			Sequence:      accSeqs[i],
   242  			PubKey:        priv.PubKey(),
   243  		}
   244  		sigV2, err := tx.SignWithPrivKey(
   245  			ctx, signMode, signerData,
   246  			suite.txBuilder, priv, suite.clientCtx.TxConfig, accSeqs[i])
   247  		if err != nil {
   248  			return nil, err
   249  		}
   250  
   251  		sigsV2 = append(sigsV2, sigV2)
   252  	}
   253  	err = suite.txBuilder.SetSignatures(sigsV2...)
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  
   258  	return suite.txBuilder.GetTx(), nil
   259  }