cosmos-sdk/crypto/keys/multisig/multisig.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
}