Add SignatureV2 infrastructure (#6373)

* Updte tx generator

* Add sigv2, PublicKeyCodec

* revert changes

* revert changes

* updates

* Updates

* Integrate multisig support

* Undo move

* remove func

* undo

* godocs

* godocs, cleanup

* Cleanup

* godocs, tests

* lint

* Re-enable VerifyBytes

* Address comments

* Fix imports

* Update crypto/types/multisig/multisignature.go

* Add test for MultiSignatureData

* Add nested multisig case

* Add test for AddSignatureV2

* Add changelog

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
Co-authored-by: sahith-narahari <sahithnarahari@gmail.com>
This commit is contained in:
Aaron Craelius
2020-06-12 18:13:20 +00:00
committed by GitHub
co-authored by Federico Kunze sahith-narahari
parent 7871910359
commit 60f7edfe3d
25 changed files with 752 additions and 286 deletions
+2 -2
View File
@@ -306,7 +306,7 @@ func DefaultSigVerificationGasConsumer(
return nil
case multisig.PubKeyMultisigThreshold:
var multisignature multisig.Multisignature
var multisignature multisig.AminoMultisignature
legacy.Cdc.MustUnmarshalBinaryBare(sig, &multisignature)
ConsumeMultisignatureVerificationGas(meter, multisignature, pubkey, params)
@@ -319,7 +319,7 @@ func DefaultSigVerificationGasConsumer(
// ConsumeMultisignatureVerificationGas consumes gas from a GasMeter for verifying a multisig pubkey signature
func ConsumeMultisignatureVerificationGas(
meter sdk.GasMeter, sig multisig.Multisignature, pubkey multisig.PubKeyMultisigThreshold, params types.Params,
meter sdk.GasMeter, sig multisig.AminoMultisignature, pubkey multisig.PubKeyMultisigThreshold, params types.Params,
) {
size := sig.BitArray.Size()
+10 -2
View File
@@ -4,6 +4,8 @@ import (
"fmt"
"testing"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
@@ -58,15 +60,21 @@ func TestSetPubKey(t *testing.T) {
func TestConsumeSignatureVerificationGas(t *testing.T) {
params := types.DefaultParams()
msg := []byte{1, 2, 3, 4}
_, cdc := simapp.MakeCodecs()
pkSet1, sigSet1 := generatePubKeysAndSignatures(5, msg, false)
multisigKey1 := multisig.NewPubKeyMultisigThreshold(2, pkSet1)
multisignature1 := multisig.NewMultisig(len(pkSet1))
expectedCost1 := expectedGasCostByKeys(pkSet1)
for i := 0; i < len(pkSet1); i++ {
err := multisignature1.AddSignatureFromPubKey(sigSet1[i], pkSet1[i], pkSet1)
stdSig := types.StdSignature{PubKey: pkSet1[i].Bytes(), Signature: sigSet1[i]}
sigV2, err := types.StdSignatureToSignatureV2(cdc, stdSig)
require.NoError(t, err)
err = multisig.AddSignatureV2(multisignature1, sigV2, pkSet1)
require.NoError(t, err)
}
aminoMultisignature1, err := types.SignatureDataToAminoSignature(cdc, multisignature1)
require.NoError(t, err)
type args struct {
meter sdk.GasMeter
@@ -82,7 +90,7 @@ func TestConsumeSignatureVerificationGas(t *testing.T) {
}{
{"PubKeyEd25519", args{sdk.NewInfiniteGasMeter(), nil, ed25519.GenPrivKey().PubKey(), params}, types.DefaultSigVerifyCostED25519, true},
{"PubKeySecp256k1", args{sdk.NewInfiniteGasMeter(), nil, secp256k1.GenPrivKey().PubKey(), params}, types.DefaultSigVerifyCostSecp256k1, false},
{"Multisig", args{sdk.NewInfiniteGasMeter(), multisignature1.Marshal(), multisigKey1, params}, expectedCost1, false},
{"Multisig", args{sdk.NewInfiniteGasMeter(), aminoMultisignature1, multisigKey1, params}, expectedCost1, false},
{"unknown key", args{sdk.NewInfiniteGasMeter(), nil, nil, params}, 0, true},
}
for _, tt := range tests {
+14 -3
View File
@@ -107,13 +107,24 @@ func makeMultiSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string)
if ok := stdSig.GetPubKey().VerifyBytes(sigBytes, stdSig.Signature); !ok {
return fmt.Errorf("couldn't verify signature")
}
if err := multisigSig.AddSignatureFromPubKey(stdSig.Signature, stdSig.GetPubKey(), multisigPub.PubKeys); err != nil {
sigV2, err := types.StdSignatureToSignatureV2(cdc, stdSig)
if err != nil {
return nil
}
if err := multisig.AddSignatureV2(multisigSig, sigV2, multisigPub.PubKeys); err != nil {
return err
}
}
newStdSig := types.StdSignature{Signature: cdc.MustMarshalBinaryBare(multisigSig), PubKey: multisigPub.Bytes()} //nolint:staticcheck
newTx := types.NewStdTx(stdTx.GetMsgs(), stdTx.Fee, []types.StdSignature{newStdSig}, stdTx.GetMemo()) //nolint:staticcheck
sigBz, err := types.SignatureDataToAminoSignature(cdc, multisigSig)
if err != nil {
return err
}
newStdSig := types.StdSignature{Signature: sigBz, PubKey: multisigPub.Bytes()} //nolint:staticcheck
newTx := types.NewStdTx(stdTx.GetMsgs(), stdTx.Fee, []types.StdSignature{newStdSig}, stdTx.GetMemo()) //nolint:staticcheck
sigOnly := viper.GetBool(flagSigOnly)
var json []byte
+1 -1
View File
@@ -108,7 +108,7 @@ func printAndValidateSigs(
multiPK, ok := sig.GetPubKey().(multisig.PubKeyMultisigThreshold)
if ok {
var multiSig multisig.Multisignature
var multiSig multisig.AminoMultisignature
clientCtx.Codec.MustUnmarshalBinaryBare(sig.Signature, &multiSig)
var b strings.Builder
+8 -8
View File
@@ -4,7 +4,7 @@ import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
types "github.com/cosmos/cosmos-sdk/types/tx"
signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
@@ -16,19 +16,19 @@ type LegacyAminoJSONHandler struct{}
var _ signing.SignModeHandler = LegacyAminoJSONHandler{}
// DefaultMode implements SignModeHandler.DefaultMode
func (h LegacyAminoJSONHandler) DefaultMode() types.SignMode {
return types.SignMode_SIGN_MODE_LEGACY_AMINO_JSON
func (h LegacyAminoJSONHandler) DefaultMode() signingtypes.SignMode {
return signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON
}
// Modes implements SignModeHandler.Modes
func (LegacyAminoJSONHandler) Modes() []types.SignMode {
return []types.SignMode{types.SignMode_SIGN_MODE_LEGACY_AMINO_JSON}
func (LegacyAminoJSONHandler) Modes() []signingtypes.SignMode {
return []signingtypes.SignMode{signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON}
}
// DefaultMode implements SignModeHandler.GetSignBytes
func (LegacyAminoJSONHandler) GetSignBytes(mode types.SignMode, data signing.SignerData, tx sdk.Tx) ([]byte, error) {
if mode != types.SignMode_SIGN_MODE_LEGACY_AMINO_JSON {
return nil, fmt.Errorf("expected %s, got %s", types.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, mode)
func (LegacyAminoJSONHandler) GetSignBytes(mode signingtypes.SignMode, data signing.SignerData, tx sdk.Tx) ([]byte, error) {
if mode != signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON {
return nil, fmt.Errorf("expected %s, got %s", signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, mode)
}
feeTx, ok := tx.(ante.FeeTx)
+5 -5
View File
@@ -8,7 +8,7 @@ import (
"github.com/tendermint/tendermint/crypto/secp256k1"
sdk "github.com/cosmos/cosmos-sdk/types"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/cosmos/cosmos-sdk/x/auth/signing/amino"
@@ -55,7 +55,7 @@ func TestLegacyAminoJSONHandler_GetSignBytes(t *testing.T) {
AccountNumber: accNum,
AccountSequence: seqNum,
}
signBz, err := handler.GetSignBytes(txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx)
signBz, err := handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx)
require.NoError(t, err)
expectedSignBz := auth.StdSignBytes(chainId, accNum, seqNum, fee, msgs, memo)
@@ -63,16 +63,16 @@ func TestLegacyAminoJSONHandler_GetSignBytes(t *testing.T) {
require.Equal(t, expectedSignBz, signBz)
// expect error with wrong sign mode
_, err = handler.GetSignBytes(txtypes.SignMode_SIGN_MODE_DIRECT, signingData, tx)
_, err = handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_DIRECT, signingData, tx)
require.Error(t, err)
}
func TestLegacyAminoJSONHandler_DefaultMode(t *testing.T) {
handler := amino.LegacyAminoJSONHandler{}
require.Equal(t, txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, handler.DefaultMode())
require.Equal(t, signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, handler.DefaultMode())
}
func TestLegacyAminoJSONHandler_Modes(t *testing.T) {
handler := amino.LegacyAminoJSONHandler{}
require.Equal(t, []txtypes.SignMode{txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON}, handler.Modes())
require.Equal(t, []signingtypes.SignMode{signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON}, handler.Modes())
}
+11 -10
View File
@@ -3,24 +3,25 @@ package signing
import (
"fmt"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
sdk "github.com/cosmos/cosmos-sdk/types"
types "github.com/cosmos/cosmos-sdk/types/tx"
)
// SignModeHandlerMap is SignModeHandler that aggregates multiple SignModeHandler's into
// a single handler
type SignModeHandlerMap struct {
defaultMode types.SignMode
modes []types.SignMode
signModeHandlers map[types.SignMode]SignModeHandler
defaultMode signing.SignMode
modes []signing.SignMode
signModeHandlers map[signing.SignMode]SignModeHandler
}
var _ SignModeHandler = SignModeHandlerMap{}
// NewSignModeHandlerMap returns a new SignModeHandlerMap with the provided defaultMode and handlers
func NewSignModeHandlerMap(defaultMode types.SignMode, handlers []SignModeHandler) SignModeHandlerMap {
handlerMap := make(map[types.SignMode]SignModeHandler)
var modes []types.SignMode
func NewSignModeHandlerMap(defaultMode signing.SignMode, handlers []SignModeHandler) SignModeHandlerMap {
handlerMap := make(map[signing.SignMode]SignModeHandler)
var modes []signing.SignMode
for _, h := range handlers {
for _, m := range h.Modes() {
@@ -40,17 +41,17 @@ func NewSignModeHandlerMap(defaultMode types.SignMode, handlers []SignModeHandle
}
// DefaultMode implements SignModeHandler.DefaultMode
func (h SignModeHandlerMap) DefaultMode() types.SignMode {
func (h SignModeHandlerMap) DefaultMode() signing.SignMode {
return h.defaultMode
}
// Modes implements SignModeHandler.Modes
func (h SignModeHandlerMap) Modes() []types.SignMode {
func (h SignModeHandlerMap) Modes() []signing.SignMode {
return h.modes
}
// DefaultMode implements SignModeHandler.GetSignBytes
func (h SignModeHandlerMap) GetSignBytes(mode types.SignMode, data SignerData, tx sdk.Tx) ([]byte, error) {
func (h SignModeHandlerMap) GetSignBytes(mode signing.SignMode, data SignerData, tx sdk.Tx) ([]byte, error) {
handler, found := h.signModeHandlers[mode]
if !found {
return nil, fmt.Errorf("can't verify sign mode %s", mode.String())
+7 -7
View File
@@ -8,7 +8,7 @@ import (
"github.com/tendermint/tendermint/crypto/secp256k1"
sdk "github.com/cosmos/cosmos-sdk/types"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/cosmos/cosmos-sdk/x/auth/signing/amino"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
@@ -17,7 +17,7 @@ import (
func MakeTestHandlerMap() signing.SignModeHandler {
return signing.NewSignModeHandlerMap(
txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
[]signing.SignModeHandler{
amino.LegacyAminoJSONHandler{},
},
@@ -66,27 +66,27 @@ func TestHandlerMap_GetSignBytes(t *testing.T) {
AccountNumber: accNum,
AccountSequence: seqNum,
}
signBz, err := handler.GetSignBytes(txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx)
signBz, err := handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx)
require.NoError(t, err)
expectedSignBz, err := aminoJSONHandler.GetSignBytes(txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx)
expectedSignBz, err := aminoJSONHandler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx)
require.NoError(t, err)
require.Equal(t, expectedSignBz, signBz)
// expect error with wrong sign mode
_, err = aminoJSONHandler.GetSignBytes(txtypes.SignMode_SIGN_MODE_DIRECT, signingData, tx)
_, err = aminoJSONHandler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_DIRECT, signingData, tx)
require.Error(t, err)
}
func TestHandlerMap_DefaultMode(t *testing.T) {
handler := MakeTestHandlerMap()
require.Equal(t, txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, handler.DefaultMode())
require.Equal(t, signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, handler.DefaultMode())
}
func TestHandlerMap_Modes(t *testing.T) {
handler := MakeTestHandlerMap()
modes := handler.Modes()
require.Contains(t, modes, txtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
require.Contains(t, modes, signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
require.Len(t, modes, 1)
}
+4 -4
View File
@@ -2,7 +2,7 @@ package signing
import (
sdk "github.com/cosmos/cosmos-sdk/types"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
)
// SignModeHandler defines a interface to be implemented by types which will handle
@@ -10,14 +10,14 @@ import (
type SignModeHandler interface {
// DefaultMode is the default mode that is to be used with this handler if no
// other mode is specified. This can be useful for testing and CLI usage
DefaultMode() txtypes.SignMode
DefaultMode() signing.SignMode
// Modes is the list of modes supporting by this handler
Modes() []txtypes.SignMode
Modes() []signing.SignMode
// GetSignBytes returns the sign bytes for the provided SignMode, SignerData and Tx,
// or an error
GetSignBytes(mode txtypes.SignMode, data SignerData, tx sdk.Tx) ([]byte, error)
GetSignBytes(mode signing.SignMode, data SignerData, tx sdk.Tx) ([]byte, error)
}
// SignerData is the specific information needed to sign a transaction that generally
+95
View File
@@ -4,6 +4,8 @@ import (
"encoding/json"
"fmt"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/tendermint/tendermint/crypto"
yaml "gopkg.in/yaml.v2"
@@ -356,3 +358,96 @@ func (tx StdTx) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
}
return nil
}
// StdSignatureToSignatureV2 converts a StdSignature to a SignatureV2
func StdSignatureToSignatureV2(cdc *codec.Codec, sig StdSignature) (signing.SignatureV2, error) {
pk := sig.GetPubKey()
data, err := pubKeySigToSigData(cdc, pk, sig.Signature)
if err != nil {
return signing.SignatureV2{}, err
}
return signing.SignatureV2{
PubKey: pk,
Data: data,
}, nil
}
func pubKeySigToSigData(cdc *codec.Codec, key crypto.PubKey, sig []byte) (signing.SignatureData, error) {
multiPK, ok := key.(multisig.PubKey)
if !ok {
return &signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
Signature: sig,
}, nil
}
var multiSig multisig.AminoMultisignature
err := cdc.UnmarshalBinaryBare(sig, &multiSig)
if err != nil {
return nil, err
}
sigs := multiSig.Sigs
sigDatas := make([]signing.SignatureData, len(sigs))
pubKeys := multiPK.GetPubKeys()
bitArray := multiSig.BitArray
n := multiSig.BitArray.Size()
signatures := multisig.NewMultisig(n)
sigIdx := 0
for i := 0; i < n; i++ {
if bitArray.GetIndex(i) {
data, err := pubKeySigToSigData(cdc, pubKeys[i], multiSig.Sigs[sigIdx])
if err != nil {
return nil, sdkerrors.Wrapf(err, "Unable to convert Signature to SigData %d", sigIdx)
}
sigDatas[sigIdx] = data
multisig.AddSignature(signatures, data, sigIdx)
sigIdx++
}
}
return signatures, nil
}
// MultiSignatureDataToAminoMultisignature converts a MultiSignatureData to an AminoMultisignature.
// Only SIGN_MODE_LEGACY_AMINO_JSON is supported.
func MultiSignatureDataToAminoMultisignature(cdc *codec.Codec, mSig *signing.MultiSignatureData) (multisig.AminoMultisignature, error) {
n := len(mSig.Signatures)
sigs := make([][]byte, n)
for i := 0; i < n; i++ {
var err error
sigs[i], err = SignatureDataToAminoSignature(cdc, mSig.Signatures[i])
if err != nil {
return multisig.AminoMultisignature{}, sdkerrors.Wrapf(err, "Unable to convert Signature Data to signature %d", i)
}
}
return multisig.AminoMultisignature{
BitArray: mSig.BitArray,
Sigs: sigs,
}, nil
}
// SignatureDataToAminoSignature converts a SignatureData to amino-encoded signature bytes.
// Only SIGN_MODE_LEGACY_AMINO_JSON is supported.
func SignatureDataToAminoSignature(cdc *codec.Codec, data signing.SignatureData) ([]byte, error) {
switch data := data.(type) {
case *signing.SingleSignatureData:
if data.SignMode != signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON {
return nil, fmt.Errorf("expected %s, got %s", signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, data.SignMode)
}
return data.Signature, nil
case *signing.MultiSignatureData:
aminoMSig, err := MultiSignatureDataToAminoMultisignature(cdc, data)
if err != nil {
return nil, err
}
return cdc.MarshalBinaryBare(aminoMSig)
default:
return nil, fmt.Errorf("unexpected signature data %T", data)
}
}
+60
View File
@@ -4,6 +4,11 @@ import (
"fmt"
"testing"
"github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
@@ -167,3 +172,58 @@ func TestStdSignatureMarshalYAML(t *testing.T) {
require.Equal(t, tc.output, string(bz), "test case #%d", i)
}
}
func TestSignatureV2Conversions(t *testing.T) {
_, pubKey, _ := KeyTestPubAddr()
cdc := codec.New()
sdk.RegisterCodec(cdc)
RegisterCodec(cdc)
dummy := []byte("dummySig")
sig := StdSignature{PubKey: pubKey.Bytes(), Signature: dummy}
sigV2, err := StdSignatureToSignatureV2(cdc, sig)
require.NoError(t, err)
require.Equal(t, pubKey, sigV2.PubKey)
require.Equal(t, &signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
Signature: dummy,
}, sigV2.Data)
sigBz, err := SignatureDataToAminoSignature(cdc, sigV2.Data)
require.NoError(t, err)
require.Equal(t, dummy, sigBz)
// multisigs
_, pubKey2, _ := KeyTestPubAddr()
multiPK := multisig.NewPubKeyMultisigThreshold(1, []crypto.PubKey{
pubKey, pubKey2,
})
dummy2 := []byte("dummySig2")
bitArray := types.NewCompactBitArray(2)
bitArray.SetIndex(0, true)
bitArray.SetIndex(1, true)
msigData := &signing.MultiSignatureData{
BitArray: bitArray,
Signatures: []signing.SignatureData{
&signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
Signature: dummy,
},
&signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
Signature: dummy2,
},
},
}
msig, err := SignatureDataToAminoSignature(cdc, msigData)
require.NoError(t, err)
sigV2, err = StdSignatureToSignatureV2(cdc, StdSignature{
PubKey: multiPK.Bytes(),
Signature: msig,
})
require.NoError(t, err)
require.Equal(t, multiPK, sigV2.PubKey)
require.Equal(t, msigData, sigV2.Data)
}