Skip to main content

dkls23_core/protocols/
dkg.rs

1//! Distributed Key Generation protocol.
2//!
3//!  This file implements Protocol 9.1 in <https://eprint.iacr.org/2023/602.pdf>,
4//! as instructed in `DKLs23` (<https://eprint.iacr.org/2023/765.pdf>). It is
5//! the distributed key generation which setups the main signing protocol.
6//!
7//! During the protocol, we also initialize the functionalities that will
8//! be used during signing.
9//!
10//! # Phases
11//!
12//! We group the steps in phases. A phase consists of all steps that can be
13//! executed in order without the need of communication. Phases should be
14//! intercalated with communication rounds: broadcasts and/or private messages
15//! containing the session id.
16//!
17//! We also include here the initialization procedures of Functionalities 3.4
18//! and 3.5 of `DKLs23`. The first one comes from [here](crate::utilities::zero_shares)
19//! and needs two communication rounds (hence, it starts on Phase 2). The second one
20//! comes from [here](crate::utilities::multiplication) and needs one communication round
21//! (hence, it starts on Phase 3).
22//!
23//! For key derivation (following BIP-32: <https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki>),
24//! parties must agree on a common chain code for their shared master key. Using the
25//! commitment functionality, we need two communication rounds, so this part starts
26//! only on Phase 2.
27//!
28//! # Nomenclature
29//!
30//! For the initialization structs, we will use the following nomenclature:
31//!
32//! **Transmit** messages refer to only one counterparty, hence
33//! we must produce a whole vector of them. Each message in this
34//! vector contains the party index to whom we should send it.
35//!
36//! **Broadcast** messages refer to all counterparties at once,
37//! hence we only need to produce a unique instance of it.
38//! This message is broadcasted to all parties.
39//!
40//! ATTENTION: we broadcast the message to ourselves as well!
41//!
42//! **Keep** messages refer to only one counterparty, hence
43//! we must keep a whole vector of them. In this implementation,
44//! we use a `BTreeMap` instead of a vector, where one can put
45//! some party index in the key to retrieve the corresponding data.
46//!
47//! **Unique keep** messages refer to all counterparties at once,
48//! hence we only need to keep a unique instance of it.
49//!
50//! The keep-state types in this module, along with [`phase1`] through
51//! [`phase4`], are public low-level APIs for advanced resumable/stateless
52//! orchestration. [`crate::DkgSession`] remains the preferred high-level API.
53
54use std::collections::BTreeMap;
55
56use rustcrypto_ff::Field;
57use rustcrypto_group::prime::PrimeCurveAffine;
58use rustcrypto_group::Curve as GroupCurve;
59
60use rand::RngExt;
61
62use crate::curve::DklsCurve;
63use crate::protocols::derivation::{ChainCode, DerivData, CHAIN_CODE_LEN};
64use crate::protocols::{
65    Abort, AbortReason, Parameters, PartiesMessage, Party, PartyIndex, PublicKeyPackage,
66};
67
68use crate::utilities::commits;
69use crate::utilities::hashes::HashOutput;
70use crate::utilities::multiplication::{MulReceiver, MulSender};
71use crate::utilities::ot;
72use crate::utilities::proofs::{DLogProof, EncProof};
73use crate::utilities::rng;
74use crate::utilities::zero_shares::{self, ZeroShare};
75
76/// Used during key generation.
77///
78/// After Phase 2, only the values `index` and `commitment` are broadcasted.
79///
80/// The `proof` is broadcasted after Phase 3.
81#[derive(Debug, Clone)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
83#[cfg_attr(
84    feature = "serde",
85    serde(bound(
86        serialize = "C::AffinePoint: serde::Serialize, C::Scalar: serde::Serialize",
87        deserialize = "C::AffinePoint: serde::Deserialize<'de>, C::Scalar: serde::Deserialize<'de>"
88    ))
89)]
90pub struct ProofCommitment<C: DklsCurve> {
91    pub index: PartyIndex,
92    pub proof: DLogProof<C>,
93    pub commitment: HashOutput,
94}
95
96/// Data needed to start key generation and is used during the phases.
97#[derive(Clone)]
98#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
99pub struct SessionData {
100    pub parameters: Parameters,
101    pub party_index: PartyIndex,
102    pub session_id: Vec<u8>,
103}
104
105// INITIALIZING ZERO SHARES PROTOCOL.
106
107/// Transmit - Initialization of zero shares protocol.
108///
109/// The message is produced/sent during Phase 2 and used in Phase 4.
110#[derive(Clone, Debug)]
111#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
112pub struct TransmitInitZeroSharePhase2to4 {
113    pub parties: PartiesMessage,
114    pub commitment: HashOutput,
115}
116
117/// Transmit - Initialization of zero shares protocol.
118///
119/// The message is produced/sent during Phase 3 and used in Phase 4.
120#[derive(Clone, Debug)]
121#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
122pub struct TransmitInitZeroSharePhase3to4 {
123    pub parties: PartiesMessage,
124    pub seed: zero_shares::Seed,
125    pub salt: Vec<u8>,
126}
127
128/// Keep - Initialization of zero shares protocol.
129///
130/// The message is produced during Phase 2 and used in Phase 3.
131#[derive(Clone, Debug)]
132#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
133pub struct KeepInitZeroSharePhase2to3 {
134    pub seed: zero_shares::Seed,
135    pub salt: Vec<u8>,
136}
137
138/// Keep - Initialization of zero shares protocol.
139///
140/// The message is produced during Phase 3 and used in Phase 4.
141#[derive(Clone, Debug)]
142#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
143pub struct KeepInitZeroSharePhase3to4 {
144    pub seed: zero_shares::Seed,
145}
146
147// INITIALIZING TWO-PARTY MULTIPLICATION PROTOCOL.
148
149/// Transmit - Initialization of multiplication protocol.
150///
151/// The message is produced/sent during Phase 3 and used in Phase 4.
152#[derive(Clone, Debug)]
153#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
154#[cfg_attr(
155    feature = "serde",
156    serde(bound(
157        serialize = "C::AffinePoint: serde::Serialize, C::Scalar: serde::Serialize",
158        deserialize = "C::AffinePoint: serde::Deserialize<'de>, C::Scalar: serde::Deserialize<'de>"
159    ))
160)]
161pub struct TransmitInitMulPhase3to4<C: DklsCurve> {
162    pub parties: PartiesMessage,
163
164    pub dlog_proof: DLogProof<C>,
165    pub nonce: C::Scalar,
166
167    pub enc_proofs: Vec<EncProof<C>>,
168    pub seed: ot::base::Seed,
169}
170
171/// Keep - Initialization of multiplication protocol.
172///
173/// The message is produced during Phase 3 and used in Phase 4.
174#[derive(Clone, Debug)]
175#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
176#[cfg_attr(
177    feature = "serde",
178    serde(bound(
179        serialize = "C::AffinePoint: serde::Serialize, C::Scalar: serde::Serialize",
180        deserialize = "C::AffinePoint: serde::Deserialize<'de>, C::Scalar: serde::Deserialize<'de>"
181    ))
182)]
183pub struct KeepInitMulPhase3to4<C: DklsCurve> {
184    pub ot_sender: ot::base::OTSender<C>,
185    pub nonce: C::Scalar,
186
187    pub ot_receiver: ot::base::OTReceiver,
188    pub correlation: Vec<bool>,
189    pub vec_r: Vec<C::Scalar>,
190}
191
192// INITIALIZING KEY DERIVATION (VIA BIP-32).
193
194/// Broadcast - Initialization for key derivation.
195///
196/// The message is produced/sent during Phase 2 and used in Phase 4.
197#[derive(Clone, Debug)]
198#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
199pub struct BroadcastDerivationPhase2to4 {
200    pub sender_index: PartyIndex,
201    pub cc_commitment: HashOutput,
202}
203
204/// Broadcast - Initialization for key derivation.
205///
206/// The message is produced/sent during Phase 3 and used in Phase 4.
207#[derive(Clone, Debug)]
208#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
209pub struct BroadcastDerivationPhase3to4 {
210    pub sender_index: PartyIndex,
211    pub aux_chain_code: ChainCode,
212    pub cc_salt: Vec<u8>,
213}
214
215/// Unique keep - Initialization for key derivation.
216///
217/// The message is produced during Phase 2 and used in Phase 3.
218#[derive(Clone, Debug)]
219#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
220pub struct UniqueKeepDerivationPhase2to3 {
221    pub aux_chain_code: ChainCode,
222    pub cc_salt: Vec<u8>,
223}
224
225// MessageTag implementations.
226#[cfg(feature = "serde")]
227mod message_tags {
228    use super::*;
229    use crate::protocols::messages::MessageTag;
230
231    impl<C: DklsCurve> MessageTag for ProofCommitment<C>
232    where
233        C::AffinePoint: serde::Serialize + serde::de::DeserializeOwned,
234        C::Scalar: serde::Serialize + serde::de::DeserializeOwned,
235    {
236        const TAG: u8 = 0x01;
237    }
238    impl MessageTag for TransmitInitZeroSharePhase2to4 {
239        const TAG: u8 = 0x02;
240    }
241    impl MessageTag for TransmitInitZeroSharePhase3to4 {
242        const TAG: u8 = 0x03;
243    }
244    impl<C: DklsCurve> MessageTag for TransmitInitMulPhase3to4<C>
245    where
246        C::AffinePoint: serde::Serialize + serde::de::DeserializeOwned,
247        C::Scalar: serde::Serialize + serde::de::DeserializeOwned,
248    {
249        const TAG: u8 = 0x04;
250    }
251    impl MessageTag for BroadcastDerivationPhase2to4 {
252        const TAG: u8 = 0x05;
253    }
254    impl MessageTag for BroadcastDerivationPhase3to4 {
255        const TAG: u8 = 0x06;
256    }
257}
258
259// DISTRIBUTED KEY GENERATION (DKG)
260
261// STEPS
262// We implement each step of the DKLs23 protocol.
263
264/// Generates a random polynomial of degree t-1.
265///
266/// This is Step 1 from Protocol 9.1 in <https://eprint.iacr.org/2023/602.pdf>.
267#[must_use]
268pub(crate) fn step1<C: DklsCurve>(parameters: &Parameters) -> Vec<C::Scalar> {
269    // We represent the polynomial by its coefficients.
270    let mut rng = rng::get_rng(); // Reuse RNG
271    let mut polynomial: Vec<C::Scalar> = Vec::with_capacity(parameters.threshold as usize);
272    for _ in 0..parameters.threshold {
273        polynomial.push(<C::Scalar as Field>::random(&mut rng)); // Pass the RNG explicitly
274    }
275    polynomial
276}
277
278/// Evaluates the polynomial from the previous step at every point.
279///
280/// If `p_i` denotes such polynomial, then the output is of the form
281/// \[`p_i(1)`, `p_i(2)`, ..., `p_i(n)`\] in this order, where `n` = `parameters.share_count`.
282///
283/// The value `p_i(j)` should be transmitted to the party with index `j`.
284/// Here, `i` denotes our index, so we should keep `p_i(i)` for the future.
285///
286/// This is Step 2 from Protocol 9.1 in <https://eprint.iacr.org/2023/602.pdf>.
287#[must_use]
288pub(crate) fn step2<C: DklsCurve>(
289    parameters: &Parameters,
290    polynomial: &[C::Scalar],
291) -> Vec<C::Scalar> {
292    let mut points: Vec<C::Scalar> = Vec::with_capacity(parameters.share_count as usize);
293    let last_index = (parameters.threshold - 1) as usize;
294
295    for j in 1..=parameters.share_count {
296        let j_scalar = C::Scalar::from(u64::from(j));
297
298        // Using Horner's method for polynomial evaluation
299        let mut evaluation_at_j = polynomial[last_index];
300
301        for &coefficient in polynomial[..last_index].iter().rev() {
302            evaluation_at_j = evaluation_at_j * j_scalar + coefficient;
303        }
304
305        points.push(evaluation_at_j);
306    }
307
308    points
309}
310
311/// Computes `poly_point` and the corresponding "public key" together with a zero-knowledge proof.
312///
313/// The variable `poly_fragments` is just a vector containing (in any order)
314/// the scalars received from the other parties after the previous step.
315///
316/// The commitment from [`ProofCommitment`] should be broadcasted at this point.
317///
318/// This is Step 3 from Protocol 9.1 in <https://eprint.iacr.org/2023/602.pdf>.
319/// There, `poly_point` is denoted by `p(i)` and the "public key" is `P(i)`.
320///
321/// The Step 4 of the protocol is broadcasting the rest of [`ProofCommitment`] after
322/// having received all commitments.
323/// # Panics
324///
325/// Panics if the Fischlin proof-of-work search is exhausted, which is
326/// astronomically unlikely with a correct RNG.
327#[must_use]
328pub(crate) fn step3<C: DklsCurve>(
329    party_index: PartyIndex,
330    session_id: &[u8],
331    poly_fragments: &[C::Scalar],
332) -> (C::Scalar, ProofCommitment<C>) {
333    let poly_point: C::Scalar = poly_fragments.iter().copied().sum();
334
335    let (proof, commitment) = DLogProof::<C>::prove_commit(&poly_point, session_id)
336        .expect("Fischlin proof-of-work search exhausted — RNG failure");
337    let proof_commitment = ProofCommitment {
338        index: party_index,
339        proof,
340        commitment,
341    };
342
343    (poly_point, proof_commitment)
344}
345
346/// Validates the other proofs, runs a consistency check
347/// and computes the public key.
348///
349/// The variable `proofs_commitments` is just a vector containing (in any order)
350/// the instances of [`ProofCommitment`] received from the other parties after the
351/// previous step (including ours).
352///
353/// This is Step 5 from Protocol 9.1 in <https://eprint.iacr.org/2023/602.pdf>.
354/// Step 6 is essentially the same, so it is also done here.
355///
356/// # Errors
357///
358/// Will return `Err` if one of the proofs/commitments doesn't
359/// verify or if the consistency check for the public key fails.
360///
361/// # Panics
362///
363/// Will panic if the list of indices in `proofs_commitments`
364/// are not the numbers from 1 to `parameters.share_count`.
365pub(crate) fn step5<C: DklsCurve>(
366    parameters: &Parameters,
367    party_index: PartyIndex,
368    session_id: &[u8],
369    proofs_commitments: &[ProofCommitment<C>],
370) -> Result<(C::AffinePoint, BTreeMap<PartyIndex, C::AffinePoint>), Abort> {
371    let mut committed_points: BTreeMap<PartyIndex, C::AffinePoint> = BTreeMap::new(); //The "public key fragments"
372
373    // Verify the proofs and gather the committed points.
374    for party_j in proofs_commitments {
375        if party_j.index != party_index {
376            let verification =
377                DLogProof::<C>::decommit_verify(&party_j.proof, &party_j.commitment, session_id);
378            if !verification {
379                return Err(Abort::recoverable(
380                    party_index,
381                    AbortReason::ProofVerificationFailed {
382                        counterparty: party_j.index,
383                    },
384                ));
385            }
386        }
387        committed_points.insert(party_j.index, party_j.proof.point);
388    }
389
390    // Initializes what will be the public key.
391    let identity = <C::AffinePoint as PrimeCurveAffine>::identity();
392    let mut pk = identity;
393
394    // Verify that all points come from the same polynomial. To do so, for each contiguous set of parties,
395    // perform Shamir reconstruction in the exponent and check if the results agree.
396    // The common value calculated is the public key.
397    for i in 1..=(parameters.share_count - parameters.threshold + 1) {
398        let mut current_pk = identity;
399        for j in i..(i + parameters.threshold) {
400            // We find the Lagrange coefficient l(j) corresponding to j (and the contiguous set of parties).
401            // It is such that the sum of l(j) * p(j) over all j is p(0), where p is the polynomial from Step 3.
402            let j_scalar = C::Scalar::from(u64::from(j));
403            let mut lj_numerator = <C::Scalar as Field>::ONE;
404            let mut lj_denominator = <C::Scalar as Field>::ONE;
405
406            for k in i..(i + parameters.threshold) {
407                if k != j {
408                    let k_scalar = C::Scalar::from(u64::from(k));
409                    lj_numerator *= k_scalar;
410                    lj_denominator *= k_scalar - j_scalar;
411                }
412            }
413
414            let lj = lj_numerator * (lj_denominator.invert().unwrap());
415            let point_j = committed_points
416                .get(&PartyIndex::new(j).unwrap())
417                .ok_or_else(|| {
418                    Abort::recoverable(
419                        party_index,
420                        AbortReason::MissingCommittedPoint {
421                            party: PartyIndex::new(j).unwrap(),
422                        },
423                    )
424                })?;
425            let lj_times_point = *point_j * lj;
426
427            current_pk = (lj_times_point + current_pk).to_affine();
428        }
429
430        // The first value is taken as the public key. It should coincide with the next values.
431        if i == 1 {
432            pk = current_pk;
433        } else if pk != current_pk {
434            return Err(Abort::recoverable(
435                party_index,
436                AbortReason::PolynomialInconsistency,
437            ));
438        }
439    }
440    Ok((pk, committed_points))
441}
442
443// PHASES
444
445/// Phase 1 = [`step1`] and [`step2`].
446///
447/// # Input
448///
449/// Parameters for the key generation.
450///
451/// # Output
452///
453/// Evaluation of a random polynomial at every party index.
454/// The j-th coordinate of the output vector must be sent
455/// to the party with index j.
456///
457/// ATTENTION: In particular, we keep the coordinate corresponding
458/// to our party index for the next phase.
459#[must_use]
460pub fn phase1<C: DklsCurve>(data: &SessionData) -> Vec<C::Scalar> {
461    // DKG
462    let secret_polynomial = step1::<C>(&data.parameters);
463
464    step2::<C>(&data.parameters, &secret_polynomial)
465}
466
467// Communication round 1
468// DKG: Party i keeps the i-th point and sends the j-th point to Party j for j != i.
469// At the end, Party i should have received all fragments indexed by i.
470// They should add up to p(i), where p is a polynomial not depending on i.
471
472/// Phase 2 = [`step3`].
473///
474/// # Input
475///
476/// Fragments received from the previous phase.
477///
478/// # Output
479///
480/// The variable `poly_point` (= `p(i)`), which should be kept, and a proof of
481/// discrete logarithm with commitment. You should transmit the commitment
482/// now and, after finishing Phase 3, you send the rest. Remember to also
483/// save a copy of your [`ProofCommitment`] for the final phase.
484///
485/// There is also some initialization data to keep and to transmit, following the
486/// conventions [here](self).
487#[must_use]
488pub fn phase2<C: DklsCurve>(
489    data: &SessionData,
490    poly_fragments: &[C::Scalar],
491) -> (
492    C::Scalar,
493    ProofCommitment<C>,
494    BTreeMap<PartyIndex, KeepInitZeroSharePhase2to3>,
495    Vec<TransmitInitZeroSharePhase2to4>,
496    UniqueKeepDerivationPhase2to3,
497    BroadcastDerivationPhase2to4,
498) {
499    // DKG
500    let (poly_point, proof_commitment) =
501        step3::<C>(data.party_index, &data.session_id, poly_fragments);
502
503    // Initialization - Zero shares.
504
505    // We will use BTreeMap to keep messages: the key indicates the party to whom the message refers.
506    let mut zero_keep = BTreeMap::new();
507    let mut zero_transmit = Vec::with_capacity((data.parameters.share_count - 1) as usize);
508
509    for i in 1..=data.parameters.share_count {
510        let i_idx = PartyIndex::new(i).unwrap();
511        if i_idx == data.party_index {
512            continue;
513        }
514
515        // Generate initial seeds.
516        let (seed, commitment, salt) = ZeroShare::generate_seed_with_commitment();
517
518        // We first send the commitments. We keep the rest to send later.
519        zero_keep.insert(i_idx, KeepInitZeroSharePhase2to3 { seed, salt });
520        zero_transmit.push(TransmitInitZeroSharePhase2to4 {
521            parties: PartiesMessage {
522                sender: data.party_index,
523                receiver: i_idx,
524            },
525            commitment,
526        });
527    }
528
529    // Initialization - BIP-32.
530
531    // Each party samples a random auxiliary chain code.
532    let aux_chain_code: ChainCode = rng::get_rng().random();
533    let (cc_commitment, cc_salt) = commits::commit(&aux_chain_code);
534
535    let bip_keep = UniqueKeepDerivationPhase2to3 {
536        aux_chain_code,
537        cc_salt,
538    };
539
540    // For simplicity, this message should be sent to us too.
541    let bip_broadcast = BroadcastDerivationPhase2to4 {
542        sender_index: data.party_index,
543        cc_commitment,
544    };
545
546    (
547        poly_point,
548        proof_commitment,
549        zero_keep,
550        zero_transmit,
551        bip_keep,
552        bip_broadcast,
553    )
554}
555
556// Communication round 2
557// DKG: Party i broadcasts his commitment to the proof and receive the other commitments.
558//
559// Init: Each party transmits messages for the zero shares protocol (one for each party)
560// and broadcasts a message for key derivation (the same for every party).
561
562/// Phase 3 = No steps in DKG (just initialization).
563///
564/// # Input
565///
566/// Initialization data kept from the previous phase.
567///
568/// # Output
569///
570/// Some initialization data to keep and to transmit, following the
571/// conventions [here](self).
572#[must_use]
573#[allow(clippy::type_complexity)]
574pub fn phase3<C: DklsCurve>(
575    data: &SessionData,
576    zero_kept: &BTreeMap<PartyIndex, KeepInitZeroSharePhase2to3>,
577    bip_kept: &UniqueKeepDerivationPhase2to3,
578) -> (
579    BTreeMap<PartyIndex, KeepInitZeroSharePhase3to4>,
580    Vec<TransmitInitZeroSharePhase3to4>,
581    BTreeMap<PartyIndex, KeepInitMulPhase3to4<C>>,
582    Vec<TransmitInitMulPhase3to4<C>>,
583    BroadcastDerivationPhase3to4,
584) {
585    // Initialization - Zero shares.
586    let share_count = (data.parameters.share_count - 1) as usize;
587    let mut zero_keep = BTreeMap::new();
588    let mut zero_transmit = Vec::with_capacity(share_count);
589
590    for (&target_party, message_kept) in zero_kept.iter() {
591        // The messages kept contain the seed and the salt.
592        // They have to be transmitted to the target party.
593        // We keep the seed with us for the next phase.
594        let keep = KeepInitZeroSharePhase3to4 {
595            seed: message_kept.seed,
596        };
597        let transmit = TransmitInitZeroSharePhase3to4 {
598            parties: PartiesMessage {
599                sender: data.party_index,
600                receiver: target_party,
601            },
602            seed: message_kept.seed,
603            salt: message_kept.salt.clone(),
604        };
605
606        zero_keep.insert(target_party, keep);
607        zero_transmit.push(transmit);
608    }
609
610    // Initialization - Two-party multiplication.
611    // Each party prepares initialization both as
612    // a receiver and as a sender.
613    // Initialization - Two-party multiplication.
614    let mut mul_keep = BTreeMap::new();
615    let mut mul_transmit = Vec::with_capacity(share_count);
616
617    for i in 1..=data.parameters.share_count {
618        let i_idx = PartyIndex::new(i).unwrap();
619        if i_idx == data.party_index {
620            continue;
621        }
622
623        // RECEIVER
624        // We are the receiver and i = sender.
625
626        // We first compute a new session id.
627        // As in Protocol 3.6 of DKLs23, we include the indexes from the parties.
628        let mul_sid_receiver = [
629            "Multiplication protocol".as_bytes(),
630            &data.party_index.as_u8().to_be_bytes(),
631            &i.to_be_bytes(),
632            &data.session_id[..],
633        ]
634        .concat();
635
636        let (ot_sender, dlog_proof, nonce) = MulReceiver::<C>::init_phase1(&mul_sid_receiver);
637
638        // SENDER
639        // We are the sender and i = receiver.
640
641        // New session id as above.
642        // Note that the indexes are now in the opposite order.
643        let mul_sid_sender = [
644            "Multiplication protocol".as_bytes(),
645            &i.to_be_bytes(),
646            &data.party_index.as_u8().to_be_bytes(),
647            &data.session_id[..],
648        ]
649        .concat();
650
651        let (ot_receiver, correlation, vec_r, enc_proofs) =
652            MulSender::<C>::init_phase1(&mul_sid_sender);
653
654        // We gather these values.
655
656        let transmit = TransmitInitMulPhase3to4 {
657            parties: PartiesMessage {
658                sender: data.party_index,
659                receiver: i_idx,
660            },
661
662            // Us = Receiver
663            dlog_proof,
664            nonce,
665
666            // Us = Sender
667            enc_proofs,
668            seed: ot_receiver.seed,
669        };
670        let keep = KeepInitMulPhase3to4 {
671            // Us = Receiver
672            ot_sender,
673            nonce,
674
675            // Us = Sender
676            ot_receiver,
677            correlation,
678            vec_r,
679        };
680
681        mul_keep.insert(i_idx, keep);
682        mul_transmit.push(transmit);
683    }
684
685    // Initialization - BIP-32.
686    // After having transmitted the commitment, we broadcast
687    // our auxiliary chain code and the corresponding salt.
688    // For simplicity, this message should be sent to us too.
689    let bip_broadcast = BroadcastDerivationPhase3to4 {
690        sender_index: data.party_index,
691        aux_chain_code: bip_kept.aux_chain_code,
692        cc_salt: bip_kept.cc_salt.clone(),
693    };
694
695    (
696        zero_keep,
697        zero_transmit,
698        mul_keep,
699        mul_transmit,
700        bip_broadcast,
701    )
702}
703
704// Communication round 3
705// DKG: We execute Step 4 of the protocol: after having received all commitments, each party broadcasts his proof.
706//
707// Init: Each party transmits messages for the zero shares and multiplication protocols (one for each party)
708// and broadcasts a message for key derivation (the same for every party).
709
710/// Phase 4 = [`step5`].
711///
712/// # Input
713///
714/// The `poly_point` scalar generated in Phase 2;
715///
716/// A vector containing (in any order) the [`ProofCommitment`]'s
717/// received from the other parties (including ours);
718///
719/// The initialization data kept from the previous phases;
720///
721/// The initialization data received from the other parties in
722/// the previous phases. They must be grouped in vectors (in any
723/// order) according to the type or, in the case of the messages
724/// related to derivation BIP-32, in a `BTreeMap` where the key
725/// represents the index of the party that transmitted the message.
726///
727/// # Output
728///
729/// An instance of [`Party`] ready to execute the other protocols.
730///
731/// # Errors
732///
733/// Will return `Err` if a message is not meant for the party
734/// or if one of the initializations fails. With very low probability,
735/// it may also fail if the secret data is trivial.
736///
737/// # Panics
738///
739/// Will panic if the list of keys in the `BTreeMap`'s are incompatible
740/// with the party indices in the received vectors.
741#[allow(clippy::too_many_arguments)]
742pub fn phase4<C: DklsCurve>(
743    data: &SessionData,
744    poly_point: &C::Scalar,
745    proofs_commitments: &[ProofCommitment<C>],
746    zero_kept: &BTreeMap<PartyIndex, KeepInitZeroSharePhase3to4>,
747    zero_received_phase2: &[TransmitInitZeroSharePhase2to4],
748    zero_received_phase3: &[TransmitInitZeroSharePhase3to4],
749    mul_kept: &BTreeMap<PartyIndex, KeepInitMulPhase3to4<C>>,
750    mul_received: &[TransmitInitMulPhase3to4<C>],
751    bip_received_phase2: &BTreeMap<PartyIndex, BroadcastDerivationPhase2to4>,
752    bip_received_phase3: &BTreeMap<PartyIndex, BroadcastDerivationPhase3to4>,
753    address_fn: impl Fn(&C::AffinePoint) -> String,
754) -> Result<(Party<C>, PublicKeyPackage<C>), Abort> {
755    // DKG
756    let (pk, verifying_shares) = step5::<C>(
757        &data.parameters,
758        data.party_index,
759        &data.session_id,
760        proofs_commitments,
761    )?;
762
763    // The public key cannot be the point at infinity.
764    // This is practically impossible, but easy to check.
765    // We also verify that pk is not the generator point, because
766    // otherwise it would be trivial to find the "total" secret key.
767    let identity = <C::AffinePoint as PrimeCurveAffine>::identity();
768    let generator = <C::AffinePoint as PrimeCurveAffine>::generator();
769    if pk == identity || pk == generator {
770        return Err(Abort::recoverable(
771            data.party_index,
772            AbortReason::TrivialPublicKey,
773        ));
774    }
775
776    // Our key share (that is, poly_point), should not be trivial.
777    // Note that the other parties can deduce the triviality from
778    // the corresponding proof in proofs_commitments.
779    if *poly_point == <C::Scalar as Field>::ZERO || *poly_point == <C::Scalar as Field>::ONE {
780        return Err(Abort::recoverable(
781            data.party_index,
782            AbortReason::TrivialKeyShare,
783        ));
784    }
785
786    // Initialization - Zero shares.
787    let mut zero_received_phase2_by_sender: BTreeMap<PartyIndex, &TransmitInitZeroSharePhase2to4> =
788        BTreeMap::new();
789    for message in zero_received_phase2 {
790        if message.parties.receiver != data.party_index {
791            return Err(Abort::recoverable(
792                data.party_index,
793                AbortReason::MisroutedMessage {
794                    expected_receiver: data.party_index,
795                    actual_receiver: message.parties.receiver,
796                },
797            ));
798        }
799        if !zero_kept.contains_key(&message.parties.sender) {
800            return Err(Abort::recoverable(
801                data.party_index,
802                AbortReason::UnexpectedSender {
803                    sender: message.parties.sender,
804                },
805            ));
806        }
807        if zero_received_phase2_by_sender
808            .insert(message.parties.sender, message)
809            .is_some()
810        {
811            return Err(Abort::recoverable(
812                data.party_index,
813                AbortReason::DuplicateSender {
814                    sender: message.parties.sender,
815                },
816            ));
817        }
818    }
819    let mut zero_received_phase3_by_sender: BTreeMap<PartyIndex, &TransmitInitZeroSharePhase3to4> =
820        BTreeMap::new();
821    for message in zero_received_phase3 {
822        if message.parties.receiver != data.party_index {
823            return Err(Abort::recoverable(
824                data.party_index,
825                AbortReason::MisroutedMessage {
826                    expected_receiver: data.party_index,
827                    actual_receiver: message.parties.receiver,
828                },
829            ));
830        }
831        if !zero_kept.contains_key(&message.parties.sender) {
832            return Err(Abort::recoverable(
833                data.party_index,
834                AbortReason::UnexpectedSender {
835                    sender: message.parties.sender,
836                },
837            ));
838        }
839        if zero_received_phase3_by_sender
840            .insert(message.parties.sender, message)
841            .is_some()
842        {
843            return Err(Abort::recoverable(
844                data.party_index,
845                AbortReason::DuplicateSender {
846                    sender: message.parties.sender,
847                },
848            ));
849        }
850    }
851    if zero_received_phase2_by_sender.len() != zero_kept.len() {
852        return Err(Abort::recoverable(
853            data.party_index,
854            AbortReason::WrongMessageCount {
855                expected: zero_kept.len(),
856                got: zero_received_phase2_by_sender.len(),
857            },
858        ));
859    }
860    if zero_received_phase3_by_sender.len() != zero_kept.len() {
861        return Err(Abort::recoverable(
862            data.party_index,
863            AbortReason::WrongMessageCount {
864                expected: zero_kept.len(),
865                got: zero_received_phase3_by_sender.len(),
866            },
867        ));
868    }
869
870    let mut seeds: Vec<zero_shares::SeedPair> =
871        Vec::with_capacity((data.parameters.share_count - 1) as usize);
872    for (target_party, message_kept) in zero_kept {
873        let message_received_2 = zero_received_phase2_by_sender
874            .get(target_party)
875            .ok_or_else(|| {
876                Abort::recoverable(
877                    data.party_index,
878                    AbortReason::MissingMessageFromParty {
879                        party: *target_party,
880                    },
881                )
882            })?;
883        let message_received_3 = zero_received_phase3_by_sender
884            .get(target_party)
885            .ok_or_else(|| {
886                Abort::recoverable(
887                    data.party_index,
888                    AbortReason::MissingMessageFromParty {
889                        party: *target_party,
890                    },
891                )
892            })?;
893
894        // We verify the commitment.
895        let verification = ZeroShare::verify_seed(
896            &message_received_3.seed,
897            &message_received_2.commitment,
898            &message_received_3.salt,
899        );
900        if !verification {
901            return Err(Abort::recoverable(
902                data.party_index,
903                AbortReason::ZeroShareDecommitFailed {
904                    counterparty: *target_party,
905                },
906            ));
907        }
908
909        // We form the final seed pairs.
910        seeds.push(ZeroShare::generate_seed_pair(
911            data.party_index,
912            *target_party,
913            &message_kept.seed,
914            &message_received_3.seed,
915        ));
916    }
917
918    // This finishes the initialization.
919    let zero_share = ZeroShare::initialize(seeds);
920
921    // Initialization - Two-party multiplication.
922    let mut mul_receivers: BTreeMap<PartyIndex, MulReceiver<C>> = BTreeMap::new();
923    let mut mul_senders: BTreeMap<PartyIndex, MulSender<C>> = BTreeMap::new();
924    let mut mul_received_by_sender: BTreeMap<PartyIndex, &TransmitInitMulPhase3to4<C>> =
925        BTreeMap::new();
926    for message in mul_received {
927        if message.parties.receiver != data.party_index {
928            return Err(Abort::recoverable(
929                data.party_index,
930                AbortReason::MisroutedMessage {
931                    expected_receiver: data.party_index,
932                    actual_receiver: message.parties.receiver,
933                },
934            ));
935        }
936        if !mul_kept.contains_key(&message.parties.sender) {
937            return Err(Abort::recoverable(
938                data.party_index,
939                AbortReason::UnexpectedSender {
940                    sender: message.parties.sender,
941                },
942            ));
943        }
944        if mul_received_by_sender
945            .insert(message.parties.sender, message)
946            .is_some()
947        {
948            return Err(Abort::recoverable(
949                data.party_index,
950                AbortReason::DuplicateSender {
951                    sender: message.parties.sender,
952                },
953            ));
954        }
955    }
956    if mul_received_by_sender.len() != mul_kept.len() {
957        return Err(Abort::recoverable(
958            data.party_index,
959            AbortReason::WrongMessageCount {
960                expected: mul_kept.len(),
961                got: mul_received_by_sender.len(),
962            },
963        ));
964    }
965
966    for (target_party, message_kept) in mul_kept {
967        let message_received = mul_received_by_sender.get(target_party).ok_or_else(|| {
968            Abort::recoverable(
969                data.party_index,
970                AbortReason::MissingMessageFromParty {
971                    party: *target_party,
972                },
973            )
974        })?;
975
976        // RECEIVER
977        // We are the receiver and target_party = sender.
978
979        // We retrieve the id used for multiplication. Note that the first party
980        // is the receiver and the second, the sender.
981        let mul_sid_receiver = [
982            "Multiplication protocol".as_bytes(),
983            &data.party_index.as_u8().to_be_bytes(),
984            &target_party.as_u8().to_be_bytes(),
985            &data.session_id[..],
986        ]
987        .concat();
988
989        let receiver_result = MulReceiver::<C>::init_phase2(
990            &message_kept.ot_sender,
991            &mul_sid_receiver,
992            &message_received.seed,
993            &message_received.enc_proofs,
994            &message_kept.nonce,
995        );
996
997        let mul_receiver: MulReceiver<C> = match receiver_result {
998            Ok(r) => r,
999            Err(error) => {
1000                // In DKG this failed run does not produce reusable OT state,
1001                // so we keep abort classification recoverable.
1002                return Err(Abort::recoverable(
1003                    data.party_index,
1004                    AbortReason::MultiplicationVerificationFailed {
1005                        counterparty: *target_party,
1006                        detail: error.description.clone(),
1007                    },
1008                ));
1009            }
1010        };
1011
1012        // SENDER
1013        // We are the sender and target_party = receiver.
1014
1015        // We retrieve the id used for multiplication. Note that the first party
1016        // is the receiver and the second, the sender.
1017        let mul_sid_sender = [
1018            "Multiplication protocol".as_bytes(),
1019            &target_party.as_u8().to_be_bytes(),
1020            &data.party_index.as_u8().to_be_bytes(),
1021            &data.session_id[..],
1022        ]
1023        .concat();
1024
1025        let sender_result = MulSender::<C>::init_phase2(
1026            &message_kept.ot_receiver,
1027            &mul_sid_sender,
1028            message_kept.correlation.clone(),
1029            &message_kept.vec_r,
1030            &message_received.dlog_proof,
1031            &message_received.nonce,
1032        );
1033
1034        let mul_sender: MulSender<C> = match sender_result {
1035            Ok(s) => s,
1036            Err(error) => {
1037                // In DKG this failed run does not produce reusable OT state,
1038                // so we keep abort classification recoverable.
1039                return Err(Abort::recoverable(
1040                    data.party_index,
1041                    AbortReason::MultiplicationVerificationFailed {
1042                        counterparty: *target_party,
1043                        detail: error.description.clone(),
1044                    },
1045                ));
1046            }
1047        };
1048
1049        // We finish the initialization.
1050        mul_receivers.insert(*target_party, mul_receiver);
1051        mul_senders.insert(*target_party, mul_sender.clone());
1052    }
1053
1054    // Initialization - BIP-32.
1055    // We check the commitments and create the final chain code.
1056    // It will be given by the XOR of the auxiliary chain codes.
1057    let mut chain_code: ChainCode = [0; CHAIN_CODE_LEN];
1058    for i in 1..=data.parameters.share_count {
1059        let i_idx = PartyIndex::new(i).unwrap();
1060        // We take the messages in the correct order (that's why the BTreeMap).
1061        let phase3_msg = bip_received_phase3.get(&i_idx).ok_or_else(|| {
1062            Abort::recoverable(
1063                data.party_index,
1064                AbortReason::MissingMessageFromParty { party: i_idx },
1065            )
1066        })?;
1067        let phase2_msg = bip_received_phase2.get(&i_idx).ok_or_else(|| {
1068            Abort::recoverable(
1069                data.party_index,
1070                AbortReason::MissingMessageFromParty { party: i_idx },
1071            )
1072        })?;
1073        let verification = commits::verify_commitment(
1074            &phase3_msg.aux_chain_code,
1075            &phase2_msg.cc_commitment,
1076            &phase3_msg.cc_salt,
1077        );
1078        if !verification {
1079            return Err(Abort::recoverable(
1080                data.party_index,
1081                AbortReason::ChainCodeCommitmentFailed { party: i_idx },
1082            ));
1083        }
1084
1085        // We XOR this auxiliary chain code to the final result.
1086        let current_aux_chain_code = phase3_msg.aux_chain_code;
1087        for j in 0..CHAIN_CODE_LEN {
1088            chain_code[j] ^= current_aux_chain_code[j];
1089        }
1090    }
1091
1092    // We can finally finish key generation!
1093
1094    let derivation_data = DerivData {
1095        depth: 0,
1096        child_number: 0, // These three values are initialized as zero for the master node.
1097        parent_fingerprint: [0; 4],
1098        poly_point: *poly_point,
1099        pk,
1100        chain_code,
1101    };
1102
1103    let address = address_fn(&pk);
1104
1105    let party = Party {
1106        parameters: data.parameters.clone(),
1107        party_index: data.party_index,
1108        session_id: data.session_id.clone(),
1109
1110        poly_point: *poly_point,
1111        pk,
1112
1113        zero_share,
1114
1115        mul_senders,
1116        mul_receivers,
1117
1118        derivation_data,
1119
1120        address,
1121    };
1122
1123    let public_key_package = PublicKeyPackage::new(pk, verifying_shares, data.parameters.clone());
1124
1125    Ok((party, public_key_package))
1126}
1127
1128#[cfg(test)]
1129mod tests {
1130
1131    use super::*;
1132    use elliptic_curve::CurveArithmetic;
1133    use k256::{AffinePoint, Scalar};
1134    use rand::RngExt;
1135
1136    type TestCurve = k256::Secp256k1;
1137
1138    fn no_address(_pk: &<TestCurve as CurveArithmetic>::AffinePoint) -> String {
1139        String::new()
1140    }
1141
1142    struct DkgPhase4Inputs {
1143        all_data: Vec<SessionData>,
1144        poly_points: Vec<Scalar>,
1145        proofs_commitments: Vec<ProofCommitment<TestCurve>>,
1146        zero_kept_3to4: Vec<BTreeMap<PartyIndex, KeepInitZeroSharePhase3to4>>,
1147        zero_received_2to4: Vec<Vec<TransmitInitZeroSharePhase2to4>>,
1148        zero_received_3to4: Vec<Vec<TransmitInitZeroSharePhase3to4>>,
1149        mul_kept_3to4: Vec<BTreeMap<PartyIndex, KeepInitMulPhase3to4<TestCurve>>>,
1150        mul_received_3to4: Vec<Vec<TransmitInitMulPhase3to4<TestCurve>>>,
1151        bip_broadcast_2to4: BTreeMap<PartyIndex, BroadcastDerivationPhase2to4>,
1152        bip_broadcast_3to4: BTreeMap<PartyIndex, BroadcastDerivationPhase3to4>,
1153    }
1154
1155    fn setup_two_party_dkg_phase4_inputs() -> DkgPhase4Inputs {
1156        let parameters = Parameters {
1157            threshold: 2,
1158            share_count: 2,
1159        };
1160        const SESSION_ID_LEN: usize = 32;
1161        let session_id = rng::get_rng().random::<[u8; SESSION_ID_LEN]>();
1162
1163        // Each party prepares their data for this DKG.
1164        let mut all_data: Vec<SessionData> = Vec::with_capacity(parameters.share_count as usize);
1165        for i in 0..parameters.share_count {
1166            all_data.push(SessionData {
1167                parameters: parameters.clone(),
1168                party_index: PartyIndex::new(i + 1).unwrap(),
1169                session_id: session_id.to_vec(),
1170            });
1171        }
1172
1173        // Phase 1
1174        let mut dkg_1: Vec<Vec<Scalar>> = Vec::with_capacity(parameters.share_count as usize);
1175        for i in 0..parameters.share_count {
1176            let out1 = phase1::<TestCurve>(&all_data[i as usize]);
1177            dkg_1.push(out1);
1178        }
1179
1180        // Communication round 1
1181        let mut poly_fragments = vec![
1182            Vec::<Scalar>::with_capacity(parameters.share_count as usize);
1183            parameters.share_count as usize
1184        ];
1185        for row_i in dkg_1 {
1186            for j in 0..parameters.share_count {
1187                poly_fragments[j as usize].push(row_i[j as usize]);
1188            }
1189        }
1190
1191        // Phase 2
1192        let mut poly_points: Vec<Scalar> = Vec::with_capacity(parameters.share_count as usize);
1193        let mut proofs_commitments: Vec<ProofCommitment<TestCurve>> =
1194            Vec::with_capacity(parameters.share_count as usize);
1195        let mut zero_kept_2to3: Vec<BTreeMap<PartyIndex, KeepInitZeroSharePhase2to3>> =
1196            Vec::with_capacity(parameters.share_count as usize);
1197        let mut zero_transmit_2to4: Vec<Vec<TransmitInitZeroSharePhase2to4>> =
1198            Vec::with_capacity(parameters.share_count as usize);
1199        let mut bip_kept_2to3: Vec<UniqueKeepDerivationPhase2to3> =
1200            Vec::with_capacity(parameters.share_count as usize);
1201        let mut bip_broadcast_2to4: BTreeMap<PartyIndex, BroadcastDerivationPhase2to4> =
1202            BTreeMap::new();
1203        for i in 0..parameters.share_count {
1204            let (out1, out2, out3, out4, out5, out6) =
1205                phase2::<TestCurve>(&all_data[i as usize], &poly_fragments[i as usize]);
1206
1207            poly_points.push(out1);
1208            proofs_commitments.push(out2);
1209            zero_kept_2to3.push(out3);
1210            zero_transmit_2to4.push(out4);
1211            bip_kept_2to3.push(out5);
1212            bip_broadcast_2to4.insert(PartyIndex::new(i + 1).unwrap(), out6);
1213        }
1214
1215        // Communication round 2
1216        let mut zero_received_2to4: Vec<Vec<TransmitInitZeroSharePhase2to4>> =
1217            Vec::with_capacity(parameters.share_count as usize);
1218        for i in 1..=parameters.share_count {
1219            let i_idx = PartyIndex::new(i).unwrap();
1220            let mut new_row: Vec<TransmitInitZeroSharePhase2to4> =
1221                Vec::with_capacity((parameters.share_count - 1) as usize);
1222            for party in &zero_transmit_2to4 {
1223                for message in party {
1224                    if message.parties.receiver == i_idx {
1225                        new_row.push(message.clone());
1226                    }
1227                }
1228            }
1229            zero_received_2to4.push(new_row);
1230        }
1231
1232        // Phase 3
1233        let mut zero_kept_3to4: Vec<BTreeMap<PartyIndex, KeepInitZeroSharePhase3to4>> =
1234            Vec::with_capacity(parameters.share_count as usize);
1235        let mut zero_transmit_3to4: Vec<Vec<TransmitInitZeroSharePhase3to4>> =
1236            Vec::with_capacity(parameters.share_count as usize);
1237        let mut mul_kept_3to4: Vec<BTreeMap<PartyIndex, KeepInitMulPhase3to4<TestCurve>>> =
1238            Vec::with_capacity(parameters.share_count as usize);
1239        let mut mul_transmit_3to4: Vec<Vec<TransmitInitMulPhase3to4<TestCurve>>> =
1240            Vec::with_capacity(parameters.share_count as usize);
1241        let mut bip_broadcast_3to4: BTreeMap<PartyIndex, BroadcastDerivationPhase3to4> =
1242            BTreeMap::new();
1243        for i in 0..parameters.share_count {
1244            let (out1, out2, out3, out4, out5) = phase3::<TestCurve>(
1245                &all_data[i as usize],
1246                &zero_kept_2to3[i as usize],
1247                &bip_kept_2to3[i as usize],
1248            );
1249
1250            zero_kept_3to4.push(out1);
1251            zero_transmit_3to4.push(out2);
1252            mul_kept_3to4.push(out3);
1253            mul_transmit_3to4.push(out4);
1254            bip_broadcast_3to4.insert(PartyIndex::new(i + 1).unwrap(), out5);
1255        }
1256
1257        // Communication round 3
1258        let mut zero_received_3to4: Vec<Vec<TransmitInitZeroSharePhase3to4>> =
1259            Vec::with_capacity(parameters.share_count as usize);
1260        let mut mul_received_3to4: Vec<Vec<TransmitInitMulPhase3to4<TestCurve>>> =
1261            Vec::with_capacity(parameters.share_count as usize);
1262        for i in 1..=parameters.share_count {
1263            let i_idx = PartyIndex::new(i).unwrap();
1264            let mut zero_row: Vec<TransmitInitZeroSharePhase3to4> =
1265                Vec::with_capacity((parameters.share_count - 1) as usize);
1266            for party in &zero_transmit_3to4 {
1267                for message in party {
1268                    if message.parties.receiver == i_idx {
1269                        zero_row.push(message.clone());
1270                    }
1271                }
1272            }
1273            zero_received_3to4.push(zero_row);
1274
1275            let mut mul_row: Vec<TransmitInitMulPhase3to4<TestCurve>> =
1276                Vec::with_capacity((parameters.share_count - 1) as usize);
1277            for party in &mul_transmit_3to4 {
1278                for message in party {
1279                    if message.parties.receiver == i_idx {
1280                        mul_row.push(message.clone());
1281                    }
1282                }
1283            }
1284            mul_received_3to4.push(mul_row);
1285        }
1286
1287        DkgPhase4Inputs {
1288            all_data,
1289            poly_points,
1290            proofs_commitments,
1291            zero_kept_3to4,
1292            zero_received_2to4,
1293            zero_received_3to4,
1294            mul_kept_3to4,
1295            mul_received_3to4,
1296            bip_broadcast_2to4,
1297            bip_broadcast_3to4,
1298        }
1299    }
1300
1301    /// Tests if phase 4 aborts when multiplication-init messages are missing.
1302    #[test]
1303    fn test_dkg_phase4_rejects_missing_mul_init_messages() {
1304        let mut data = setup_two_party_dkg_phase4_inputs();
1305        data.mul_received_3to4[0].clear();
1306
1307        let result = phase4::<TestCurve>(
1308            &data.all_data[0],
1309            &data.poly_points[0],
1310            &data.proofs_commitments,
1311            &data.zero_kept_3to4[0],
1312            &data.zero_received_2to4[0],
1313            &data.zero_received_3to4[0],
1314            &data.mul_kept_3to4[0],
1315            &data.mul_received_3to4[0],
1316            &data.bip_broadcast_2to4,
1317            &data.bip_broadcast_3to4,
1318            no_address,
1319        );
1320        let abort = result.expect_err("missing multiplication-init message should be rejected");
1321        assert!(matches!(
1322            abort.reason,
1323            AbortReason::WrongMessageCount { .. }
1324        ));
1325    }
1326
1327    // DISTRIBUTED KEY GENERATION (without initializations)
1328
1329    // We are not testing in the moment the initializations for zero shares
1330    // and multiplication here because they are only used during signing.
1331
1332    // The initializations are checked after these tests (see below).
1333
1334    /// Tests if the main steps of the protocol do not generate
1335    /// an unexpected [`Abort`] in the 2-of-2 scenario.
1336    #[test]
1337    fn test_dkg_t2_n2() {
1338        let parameters = Parameters {
1339            threshold: 2,
1340            share_count: 2,
1341        };
1342        const SESSION_ID_LEN: usize = 32;
1343        let session_id = rng::get_rng().random::<[u8; SESSION_ID_LEN]>();
1344
1345        // Phase 1 (Steps 1 and 2)
1346        let p1_phase1 = step2::<TestCurve>(&parameters, &step1::<TestCurve>(&parameters)); //p1 = Party 1
1347        let p2_phase1 = step2::<TestCurve>(&parameters, &step1::<TestCurve>(&parameters)); //p2 = Party 2
1348
1349        assert_eq!(p1_phase1.len(), 2);
1350        assert_eq!(p2_phase1.len(), 2);
1351
1352        // Communication round 1
1353        let p1_poly_fragments = vec![p1_phase1[0], p2_phase1[0]];
1354        let p2_poly_fragments = vec![p1_phase1[1], p2_phase1[1]];
1355
1356        // Phase 2 (Step 3)
1357        let p1_phase2 =
1358            step3::<TestCurve>(PartyIndex::new(1).unwrap(), &session_id, &p1_poly_fragments);
1359        let p2_phase2 =
1360            step3::<TestCurve>(PartyIndex::new(2).unwrap(), &session_id, &p2_poly_fragments);
1361
1362        let (_, p1_proof_commitment) = p1_phase2;
1363        let (_, p2_proof_commitment) = p2_phase2;
1364
1365        // Communication rounds 2 and 3
1366        // For tests, they can be done simultaneously
1367        let proofs_commitments = vec![p1_proof_commitment, p2_proof_commitment];
1368
1369        // Phase 4 (Step 5)
1370        let p1_result = step5::<TestCurve>(
1371            &parameters,
1372            PartyIndex::new(1).unwrap(),
1373            &session_id,
1374            &proofs_commitments,
1375        );
1376        let p2_result = step5::<TestCurve>(
1377            &parameters,
1378            PartyIndex::new(2).unwrap(),
1379            &session_id,
1380            &proofs_commitments,
1381        );
1382
1383        assert!(p1_result.is_ok());
1384        assert!(p2_result.is_ok());
1385    }
1386
1387    /// Tests if the main steps of the protocol do not generate
1388    /// an unexpected [`Abort`] in the t-of-n scenario, where
1389    /// t and n are small random values.
1390    #[test]
1391    fn test_dkg_random() {
1392        let threshold = rng::get_rng().random_range(2..=5); // You can change the ranges here.
1393        let offset = rng::get_rng().random_range(0..=5);
1394
1395        let parameters = Parameters {
1396            threshold,
1397            share_count: threshold + offset,
1398        }; // You can fix the parameters if you prefer.
1399        const SESSION_ID_LEN: usize = 32;
1400        let session_id = rng::get_rng().random::<[u8; SESSION_ID_LEN]>();
1401
1402        // Phase 1 (Steps 1 and 2)
1403        // Matrix of polynomial points
1404        let mut phase1: Vec<Vec<Scalar>> = Vec::with_capacity(parameters.share_count as usize);
1405        for _ in 0..parameters.share_count {
1406            let party_phase1 = step2::<TestCurve>(&parameters, &step1::<TestCurve>(&parameters));
1407            assert_eq!(party_phase1.len(), parameters.share_count as usize);
1408            phase1.push(party_phase1);
1409        }
1410
1411        // Communication round 1
1412        // We transpose the matrix
1413        let mut poly_fragments = vec![
1414            Vec::<Scalar>::with_capacity(parameters.share_count as usize);
1415            parameters.share_count as usize
1416        ];
1417        for row_i in phase1 {
1418            for j in 0..parameters.share_count {
1419                poly_fragments[j as usize].push(row_i[j as usize]);
1420            }
1421        }
1422
1423        // Phase 2 (Step 3) + Communication rounds 2 and 3
1424        let mut proofs_commitments: Vec<ProofCommitment<TestCurve>> =
1425            Vec::with_capacity(parameters.share_count as usize);
1426        for i in 0..parameters.share_count {
1427            let party_i_phase2 = step3::<TestCurve>(
1428                PartyIndex::new(i + 1).unwrap(),
1429                &session_id,
1430                &poly_fragments[i as usize],
1431            );
1432            let (_, party_i_proof_commitment) = party_i_phase2;
1433            proofs_commitments.push(party_i_proof_commitment);
1434        }
1435
1436        // Phase 4 (Step 5)
1437        for i in 0..parameters.share_count {
1438            let result = step5::<TestCurve>(
1439                &parameters,
1440                PartyIndex::new(i + 1).unwrap(),
1441                &session_id,
1442                &proofs_commitments,
1443            );
1444            assert!(result.is_ok());
1445        }
1446    }
1447
1448    /// Tests if the main steps of the protocol generate
1449    /// the expected public key.
1450    ///
1451    /// In this case, we remove the randomness of [Step 1](step1)
1452    /// by providing fixed values.
1453    ///
1454    /// This functions treats the 2-of-2 scenario.
1455    #[test]
1456    fn test1_dkg_t2_n2_fixed_polynomials() {
1457        let parameters = Parameters {
1458            threshold: 2,
1459            share_count: 2,
1460        };
1461        const SESSION_ID_LEN: usize = 32;
1462        let session_id = rng::get_rng().random::<[u8; SESSION_ID_LEN]>();
1463
1464        // We will define the fragments directly
1465        let p1_poly_fragments = vec![Scalar::from(1u32), Scalar::from(3u32)];
1466        let p2_poly_fragments = vec![Scalar::from(2u32), Scalar::from(4u32)];
1467
1468        // In this case, the secret polynomial p is of degree 1 and satisfies p(1) = 1+3 = 4 and p(2) = 2+4 = 6
1469        // In particular, we must have p(0) = 2, which is the "hypothetical" secret key.
1470        // For this reason, we should expect the public key to be 2 * generator.
1471
1472        // Phase 2 (Step 3)
1473        let p1_phase2 =
1474            step3::<TestCurve>(PartyIndex::new(1).unwrap(), &session_id, &p1_poly_fragments);
1475        let p2_phase2 =
1476            step3::<TestCurve>(PartyIndex::new(2).unwrap(), &session_id, &p2_poly_fragments);
1477
1478        let (_, p1_proof_commitment) = p1_phase2;
1479        let (_, p2_proof_commitment) = p2_phase2;
1480
1481        // Communication rounds 2 and 3
1482        // For tests, they can be done simultaneously
1483        let proofs_commitments = vec![p1_proof_commitment, p2_proof_commitment];
1484
1485        // Phase 4 (Step 5)
1486        let p1_result = step5::<TestCurve>(
1487            &parameters,
1488            PartyIndex::new(1).unwrap(),
1489            &session_id,
1490            &proofs_commitments,
1491        );
1492        let p2_result = step5::<TestCurve>(
1493            &parameters,
1494            PartyIndex::new(2).unwrap(),
1495            &session_id,
1496            &proofs_commitments,
1497        );
1498
1499        assert!(p1_result.is_ok());
1500        assert!(p2_result.is_ok());
1501
1502        let (p1_pk, _) = p1_result.unwrap();
1503        let (p2_pk, _) = p2_result.unwrap();
1504
1505        // Verifying the public key
1506        let expected_pk = (AffinePoint::GENERATOR * Scalar::from(2u32)).to_affine();
1507        assert_eq!(p1_pk, expected_pk);
1508        assert_eq!(p2_pk, expected_pk);
1509    }
1510
1511    /// Variation on [`test1_dkg_t2_n2_fixed_polynomials`].
1512    #[test]
1513    fn test2_dkg_t2_n2_fixed_polynomials() {
1514        let parameters = Parameters {
1515            threshold: 2,
1516            share_count: 2,
1517        };
1518        const SESSION_ID_LEN: usize = 32;
1519        let session_id = rng::get_rng().random::<[u8; SESSION_ID_LEN]>();
1520
1521        // We will define the fragments directly
1522        let p1_poly_fragments = vec![Scalar::from(12u32), Scalar::from(2u32)];
1523        let p2_poly_fragments = vec![Scalar::from(2u32), Scalar::from(3u32)];
1524
1525        // In this case, the secret polynomial p is of degree 1 and satisfies p(1) = 12+2 = 14 and p(2) = 2+3 = 5
1526        // In particular, we must have p(0) = 23, which is the "hypothetical" secret key.
1527        // For this reason, we should expect the public key to be 23 * generator.
1528
1529        // Phase 2 (Step 3)
1530        let p1_phase2 =
1531            step3::<TestCurve>(PartyIndex::new(1).unwrap(), &session_id, &p1_poly_fragments);
1532        let p2_phase2 =
1533            step3::<TestCurve>(PartyIndex::new(2).unwrap(), &session_id, &p2_poly_fragments);
1534
1535        let (_, p1_proof_commitment) = p1_phase2;
1536        let (_, p2_proof_commitment) = p2_phase2;
1537
1538        // Communication rounds 2 and 3
1539        // For tests, they can be done simultaneously
1540        let proofs_commitments = vec![p1_proof_commitment, p2_proof_commitment];
1541
1542        // Phase 4 (Step 5)
1543        let p1_result = step5::<TestCurve>(
1544            &parameters,
1545            PartyIndex::new(1).unwrap(),
1546            &session_id,
1547            &proofs_commitments,
1548        );
1549        let p2_result = step5::<TestCurve>(
1550            &parameters,
1551            PartyIndex::new(2).unwrap(),
1552            &session_id,
1553            &proofs_commitments,
1554        );
1555
1556        assert!(p1_result.is_ok());
1557        assert!(p2_result.is_ok());
1558
1559        let (p1_pk, _) = p1_result.unwrap();
1560        let (p2_pk, _) = p2_result.unwrap();
1561
1562        // Verifying the public key
1563        let expected_pk = (AffinePoint::GENERATOR * Scalar::from(23u32)).to_affine();
1564        assert_eq!(p1_pk, expected_pk);
1565        assert_eq!(p2_pk, expected_pk);
1566    }
1567
1568    /// The same as [`test1_dkg_t2_n2_fixed_polynomials`]
1569    /// but in the 3-of-5 scenario.
1570    #[test]
1571    fn test_dkg_t3_n5_fixed_polynomials() {
1572        let parameters = Parameters {
1573            threshold: 3,
1574            share_count: 5,
1575        };
1576        const SESSION_ID_LEN: usize = 32;
1577        let session_id = rng::get_rng().random::<[u8; SESSION_ID_LEN]>();
1578
1579        // We will define the fragments directly
1580        let poly_fragments = [
1581            vec![
1582                Scalar::from(5u32),
1583                Scalar::from(1u32),
1584                Scalar::from(5u32).negate(),
1585                Scalar::from(2u32).negate(),
1586                Scalar::from(3u32).negate(),
1587            ],
1588            vec![
1589                Scalar::from(9u32),
1590                Scalar::from(3u32),
1591                Scalar::from(4u32).negate(),
1592                Scalar::from(5u32).negate(),
1593                Scalar::from(7u32).negate(),
1594            ],
1595            vec![
1596                Scalar::from(15u32),
1597                Scalar::from(7u32),
1598                Scalar::from(1u32).negate(),
1599                Scalar::from(10u32).negate(),
1600                Scalar::from(13u32).negate(),
1601            ],
1602            vec![
1603                Scalar::from(23u32),
1604                Scalar::from(13u32),
1605                Scalar::from(4u32),
1606                Scalar::from(17u32).negate(),
1607                Scalar::from(21u32).negate(),
1608            ],
1609            vec![
1610                Scalar::from(33u32),
1611                Scalar::from(21u32),
1612                Scalar::from(11u32),
1613                Scalar::from(26u32).negate(),
1614                Scalar::from(31u32).negate(),
1615            ],
1616        ];
1617
1618        // In this case, the secret polynomial p is of degree 2 and satisfies:
1619        // p(1) = -4, p(2) = -4, p(3) = -2, p(4) = 2, p(5) = 8.
1620        // Hence we must have p(x) = x^2 - 3x - 2.
1621        // In particular, we must have p(0) = -2, which is the "hypothetical" secret key.
1622        // For this reason, we should expect the public key to be (-2) * generator.
1623
1624        // Phase 2 (Step 3) + Communication rounds 2 and 3
1625        let mut proofs_commitments: Vec<ProofCommitment<TestCurve>> =
1626            Vec::with_capacity(parameters.share_count as usize);
1627        for i in 0..parameters.share_count {
1628            let party_i_phase2 = step3::<TestCurve>(
1629                PartyIndex::new(i + 1).unwrap(),
1630                &session_id,
1631                &poly_fragments[i as usize],
1632            );
1633            let (_, party_i_proof_commitment) = party_i_phase2;
1634            proofs_commitments.push(party_i_proof_commitment);
1635        }
1636
1637        // Phase 4 (Step 5)
1638        let mut public_keys: Vec<AffinePoint> = Vec::with_capacity(parameters.share_count as usize);
1639        for i in 0..parameters.share_count {
1640            let (pk, _) = step5::<TestCurve>(
1641                &parameters,
1642                PartyIndex::new(i + 1).unwrap(),
1643                &session_id,
1644                &proofs_commitments,
1645            )
1646            .unwrap_or_else(|abort| {
1647                panic!("Party {} aborted: {:?}", abort.index, abort.description());
1648            });
1649            public_keys.push(pk);
1650        }
1651
1652        // Verifying the public key
1653        let expected_pk = (AffinePoint::GENERATOR * Scalar::from(2u32).negate()).to_affine();
1654        for pk in public_keys {
1655            assert_eq!(pk, expected_pk);
1656        }
1657    }
1658
1659    // DISTRIBUTED KEY GENERATION (with initializations)
1660
1661    // We now test if the initialization procedures don't abort.
1662    // The verification that they really work is done in signing.rs.
1663
1664    // Disclaimer: this implementation is not the most efficient,
1665    // we are only testing if everything works! Note as well that
1666    // parties are being simulated one after the other, but they
1667    // should actually execute the protocol simultaneously.
1668
1669    /// Tests if the whole DKG protocol (with initializations)
1670    /// does not generate an unexpected [`Abort`].
1671    ///
1672    /// The correctness of the protocol is verified on `test_dkg_and_signing`.
1673    #[test]
1674    fn test_dkg_initialization() {
1675        let threshold = rng::get_rng().random_range(2..=5); // You can change the ranges here.
1676        let offset = rng::get_rng().random_range(0..=5);
1677
1678        let parameters = Parameters {
1679            threshold,
1680            share_count: threshold + offset,
1681        }; // You can fix the parameters if you prefer.
1682        const SESSION_ID_LEN: usize = 32;
1683        let session_id = rng::get_rng().random::<[u8; SESSION_ID_LEN]>();
1684
1685        // Each party prepares their data for this DKG.
1686        let mut all_data: Vec<SessionData> = Vec::with_capacity(parameters.share_count as usize);
1687        for i in 0..parameters.share_count {
1688            all_data.push(SessionData {
1689                parameters: parameters.clone(),
1690                party_index: PartyIndex::new(i + 1).unwrap(),
1691                session_id: session_id.to_vec(),
1692            });
1693        }
1694
1695        // Phase 1
1696        let mut dkg_1: Vec<Vec<Scalar>> = Vec::with_capacity(parameters.share_count as usize);
1697        for i in 0..parameters.share_count {
1698            let out1 = phase1::<TestCurve>(&all_data[i as usize]);
1699
1700            dkg_1.push(out1);
1701        }
1702
1703        // Communication round 1 - Each party receives a fragment from each counterparty.
1704        // They also produce a fragment for themselves.
1705        let mut poly_fragments = vec![
1706            Vec::<Scalar>::with_capacity(parameters.share_count as usize);
1707            parameters.share_count as usize
1708        ];
1709        for row_i in dkg_1 {
1710            for j in 0..parameters.share_count {
1711                poly_fragments[j as usize].push(row_i[j as usize]);
1712            }
1713        }
1714
1715        // Phase 2
1716        let mut poly_points: Vec<Scalar> = Vec::with_capacity(parameters.share_count as usize);
1717        let mut proofs_commitments: Vec<ProofCommitment<TestCurve>> =
1718            Vec::with_capacity(parameters.share_count as usize);
1719        let mut zero_kept_2to3: Vec<BTreeMap<PartyIndex, KeepInitZeroSharePhase2to3>> =
1720            Vec::with_capacity(parameters.share_count as usize);
1721        let mut zero_transmit_2to4: Vec<Vec<TransmitInitZeroSharePhase2to4>> =
1722            Vec::with_capacity(parameters.share_count as usize);
1723        let mut bip_kept_2to3: Vec<UniqueKeepDerivationPhase2to3> =
1724            Vec::with_capacity(parameters.share_count as usize);
1725        let mut bip_broadcast_2to4: BTreeMap<PartyIndex, BroadcastDerivationPhase2to4> =
1726            BTreeMap::new();
1727        for i in 0..parameters.share_count {
1728            let (out1, out2, out3, out4, out5, out6) =
1729                phase2::<TestCurve>(&all_data[i as usize], &poly_fragments[i as usize]);
1730
1731            poly_points.push(out1);
1732            proofs_commitments.push(out2);
1733            zero_kept_2to3.push(out3);
1734            zero_transmit_2to4.push(out4);
1735            bip_kept_2to3.push(out5);
1736            bip_broadcast_2to4.insert(PartyIndex::new(i + 1).unwrap(), out6); // This variable should be grouped into a BTreeMap.
1737        }
1738
1739        // Communication round 2
1740        let mut zero_received_2to4: Vec<Vec<TransmitInitZeroSharePhase2to4>> =
1741            Vec::with_capacity(parameters.share_count as usize);
1742        for i in 1..=parameters.share_count {
1743            let i_idx = PartyIndex::new(i).unwrap();
1744            // We don't need to transmit the commitments because proofs_commitments is already what we need.
1745            // In practice, this should be done here.
1746
1747            let mut new_row: Vec<TransmitInitZeroSharePhase2to4> =
1748                Vec::with_capacity((parameters.share_count - 1) as usize);
1749            for party in &zero_transmit_2to4 {
1750                for message in party {
1751                    // Check if this message should be sent to us.
1752                    if message.parties.receiver == i_idx {
1753                        new_row.push(message.clone());
1754                    }
1755                }
1756            }
1757            zero_received_2to4.push(new_row);
1758        }
1759
1760        // bip_transmit_2to4 is already in the format we need.
1761        // In practice, the messages received should be grouped into a BTreeMap.
1762
1763        // Phase 3
1764        let mut zero_kept_3to4: Vec<BTreeMap<PartyIndex, KeepInitZeroSharePhase3to4>> =
1765            Vec::with_capacity(parameters.share_count as usize);
1766        let mut zero_transmit_3to4: Vec<Vec<TransmitInitZeroSharePhase3to4>> =
1767            Vec::with_capacity(parameters.share_count as usize);
1768        let mut mul_kept_3to4: Vec<BTreeMap<PartyIndex, KeepInitMulPhase3to4<TestCurve>>> =
1769            Vec::with_capacity(parameters.share_count as usize);
1770        let mut mul_transmit_3to4: Vec<Vec<TransmitInitMulPhase3to4<TestCurve>>> =
1771            Vec::with_capacity(parameters.share_count as usize);
1772        let mut bip_broadcast_3to4: BTreeMap<PartyIndex, BroadcastDerivationPhase3to4> =
1773            BTreeMap::new();
1774        for i in 0..parameters.share_count {
1775            let (out1, out2, out3, out4, out5) = phase3::<TestCurve>(
1776                &all_data[i as usize],
1777                &zero_kept_2to3[i as usize],
1778                &bip_kept_2to3[i as usize],
1779            );
1780
1781            zero_kept_3to4.push(out1);
1782            zero_transmit_3to4.push(out2);
1783            mul_kept_3to4.push(out3);
1784            mul_transmit_3to4.push(out4);
1785            bip_broadcast_3to4.insert(PartyIndex::new(i + 1).unwrap(), out5); // This variable should be grouped into a BTreeMap.
1786        }
1787
1788        // Communication round 3
1789        let mut zero_received_3to4: Vec<Vec<TransmitInitZeroSharePhase3to4>> =
1790            Vec::with_capacity(parameters.share_count as usize);
1791        let mut mul_received_3to4: Vec<Vec<TransmitInitMulPhase3to4<TestCurve>>> =
1792            Vec::with_capacity(parameters.share_count as usize);
1793        for i in 1..=parameters.share_count {
1794            let i_idx = PartyIndex::new(i).unwrap();
1795            // We don't need to transmit the proofs because proofs_commitments is already what we need.
1796            // In practice, this should be done here.
1797
1798            let mut new_row: Vec<TransmitInitZeroSharePhase3to4> =
1799                Vec::with_capacity((parameters.share_count - 1) as usize);
1800            for party in &zero_transmit_3to4 {
1801                for message in party {
1802                    // Check if this message should be sent to us.
1803                    if message.parties.receiver == i_idx {
1804                        new_row.push(message.clone());
1805                    }
1806                }
1807            }
1808            zero_received_3to4.push(new_row);
1809
1810            let mut new_row: Vec<TransmitInitMulPhase3to4<TestCurve>> =
1811                Vec::with_capacity((parameters.share_count - 1) as usize);
1812            for party in &mul_transmit_3to4 {
1813                for message in party {
1814                    // Check if this message should be sent to us.
1815                    if message.parties.receiver == i_idx {
1816                        new_row.push(message.clone());
1817                    }
1818                }
1819            }
1820            mul_received_3to4.push(new_row);
1821        }
1822
1823        // bip_transmit_3to4 is already in the format we need.
1824        // In practice, the messages received should be grouped into a BTreeMap.
1825
1826        // Phase 4
1827        let mut parties: Vec<Party<TestCurve>> =
1828            Vec::with_capacity((parameters.share_count) as usize);
1829        let mut pkgs: Vec<PublicKeyPackage<TestCurve>> =
1830            Vec::with_capacity(parameters.share_count as usize);
1831        for i in 0..parameters.share_count {
1832            let result = phase4::<TestCurve>(
1833                &all_data[i as usize],
1834                &poly_points[i as usize],
1835                &proofs_commitments,
1836                &zero_kept_3to4[i as usize],
1837                &zero_received_2to4[i as usize],
1838                &zero_received_3to4[i as usize],
1839                &mul_kept_3to4[i as usize],
1840                &mul_received_3to4[i as usize],
1841                &bip_broadcast_2to4,
1842                &bip_broadcast_3to4,
1843                no_address,
1844            );
1845            match result {
1846                Err(abort) => {
1847                    panic!("Party {} aborted: {:?}", abort.index, abort.description());
1848                }
1849                Ok((party, pkg)) => {
1850                    parties.push(party);
1851                    pkgs.push(pkg);
1852                }
1853            }
1854        }
1855
1856        // We check if the public keys and chain codes are the same.
1857        let expected_pk = parties[0].pk;
1858        let expected_chain_code = parties[0].derivation_data.chain_code;
1859        for party in &parties {
1860            assert_eq!(expected_pk, party.pk);
1861            assert_eq!(expected_chain_code, party.derivation_data.chain_code);
1862        }
1863
1864        // All parties must produce identical PublicKeyPackages.
1865        let pkg0 = &pkgs[0];
1866        assert_eq!(*pkg0.verifying_key(), expected_pk);
1867        assert_eq!(pkg0.threshold(), parameters.threshold);
1868        assert_eq!(pkg0.share_count(), parameters.share_count);
1869        for pkg in &pkgs[1..] {
1870            assert_eq!(pkg.verifying_key(), pkg0.verifying_key());
1871            for i in 1..=parameters.share_count {
1872                let idx = PartyIndex::new(i).unwrap();
1873                assert_eq!(pkg.verifying_share(idx), pkg0.verifying_share(idx));
1874            }
1875        }
1876
1877        // Each verification share must equal poly_point * G for the corresponding party.
1878        for party in &parties {
1879            let expected_share = (AffinePoint::GENERATOR * party.poly_point).to_affine();
1880            assert!(pkg0.verify_share(party.party_index, &expected_share));
1881        }
1882
1883        // Address must be empty (no_address stub).
1884        assert_eq!(parties[0].address, String::new());
1885    }
1886}