Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix sequence number handling for LegacyAmino > SignatureV2 #7285

Merged
merged 14 commits into from
Sep 16, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion types/tx/signing/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ type SignatureV2 struct {
// - false (by default) in SIGN_MODE_DIRECT
// - true in SIGN_MODE_LEGACY_AMINO_JSON
// ref: https://github.com/cosmos/cosmos-sdk/issues/7229
SkipSequenceCheck bool
clevinson marked this conversation as resolved.
Show resolved Hide resolved
}

// SignatureDataToProto converts a SignatureData to SignatureDescriptor_Data.
Expand Down
22 changes: 20 additions & 2 deletions x/auth/ante/sigverify.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,22 @@ func NewSigVerificationDecorator(ak AccountKeeper, signModeHandler authsigning.S
}
}

func OnlyLegacyAminoSigners(sigData signing.SignatureData) bool {
clevinson marked this conversation as resolved.
Show resolved Hide resolved
switch v := sigData.(type) {
case *signing.SingleSignatureData:
return v.SignMode == signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON
case *signing.MultiSignatureData:
for _, s := range v.Signatures {
if !OnlyLegacyAminoSigners(s) {
return false
}
}
return true
default:
return false
}
}

func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
// no need to verify signatures on recheck tx
if ctx.IsReCheckTx() {
Expand Down Expand Up @@ -211,7 +227,7 @@ func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul
// the SignatureV2 struct (it's only in the SignDoc). In this case, we
// cannot check sequence directly, and must do it via signature
// verification.
if !sig.SkipSequenceCheck {
if !OnlyLegacyAminoSigners(sig.Data) {
robert-zaremba marked this conversation as resolved.
Show resolved Hide resolved
if sig.Sequence != acc.GetSequence() {
return ctx, sdkerrors.Wrapf(
sdkerrors.ErrWrongSequence,
Expand All @@ -237,7 +253,9 @@ func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul
err := authsigning.VerifySignature(pubKey, signerData, sig.Data, svd.signModeHandler, tx)
if err != nil {
var errMsg string
if sig.SkipSequenceCheck {
if OnlyLegacyAminoSigners(sig.Data) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in line 230 we already evaluated OnlyLegacyAminoSigners(sig.Data). So let's store the result above and use it in both if statements.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we just fail a transaction that contains mixed Sign modes?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on the users. This would solve my other concern.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mixed sign modes I believe is a valid use case that we need to support (see ADR020, and this issue thread on protobuf tx signing). When dealing with multisig transactions, each signer may be using their own independent sign mode, right?

// If all signers are using SIGN_MODE_LEGACY_AMINO, we rely on VerifySignature to check account sequence number,
// and therefore communicate sequence number as a potential cause of error
errMsg = fmt.Sprintf("signature verification failed; please verify account number (%d), sequence (%d) and chain-id (%s)", accNum, acc.GetSequence(), chainID)
} else {
errMsg = fmt.Sprintf("signature verification failed; please verify account number (%d) and chain-id (%s)", accNum, chainID)
Expand Down
68 changes: 68 additions & 0 deletions x/auth/client/rest/rest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package rest_test

import (
"fmt"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -103,6 +107,70 @@ func (s *IntegrationTestSuite) TestBroadcastTxRequest() {
s.Require().NotEmpty(txRes.TxHash)
}

func (s *IntegrationTestSuite) TestMultipleSyncBroadcastTxRequests() {

val0 := s.network.Validators[0]
txConfig := authtypes.StdTxConfig{Cdc: s.cfg.LegacyAmino}

// First test transaction from validator should have sequence=1 (non-genesis tx)
testCases := []struct {
desc string
sequence uint64
}{
{
"First tx",
1,
},
{
"Second tx",
2,
},
}
for _, tc := range testCases {
s.Run(fmt.Sprintf("Case %s", tc.desc), func() {

msg := &types.MsgSend{
val0.Address,
val0.Address,
sdk.Coins{sdk.NewInt64Coin("foo", 100)},
}

// prepare txBuilder with msg
txBuilder := txConfig.NewTxBuilder()
feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}
gasLimit := testdata.NewTestGasLimit()
txBuilder.SetMsgs(msg)
txBuilder.SetFeeAmount(feeAmount)
txBuilder.SetGasLimit(gasLimit)

// setup txFactory
txFactory := tx.Factory{}
txFactory = txFactory.
WithChainID(val0.ClientCtx.ChainID).
WithKeybase(val0.ClientCtx.Keyring).
WithTxConfig(txConfig).
WithSignMode(signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON).
WithSequence(tc.sequence)

// sign Tx (offline mode so we can manually set sequence number)
err := authclient.SignTx(txFactory, val0.ClientCtx, val0.Moniker, txBuilder, true)
s.Require().NoError(err)

// broadcast test with sync mode, as we want to run CheckTx to verify account sequence is correct
stdTx := txBuilder.GetTx().(authtypes.StdTx)
res, err := s.broadcastReq(stdTx, "sync")
s.Require().NoError(err)

var txRes sdk.TxResponse
// NOTE: this uses amino explicitly, don't migrate it!
s.Require().NoError(s.cfg.LegacyAmino.UnmarshalJSON(res, &txRes))
// we check for a exitCode=0, indicating a successful broadcast
s.Require().Equal(uint32(0), txRes.Code)
})
}

}

func (s *IntegrationTestSuite) broadcastReq(stdTx authtypes.StdTx, mode string) ([]byte, error) {
val := s.network.Validators[0]

Expand Down
1 change: 0 additions & 1 deletion x/auth/testutil/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,6 @@ func (s *TxConfigTestSuite) TestTxEncodeDecode() {
SignMode: signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
Signature: dummySig,
},
SkipSequenceCheck: s.TxConfig.SignModeHandler().DefaultMode() == signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
}

txBuilder := s.TxConfig.NewTxBuilder()
Expand Down
5 changes: 2 additions & 3 deletions x/auth/types/stdtx.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,8 @@ func StdSignatureToSignatureV2(cdc *codec.LegacyAmino, sig StdSignature) (signin
}

return signing.SignatureV2{
PubKey: pk,
Data: data,
SkipSequenceCheck: true,
PubKey: pk,
Data: data,
}, nil
}

Expand Down