230 lines
7.6 KiB
Go
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
|
|
}
|