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

206 lines
7.1 KiB
Go

package middleware_test
import (
"time"
abci "github.com/tendermint/tendermint/abci/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/cosmos/cosmos-sdk/client"
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
)
var initialRegens = sdk.NewCoins(sdk.NewCoin("regen", sdk.NewInt(1000)))
var initialAtoms = sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(1000)))
// setupAcctsForTips sets up 2 accounts:
// - tipper has 1000 regens
// - feePayer has 1000 atoms and 1000 regens
func (s *MWTestSuite) setupAcctsForTips(ctx sdk.Context) (sdk.Context, []testAccount) {
accts := s.createTestAccounts(ctx, 2, initialRegens)
feePayer := accts[1]
err := s.app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, initialAtoms)
s.Require().NoError(err)
err = s.app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, feePayer.acc.GetAddress(), initialAtoms)
s.Require().NoError(err)
// Create dummy proposal for tipper to vote on.
prop, err := govtypes.NewProposal(govtypes.NewTextProposal("foo", "bar"), 1, time.Now(), time.Now().Add(time.Hour))
s.Require().NoError(err)
s.app.GovKeeper.SetProposal(ctx, prop)
s.app.GovKeeper.ActivateVotingPeriod(ctx, prop)
// Move to next block to commit previous data to state.
s.app.EndBlock(abci.RequestEndBlock{Height: ctx.BlockHeight()})
s.app.Commit()
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
s.app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: ctx.BlockHeight()}})
return ctx, accts
}
func (s *MWTestSuite) TestTips() {
var msg sdk.Msg
testcases := []struct {
name string
tip sdk.Coins
fee sdk.Coins
gasLimit uint64
expErr bool
expErrStr string
}{
{
"wrong tip denom",
sdk.NewCoins(sdk.NewCoin("foobar", sdk.NewInt(1000))), initialAtoms, 200000,
true, "0foobar is smaller than 1000foobar: insufficient funds",
},
{
"insufficient tip from tipper",
sdk.NewCoins(sdk.NewCoin("regen", sdk.NewInt(5000))), initialAtoms, 200000,
true, "1000regen is smaller than 5000regen: insufficient funds",
},
{
"insufficient fees from feePayer",
initialRegens, sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(5000))), 200000,
true, "1000atom is smaller than 5000atom: insufficient funds: insufficient funds",
},
{
"insufficient gas",
initialRegens, initialAtoms, 100,
true, "out of gas in location: ReadFlat; gasWanted: 100, gasUsed: 1000: out of gas",
},
{
"happy case",
initialRegens, initialAtoms, 200000,
false, "",
},
}
for _, tc := range testcases {
tc := tc
s.Run(tc.name, func() {
ctx := s.SetupTest(false) // reset
ctx, accts := s.setupAcctsForTips(ctx)
tipper, feePayer := accts[0], accts[1]
msg = govtypes.NewMsgVote(tipper.acc.GetAddress(), 1, govtypes.OptionYes)
auxSignerData := s.mkTipperAuxSignerData(tipper.priv, msg, tc.tip, signing.SignMode_SIGN_MODE_DIRECT_AUX, tipper.accNum, 0, ctx.ChainID())
feePayerTxBuilder := s.mkFeePayerTxBuilder(s.clientCtx, auxSignerData, feePayer.priv, signing.SignMode_SIGN_MODE_DIRECT, tx.Fee{Amount: tc.fee, GasLimit: tc.gasLimit}, feePayer.accNum, 0, ctx.ChainID())
_, res, err := s.app.SimDeliver(s.clientCtx.TxConfig.TxEncoder(), feePayerTxBuilder.GetTx())
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.expErrStr)
} else {
s.Require().NoError(err)
s.Require().NotNil(res)
// Move to next block to commit previous data to state.
s.app.EndBlock(abci.RequestEndBlock{Height: ctx.BlockHeight()})
s.app.Commit()
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
s.app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: ctx.BlockHeight()}})
// Make sure tip is correctly transferred to feepayer, and fee is paid.
expTipperRegens := initialRegens.Sub(tc.tip)
expFeePayerRegens := initialRegens.Add(tc.tip...)
expFeePayerAtoms := initialAtoms.Sub(tc.fee)
s.Require().True(expTipperRegens.AmountOf("regen").Equal(s.app.BankKeeper.GetBalance(ctx, tipper.acc.GetAddress(), "regen").Amount))
s.Require().True(expFeePayerRegens.AmountOf("regen").Equal(s.app.BankKeeper.GetBalance(ctx, feePayer.acc.GetAddress(), "regen").Amount))
s.Require().True(expFeePayerAtoms.AmountOf("atom").Equal(s.app.BankKeeper.GetBalance(ctx, feePayer.acc.GetAddress(), "atom").Amount))
// Make sure MsgVote has been submitted by tipper.
votes := s.app.GovKeeper.GetAllVotes(ctx)
s.Require().Len(votes, 1)
s.Require().Equal(tipper.acc.GetAddress().String(), votes[0].Voter)
}
})
}
}
func (s *MWTestSuite) mkTipperAuxSignerData(
tipperPriv cryptotypes.PrivKey, msg sdk.Msg, tip sdk.Coins,
signMode signing.SignMode, accNum, accSeq uint64, chainID string,
) tx.AuxSignerData {
tipperAddr := sdk.AccAddress(tipperPriv.PubKey().Address()).String()
b := clienttx.NewAuxTxBuilder()
b.SetAddress(tipperAddr)
b.SetAccountNumber(accNum)
b.SetSequence(accSeq)
err := b.SetMsgs(msg)
s.Require().NoError(err)
b.SetTip(&tx.Tip{Amount: tip, Tipper: tipperAddr})
err = b.SetSignMode(signMode)
s.Require().NoError(err)
b.SetSequence(accSeq)
err = b.SetPubKey(tipperPriv.PubKey())
s.Require().NoError(err)
b.SetChainID(chainID)
signBz, err := b.GetSignBytes()
s.Require().NoError(err)
sig, err := tipperPriv.Sign(signBz)
s.Require().NoError(err)
b.SetSignature(sig)
auxSignerData, err := b.GetAuxSignerData()
s.Require().NoError(err)
return auxSignerData
}
func (s *MWTestSuite) mkFeePayerTxBuilder(
clientCtx client.Context,
auxSignerData tx.AuxSignerData,
feePayerPriv cryptotypes.PrivKey, signMode signing.SignMode,
fee tx.Fee, accNum, accSeq uint64, chainID string,
) client.TxBuilder {
txBuilder := clientCtx.TxConfig.NewTxBuilder()
err := txBuilder.AddAuxSignerData(auxSignerData)
s.Require().NoError(err)
txBuilder.SetFeePayer(sdk.AccAddress(feePayerPriv.PubKey().Address()))
txBuilder.SetFeeAmount(fee.Amount)
txBuilder.SetGasLimit(fee.GasLimit)
// Calling SetSignatures with empty sig to populate AuthInfo.
tipperSigsV2, err := auxSignerData.GetSignatureV2()
s.Require().NoError(err)
feePayerSigV2 := signing.SignatureV2{
PubKey: feePayerPriv.PubKey(),
Data: &signing.SingleSignatureData{
SignMode: signMode,
Signature: nil,
}}
sigsV2 := append([]signing.SignatureV2{tipperSigsV2}, feePayerSigV2)
txBuilder.SetSignatures(sigsV2...)
// Actually sign the data.
signerData := authsigning.SignerData{
Address: sdk.AccAddress(feePayerPriv.PubKey().Address()).String(),
ChainID: chainID,
AccountNumber: accNum,
Sequence: accSeq,
PubKey: feePayerPriv.PubKey(),
}
feePayerSigV2, err = clienttx.SignWithPrivKey(
signMode, signerData,
txBuilder, feePayerPriv, clientCtx.TxConfig, accSeq)
s.Require().NoError(err)
sigsV2 = append([]signing.SignatureV2{tipperSigsV2}, feePayerSigV2)
err = txBuilder.SetSignatures(sigsV2...)
s.Require().NoError(err)
return txBuilder
}