mirror of
https://github.com/fluencelabs/tendermint
synced 2025-05-04 11:02:13 +00:00
* lint docs with write-good, stop-words * remove package-lock.json * update changelog * fix wrong paragraph formatting * fix some docs formatting * fix docs format * fix abci spec format
161 lines
5.9 KiB
Markdown
161 lines
5.9 KiB
Markdown
# ADR 019: Encoding standard for Multisignatures
|
|
|
|
## Changelog
|
|
|
|
06-08-2018: Minor updates
|
|
|
|
27-07-2018: Update draft to use amino encoding
|
|
|
|
11-07-2018: Initial Draft
|
|
|
|
## Context
|
|
|
|
Multisignatures, or technically _Accountable Subgroup Multisignatures_ (ASM),
|
|
are signature schemes which enable any subgroup of a set of signers to sign any message,
|
|
and reveal to the verifier exactly who the signers were.
|
|
This allows for complex conditionals of when to validate a signature.
|
|
|
|
Suppose the set of signers is of size _n_.
|
|
If we validate a signature if any subgroup of size _k_ signs a message,
|
|
this becomes what is commonly reffered to as a _k of n multisig_ in Bitcoin.
|
|
|
|
This ADR specifies the encoding standard for general accountable subgroup multisignatures,
|
|
k of n accountable subgroup multisignatures, and its weighted variant.
|
|
|
|
In the future, we can also allow for more complex conditionals on the accountable subgroup.
|
|
|
|
## Proposed Solution
|
|
|
|
### New structs
|
|
|
|
Every ASM will then have its own struct, implementing the crypto.Pubkey interface.
|
|
|
|
This ADR assumes that [replacing crypto.Signature with []bytes](https://github.com/tendermint/tendermint/issues/1957) has been accepted.
|
|
|
|
#### K of N threshold signature
|
|
|
|
The pubkey is the following struct:
|
|
|
|
```golang
|
|
type ThresholdMultiSignaturePubKey struct { // K of N threshold multisig
|
|
K uint `json:"threshold"`
|
|
Pubkeys []crypto.Pubkey `json:"pubkeys"`
|
|
}
|
|
```
|
|
|
|
We will derive N from the length of pubkeys. (For spatial efficiency in encoding)
|
|
|
|
`Verify` will expect an `[]byte` encoded version of the Multisignature.
|
|
(Multisignature is described in the next section)
|
|
The multisignature will be rejected if the bitmap has less than k indices,
|
|
or if any signature at any of the k indices is not a valid signature from
|
|
the kth public key on the message.
|
|
(If more than k signatures are included, all must be valid)
|
|
|
|
`Bytes` will be the amino encoded version of the pubkey.
|
|
|
|
Address will be `Hash(amino_encoded_pubkey)`
|
|
|
|
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.
|
|
e.g. for `n` of size `24`, that would only be more space efficient for `k < 3`.
|
|
This seems less likely, and that it should not be the case optimized for.
|
|
|
|
#### Weighted threshold signature
|
|
|
|
The pubkey is the following struct:
|
|
|
|
```golang
|
|
type WeightedThresholdMultiSignaturePubKey struct {
|
|
Weights []uint `json:"weights"`
|
|
Threshold uint `json:"threshold"`
|
|
Pubkeys []crypto.Pubkey `json:"pubkeys"`
|
|
}
|
|
```
|
|
|
|
Weights and Pubkeys must be of the same length.
|
|
Everything else proceeds identically to the K of N multisig,
|
|
except the multisig fails if the sum of the weights is less than the threshold.
|
|
|
|
#### Multisignature
|
|
|
|
The inter-mediate phase of the signatures (as it accrues more signatures) will be the following struct:
|
|
|
|
```golang
|
|
type Multisignature struct {
|
|
BitArray CryptoBitArray // Documented later
|
|
Sigs [][]byte
|
|
```
|
|
|
|
It is important to recall that each private key will output a signature on the provided message itself.
|
|
So no signing algorithm ever outputs the multisignature.
|
|
The UI will take a signature, cast into a multisignature, and then keep adding
|
|
new signatures into it, and when done marshal into `[]byte`.
|
|
This will require the following helper methods:
|
|
|
|
```golang
|
|
func SigToMultisig(sig []byte, n int)
|
|
func GetIndex(pk crypto.Pubkey, []crypto.Pubkey)
|
|
func AddSignature(sig Signature, index int, multiSig *Multisignature)
|
|
```
|
|
|
|
The multisignature will be converted to an `[]byte` using amino.MarshalBinaryBare. \*
|
|
|
|
#### Bit Array
|
|
|
|
We would be using a new implementation of a bitarray. The struct it would be encoded/decoded from is
|
|
|
|
```golang
|
|
type CryptoBitArray struct {
|
|
ExtraBitsStored byte `json:"extra_bits"` // The number of extra bits in elems.
|
|
Elems []byte `json:"elems"`
|
|
}
|
|
```
|
|
|
|
The reason for not using the BitArray currently implemented in `libs/common/bit_array.go`
|
|
is that it is less space efficient, due to a space / time trade-off.
|
|
Evidence for this is outlined in [this issue](https://github.com/tendermint/tendermint/issues/2077).
|
|
|
|
In the multisig, we will not be performing arithmetic operations,
|
|
so there is no performance increase with the current implementation,
|
|
and just loss of spatial efficiency.
|
|
Implementing this new bit array with `[]byte` _should_ be simple, as no
|
|
arithmetic operations between bit arrays are required, and save a couple of bytes.
|
|
(Explained in that same issue)
|
|
|
|
When this bit array encoded, the number of elements is encoded due to amino.
|
|
However we may be encoding a full byte for what we actually only need 1-7 bits for.
|
|
We store that difference in ExtraBitsStored.
|
|
This allows for us to have an unbounded number of signers, and is more space efficient than what is currently used in `libs/common`.
|
|
Again the implementation of this space saving feature is straight forward.
|
|
|
|
### Encoding the structs
|
|
|
|
We will use straight forward amino encoding. This is chosen for ease of compatibility in other languages.
|
|
|
|
### Future points of discussion
|
|
|
|
If desired, we can use ed25519 batch verification for all ed25519 keys.
|
|
This is a future point of discussion, but would be backwards compatible as this information won't need to be marshalled.
|
|
(There may even be cofactor concerns without ristretto)
|
|
Aggregation of pubkeys / sigs in Schnorr sigs / BLS sigs is not backwards compatible, and would need to be a new ASM type.
|
|
|
|
## Status
|
|
|
|
Proposed.
|
|
|
|
## Consequences
|
|
|
|
### Positive
|
|
|
|
- Supports multisignatures, in a way that won't require any special cases in our downstream verification code.
|
|
- Easy to serialize / deserialize
|
|
- Unbounded number of signers
|
|
|
|
### Negative
|
|
|
|
- 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)
|
|
- Space inefficient due to utilization of amino encoding
|
|
- Suggested implementation requires a new struct for every ASM.
|
|
|
|
### Neutral
|