package multisig import ( fmt "fmt" tmcrypto "github.com/tendermint/tendermint/crypto" proto "github.com/gogo/protobuf/proto" "github.com/cosmos/cosmos-sdk/codec/types" crypto "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. // Panics if len(pubKeys) < k or 0 >= k. func NewLegacyAminoPubKey(k int, pubKeys []tmcrypto.PubKey) *LegacyAminoPubKey { if k <= 0 { panic("threshold k of n multisignature: k <= 0") } if len(pubKeys) < k { panic("threshold k of n multisignature: len(pubKeys) < k") } anyPubKeys, err := packPubKeys(pubKeys) if err != nil { panic(err) } return &LegacyAminoPubKey{Threshold: uint32(k), PubKeys: anyPubKeys} } // Address implements crypto.PubKey Address method func (m *LegacyAminoPubKey) Address() tmcrypto.Address { return tmcrypto.AddressHash(m.Bytes()) } // Bytes returns the proto encoded version of the LegacyAminoPubKey func (m *LegacyAminoPubKey) Bytes() []byte { return AminoCdc.MustMarshalBinaryBare(m) } // VerifyMultisignature implements the multisigtypes.PubKey VerifyMultisignature method 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 %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("minimum number of signatures not 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 crypto.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() []tmcrypto.PubKey { if m != nil { pubKeys := make([]tmcrypto.PubKey, len(m.PubKeys)) for i := 0; i < len(m.PubKeys); i++ { pubKeys[i] = m.PubKeys[i].GetCachedValue().(tmcrypto.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 tmcrypto.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 crypto.PubKey err := unpacker.UnpackAny(any, &pk) if err != nil { return err } } return nil } func packPubKeys(pubKeys []tmcrypto.PubKey) ([]*types.Any, error) { anyPubKeys := make([]*types.Any, len(pubKeys)) for i := 0; i < len(pubKeys); i++ { any, err := types.NewAnyWithValue(pubKeys[i].(proto.Message)) if err != nil { return nil, err } anyPubKeys[i] = any } return anyPubKeys, nil }