173 lines
5.4 KiB
Go
173 lines
5.4 KiB
Go
package multisig
|
|
|
|
import (
|
|
fmt "fmt"
|
|
|
|
tmcrypto "github.com/tendermint/tendermint/crypto"
|
|
|
|
"github.com/cosmos/cosmos-sdk/codec/types"
|
|
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
|
multisigtypes "github.com/cosmos/cosmos-sdk/crypto/types/multisig"
|
|
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
|
)
|
|
|
|
var _ multisigtypes.PubKey = &LegacyAminoPubKey{}
|
|
var _ types.UnpackInterfacesMessage = &LegacyAminoPubKey{}
|
|
|
|
// NewLegacyAminoPubKey returns a new LegacyAminoPubKey.
|
|
// Multisig can be constructed with multiple same keys - it will increase the power of
|
|
// the owner of that key (he will still need to add multiple signatures in the right order).
|
|
// Panics if len(pubKeys) < k or 0 >= k.
|
|
func NewLegacyAminoPubKey(threshold int, pubKeys []cryptotypes.PubKey) *LegacyAminoPubKey {
|
|
if threshold <= 0 {
|
|
panic("threshold k of n multisignature: k <= 0")
|
|
}
|
|
if len(pubKeys) < threshold {
|
|
panic("threshold k of n multisignature: len(pubKeys) < k")
|
|
}
|
|
anyPubKeys, err := packPubKeys(pubKeys)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return &LegacyAminoPubKey{Threshold: uint32(threshold), PubKeys: anyPubKeys}
|
|
}
|
|
|
|
// Address implements cryptotypes.PubKey Address method
|
|
func (m *LegacyAminoPubKey) Address() cryptotypes.Address {
|
|
return tmcrypto.AddressHash(m.Bytes())
|
|
}
|
|
|
|
// Bytes returns the proto encoded version of the LegacyAminoPubKey
|
|
func (m *LegacyAminoPubKey) Bytes() []byte {
|
|
return AminoCdc.MustMarshal(m)
|
|
}
|
|
|
|
// VerifyMultisignature implements the multisigtypes.PubKey VerifyMultisignature method.
|
|
// The signatures must be added in an order corresponding to the public keys order in
|
|
// LegacyAminoPubKey. It's OK to have multiple same keys in the multisig - it will increase
|
|
// the power of the owner of that key - in that case the signer will still need to append
|
|
// multiple same signatures in the right order.
|
|
func (m *LegacyAminoPubKey) VerifyMultisignature(getSignBytes multisigtypes.GetSignBytesFunc, sig *signing.MultiSignatureData) error {
|
|
bitarray := sig.BitArray
|
|
sigs := sig.Signatures
|
|
size := bitarray.Count()
|
|
pubKeys := m.GetPubKeys()
|
|
// ensure bit array is the correct size
|
|
if len(pubKeys) != size {
|
|
return fmt.Errorf("bit array size is incorrect, expecting: %d", len(pubKeys))
|
|
}
|
|
// ensure size of signature list
|
|
if len(sigs) < int(m.Threshold) || 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(m.Threshold) {
|
|
return fmt.Errorf("not enough signatures set, have %d, expected %d", bitarray.NumTrueBitsBefore(size), int(m.Threshold))
|
|
}
|
|
// 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 !pubKeys[i].VerifySignature(msg, si.Signature) {
|
|
return fmt.Errorf("unable to verify signature at index %d", i)
|
|
}
|
|
case *signing.MultiSignatureData:
|
|
nestedMultisigPk, ok := pubKeys[i].(multisigtypes.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
|
|
}
|
|
|
|
// VerifySignature implements cryptotypes.PubKey VerifySignature method,
|
|
// it panics because it can't handle MultiSignatureData
|
|
// cf. https://github.com/cosmos/cosmos-sdk/issues/7109#issuecomment-686329936
|
|
func (m *LegacyAminoPubKey) VerifySignature(msg []byte, sig []byte) bool {
|
|
panic("not implemented")
|
|
}
|
|
|
|
// GetPubKeys implements the PubKey.GetPubKeys method
|
|
func (m *LegacyAminoPubKey) GetPubKeys() []cryptotypes.PubKey {
|
|
if m != nil {
|
|
pubKeys := make([]cryptotypes.PubKey, len(m.PubKeys))
|
|
for i := 0; i < len(m.PubKeys); i++ {
|
|
pubKeys[i] = m.PubKeys[i].GetCachedValue().(cryptotypes.PubKey)
|
|
}
|
|
return pubKeys
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Equals returns true if m and other both have the same number of keys, and
|
|
// all constituent keys are the same, and in the same order.
|
|
func (m *LegacyAminoPubKey) Equals(key cryptotypes.PubKey) bool {
|
|
otherKey, ok := key.(multisigtypes.PubKey)
|
|
if !ok {
|
|
return false
|
|
}
|
|
pubKeys := m.GetPubKeys()
|
|
otherPubKeys := otherKey.GetPubKeys()
|
|
if m.GetThreshold() != otherKey.GetThreshold() || len(pubKeys) != len(otherPubKeys) {
|
|
return false
|
|
}
|
|
|
|
for i := 0; i < len(pubKeys); i++ {
|
|
if !pubKeys[i].Equals(otherPubKeys[i]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// GetThreshold implements the PubKey.GetThreshold method
|
|
func (m *LegacyAminoPubKey) GetThreshold() uint {
|
|
return uint(m.Threshold)
|
|
}
|
|
|
|
// Type returns multisig type
|
|
func (m *LegacyAminoPubKey) Type() string {
|
|
return "PubKeyMultisigThreshold"
|
|
}
|
|
|
|
// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces
|
|
func (m *LegacyAminoPubKey) UnpackInterfaces(unpacker types.AnyUnpacker) error {
|
|
for _, any := range m.PubKeys {
|
|
var pk cryptotypes.PubKey
|
|
err := unpacker.UnpackAny(any, &pk)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func packPubKeys(pubKeys []cryptotypes.PubKey) ([]*types.Any, error) {
|
|
anyPubKeys := make([]*types.Any, len(pubKeys))
|
|
|
|
for i := 0; i < len(pubKeys); i++ {
|
|
any, err := types.NewAnyWithValue(pubKeys[i])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
anyPubKeys[i] = any
|
|
}
|
|
return anyPubKeys, nil
|
|
}
|