Name, Doc and Serialization Tweaks, and a Test Plan

This commit is contained in:
teor 2021-05-13 10:36:58 +10:00 committed by Deirdre Connolly
parent b0c5512a71
commit 8bb106a53d
1 changed files with 67 additions and 25 deletions

View File

@ -20,6 +20,7 @@ Assuming all participants have a FROST library available we need to define messa
We propose a message separated in 2 parts, a header and a payload: We propose a message separated in 2 parts, a header and a payload:
```rust ```rust
/// The data required to serialize a frost message.
struct Message { struct Message {
header: Header, header: Header,
payload: Payload, payload: Payload,
@ -29,8 +30,10 @@ struct Message {
`Header` will look as follows: `Header` will look as follows:
```rust ```rust
/// The data required to serialize the common header fields for every message.
///
/// Note: the `msg_type` is derived from the `payload` enum variant.
struct Header { struct Header {
msg_type: MsgType,
version: MsgVersion, version: MsgVersion,
sender: ParticipantID, sender: ParticipantID,
receiver: ParticipantID, receiver: ParticipantID,
@ -40,6 +43,7 @@ struct Header {
While `Payload` will be defined as: While `Payload` will be defined as:
```rust ```rust
/// The data required to serialize the payload for a message.
enum Payload { enum Payload {
DealerBroadcast(MsgDealerBroadcast), DealerBroadcast(MsgDealerBroadcast),
Commitments(MsgCommitments), Commitments(MsgCommitments),
@ -60,6 +64,7 @@ Here we explore in detail the header types and all the message payloads.
Fields of the header define new types. Proposed implementation for them is as follows: Fields of the header define new types. Proposed implementation for them is as follows:
```rust ```rust
/// The numeric values used to identify each `Payload` variant during serialization.
#[repr(u8)] #[repr(u8)]
#[non_exhaustive] #[non_exhaustive]
enum MsgType { enum MsgType {
@ -70,9 +75,11 @@ enum MsgType {
FinalSignature, FinalSignature,
} }
/// The numeric values used to identify the protocol version during serialization.
struct MsgVersion(u8); struct MsgVersion(u8);
struct ParticipantID(u8); /// The numeric values used to identify each participant during serialization.
struct ParticipantId(u8);
``` ```
### Payloads ### Payloads
@ -80,13 +87,15 @@ struct ParticipantID(u8);
Each payload defines a new message: Each payload defines a new message:
```rust ```rust
/// The data required to serialize `frost::SharePackage`.
///
/// Dealer must send this message with initial data to each participant involved. /// Dealer must send this message with initial data to each participant involved.
/// With this, the participant should be able to build a `SharePackage` and use /// With this, the participant should be able to build a `SharePackage` and use
/// the `sign()` function. /// the `sign()` function.
/// ///
/// Note: `frost::SharePackage.public` can be calculated from `secret_share`. /// Note: `frost::SharePackage.public` can be calculated from `secret_share`.
struct MsgDealerBroadcast { struct messages::SharePackage {
/// This participant's secret key share: `frost::Share.value`. /// This participant's secret key share: `frost::SharePackage.share.value`.
secret_share: frost::Scalar, secret_share: frost::Scalar,
/// Commitment for the signer as a single jubjub::AffinePoint. /// Commitment for the signer as a single jubjub::AffinePoint.
/// A set of commitments to the coefficients (which themselves are scalars) /// A set of commitments to the coefficients (which themselves are scalars)
@ -97,25 +106,21 @@ struct MsgDealerBroadcast {
group_public: jubjub::AffinePoint, group_public: jubjub::AffinePoint,
} }
/// Each signer participant send to the aggregator the 2 points /// The data required to serialize `frost::SigningCommitments`.
/// needed for commitment building. ///
struct MsgCommitments {
/// A commitment to a single signature by this signer:
/// `frost::SigningPackage.signing_commitments`
signing_commitments: SigningCommitments,
}
/// A signing commitment from the first round of the signing protocol. /// A signing commitment from the first round of the signing protocol.
struct SigningCommitments { struct messages::SigningCommitments {
/// The hiding point: `frost::SigningCommitments.hiding` /// The hiding point: `frost::SigningCommitments.hiding`
hiding: jubjub::AffinePoint, hiding: jubjub::AffinePoint,
/// The binding point: `frost::SigningCommitments.binding` /// The binding point: `frost::SigningCommitments.binding`
binding: jubjub::AffinePoint, binding: jubjub::AffinePoint,
} }
/// The data required to serialize `frost::SigningPackage`.
///
/// The aggregator decides what message is going to be signed and /// The aggregator decides what message is going to be signed and
/// sends it to each participant with all the commitments collected. /// sends it to each participant with all the commitments collected.
struct MsgSigningPackage { struct messages::SigningPackage {
/// The message to be signed: `frost::SigningPackage.message` /// The message to be signed: `frost::SigningPackage.message`
message: Vec<u8>, message: Vec<u8>,
/// The collected commitments for each signer as a hashmap of /// The collected commitments for each signer as a hashmap of
@ -125,16 +130,20 @@ struct MsgSigningPackage {
signing_commitments: HashMap<ParticipantID, SigningCommitments>, signing_commitments: HashMap<ParticipantID, SigningCommitments>,
} }
/// The data required to serialize `frost::SignatureShare`.
///
/// Each signer sends their signatures to the aggregator who is going to collect them /// Each signer sends their signatures to the aggregator who is going to collect them
/// and generate a final spend signature. /// and generate a final spend signature.
struct MsgSignatureShare { struct messages::SignatureShare {
/// This participant's signature over the message: /// This participant's signature over the message:
/// `frost::SignatureShare.signature` /// `frost::SignatureShare.signature`
signature: frost::Scalar, signature: frost::Scalar,
} }
/// The data required to serialize a successful output from `frost::aggregate()`.
///
/// The final signature is broadcasted by the aggregator to any participant. /// The final signature is broadcasted by the aggregator to any participant.
struct MsgFinalSignature { struct messages::AggregateSignature {
/// The aggregated group commitment: `Signature<SpendAuth>.r_bytes` returned by `frost::aggregate` /// The aggregated group commitment: `Signature<SpendAuth>.r_bytes` returned by `frost::aggregate`
group_commitment: jubjub::AffinePoint, group_commitment: jubjub::AffinePoint,
/// A plain Schnorr signature created by summing all the signature shares: /// A plain Schnorr signature created by summing all the signature shares:
@ -200,14 +209,19 @@ The following rules must be implemented:
#### Header #### Header
- `msg_type` must be a known `MsgType` value.
- `version` must be a supported version.
- `sender` and `receiver` can't be the same. - `sender` and `receiver` can't be the same.
- `sender` and `receiver` must be less than the maximum number of participants.
#### Payloads #### Payloads
- Each jubjub type must be validated during deserialization. - Each jubjub type must be validated during deserialization.
- `share_commitments`: For each round, the maximum number of participants is set by the length of `share_commitments`. - `share_commitments`: For each round, the maximum number of participants is set by the length of `share_commitments`.
- `signing_commitments`: - `signing_commitments`:
- Signing packages that contain duplicate or missing `ParticipantID`s are invalid - Signing packages that contain duplicate `ParticipantID`s are invalid
- Signing packages that contain missing `ParticipantID`s are invalid
- TODO: check if missing participants are allowed
- The length of `signing_commitments` must be less than or equal to the length of the `share_commitments` for this round. - The length of `signing_commitments` must be less than or equal to the length of the `share_commitments` for this round.
- `message`: signed messages have a protocol-specific length limit. For Zcash, that limit is the maximum network protocol message length: `2^21` bytes (2 MB). - `message`: signed messages have a protocol-specific length limit. For Zcash, that limit is the maximum network protocol message length: `2^21` bytes (2 MB).
@ -239,7 +253,7 @@ Bytes | Field name | Data type
#### `Scalar` #### `Scalar`
`Scalar` is a an alias for `jubjub::Fr`. We use `Scalar::to_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L252 `Scalar` is a an alias for `jubjub::Fr`. We use `Scalar::to_bytes` and `Scalar::from_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/bls12_381/blob/main/src/scalar.rs#L252
#### `AffinePoint` #### `AffinePoint`
@ -250,9 +264,9 @@ Conversion from one type to the other is trivial:
https://docs.rs/jubjub/0.6.0/jubjub/struct.AffinePoint.html#impl-From%3CExtendedPoint%3E https://docs.rs/jubjub/0.6.0/jubjub/struct.AffinePoint.html#impl-From%3CExtendedPoint%3E
https://docs.rs/jubjub/0.6.0/jubjub/struct.ExtendedPoint.html#impl-From%3CAffinePoint%3E https://docs.rs/jubjub/0.6.0/jubjub/struct.ExtendedPoint.html#impl-From%3CAffinePoint%3E
We use `AffinePoint::to_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L443 We use `AffinePoint::to_bytes` and `AffinePoint::from_bytes` to get a 32-byte little-endian canonical representation. See https://github.com/zkcrypto/jubjub/blob/main/src/lib.rs#L443
Similarly, `VerificationKey`s can be serialized using `into::<[u8;32]>`. See https://github.com/ZcashFoundation/redjubjub/blob/main/src/verification_key.rs#L86 Similarly, `VerificationKey`s can be serialized using `<[u8; 32]>::from` and `VerificationKey::from`. See https://github.com/ZcashFoundation/redjubjub/blob/main/src/verification_key.rs#L86
### Payload ### Payload
@ -307,8 +321,36 @@ The following are a few things this RFC is not considering:
## Testing plan ## Testing plan
- Create a happy path unit test similar to https://github.com/ZcashFoundation/redjubjub/blob/frost-messages/tests/frost.rs#L7 and: ### Test Vectors
- Make messages on each step.
- Simulate send/receive. #### Conversion on Test Vectors
- Test round trip serialization/deserialization on each message.
- Create property tests for each message. - Test conversion from `frost` to `message` on a test vector
1. Implement the Rust `message` struct
2. Implement conversion from and to the `frost` type
3. Do a round-trip test from `frost` to `message` on a test vector
- Test conversion from `message` to bytes on a test vector
1. Implement conversion from and to the `message` type
2. Do a round-trip test from `message` to bytes on a test vector
#### Signing Rounds on Test Vectors
- Test signing using `frost` types on a test vector
1. Implement a single round of `frost` signing using a test vector
- Test signing using `message` types on a test vector
- Test signing using byte vectors on a test vector
### Property Tests
#### Conversion Property Tests
- Create property tests for each message
- Test round-trip conversion from `frost` to `message` types
- Test round-trip serialization and deserialization for each `message` type
#### Signing Round Property Tests
- Create property tests for signing rounds
- Test a signing round with `frost` types
- Test a signing round with `message` types
- Test a signing round with byte vectors