cosmos-sdk/x/auth/tx/aux_test.go
2025-01-22 11:15:49 +00:00

230 lines
7.6 KiB
Go

package tx_test
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/anypb"
apisigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
txsigning "cosmossdk.io/x/tx/signing"
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
_ "github.com/cosmos/cosmos-sdk/testutil/testdata/testpb"
sdk "github.com/cosmos/cosmos-sdk/types"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
"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"
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) {
t.Skip("restore when we re-enable aux on the TX builder")
encodingConfig := moduletestutil.MakeTestEncodingConfig(codectestutil.CodecOptions{})
interfaceRegistry := encodingConfig.InterfaceRegistry
txConfig := encodingConfig.TxConfig
testdata.RegisterInterfaces(interfaceRegistry)
// Create an AuxTxBuilder for tipper (1st signer)
txBuilder, txSig := makeTxBuilder(t)
txSignerData, err := txBuilder.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)
err = aux2Builder.SetMsgs(msg)
require.NoError(t, err)
err = aux2Builder.SetPubKey(aux2Pk)
require.NoError(t, err)
extOptAny, err := codectypes.NewAnyWithValue(extOpt)
require.NoError(t, err)
aux2Builder.SetExtensionOptions(extOptAny)
aux2Builder.SetNonCriticalExtensionOptions(extOptAny)
err = aux2Builder.SetSignMode(apisigning.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 := 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() { txBuilder.SetAddress("foobar") }, true},
{"memo mismatch", func() { txBuilder.SetMemo("mismatch") }, true},
{"timeout height mismatch", func() { txBuilder.SetTimeoutHeight(98) }, true},
{"extension options length mismatch", func() { txBuilder.SetExtensionOptions() }, true},
{"extension options member mismatch", func() { txBuilder.SetExtensionOptions(&codectypes.Any{}) }, true},
{"non-critical extension options length mismatch", func() { txBuilder.SetNonCriticalExtensionOptions() }, true},
{"non-critical extension options member mismatch", func() { txBuilder.SetNonCriticalExtensionOptions(&codectypes.Any{}) }, true},
{"happy case", func() {}, false},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
txBuilder, txSig = makeTxBuilder(t)
tc.malleate()
_, err := txBuilder.GetSignBytes()
require.NoError(t, err)
txSignerData, err = txBuilder.GetAuxSignerData()
require.NoError(t, err)
err = w.AddAuxSignerData(txSignerData)
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)
txSigV2 := sigs[0]
aux2SigV2 := sigs[1]
// Set all signer infos.
err = w.SetSignatures(txSigV2, aux2SigV2, signing.SignatureV2{
PubKey: feepayerPk,
Sequence: 15,
})
require.NoError(t, err)
anyPk, err := codectypes.NewAnyWithValue(feepayerPk)
require.NoError(t, err)
signerData := txsigning.SignerData{
Address: feepayerAddr.String(),
ChainID: chainID,
AccountNumber: 11,
Sequence: 15,
PubKey: &anypb.Any{
TypeUrl: anyPk.TypeUrl,
Value: anyPk.Value,
},
}
signBz, err = authsigning.GetSignBytesAdapter(
context.Background(), txConfig.SignModeHandler(), apisigning.SignMode_SIGN_MODE_DIRECT,
signerData, w.GetTx())
require.NoError(t, err)
feepayerSig, err := feepayerPriv.Sign(signBz)
require.NoError(t, err)
// Set all signatures.
err = w.SetSignatures(txSigV2, aux2SigV2, signing.SignatureV2{
PubKey: feepayerPk,
Data: &signing.SingleSignatureData{
SignMode: apisigning.SignMode_SIGN_MODE_DIRECT,
Signature: feepayerSig,
},
Sequence: 22,
})
require.NoError(t, err)
// Make sure tx is correct.
txBz, err := txConfig.TxEncoder()(w.GetTx())
require.NoError(t, err)
tx, err := txConfig.TxDecoder()(txBz)
require.NoError(t, err)
require.Equal(t, tx.(sdk.FeeTx).FeePayer(), []byte(feepayerAddr))
require.Equal(t, tx.(sdk.FeeTx).GetFee(), fee)
require.Equal(t, tx.(sdk.FeeTx).GetGas(), gas)
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: apisigning.SignMode_SIGN_MODE_DIRECT_AUX, Signature: txSig},
Sequence: 2,
}, sigs[0])
require.Equal(t, signing.SignatureV2{
PubKey: aux2Pk,
Data: &signing.SingleSignatureData{SignMode: apisigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, Signature: aux2Sig},
Sequence: 12,
}, sigs[1])
require.Equal(t, signing.SignatureV2{
PubKey: feepayerPk,
Data: &signing.SingleSignatureData{SignMode: apisigning.SignMode_SIGN_MODE_DIRECT, Signature: feepayerSig},
Sequence: 22,
}, sigs[2])
}
func makeTxBuilder(t *testing.T) (clienttx.AuxTxBuilder, []byte) {
t.Helper()
txBuilder := clienttx.NewAuxTxBuilder()
txBuilder.SetAddress(tipperAddr.String())
txBuilder.SetAccountNumber(1)
txBuilder.SetSequence(2)
txBuilder.SetTimeoutHeight(3)
txBuilder.SetMemo(memo)
txBuilder.SetChainID(chainID)
err := txBuilder.SetMsgs(msg)
require.NoError(t, err)
err = txBuilder.SetPubKey(tipperPk)
require.NoError(t, err)
extOptAny, err := codectypes.NewAnyWithValue(extOpt)
require.NoError(t, err)
txBuilder.SetExtensionOptions(extOptAny)
txBuilder.SetNonCriticalExtensionOptions(extOptAny)
err = txBuilder.SetSignMode(apisigning.SignMode_SIGN_MODE_DIRECT_AUX)
require.NoError(t, err)
signBz, err := txBuilder.GetSignBytes()
require.NoError(t, err)
tipperSig, err := tipperPriv.Sign(signBz)
require.NoError(t, err)
txBuilder.SetSignature(tipperSig)
return txBuilder, tipperSig
}