cosmos-sdk/x/auth/tx/aux_test.go

212 lines
7.4 KiB
Go

package tx_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/client/tx"
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
)
var (
// The final TX has 3 signers, in this order.
tipperPriv, tipperPk, tipperAddr = testdata.KeyTestPubAddr()
aux2Priv, aux2Pk, aux2Addr = testdata.KeyTestPubAddr()
feepayerPriv, feepayerPk, feepayerAddr = testdata.KeyTestPubAddr()
msg = testdata.NewTestMsg(tipperAddr, aux2Addr)
memo = "test-memo"
tip = &txtypes.Tip{Tipper: tipperAddr.String(), Amount: sdk.NewCoins(sdk.NewCoin("tip-denom", sdk.NewIntFromUint64(123)))}
chainID = "test-chain"
gas = testdata.NewTestGasLimit()
fee = testdata.NewTestFeeAmount()
extOpt = &testdata.Cat{}
)
// TestBuilderWithAux creates a tx with 2 aux signers:
// - 1st one is tipper,
// - 2nd one is just an aux signer.
// Then it tests integrating the 2 AuxSignerData into a
// client.TxBuilder created by the fee payer.
func TestBuilderWithAux(t *testing.T) {
encCfg := simapp.MakeTestEncodingConfig()
testdata.RegisterInterfaces(encCfg.InterfaceRegistry)
// Create an AuxTxBuilder for tipper (1st signer)
tipperBuilder, tipperSig := makeTipperTxBuilder(t)
tipperSignerData, err := tipperBuilder.GetAuxSignerData()
require.NoError(t, err)
// Create an AuxTxBuilder for aux2 (2nd signer)
aux2Builder := clienttx.NewAuxTxBuilder()
aux2Builder.SetAddress(aux2Addr.String())
aux2Builder.SetAccountNumber(11)
aux2Builder.SetSequence(12)
aux2Builder.SetTimeoutHeight(3)
aux2Builder.SetMemo(memo)
aux2Builder.SetChainID(chainID)
aux2Builder.SetMsgs(msg)
aux2Builder.SetPubKey(aux2Pk)
aux2Builder.SetTip(tip)
extOptAny, err := codectypes.NewAnyWithValue(extOpt)
require.NoError(t, err)
aux2Builder.SetExtensionOptions(extOptAny)
aux2Builder.SetNonCriticalExtensionOptions(extOptAny)
err = aux2Builder.SetSignMode(signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
require.NoError(t, err)
signBz, err := aux2Builder.GetSignBytes()
require.NoError(t, err)
aux2Sig, err := aux2Priv.Sign(signBz)
require.NoError(t, err)
aux2Builder.SetSignature(aux2Sig)
aux2SignerData, err := aux2Builder.GetAuxSignerData()
require.NoError(t, err)
// Fee payer (3rd and last signer) creates a TxBuilder.
w := encCfg.TxConfig.NewTxBuilder()
// Note: we're testing calling AddAuxSignerData in the wrong order, i.e.
// adding the aux2 signer data first before the tipper.
err = w.AddAuxSignerData(aux2SignerData)
require.NoError(t, err)
// Test that when adding another AuxSignerData, the 2nd data should match
// the 1st one.
testcases := []struct {
name string
malleate func()
expErr bool
}{
{"address and msg signer mistacher", func() { tipperBuilder.SetAddress("foobar") }, true},
{"memo mismatch", func() { tipperBuilder.SetMemo("mismatch") }, true},
{"timeout height mismatch", func() { tipperBuilder.SetTimeoutHeight(98) }, true},
{"extension options length mismatch", func() { tipperBuilder.SetExtensionOptions() }, true},
{"extension options member mismatch", func() { tipperBuilder.SetExtensionOptions(&codectypes.Any{}) }, true},
{"non-critical extension options length mismatch", func() { tipperBuilder.SetNonCriticalExtensionOptions() }, true},
{"non-critical extension options member mismatch", func() { tipperBuilder.SetNonCriticalExtensionOptions(&codectypes.Any{}) }, true},
{"tip amount mismatch", func() { tipperBuilder.SetTip(&txtypes.Tip{Tipper: tip.Tipper, Amount: sdk.NewCoins()}) }, true},
{"tipper mismatch", func() { tipperBuilder.SetTip(&txtypes.Tip{Tipper: "mismatch", Amount: tip.Amount}) }, true},
{"happy case", func() {}, false},
}
for _, tc := range testcases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
tipperBuilder, tipperSig = makeTipperTxBuilder(t)
tc.malleate()
_, err := tipperBuilder.GetSignBytes()
require.NoError(t, err)
tipperSignerData, err = tipperBuilder.GetAuxSignerData()
require.NoError(t, err)
err = w.AddAuxSignerData(tipperSignerData)
if tc.expErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
w.SetFeePayer(feepayerAddr)
w.SetFeeAmount(fee)
w.SetGasLimit(gas)
sigs, err := w.(authsigning.SigVerifiableTx).GetSignaturesV2()
require.NoError(t, err)
tipperSigV2 := sigs[0]
aux2SigV2 := sigs[1]
// Set all signer infos.
w.SetSignatures(tipperSigV2, aux2SigV2, signing.SignatureV2{
PubKey: feepayerPk,
Sequence: 15,
})
signBz, err = encCfg.TxConfig.SignModeHandler().GetSignBytes(
signing.SignMode_SIGN_MODE_DIRECT,
authsigning.SignerData{
Address: feepayerAddr.String(),
ChainID: chainID,
AccountNumber: 11,
Sequence: 15,
PubKey: feepayerPk,
},
w.GetTx(),
)
require.NoError(t, err)
feepayerSig, err := feepayerPriv.Sign(signBz)
require.NoError(t, err)
// Set all signatures.
w.SetSignatures(tipperSigV2, aux2SigV2, signing.SignatureV2{
PubKey: feepayerPk,
Data: &signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_DIRECT,
Signature: feepayerSig,
},
Sequence: 22,
})
// Make sure tx is correct.
txBz, err := encCfg.TxConfig.TxEncoder()(w.GetTx())
require.NoError(t, err)
tx, err := encCfg.TxConfig.TxDecoder()(txBz)
require.NoError(t, err)
require.Equal(t, tx.(sdk.FeeTx).FeePayer(), feepayerAddr)
require.Equal(t, tx.(sdk.FeeTx).GetFee(), fee)
require.Equal(t, tx.(sdk.FeeTx).GetGas(), gas)
require.Equal(t, tip, tx.(txtypes.TipTx).GetTip())
require.Equal(t, msg, tx.GetMsgs()[0])
require.Equal(t, memo, tx.(sdk.TxWithMemo).GetMemo())
require.Equal(t, uint64(3), tx.(sdk.TxWithTimeoutHeight).GetTimeoutHeight())
sigs, err = tx.(authsigning.Tx).GetSignaturesV2()
require.NoError(t, err)
require.Len(t, sigs, 3)
require.Equal(t, signing.SignatureV2{
PubKey: tipperPk,
Data: &signing.SingleSignatureData{SignMode: signing.SignMode_SIGN_MODE_DIRECT_AUX, Signature: tipperSig},
Sequence: 2,
}, sigs[0])
require.Equal(t, signing.SignatureV2{
PubKey: aux2Pk,
Data: &signing.SingleSignatureData{SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, Signature: aux2Sig},
Sequence: 12,
}, sigs[1])
require.Equal(t, signing.SignatureV2{
PubKey: feepayerPk,
Data: &signing.SingleSignatureData{SignMode: signing.SignMode_SIGN_MODE_DIRECT, Signature: feepayerSig},
Sequence: 22,
}, sigs[2])
}
func makeTipperTxBuilder(t *testing.T) (tx.AuxTxBuilder, []byte) {
tipperBuilder := clienttx.NewAuxTxBuilder()
tipperBuilder.SetAddress(tipperAddr.String())
tipperBuilder.SetAccountNumber(1)
tipperBuilder.SetSequence(2)
tipperBuilder.SetTimeoutHeight(3)
tipperBuilder.SetMemo(memo)
tipperBuilder.SetChainID(chainID)
tipperBuilder.SetMsgs(msg)
tipperBuilder.SetPubKey(tipperPk)
tipperBuilder.SetTip(tip)
extOptAny, err := codectypes.NewAnyWithValue(extOpt)
require.NoError(t, err)
tipperBuilder.SetExtensionOptions(extOptAny)
tipperBuilder.SetNonCriticalExtensionOptions(extOptAny)
err = tipperBuilder.SetSignMode(signing.SignMode_SIGN_MODE_DIRECT_AUX)
require.NoError(t, err)
signBz, err := tipperBuilder.GetSignBytes()
require.NoError(t, err)
tipperSig, err := tipperPriv.Sign(signBz)
require.NoError(t, err)
tipperBuilder.SetSignature(tipperSig)
return tipperBuilder, tipperSig
}