Skip to content

Commit

Permalink
Add auth integration-tests for tx with multiple signatures (#716)
Browse files Browse the repository at this point in the history
  • Loading branch information
ysv authored Nov 23, 2023
1 parent 15aa2f3 commit f5ac220
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 39 deletions.
145 changes: 145 additions & 0 deletions integration-tests/modules/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package modules

import (
"context"
"encoding/json"
"testing"

Expand All @@ -12,6 +13,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
cosmoserrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsign "github.com/cosmos/cosmos-sdk/x/auth/signing"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
authztypes "github.com/cosmos/cosmos-sdk/x/authz"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
Expand Down Expand Up @@ -522,3 +524,146 @@ func TestAuthSignModeDirectAux(t *testing.T) {
requireT.Equal(chain.NewCoin(sdk.ZeroInt()).String(), feePayerBalanceResp.Balance.String())
requireT.Equal(chain.NewCoin(amountToSend).String(), recipientBalanceResp.Balance.String())
}

// TestTxWithMultipleSignatures verifies that transaction with multiple signatures is executed correctly.
// For more details check: func signTxWithMultipleSignatures.
func TestTxWithMultipleSignatures(t *testing.T) {
t.Parallel()
requireT := require.New(t)

ctx, chain := integrationtests.NewCoreumTestingContext(t)

sender1 := chain.GenAccount()
sender2 := chain.GenAccount()
receiver := chain.GenAccount()

sendAmount1 := chain.NewCoin(sdkmath.NewInt(100))
sendAmount2 := chain.NewCoin(sdkmath.NewInt(50))

msgs := []sdk.Msg{
&banktypes.MsgSend{
FromAddress: sender1.String(),
ToAddress: receiver.String(),
Amount: sdk.NewCoins(sendAmount1),
},
&banktypes.MsgSend{
FromAddress: sender2.String(),
ToAddress: receiver.String(),
Amount: sdk.NewCoins(sendAmount2),
},
}

chain.FundAccountWithOptions(ctx, t, sender1, integration.BalancesOptions{
Amount: sendAmount1.Amount,
Messages: msgs, // note that first signer pays fees for the whole tx.
})
chain.FundAccountWithOptions(ctx, t, sender2, integration.BalancesOptions{
Amount: sendAmount2.Amount,
})

tx := signTxWithMultipleSignatures(ctx, t, chain, msgs, []sdk.AccAddress{sender1, sender2})

txBytes, err := chain.ClientContext.TxConfig().TxEncoder()(tx)
requireT.NoError(err)

_, err = client.BroadcastRawTx(ctx, chain.ClientContext, txBytes)
requireT.NoError(err)

balanceResp, err := banktypes.NewQueryClient(chain.ClientContext).Balance(
ctx,
&banktypes.QueryBalanceRequest{
Address: receiver.String(),
Denom: chain.ChainSettings.Denom,
},
)
requireT.NoError(err)
requireT.Equal(sendAmount1.Amount.Add(sendAmount2.Amount).String(), balanceResp.Balance.Amount.String())
}

// signTxWithMultipleSignatures signs a transaction with multiple signatures.
// Reference: cosmos-sdk/testutil/sims/tx_helpers.go (GenSignedMockTx)
// Note the difference between multisig account transaction and multiple signer account tx.
//
// multisig account tx signature sample:
// "signatures": [
//
// "CkAnDHXdaoGxCtO97cMJOxAAg2r5M286FnvZ1Dm2lOiHGhnFesLrNHmdmEFJH8yzaMuBGpMgLs2NsjrP3aD4J..."
// ]
//
// multiple signer account tx:
// "signatures": [
//
// "80/z4w/4JaNoxSOBRt1J5bOXyZN27V5Jn9Ssfp/FQ9l5wn/z5jcHMpXTIt7EIcW5vU9nFaoztL+SwYG8FTzC9Q==",
// "CBeIHV6NTWPfOcxn/bTKUI/OMOT0SQk3jstEvGgmbhpQJJPDSpC2mQmm8f9AOHBI78FxJ4li2AuCRhFBZEm0Zw=="
// ]
//
// Multisig account tx contains single string in array where multiple signatures are combined.
// While multiple signer account tx contains each signature as a separate element in array.
func signTxWithMultipleSignatures(
ctx context.Context,
t *testing.T,
chain integration.CoreumChain,
msgs []sdk.Msg,
signers []sdk.AccAddress,
) sdk.Tx {
requireT := require.New(t)

txConfig := chain.ClientContext.TxConfig()
signMod := txConfig.SignModeHandler().DefaultMode()

signerAccInfos := make([]authtypes.AccountI, len(signers))
// Fetch account info for all signers.
for i, signer := range signers {
accInfo, err := client.GetAccountInfo(ctx, chain.ClientContext, signer)
requireT.NoError(err)
signerAccInfos[i] = accInfo
}

sigs := make([]signing.SignatureV2, len(signers))

// 1st round: set SignatureV2 with empty signatures, to set correct
// signer infos. This is needed for GetSignBytes to return all bytes
// which should be signed by each signer.
// Check cosmos-sdk@v0.47.5/x/auth/tx/direct.go DirectSignBytes for more details.
for i, signer := range signers {
k, err := chain.TxFactory().Keybase().KeyByAddress(signer)
requireT.NoError(err)

pubKey, err := k.GetPubKey()
requireT.NoError(err)

sigs[i] = signing.SignatureV2{
PubKey: pubKey,
Data: &signing.SingleSignatureData{
SignMode: signMod,
},
Sequence: signerAccInfos[i].GetSequence(),
}
}

txBuilder, err := chain.TxFactory().
WithGas(chain.GasLimitByMsgs(msgs...)).
BuildUnsignedTx(msgs...)
requireT.NoError(err)
requireT.NoError(txBuilder.SetSignatures(sigs...))

// 2nd round: sign and set real signatures.
for i, signer := range signers {
signerData := authsign.SignerData{
Address: signer.String(),
ChainID: chain.ChainContext.ChainSettings.ChainID,
AccountNumber: signerAccInfos[i].GetAccountNumber(),
Sequence: signerAccInfos[i].GetSequence(),
PubKey: sigs[i].PubKey,
}
signBytes, err := txConfig.SignModeHandler().GetSignBytes(signMod, signerData, txBuilder.GetTx())
requireT.NoError(err)
sig, _, err := chain.TxFactory().Keybase().SignByAddress(signer, signBytes)
requireT.NoError(err)

sigs[i].Data.(*signing.SingleSignatureData).Signature = sig
}
requireT.NoError(txBuilder.SetSignatures(sigs...))

return txBuilder.GetTx()
}
42 changes: 3 additions & 39 deletions integration-tests/modules/bank_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
sdk "github.com/cosmos/cosmos-sdk/types"
cosmoserrors "github.com/cosmos/cosmos-sdk/types/errors"
sdksigning "github.com/cosmos/cosmos-sdk/types/tx/signing"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -615,12 +614,7 @@ func TestTryBankMultiSendFromMultipleAccounts(t *testing.T) {
requireT := require.New(t)

sender1 := chain.GenAccount()
sender1KeyInfo, err := chain.ClientContext.Keyring().KeyByAddress(sender1)
requireT.NoError(err)

sender2 := chain.GenAccount()
sender2KeyInfo, err := chain.ClientContext.Keyring().KeyByAddress(sender2)
requireT.NoError(err)

recipient1 := chain.GenAccount()
recipient2 := chain.GenAccount()
Expand Down Expand Up @@ -707,7 +701,7 @@ func TestTryBankMultiSendFromMultipleAccounts(t *testing.T) {
})

// issue first fungible token
_, err = client.BroadcastTx(
_, err := client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(sender1),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(issue1Msg)),
Expand All @@ -723,40 +717,10 @@ func TestTryBankMultiSendFromMultipleAccounts(t *testing.T) {
)
requireT.NoError(err)

// create MultiSend tx message and sign it from 2 accounts
sender1AccInfo, err := client.GetAccountInfo(ctx, chain.ClientContext, sender1)
requireT.NoError(err)

// set sender1 params for the signature
txF := chain.TxFactory().
WithAccountNumber(sender1AccInfo.GetAccountNumber()).
WithSequence(sender1AccInfo.GetSequence()).
WithGas(chain.GasLimitByMsgs(multiSendMsg)).
WithSignMode(sdksigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)

txBuilder, err := txF.BuildUnsignedTx(multiSendMsg)
requireT.NoError(err)

// sign from sender1
err = client.Sign(txF, sender1KeyInfo.Name, txBuilder, false)
requireT.NoError(err)

sender2AccInfo, err := client.GetAccountInfo(ctx, chain.ClientContext, sender2)
requireT.NoError(err)

// set sender2 params for the signature
txF = chain.TxFactory().
WithAccountNumber(sender2AccInfo.GetAccountNumber()).
WithSequence(sender2AccInfo.GetSequence()).
WithGas(chain.GasLimitByMsgs(multiSendMsg)).
WithSignMode(sdksigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)

// sign from sender2
err = client.Sign(txF, sender2KeyInfo.Name, txBuilder, false)
requireT.NoError(err)
tx := signTxWithMultipleSignatures(ctx, t, chain, []sdk.Msg{multiSendMsg}, []sdk.AccAddress{sender1, sender2})

// encode tx and broadcast
encodedMultiSendTx, err := chain.ClientContext.TxConfig().TxEncoder()(txBuilder.GetTx())
encodedMultiSendTx, err := chain.ClientContext.TxConfig().TxEncoder()(tx)
requireT.NoError(err)
_, err = client.BroadcastRawTx(
ctx,
Expand Down

0 comments on commit f5ac220

Please sign in to comment.