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  }