package multisig import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/tmhash" ) // ThresholdMultiSignaturePubKey implements a K of N threshold multisig. type ThresholdMultiSignaturePubKey struct { K uint `json:"threshold"` Pubkeys []crypto.PubKey `json:"pubkeys"` } var _ crypto.PubKey = &ThresholdMultiSignaturePubKey{} // NewThresholdMultiSignaturePubKey returns a new ThresholdMultiSignaturePubKey. // Panics if len(pubkeys) < k or 0 >= k. func NewThresholdMultiSignaturePubKey(k int, pubkeys []crypto.PubKey) crypto.PubKey { if k <= 0 { panic("threshold k of n multisignature: k <= 0") } if len(pubkeys) < k { panic("threshold k of n multisignature: len(pubkeys) < k") } return &ThresholdMultiSignaturePubKey{uint(k), pubkeys} } // VerifyBytes expects sig to be an amino encoded version of a MultiSignature. // Returns true iff the multisignature contains k or more signatures // for the correct corresponding keys, // and all signatures are valid. (Not just k of the signatures) // The multisig uses a bitarray, so multiple signatures for the same key is not // a concern. func (pk *ThresholdMultiSignaturePubKey) VerifyBytes(msg []byte, marshalledSig []byte) bool { var sig *Multisignature err := cdc.UnmarshalBinaryBare(marshalledSig, &sig) if err != nil { return false } size := sig.BitArray.Size() // ensure bit array is the correct size if len(pk.Pubkeys) != size { return false } // ensure size of signature list if len(sig.Sigs) < int(pk.K) || len(sig.Sigs) > size { return false } // ensure at least k signatures are set if sig.BitArray.NumOfTrueBitsBefore(size) < int(pk.K) { return false } // index in the list of signatures which we are concerned with. sigIndex := 0 for i := 0; i < size; i++ { if sig.BitArray.GetIndex(i) { if !pk.Pubkeys[i].VerifyBytes(msg, sig.Sigs[sigIndex]) { return false } sigIndex++ } } return true } // Bytes returns the amino encoded version of the ThresholdMultiSignaturePubKey func (pk *ThresholdMultiSignaturePubKey) Bytes() []byte { return cdc.MustMarshalBinaryBare(pk) } // Address returns tmhash(ThresholdMultiSignaturePubKey.Bytes()) func (pk *ThresholdMultiSignaturePubKey) Address() crypto.Address { return crypto.Address(tmhash.Sum(pk.Bytes())) } // Equals returns true iff pk and other both have the same number of keys, and // all constituent keys are the same, and in the same order. func (pk *ThresholdMultiSignaturePubKey) Equals(other crypto.PubKey) bool { otherKey, sameType := other.(*ThresholdMultiSignaturePubKey) if !sameType { return false } if pk.K != otherKey.K || len(pk.Pubkeys) != len(otherKey.Pubkeys) { return false } for i := 0; i < len(pk.Pubkeys); i++ { if !pk.Pubkeys[i].Equals(otherKey.Pubkeys[i]) { return false } } return true }