github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/crypto/multisig/threshold_pubkey.go (about)

     1  package multisig
     2  
     3  import (
     4  	"github.com/gnolang/gno/tm2/pkg/amino"
     5  	"github.com/gnolang/gno/tm2/pkg/crypto"
     6  )
     7  
     8  // PubKeyMultisigThreshold implements a K of N threshold multisig.
     9  type PubKeyMultisigThreshold struct {
    10  	K       uint            `json:"threshold"`
    11  	PubKeys []crypto.PubKey `json:"pubkeys"`
    12  }
    13  
    14  var _ crypto.PubKey = PubKeyMultisigThreshold{}
    15  
    16  // NewPubKeyMultisigThreshold returns a new PubKeyMultisigThreshold.
    17  // Panics if len(pubkeys) < k or 0 >= k.
    18  func NewPubKeyMultisigThreshold(k int, pubkeys []crypto.PubKey) crypto.PubKey {
    19  	if k <= 0 {
    20  		panic("threshold k of n multisignature: k <= 0")
    21  	}
    22  	if len(pubkeys) < k {
    23  		panic("threshold k of n multisignature: len(pubkeys) < k")
    24  	}
    25  	for _, pubkey := range pubkeys {
    26  		if pubkey == nil {
    27  			panic("nil pubkey")
    28  		}
    29  	}
    30  	return PubKeyMultisigThreshold{uint(k), pubkeys}
    31  }
    32  
    33  func (pk PubKeyMultisigThreshold) String() string {
    34  	panic("not yet implemented")
    35  }
    36  
    37  // VerifyBytes expects sig to be an amino encoded version of a MultiSignature.
    38  // Returns true iff the multisignature contains k or more signatures
    39  // for the correct corresponding keys,
    40  // and all signatures are valid. (Not just k of the signatures)
    41  // The multisig uses a bitarray, so multiple signatures for the same key is not
    42  // a concern.
    43  func (pk PubKeyMultisigThreshold) VerifyBytes(msg []byte, marshalledSig []byte) bool {
    44  	var sig Multisignature
    45  	err := amino.Unmarshal(marshalledSig, &sig)
    46  	if err != nil {
    47  		return false
    48  	}
    49  	size := sig.BitArray.Size()
    50  	// ensure bit array is the correct size
    51  	if len(pk.PubKeys) != size {
    52  		return false
    53  	}
    54  	// ensure size of signature list
    55  	if len(sig.Sigs) < int(pk.K) || len(sig.Sigs) > size {
    56  		return false
    57  	}
    58  	// ensure at least k signatures are set
    59  	if sig.BitArray.NumTrueBitsBefore(size) < int(pk.K) {
    60  		return false
    61  	}
    62  	// index in the list of signatures which we are concerned with.
    63  	sigIndex := 0
    64  	for i := 0; i < size; i++ {
    65  		if sig.BitArray.GetIndex(i) {
    66  			if !pk.PubKeys[i].VerifyBytes(msg, sig.Sigs[sigIndex]) {
    67  				return false
    68  			}
    69  			sigIndex++
    70  		}
    71  	}
    72  	return true
    73  }
    74  
    75  // Bytes returns the amino encoded version of the PubKeyMultisigThreshold
    76  func (pk PubKeyMultisigThreshold) Bytes() []byte {
    77  	return amino.MustMarshalAny(pk)
    78  }
    79  
    80  // Address returns tmhash(PubKeyMultisigThreshold.Bytes())
    81  func (pk PubKeyMultisigThreshold) Address() crypto.Address {
    82  	return crypto.AddressFromPreimage(pk.Bytes())
    83  }
    84  
    85  // Equals returns true iff pk and other both have the same number of keys, and
    86  // all constituent keys are the same, and in the same order.
    87  func (pk PubKeyMultisigThreshold) Equals(other crypto.PubKey) bool {
    88  	otherKey, sameType := other.(PubKeyMultisigThreshold)
    89  	if !sameType {
    90  		return false
    91  	}
    92  	if pk.K != otherKey.K || len(pk.PubKeys) != len(otherKey.PubKeys) {
    93  		return false
    94  	}
    95  	for i := 0; i < len(pk.PubKeys); i++ {
    96  		if !pk.PubKeys[i].Equals(otherKey.PubKeys[i]) {
    97  			return false
    98  		}
    99  	}
   100  	return true
   101  }