github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/docs/architecture/adr-019-multisigs.md (about)

     1  # ADR 019: Encoding standard for Multisignatures
     2  
     3  ## Changelog
     4  
     5  06-08-2018: Minor updates
     6  
     7  27-07-2018: Update draft to use amino encoding
     8  
     9  11-07-2018: Initial Draft
    10  
    11  5-26-2021: Multisigs were moved into the Cosmos-sdk
    12  
    13  ## Context
    14  
    15  Multisignatures, or technically _Accountable Subgroup Multisignatures_ (ASM),
    16  are signature schemes which enable any subgroup of a set of signers to sign any message,
    17  and reveal to the verifier exactly who the signers were.
    18  This allows for complex conditionals of when to validate a signature.
    19  
    20  Suppose the set of signers is of size _n_.
    21  If we validate a signature if any subgroup of size _k_ signs a message,
    22  this becomes what is commonly reffered to as a _k of n multisig_ in Bitcoin.
    23  
    24  This ADR specifies the encoding standard for general accountable subgroup multisignatures,
    25  k of n accountable subgroup multisignatures, and its weighted variant.
    26  
    27  In the future, we can also allow for more complex conditionals on the accountable subgroup.
    28  
    29  ## Proposed Solution
    30  
    31  ### New structs
    32  
    33  Every ASM will then have its own struct, implementing the crypto.Pubkey interface.
    34  
    35  This ADR assumes that [replacing crypto.Signature with []bytes](https://github.com/tendermint/tendermint/issues/1957) has been accepted.
    36  
    37  #### K of N threshold signature
    38  
    39  The pubkey is the following struct:
    40  
    41  ```golang
    42  type ThresholdMultiSignaturePubKey struct { // K of N threshold multisig
    43  	K       uint               `json:"threshold"`
    44  	Pubkeys []crypto.Pubkey    `json:"pubkeys"`
    45  }
    46  ```
    47  
    48  We will derive N from the length of pubkeys. (For spatial efficiency in encoding)
    49  
    50  `Verify` will expect an `[]byte` encoded version of the Multisignature.
    51  (Multisignature is described in the next section)
    52  The multisignature will be rejected if the bitmap has less than k indices,
    53  or if any signature at any of the k indices is not a valid signature from
    54  the kth public key on the message.
    55  (If more than k signatures are included, all must be valid)
    56  
    57  `Bytes` will be the amino encoded version of the pubkey.
    58  
    59  Address will be `Hash(amino_encoded_pubkey)`
    60  
    61  The reason this doesn't use `log_8(n)` bytes per signer is because that heavily optimizes for the case where a very small number of signers are required.
    62  e.g. for `n` of size `24`, that would only be more space efficient for `k < 3`.
    63  This seems less likely, and that it should not be the case optimized for.
    64  
    65  #### Weighted threshold signature
    66  
    67  The pubkey is the following struct:
    68  
    69  ```golang
    70  type WeightedThresholdMultiSignaturePubKey struct {
    71  	Weights []uint             `json:"weights"`
    72  	Threshold uint             `json:"threshold"`
    73  	Pubkeys []crypto.Pubkey    `json:"pubkeys"`
    74  }
    75  ```
    76  
    77  Weights and Pubkeys must be of the same length.
    78  Everything else proceeds identically to the K of N multisig,
    79  except the multisig fails if the sum of the weights is less than the threshold.
    80  
    81  #### Multisignature
    82  
    83  The inter-mediate phase of the signatures (as it accrues more signatures) will be the following struct:
    84  
    85  ```golang
    86  type Multisignature struct {
    87  	BitArray    CryptoBitArray // Documented later
    88  	Sigs        [][]byte
    89  ```
    90  
    91  It is important to recall that each private key will output a signature on the provided message itself.
    92  So no signing algorithm ever outputs the multisignature.
    93  The UI will take a signature, cast into a multisignature, and then keep adding
    94  new signatures into it, and when done marshal into `[]byte`.
    95  This will require the following helper methods:
    96  
    97  ```golang
    98  func SigToMultisig(sig []byte, n int)
    99  func GetIndex(pk crypto.Pubkey, []crypto.Pubkey)
   100  func AddSignature(sig Signature, index int, multiSig *Multisignature)
   101  ```
   102  
   103  The multisignature will be converted to an `[]byte` using amino.MarshalBinaryBare. \*
   104  
   105  #### Bit Array
   106  
   107  We would be using a new implementation of a bitarray. The struct it would be encoded/decoded from is
   108  
   109  ```golang
   110  type CryptoBitArray struct {
   111  	ExtraBitsStored  byte      `json:"extra_bits"` // The number of extra bits in elems.
   112  	Elems            []byte    `json:"elems"`
   113  }
   114  ```
   115  
   116  The reason for not using the BitArray currently implemented in `libs/common/bit_array.go`
   117  is that it is less space efficient, due to a space / time trade-off.
   118  Evidence for this is outlined in [this issue](https://github.com/tendermint/tendermint/issues/2077).
   119  
   120  In the multisig, we will not be performing arithmetic operations,
   121  so there is no performance increase with the current implementation,
   122  and just loss of spatial efficiency.
   123  Implementing this new bit array with `[]byte` _should_ be simple, as no
   124  arithmetic operations between bit arrays are required, and save a couple of bytes.
   125  (Explained in that same issue)
   126  
   127  When this bit array encoded, the number of elements is encoded due to amino.
   128  However we may be encoding a full byte for what we actually only need 1-7 bits for.
   129  We store that difference in ExtraBitsStored.
   130  This allows for us to have an unbounded number of signers, and is more space efficient than what is currently used in `libs/common`.
   131  Again the implementation of this space saving feature is straight forward.
   132  
   133  ### Encoding the structs
   134  
   135  We will use straight forward amino encoding. This is chosen for ease of compatibility in other languages.
   136  
   137  ### Future points of discussion
   138  
   139  If desired, we can use ed25519 batch verification for all ed25519 keys.
   140  This is a future point of discussion, but would be backwards compatible as this information won't need to be marshalled.
   141  (There may even be cofactor concerns without ristretto)
   142  Aggregation of pubkeys / sigs in Schnorr sigs / BLS sigs is not backwards compatible, and would need to be a new ASM type.
   143  
   144  ## Status
   145  
   146  Implemented (moved to cosmos-sdk)
   147  
   148  ## Consequences
   149  
   150  ### Positive
   151  
   152  - Supports multisignatures, in a way that won't require any special cases in our downstream verification code.
   153  - Easy to serialize / deserialize
   154  - Unbounded number of signers
   155  
   156  ### Negative
   157  
   158  - Larger codebase, however this should reside in a subfolder of tendermint/crypto, as it provides no new interfaces. (Ref #https://github.com/tendermint/go-crypto/issues/136)
   159  - Space inefficient due to utilization of amino encoding
   160  - Suggested implementation requires a new struct for every ASM.
   161  
   162  ### Neutral