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

     1  package multisig
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/gnolang/gno/tm2/pkg/amino"
     8  	"github.com/gnolang/gno/tm2/pkg/crypto"
     9  	"github.com/gnolang/gno/tm2/pkg/crypto/multisig/bitarray"
    10  )
    11  
    12  // Multisignature is used to represent the signature object used in the multisigs.
    13  // Sigs is a list of signatures, sorted by corresponding index.
    14  type Multisignature struct {
    15  	BitArray *bitarray.CompactBitArray
    16  	Sigs     [][]byte
    17  }
    18  
    19  // NewMultisig returns a new Multisignature of size n.
    20  func NewMultisig(n int) *Multisignature {
    21  	// Default the signature list to have a capacity of two, since we can
    22  	// expect that most multisigs will require multiple signers.
    23  	return &Multisignature{bitarray.NewCompactBitArray(n), make([][]byte, 0, 2)}
    24  }
    25  
    26  // GetIndex returns the index of pk in keys. Returns -1 if not found
    27  func getIndex(pk crypto.PubKey, keys []crypto.PubKey) int {
    28  	for i := 0; i < len(keys); i++ {
    29  		if pk.Equals(keys[i]) {
    30  			return i
    31  		}
    32  	}
    33  	return -1
    34  }
    35  
    36  // AddSignature adds a signature to the multisig, at the corresponding index.
    37  // If the signature already exists, replace it.
    38  func (mSig *Multisignature) AddSignature(sig []byte, index int) {
    39  	newSigIndex := mSig.BitArray.NumTrueBitsBefore(index)
    40  	// Signature already exists, just replace the value there
    41  	if mSig.BitArray.GetIndex(index) {
    42  		mSig.Sigs[newSigIndex] = sig
    43  		return
    44  	}
    45  	mSig.BitArray.SetIndex(index, true)
    46  	// Optimization if the index is the greatest index
    47  	if newSigIndex == len(mSig.Sigs) {
    48  		mSig.Sigs = append(mSig.Sigs, sig)
    49  		return
    50  	}
    51  	// Expand slice by one with a dummy element, move all elements after i
    52  	// over by one, then place the new signature in that gap.
    53  	mSig.Sigs = append(mSig.Sigs, make([]byte, 0))
    54  	copy(mSig.Sigs[newSigIndex+1:], mSig.Sigs[newSigIndex:])
    55  	mSig.Sigs[newSigIndex] = sig
    56  }
    57  
    58  // AddSignatureFromPubKey adds a signature to the multisig, at the index in
    59  // keys corresponding to the provided pubkey.
    60  func (mSig *Multisignature) AddSignatureFromPubKey(sig []byte, pubkey crypto.PubKey, keys []crypto.PubKey) error {
    61  	index := getIndex(pubkey, keys)
    62  	if index == -1 {
    63  		keysStr := make([]string, len(keys))
    64  		for i, k := range keys {
    65  			keysStr[i] = fmt.Sprintf("%X", k.Bytes())
    66  		}
    67  
    68  		return fmt.Errorf("provided key %X doesn't exist in pubkeys: \n%s", pubkey.Bytes(), strings.Join(keysStr, "\n"))
    69  	}
    70  
    71  	mSig.AddSignature(sig, index)
    72  	return nil
    73  }
    74  
    75  // Marshal the multisignature with amino
    76  func (mSig *Multisignature) Marshal() []byte {
    77  	return amino.MustMarshal(mSig)
    78  }