pub mod debug;
pub(crate) mod error;
pub(crate) mod knowledge;
pub mod sdkg;
mod state;
pub(crate) mod vote;
pub use error::Error;
pub use state::{DkgState, VoteResponse};
pub use vote::{DkgSignedVote, NodeId};
pub use vote::DkgVote;
#[cfg(test)]
macro_rules! assert_match {
($obj:expr, $pattern:pat $(if $pred:expr)* => $result:expr) => {
match $obj {
$pattern $(if $pred)* => $result,
_ => panic!("Assertion failed")
}
}
}
#[cfg(test)]
pub(crate) use assert_match;
#[cfg(test)]
mod tests {
use super::*;
use bls::{PublicKey, SecretKey, SignatureShare};
use std::collections::BTreeMap;
#[test]
fn test_dkg_for_the_rest_of_us() {
let mut rng = bls::rand::rngs::OsRng;
let sec_key0: SecretKey = bls::rand::random();
let sec_key1: SecretKey = bls::rand::random();
let sec_key2: SecretKey = bls::rand::random();
let pub_keys: BTreeMap<u8, PublicKey> = BTreeMap::from([
(0, sec_key0.public_key()),
(1, sec_key1.public_key()),
(2, sec_key2.public_key()),
]);
let threshold = 1;
let mut dkg_state0 = DkgState::new(0, sec_key0, pub_keys.clone(), threshold, &mut rng)
.expect("Failed to create DKG state");
let mut dkg_state1 = DkgState::new(1, sec_key1, pub_keys.clone(), threshold, &mut rng)
.expect("Failed to create DKG state");
let mut dkg_state2 = DkgState::new(2, sec_key2, pub_keys, threshold, &mut rng)
.expect("Failed to create DKG state");
let part0 = dkg_state0.first_vote().expect("Failed to get first vote");
let part1 = dkg_state1.first_vote().expect("Failed to get first vote");
let part2 = dkg_state2.first_vote().expect("Failed to get first vote");
let res = &dkg_state0.handle_signed_vote(part0.clone(), &mut rng);
assert!(matches!(res.as_deref(), Ok([])));
let res = &dkg_state0.handle_signed_vote(part1.clone(), &mut rng);
assert!(matches!(
res.as_deref(),
Ok([VoteResponse::WaitingForMoreVotes])
));
let res = &dkg_state0.handle_signed_vote(part2.clone(), &mut rng);
let acks0 =
assert_match!(res.as_deref(), Ok([VoteResponse::BroadcastVote(acks)]) => *acks.clone());
let res = &dkg_state1.handle_signed_vote(part0.clone(), &mut rng);
assert!(matches!(
res.as_deref(),
Ok([VoteResponse::WaitingForMoreVotes])
));
let res = &dkg_state1.handle_signed_vote(part2, &mut rng);
let acks1 =
assert_match!(res.as_deref(), Ok([VoteResponse::BroadcastVote(acks)]) => *acks.clone());
let res = &dkg_state2.handle_signed_vote(part0, &mut rng);
assert!(matches!(
res.as_deref(),
Ok([VoteResponse::WaitingForMoreVotes])
));
let res = &dkg_state2.handle_signed_vote(part1, &mut rng);
let acks2 =
assert_match!(res.as_deref(), Ok([VoteResponse::BroadcastVote(acks)]) => *acks.clone());
let res = &dkg_state0.handle_signed_vote(acks1.clone(), &mut rng);
assert!(matches!(
res.as_deref(),
Ok([VoteResponse::WaitingForMoreVotes])
));
let res = &dkg_state0.handle_signed_vote(acks2.clone(), &mut rng);
let all_acks0 = assert_match!(res.as_deref(), Ok([VoteResponse::BroadcastVote(all_acks)]) => *all_acks.clone());
let res = &dkg_state1.handle_signed_vote(acks0.clone(), &mut rng);
assert!(matches!(
res.as_deref(),
Ok([VoteResponse::WaitingForMoreVotes])
));
let res = &dkg_state1.handle_signed_vote(acks2, &mut rng);
let all_acks1 = assert_match!(res.as_deref(), Ok([VoteResponse::BroadcastVote(all_acks)]) => *all_acks.clone());
let res = &dkg_state2.handle_signed_vote(acks0, &mut rng);
assert!(matches!(
res.as_deref(),
Ok([VoteResponse::WaitingForMoreVotes])
));
let res = &dkg_state2.handle_signed_vote(acks1, &mut rng);
let all_acks2 = assert_match!(res.as_deref(), Ok([VoteResponse::BroadcastVote(all_acks)]) => *all_acks.clone());
let res = &dkg_state0.handle_signed_vote(all_acks1.clone(), &mut rng);
assert!(matches!(
res.as_deref(),
Ok([VoteResponse::WaitingForMoreVotes])
));
let res = &dkg_state0.handle_signed_vote(all_acks2.clone(), &mut rng);
let (pubs0, sec0) = assert_match!(res.as_deref(), Ok([VoteResponse::DkgComplete(pubs0, sec0)]) => (pubs0, sec0));
let res = &dkg_state1.handle_signed_vote(all_acks0.clone(), &mut rng);
assert!(matches!(
res.as_deref(),
Ok([VoteResponse::WaitingForMoreVotes])
));
let res = &dkg_state1.handle_signed_vote(all_acks2, &mut rng);
let (pubs1, sec1) = assert_match!(res.as_deref(), Ok([VoteResponse::DkgComplete(pubs1, sec1)]) => (pubs1, sec1));
let res = &dkg_state2.handle_signed_vote(all_acks0, &mut rng);
assert!(matches!(
res.as_deref(),
Ok([VoteResponse::WaitingForMoreVotes])
));
let res = &dkg_state2.handle_signed_vote(all_acks1, &mut rng);
let (pubs2, sec2) = assert_match!(res.as_deref(), Ok([VoteResponse::DkgComplete(pubs2, sec2)]) => (pubs2, sec2));
assert_eq!(pubs0, pubs1);
assert_eq!(pubs1, pubs2);
let msg = "signed message";
let sig_shares: BTreeMap<usize, SignatureShare> =
BTreeMap::from([(0, sec0.sign(msg)), (1, sec1.sign(msg))]);
let sig = pubs2
.combine_signatures(&sig_shares)
.expect("Failed to combine signatures");
assert!(pubs2.public_key().verify(&sig, msg));
let sig_shares: BTreeMap<usize, SignatureShare> =
BTreeMap::from([(1, sec1.sign(msg)), (2, sec2.sign(msg))]);
let sig = pubs0
.combine_signatures(&sig_shares)
.expect("Failed to combine signatures");
assert!(pubs0.public_key().verify(&sig, msg));
}
}