Add `SkipSequenceCheck` flag in SignatureV2 for Amino signatures (#7234)

* Add SkipSequenceCheck

* Fix test

* Fix test

* Add explicit amino test

Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>
This commit is contained in:
Amaury Martiny 2020-09-04 19:49:07 +02:00 committed by GitHub
parent 18ac096a54
commit bae1399ebc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 127 additions and 11 deletions

View File

@ -239,6 +239,7 @@ 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()

View File

@ -19,8 +19,23 @@ type SignatureV2 struct {
// the signatures themselves for either single or multi-signatures.
Data SignatureData
// Sequence is the sequence of this account.
// Sequence is the sequence of this account. Only populated in
// SIGN_MODE_DIRECT.
Sequence uint64
// Ugly flag to keep backwards-compatibility with Amino StdSignatures.
// In SIGN_MODE_DIRECT, sequence is in AuthInfo, and will thus be populated
// in the Sequence field above. The ante handler then checks this Sequence
// with the actual sequence on-chain.
// In SIGN_MODE_LEGACY_AMINO_JSON, sequence is signed via StdSignDoc, and
// checked during signature verification. It's not populated in the
// Sequence field above. This flag indicates that the Sequence field should
// be skipped in ante handlers.
// TLDR;
// - 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
}
// SignatureDataToProto converts a SignatureData to SignatureDescriptor_Data.

View File

@ -207,12 +207,18 @@ func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul
}
// Check account sequence number.
// When using Amino StdSignatures, we actually don't have the Sequence in
// 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 sig.Sequence != acc.GetSequence() {
return ctx, sdkerrors.Wrapf(
sdkerrors.ErrWrongSequence,
"account sequence mismatch, expected %d, got %d", acc.GetSequence(), sig.Sequence,
)
}
}
// retrieve signer data
genesis := ctx.BlockHeight() == 0
@ -230,9 +236,14 @@ func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul
if !simulate {
err := authsigning.VerifySignature(pubKey, signerData, sig.Data, svd.signModeHandler, tx)
if err != nil {
return ctx, sdkerrors.Wrapf(
sdkerrors.ErrUnauthorized,
"signature verification failed; please verify account number (%d) and chain-id (%s)", accNum, chainID)
var errMsg string
if sig.SkipSequenceCheck {
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)
}
return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, errMsg)
}
}
}

View File

@ -6,6 +6,8 @@ import (
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
"github.com/cosmos/cosmos-sdk/simapp"
@ -14,6 +16,7 @@ import (
"github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
"github.com/cosmos/cosmos-sdk/x/auth/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)
func (suite *AnteTestSuite) TestSetPubKey() {
@ -179,6 +182,91 @@ func (suite *AnteTestSuite) TestSigVerification() {
}
}
// This test is exactly like the one above, but we set the codec explicitly to
// Amino.
// Once https://github.com/cosmos/cosmos-sdk/issues/6190 is in, we can remove
// this, since it'll be handled by the test matrix.
// In the meantime, we want to make double-sure amino compatibility works.
// ref: https://github.com/cosmos/cosmos-sdk/issues/7229
func (suite *AnteTestSuite) TestSigVerification_ExplicitAmino() {
suite.app, suite.ctx = createTestApp(true)
suite.ctx = suite.ctx.WithBlockHeight(1)
// Set up TxConfig.
aminoCdc := codec.New()
// We're using TestMsg amino encoding in some tests, so register it here.
txConfig := authtypes.StdTxConfig{Cdc: aminoCdc}
suite.clientCtx = client.Context{}.
WithTxConfig(txConfig)
suite.anteHandler = ante.NewAnteHandler(suite.app.AccountKeeper, suite.app.BankKeeper, ante.DefaultSigVerificationGasConsumer, txConfig.SignModeHandler())
suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder()
// make block height non-zero to ensure account numbers part of signBytes
suite.ctx = suite.ctx.WithBlockHeight(1)
// keys and addresses
priv1, _, addr1 := testdata.KeyTestPubAddr()
priv2, _, addr2 := testdata.KeyTestPubAddr()
priv3, _, addr3 := testdata.KeyTestPubAddr()
addrs := []sdk.AccAddress{addr1, addr2, addr3}
msgs := make([]sdk.Msg, len(addrs))
// set accounts and create msg for each address
for i, addr := range addrs {
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr)
suite.Require().NoError(acc.SetAccountNumber(uint64(i)))
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
msgs[i] = testdata.NewTestMsg(addr)
}
feeAmount := testdata.NewTestFeeAmount()
gasLimit := testdata.NewTestGasLimit()
spkd := ante.NewSetPubKeyDecorator(suite.app.AccountKeeper)
svd := ante.NewSigVerificationDecorator(suite.app.AccountKeeper, suite.clientCtx.TxConfig.SignModeHandler())
antehandler := sdk.ChainAnteDecorators(spkd, svd)
type testCase struct {
name string
privs []crypto.PrivKey
accNums []uint64
accSeqs []uint64
recheck bool
shouldErr bool
}
testCases := []testCase{
{"no signers", []crypto.PrivKey{}, []uint64{}, []uint64{}, false, true},
{"not enough signers", []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{0, 0}, false, true},
{"wrong order signers", []crypto.PrivKey{priv3, priv2, priv1}, []uint64{2, 1, 0}, []uint64{0, 0, 0}, false, true},
{"wrong accnums", []crypto.PrivKey{priv1, priv2, priv3}, []uint64{7, 8, 9}, []uint64{0, 0, 0}, false, true},
{"wrong sequences", []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{3, 4, 5}, false, true},
{"valid tx", []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0}, false, false},
{"no err on recheck", []crypto.PrivKey{}, []uint64{}, []uint64{}, true, false},
}
for i, tc := range testCases {
suite.ctx = suite.ctx.WithIsReCheckTx(tc.recheck)
suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // Create new txBuilder for each test
suite.Require().NoError(suite.txBuilder.SetMsgs(msgs...))
suite.txBuilder.SetFeeAmount(feeAmount)
suite.txBuilder.SetGasLimit(gasLimit)
tx, err := suite.CreateTestTx(tc.privs, tc.accNums, tc.accSeqs, suite.ctx.ChainID())
suite.Require().NoError(err)
_, err = antehandler(suite.ctx, tx, false)
if tc.shouldErr {
suite.Require().NotNil(err, "TestCase %d: %s did not error as expected", i, tc.name)
} else {
suite.Require().Nil(err, "TestCase %d: %s errored unexpectedly. Err: %v", i, tc.name, err)
}
}
}
func (suite *AnteTestSuite) TestSigIntegration() {
// generate private keys
privs := []crypto.PrivKey{secp256k1.GenPrivKey(), secp256k1.GenPrivKey(), secp256k1.GenPrivKey()}

View File

@ -389,6 +389,7 @@ func StdSignatureToSignatureV2(cdc *codec.LegacyAmino, sig StdSignature) (signin
return signing.SignatureV2{
PubKey: pk,
Data: data,
SkipSequenceCheck: true,
}, nil
}