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

Add SignatureV2 infrastructure #6373

Merged
merged 35 commits into from
Jun 12, 2020
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f8809f5
Updte tx generator
aaronc Jun 8, 2020
c28fff5
Merge branch 'master' of https://github.com/cosmos/cosmos-sdk into aa…
aaronc Jun 9, 2020
ef055c0
Add sigv2, PublicKeyCodec
aaronc Jun 9, 2020
68f6185
revert changes
aaronc Jun 9, 2020
9765b9e
Merge branch 'master' of https://github.com/cosmos/cosmos-sdk into aa…
aaronc Jun 9, 2020
1f0195b
revert changes
aaronc Jun 9, 2020
5d372a0
updates
aaronc Jun 9, 2020
7f2a117
Updates
aaronc Jun 9, 2020
b0915da
Integrate multisig support
aaronc Jun 9, 2020
29299bd
Undo move
aaronc Jun 9, 2020
d2e16a2
remove func
aaronc Jun 9, 2020
2f46b95
undo
aaronc Jun 9, 2020
bdcd4ec
godocs
aaronc Jun 9, 2020
038a472
godocs, cleanup
aaronc Jun 9, 2020
42d97fb
Cleanup
aaronc Jun 9, 2020
61cc024
godocs, tests
aaronc Jun 9, 2020
1945342
lint
aaronc Jun 9, 2020
776a120
Merge branch 'master' into aaronc/6213-sig-v2
aaronc Jun 9, 2020
c0e3b34
Merge remote-tracking branch 'origin/aaronc/6213-sig-v2' into aaronc/…
aaronc Jun 9, 2020
a9ad3be
Re-enable VerifyBytes
aaronc Jun 9, 2020
e5d4a49
Merge branch 'master' into aaronc/6213-sig-v2
fedekunze Jun 10, 2020
3f9f764
Merge branch 'master' into aaronc/6213-sig-v2
aaronc Jun 10, 2020
8f7b0be
Address comments
sahith-narahari Jun 10, 2020
8f0a957
Fix imports
sahith-narahari Jun 10, 2020
6c61036
Update crypto/types/multisig/multisignature.go
fedekunze Jun 11, 2020
383cd76
Add test for MultiSignatureData
sahith-narahari Jun 11, 2020
22cffde
Merge branch 'aaronc/6213-sig-v2' of github.com:cosmos/cosmos-sdk int…
sahith-narahari Jun 11, 2020
6ad9c63
Add nested multisig case
aaronc Jun 11, 2020
a75cce6
Add test for AddSignatureV2
sahith-narahari Jun 11, 2020
f1cefe8
Merge branch 'master' of github.com:cosmos/cosmos-sdk into aaronc/621…
sahith-narahari Jun 11, 2020
fa2f76b
Merge branch 'master' into aaronc/6213-sig-v2
aaronc Jun 12, 2020
8593343
Merge branch 'master' of https://github.com/cosmos/cosmos-sdk into aa…
aaronc Jun 12, 2020
cddcd82
Add changelog
sahith-narahari Jun 12, 2020
5caa1d0
Merge branch 'aaronc/6213-sig-v2' of github.com:cosmos/cosmos-sdk int…
sahith-narahari Jun 12, 2020
90347eb
Merge branch 'master' of github.com:cosmos/cosmos-sdk into aaronc/621…
sahith-narahari Jun 12, 2020
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: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ be used to retrieve the actual proposal `Content`. Also the `NewMsgSubmitProposa
* (types) [\#6327](https://github.com/cosmos/cosmos-sdk/pull/6327) `sdk.Msg` now inherits `proto.Message`, as a result all `sdk.Msg` types now use pointer semantics.
* (codec) [\#6330](https://github.com/cosmos/cosmos-sdk/pull/6330) `codec.RegisterCrypto` has been moved to the `crypto/codec` package and the global `codec.Cdc` Amino instance has been deprecated and moved to the `codec/legacy_global` package.
* (x/ibc) [\#6374](https://github.com/cosmos/cosmos-sdk/pull/6374) `VerifyMembership` and `VerifyNonMembership` now take a `specs []string` argument to specify the proof format used for verification. Most SDK chains can simply use `commitmenttypes.GetSDKSpecs()` for this argument.
* (crypto/types/multisig) [\#6373](https://github.com/cosmos/cosmos-sdk/pull/6373) `multisig.Multisignature` has been renamed to `AminoMultisignature`

### Features

Expand Down
16 changes: 8 additions & 8 deletions crypto/types/multisig/codec.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package multisig

import (
amino "github.com/tendermint/go-amino"
"github.com/tendermint/go-amino"

"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
Expand All @@ -10,21 +10,21 @@ import (
)

// TODO: Figure out API for others to either add their own pubkey types, or
// to make verify / marshal accept a cdc.
// to make verify / marshal accept a Cdc.
const (
PubKeyAminoRoute = "tendermint/PubKeyMultisigThreshold"
)

var cdc = amino.NewCodec()
var Cdc = amino.NewCodec()

func init() {
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
cdc.RegisterConcrete(PubKeyMultisigThreshold{},
Cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
Cdc.RegisterConcrete(PubKeyMultisigThreshold{},
PubKeyAminoRoute, nil)
cdc.RegisterConcrete(ed25519.PubKeyEd25519{},
Cdc.RegisterConcrete(ed25519.PubKeyEd25519{},
ed25519.PubKeyAminoName, nil)
cdc.RegisterConcrete(sr25519.PubKeySr25519{},
Cdc.RegisterConcrete(sr25519.PubKeySr25519{},
sr25519.PubKeyAminoName, nil)
cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{},
Cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{},
secp256k1.PubKeyAminoName, nil)
}
50 changes: 31 additions & 19 deletions crypto/types/multisig/multisignature.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,23 @@ import (
"github.com/tendermint/tendermint/crypto"

"github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
)

// Multisignature is used to represent the signature object used in the multisigs.
// AminoMultisignature is used to represent amino multi-signatures for StdTx's.
// It is assumed that all signatures were made with SIGN_MODE_LEGACY_AMINO_JSON.
// Sigs is a list of signatures, sorted by corresponding index.
type Multisignature struct {
type AminoMultisignature struct {
sahith-narahari marked this conversation as resolved.
Show resolved Hide resolved
BitArray *types.CompactBitArray
Sigs [][]byte
}

// NewMultisig returns a new Multisignature of size n.
func NewMultisig(n int) *Multisignature {
// Default the signature list to have a capacity of two, since we can
// expect that most multisigs will require multiple signers.
return &Multisignature{types.NewCompactBitArray(n), make([][]byte, 0, 2)}
// NewMultisig returns a new MultiSignatureData
func NewMultisig(n int) *signing.MultiSignatureData {
return &signing.MultiSignatureData{
BitArray: types.NewCompactBitArray(n),
Signatures: make([]signing.SignatureData, 0, n),
}
}

// GetIndex returns the index of pk in keys. Returns -1 if not found
Expand All @@ -35,29 +38,39 @@ func getIndex(pk crypto.PubKey, keys []crypto.PubKey) int {

// AddSignature adds a signature to the multisig, at the corresponding index.
// If the signature already exists, replace it.
func (mSig *Multisignature) AddSignature(sig []byte, index int) {
func AddSignature(mSig *signing.MultiSignatureData, sig signing.SignatureData, index int) {
aaronc marked this conversation as resolved.
Show resolved Hide resolved
newSigIndex := mSig.BitArray.NumTrueBitsBefore(index)
// Signature already exists, just replace the value there
if mSig.BitArray.GetIndex(index) {
mSig.Sigs[newSigIndex] = sig
mSig.Signatures[newSigIndex] = sig
return
}
mSig.BitArray.SetIndex(index, true)
// Optimization if the index is the greatest index
if newSigIndex == len(mSig.Sigs) {
mSig.Sigs = append(mSig.Sigs, sig)
if newSigIndex == len(mSig.Signatures) {
mSig.Signatures = append(mSig.Signatures, sig)
return
}
// Expand slice by one with a dummy element, move all elements after i
// over by one, then place the new signature in that gap.
mSig.Sigs = append(mSig.Sigs, make([]byte, 0))
copy(mSig.Sigs[newSigIndex+1:], mSig.Sigs[newSigIndex:])
mSig.Sigs[newSigIndex] = sig
mSig.Signatures = append(mSig.Signatures, &signing.SingleSignatureData{})
copy(mSig.Signatures[newSigIndex+1:], mSig.Signatures[newSigIndex:])
mSig.Signatures[newSigIndex] = sig
}

// AddSignatureFromPubKey adds a signature to the multisig, at the index in
// keys corresponding to the provided pubkey.
func (mSig *Multisignature) AddSignatureFromPubKey(sig []byte, pubkey crypto.PubKey, keys []crypto.PubKey) error {
func AddSignatureFromPubKey(mSig *signing.MultiSignatureData, sig signing.SignatureData, pubkey crypto.PubKey, keys []crypto.PubKey) error {
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
if mSig == nil {
return fmt.Errorf("value of mSig is nil %v", mSig)
}
if sig == nil {
return fmt.Errorf("value of sig is nil %v", sig)
}

if pubkey == nil || keys == nil {
return fmt.Errorf("pubkey or keys can't be nil %v %v", pubkey, keys)
}
index := getIndex(pubkey, keys)
if index == -1 {
keysStr := make([]string, len(keys))
Expand All @@ -68,11 +81,10 @@ func (mSig *Multisignature) AddSignatureFromPubKey(sig []byte, pubkey crypto.Pub
return fmt.Errorf("provided key %X doesn't exist in pubkeys: \n%s", pubkey.Bytes(), strings.Join(keysStr, "\n"))
}

mSig.AddSignature(sig, index)
AddSignature(mSig, sig, index)
return nil
}

// Marshal the multisignature with amino
func (mSig *Multisignature) Marshal() []byte {
return cdc.MustMarshalBinaryBare(mSig)
func AddSignatureV2(mSig *signing.MultiSignatureData, sig signing.SignatureV2, keys []crypto.PubKey) error {
sahith-narahari marked this conversation as resolved.
Show resolved Hide resolved
return AddSignatureFromPubKey(mSig, sig.Data, sig.PubKey, keys)
}
25 changes: 25 additions & 0 deletions crypto/types/multisig/pubkey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package multisig

import (
"github.com/tendermint/tendermint/crypto"

"github.com/cosmos/cosmos-sdk/types/tx/signing"
)

// PubKey defines a type which supports multi-signature verification via MultiSignatureData
// which supports multiple SignMode's.
type PubKey interface {
crypto.PubKey

// VerifyMultisignature verifies the provide multi-signature represented by MultiSignatureData
// using getSignBytes to retrieve the sign bytes to verify against for the provided mode.
VerifyMultisignature(getSignBytes GetSignBytesFunc, sig *signing.MultiSignatureData) error

// GetPubKeys returns the crypto.PubKey's nested within the multi-sig PubKey
GetPubKeys() []crypto.PubKey
}

// GetSignBytesFunc defines a function type which returns sign bytes for a given SignMode or an error.
// It will generally be implemented as a closure which wraps whatever signable object signatures are
// being verified against.
type GetSignBytesFunc func(mode signing.SignMode) ([]byte, error)
72 changes: 66 additions & 6 deletions crypto/types/multisig/threshold_pubkey.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package multisig

import (
"fmt"

"github.com/tendermint/tendermint/crypto"

"github.com/cosmos/cosmos-sdk/types/tx/signing"
)

// PubKeyMultisigThreshold implements a K of N threshold multisig.
Expand All @@ -10,11 +14,11 @@ type PubKeyMultisigThreshold struct {
PubKeys []crypto.PubKey `json:"pubkeys"`
}

var _ crypto.PubKey = PubKeyMultisigThreshold{}
var _ PubKey = PubKeyMultisigThreshold{}

// NewPubKeyMultisigThreshold returns a new PubKeyMultisigThreshold.
// Panics if len(pubkeys) < k or 0 >= k.
func NewPubKeyMultisigThreshold(k int, pubkeys []crypto.PubKey) crypto.PubKey {
func NewPubKeyMultisigThreshold(k int, pubkeys []crypto.PubKey) PubKey {
if k <= 0 {
panic("threshold k of n multisignature: k <= 0")
}
Expand All @@ -35,9 +39,12 @@ func NewPubKeyMultisigThreshold(k int, pubkeys []crypto.PubKey) crypto.PubKey {
// and all signatures are valid. (Not just k of the signatures)
// The multisig uses a bitarray, so multiple signatures for the same key is not
// a concern.
//
// NOTE: VerifyMultisignature should preferred to VerifyBytes which only works
// with amino multisignatures.
aaronc marked this conversation as resolved.
Show resolved Hide resolved
func (pk PubKeyMultisigThreshold) VerifyBytes(msg []byte, marshalledSig []byte) bool {
var sig Multisignature
err := cdc.UnmarshalBinaryBare(marshalledSig, &sig)
var sig AminoMultisignature
err := Cdc.UnmarshalBinaryBare(marshalledSig, &sig)
if err != nil {
return false
}
Expand Down Expand Up @@ -67,12 +74,65 @@ func (pk PubKeyMultisigThreshold) VerifyBytes(msg []byte, marshalledSig []byte)
return true
}

// VerifyMultisignature implements the PubKey.VerifyMultisignature method
func (pk PubKeyMultisigThreshold) VerifyMultisignature(getSignBytes GetSignBytesFunc, sig *signing.MultiSignatureData) error {
bitarray := sig.BitArray
sigs := sig.Signatures
size := bitarray.Size()
// ensure bit array is the correct size
if len(pk.PubKeys) != size {
return fmt.Errorf("bit array size is incorrect %d", len(pk.PubKeys))
}
// ensure size of signature list
if len(sigs) < int(pk.K) || len(sigs) > size {
return fmt.Errorf("signature size is incorrect %d", len(sigs))
}
// ensure at least k signatures are set
if bitarray.NumTrueBitsBefore(size) < int(pk.K) {
return fmt.Errorf("minimum number of signatures not set, have %d, expected %d", bitarray.NumTrueBitsBefore(size), int(pk.K))
}
// index in the list of signatures which we are concerned with.
sigIndex := 0
for i := 0; i < size; i++ {
if bitarray.GetIndex(i) {
si := sig.Signatures[sigIndex]
switch si := si.(type) {
case *signing.SingleSignatureData:
msg, err := getSignBytes(si.SignMode)
if err != nil {
return err
}
if !pk.PubKeys[i].VerifyBytes(msg, si.Signature) {
return err
}
case *signing.MultiSignatureData:
sahith-narahari marked this conversation as resolved.
Show resolved Hide resolved
nestedMultisigPk, ok := pk.PubKeys[i].(PubKey)
if !ok {
return fmt.Errorf("unable to parse pubkey of index %d", i)
}
if err := nestedMultisigPk.VerifyMultisignature(getSignBytes, si); err != nil {
return err
}
default:
return fmt.Errorf("improper signature data type for index %d", sigIndex)
}
sigIndex++
}
}
return nil
}

// GetPubKeys implements the PubKey.GetPubKeys method
func (pk PubKeyMultisigThreshold) GetPubKeys() []crypto.PubKey {
return pk.PubKeys
}

// Bytes returns the amino encoded version of the PubKeyMultisigThreshold
func (pk PubKeyMultisigThreshold) Bytes() []byte {
return cdc.MustMarshalBinaryBare(pk)
return Cdc.MustMarshalBinaryBare(pk)
}

// Address returns tmhash(PubKey.Bytes())
// Address returns tmhash(PubKeyMultisigThreshold.Bytes())
func (pk PubKeyMultisigThreshold) Address() crypto.Address {
return crypto.AddressHash(pk.Bytes())
}
Expand Down
Loading