github.com/cosmos/cosmos-sdk@v0.50.1/crypto/keys/multisig/multisig.go (about) 1 package multisig 2 3 import ( 4 fmt "fmt" 5 6 cmtcrypto "github.com/cometbft/cometbft/crypto" 7 8 "github.com/cosmos/cosmos-sdk/codec/types" 9 cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" 10 multisigtypes "github.com/cosmos/cosmos-sdk/crypto/types/multisig" 11 "github.com/cosmos/cosmos-sdk/types/tx/signing" 12 ) 13 14 var ( 15 _ multisigtypes.PubKey = &LegacyAminoPubKey{} 16 _ types.UnpackInterfacesMessage = &LegacyAminoPubKey{} 17 ) 18 19 // NewLegacyAminoPubKey returns a new LegacyAminoPubKey. 20 // Multisig can be constructed with multiple same keys - it will increase the power of 21 // the owner of that key (he will still need to add multiple signatures in the right order). 22 // Panics if len(pubKeys) < k or 0 >= k. 23 func NewLegacyAminoPubKey(threshold int, pubKeys []cryptotypes.PubKey) *LegacyAminoPubKey { 24 if threshold <= 0 { 25 panic("threshold k of n multisignature: k <= 0") 26 } 27 if len(pubKeys) < threshold { 28 panic("threshold k of n multisignature: len(pubKeys) < k") 29 } 30 anyPubKeys, err := packPubKeys(pubKeys) 31 if err != nil { 32 panic(err) 33 } 34 return &LegacyAminoPubKey{Threshold: uint32(threshold), PubKeys: anyPubKeys} 35 } 36 37 // Address implements cryptotypes.PubKey Address method 38 func (m *LegacyAminoPubKey) Address() cryptotypes.Address { 39 return cmtcrypto.AddressHash(m.Bytes()) 40 } 41 42 // Bytes returns the proto encoded version of the LegacyAminoPubKey 43 func (m *LegacyAminoPubKey) Bytes() []byte { 44 return AminoCdc.MustMarshal(m) 45 } 46 47 // VerifyMultisignature implements the multisigtypes.PubKey VerifyMultisignature method. 48 // The signatures must be added in an order corresponding to the public keys order in 49 // LegacyAminoPubKey. It's OK to have multiple same keys in the multisig - it will increase 50 // the power of the owner of that key - in that case the signer will still need to append 51 // multiple same signatures in the right order. 52 func (m *LegacyAminoPubKey) VerifyMultisignature(getSignBytes multisigtypes.GetSignBytesFunc, sig *signing.MultiSignatureData) error { 53 bitarray := sig.BitArray 54 sigs := sig.Signatures 55 size := bitarray.Count() 56 pubKeys := m.GetPubKeys() 57 // ensure bit array is the correct size 58 if len(pubKeys) != size { 59 return fmt.Errorf("bit array size is incorrect, expecting: %d", len(pubKeys)) 60 } 61 // ensure size of signature list 62 if len(sigs) < int(m.Threshold) || len(sigs) > size { 63 return fmt.Errorf("signature size is incorrect %d", len(sigs)) 64 } 65 // ensure at least k signatures are set 66 if bitarray.NumTrueBitsBefore(size) < int(m.Threshold) { 67 return fmt.Errorf("not enough signatures set, have %d, expected %d", bitarray.NumTrueBitsBefore(size), int(m.Threshold)) 68 } 69 // index in the list of signatures which we are concerned with. 70 sigIndex := 0 71 for i := 0; i < size; i++ { 72 if bitarray.GetIndex(i) { 73 si := sig.Signatures[sigIndex] 74 switch si := si.(type) { 75 case *signing.SingleSignatureData: 76 msg, err := getSignBytes(si.SignMode) 77 if err != nil { 78 return err 79 } 80 if !pubKeys[i].VerifySignature(msg, si.Signature) { 81 return fmt.Errorf("unable to verify signature at index %d", i) 82 } 83 case *signing.MultiSignatureData: 84 nestedMultisigPk, ok := pubKeys[i].(multisigtypes.PubKey) 85 if !ok { 86 return fmt.Errorf("unable to parse pubkey of index %d", i) 87 } 88 if err := nestedMultisigPk.VerifyMultisignature(getSignBytes, si); err != nil { 89 return err 90 } 91 default: 92 return fmt.Errorf("improper signature data type for index %d", sigIndex) 93 } 94 sigIndex++ 95 } 96 } 97 return nil 98 } 99 100 // VerifySignature implements cryptotypes.PubKey VerifySignature method, 101 // it panics because it can't handle MultiSignatureData 102 // cf. https://github.com/cosmos/cosmos-sdk/issues/7109#issuecomment-686329936 103 func (m *LegacyAminoPubKey) VerifySignature(msg, sig []byte) bool { 104 panic("not implemented") 105 } 106 107 // GetPubKeys implements the PubKey.GetPubKeys method 108 func (m *LegacyAminoPubKey) GetPubKeys() []cryptotypes.PubKey { 109 if m != nil { 110 pubKeys := make([]cryptotypes.PubKey, len(m.PubKeys)) 111 for i := 0; i < len(m.PubKeys); i++ { 112 pubKeys[i] = m.PubKeys[i].GetCachedValue().(cryptotypes.PubKey) 113 } 114 return pubKeys 115 } 116 117 return nil 118 } 119 120 // Equals returns true if m and other both have the same number of keys, and 121 // all constituent keys are the same, and in the same order. 122 func (m *LegacyAminoPubKey) Equals(key cryptotypes.PubKey) bool { 123 otherKey, ok := key.(multisigtypes.PubKey) 124 if !ok { 125 return false 126 } 127 pubKeys := m.GetPubKeys() 128 otherPubKeys := otherKey.GetPubKeys() 129 if m.GetThreshold() != otherKey.GetThreshold() || len(pubKeys) != len(otherPubKeys) { 130 return false 131 } 132 133 for i := 0; i < len(pubKeys); i++ { 134 if !pubKeys[i].Equals(otherPubKeys[i]) { 135 return false 136 } 137 } 138 return true 139 } 140 141 // GetThreshold implements the PubKey.GetThreshold method 142 func (m *LegacyAminoPubKey) GetThreshold() uint { 143 return uint(m.Threshold) 144 } 145 146 // Type returns multisig type 147 func (m *LegacyAminoPubKey) Type() string { 148 return "PubKeyMultisigThreshold" 149 } 150 151 // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces 152 func (m *LegacyAminoPubKey) UnpackInterfaces(unpacker types.AnyUnpacker) error { 153 for _, any := range m.PubKeys { 154 var pk cryptotypes.PubKey 155 err := unpacker.UnpackAny(any, &pk) 156 if err != nil { 157 return err 158 } 159 } 160 return nil 161 } 162 163 func packPubKeys(pubKeys []cryptotypes.PubKey) ([]*types.Any, error) { 164 anyPubKeys := make([]*types.Any, len(pubKeys)) 165 166 for i := 0; i < len(pubKeys); i++ { 167 any, err := types.NewAnyWithValue(pubKeys[i]) 168 if err != nil { 169 return nil, err 170 } 171 anyPubKeys[i] = any 172 } 173 return anyPubKeys, nil 174 }