use super::utils::threshold;
use crate::bls12381::{
dkg::{ops, Error},
primitives::{
group::{self, Element, Share},
poly::{self, Eval},
},
};
use crate::PublicKey;
use commonware_utils::quorum;
use std::collections::{BTreeMap, HashMap, HashSet};
#[derive(Clone)]
pub struct Output {
pub public: poly::Public,
pub commitments: Vec<poly::Public>,
pub share: Share,
}
pub struct P0 {
me: PublicKey,
threshold: u32,
previous: Option<(poly::Public, Share)>,
concurrency: usize,
dealers_ordered: HashMap<PublicKey, u32>,
recipients: Vec<PublicKey>,
recipients_ordered: HashMap<PublicKey, u32>,
}
impl P0 {
pub fn new(
me: PublicKey,
previous: Option<(poly::Public, Share)>,
mut dealers: Vec<PublicKey>,
mut recipients: Vec<PublicKey>,
concurrency: usize,
) -> Self {
dealers.sort();
let dealers_ordered = dealers
.iter()
.enumerate()
.map(|(i, pk)| (pk.clone(), i as u32))
.collect::<HashMap<_, _>>();
if !dealers_ordered.contains_key(&me) {
panic!("me must be in dealers");
}
recipients.sort();
let recipients_ordered = recipients
.iter()
.enumerate()
.map(|(i, pk)| (pk.clone(), i as u32))
.collect();
Self {
me,
threshold: threshold(recipients.len() as u32).expect("insufficient participants"),
previous,
concurrency,
dealers_ordered,
recipients,
recipients_ordered,
}
}
pub fn finalize(self) -> (Option<P1>, poly::Public, Vec<Share>) {
let (public, share) = match self.previous {
Some((public, share)) => (Some(public), Some(share)),
None => (None, None),
};
let (commitment, shares) =
ops::generate_shares(share, self.recipients.len() as u32, self.threshold);
let p1 = if self.recipients_ordered.contains_key(&self.me) {
Some(P1 {
me: self.me,
threshold: self.threshold,
previous: public,
concurrency: self.concurrency,
dealers_ordered: self.dealers_ordered,
recipients_ordered: self.recipients_ordered,
commitments: HashMap::new(),
valid: BTreeMap::new(),
})
} else {
None
};
(p1, commitment, shares)
}
}
pub struct P1 {
me: PublicKey,
threshold: u32,
previous: Option<poly::Public>,
concurrency: usize,
dealers_ordered: HashMap<PublicKey, u32>,
recipients_ordered: HashMap<PublicKey, u32>,
commitments: HashMap<PublicKey, poly::Public>,
valid: BTreeMap<u32, (poly::Public, Share)>,
}
impl P1 {
pub fn new(
me: PublicKey,
previous: Option<poly::Public>,
mut dealers: Vec<PublicKey>,
mut recipients: Vec<PublicKey>,
concurrency: usize,
) -> Self {
dealers.sort();
let dealers_ordered = dealers
.iter()
.enumerate()
.map(|(i, pk)| (pk.clone(), i as u32))
.collect();
recipients.sort();
let recipients_ordered = recipients
.iter()
.enumerate()
.map(|(i, pk)| (pk.clone(), i as u32))
.collect();
Self {
me,
threshold: threshold(recipients.len() as u32).expect("insufficient participants"),
previous,
concurrency,
dealers_ordered,
recipients_ordered,
commitments: HashMap::new(),
valid: BTreeMap::new(),
}
}
pub fn commitment(&mut self, dealer: PublicKey, commitment: poly::Public) -> Result<(), Error> {
let idx = match self.dealers_ordered.get(&dealer) {
Some(contributor) => *contributor,
None => return Err(Error::DealerInvalid),
};
ops::verify_commitment(self.previous.as_ref(), idx, &commitment, self.threshold)?;
self.commitments.insert(dealer, commitment);
Ok(())
}
pub fn has(&self, dealer: PublicKey) -> bool {
self.commitments.contains_key(&dealer)
}
pub fn count(&self) -> usize {
self.commitments.len()
}
pub fn finalize(self) -> Option<P2> {
let required_commitments = match &self.previous {
Some(_) => quorum(self.dealers_ordered.len() as u32).unwrap(),
None => quorum(self.recipients_ordered.len() as u32).unwrap(),
} as usize;
if self.commitments.len() < required_commitments {
return None;
}
Some(P2 {
me: self.me,
threshold: self.threshold,
previous: self.previous,
concurrency: self.concurrency,
dealers_ordered: self.dealers_ordered,
recipients_ordered: self.recipients_ordered,
commitments: self.commitments,
valid: self.valid,
})
}
}
pub struct P2 {
me: PublicKey,
threshold: u32,
previous: Option<poly::Public>,
concurrency: usize,
dealers_ordered: HashMap<PublicKey, u32>,
recipients_ordered: HashMap<PublicKey, u32>,
commitments: HashMap<PublicKey, poly::Public>,
valid: BTreeMap<u32, (poly::Public, Share)>,
}
impl P2 {
pub fn share(&mut self, dealer: PublicKey, share: Share) -> Result<(), Error> {
let idx = match self.dealers_ordered.get(&dealer) {
Some(contributor) => *contributor,
None => return Err(Error::DealerInvalid),
};
if share.index != self.recipients_ordered[&self.me] {
return Err(Error::MisdirectedShare);
}
let commitment = match self.commitments.get(&dealer) {
Some(commitment) => commitment.clone(),
None => return Err(Error::MissingCommitment),
};
ops::verify_share(
self.previous.as_ref(),
idx,
&commitment,
self.threshold,
share.index,
&share,
)?;
self.valid.insert(idx, (commitment, share));
Ok(())
}
pub fn finalize(mut self, commitments: Vec<u32>) -> Result<Output, Error> {
let required_commitments_u32 = match &self.previous {
Some(_) => threshold(self.dealers_ordered.len() as u32).unwrap(),
None => self.threshold,
};
let required_commitments = required_commitments_u32 as usize;
if commitments.len() != required_commitments {
return Err(Error::TooManyCommitments);
}
for dealer in &commitments {
if !self.valid.contains_key(dealer) {
return Err(Error::MissingShare);
}
}
let commitments: HashSet<_> = commitments.into_iter().collect();
for dealer in self.valid.keys().cloned().collect::<Vec<_>>() {
if !commitments.contains(&dealer) {
self.valid.remove(&dealer);
}
}
let shares = self.valid.len();
if shares < required_commitments {
return Err(Error::InsufficientDealings);
}
let mut public = poly::Public::zero();
let mut t_commitments = Vec::new();
let mut secret = group::Private::zero();
match self.previous {
None => {
for share in self.valid.values() {
public.add(&share.0);
t_commitments.push(share.0.clone());
secret.add(&share.1.private);
}
}
Some(previous) => {
let commitments: BTreeMap<u32, poly::Public> = self
.valid
.iter()
.take(required_commitments)
.map(|(dealer, (commitment, _))| (*dealer, commitment.clone()))
.collect();
t_commitments = commitments.values().cloned().collect();
public =
ops::recover_public(&previous, commitments, self.threshold, self.concurrency)?;
let shares = self
.valid
.into_iter()
.take(required_commitments)
.map(|(dealer, (_, share))| Eval {
index: dealer,
value: share.private,
})
.collect::<Vec<_>>();
secret = match poly::Private::recover(required_commitments_u32, shares) {
Ok(share) => share,
Err(_) => return Err(Error::ShareInterpolationFailed),
};
}
}
Ok(Output {
public,
commitments: t_commitments,
share: Share {
index: self.recipients_ordered[&self.me],
private: secret,
},
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Ed25519, Scheme};
use std::collections::HashMap;
fn create_and_verify_shares(n: u32, dealers: u32, concurrency: usize) {
let mut contributors = (0..n)
.map(|i| Ed25519::from_seed(i as u64).public_key())
.collect::<Vec<_>>();
contributors.sort();
let mut contributor_shares = HashMap::new();
let mut commitments = Vec::new();
for i in 0..n {
let me = contributors[i as usize].clone();
let contributor = P0::new(
me,
None,
contributors.clone(),
contributors.clone(),
concurrency,
);
let (contributor, public, shares) = contributor.finalize();
contributor_shares.insert(i, (public.clone(), shares, contributor.unwrap()));
commitments.push(public);
}
for i in 0..dealers {
let dealer = contributors[i as usize].clone();
for j in 0..n {
let (commitment, _, _) = contributor_shares.get(&i).unwrap();
let commitment = commitment.clone();
let (_, _, ref mut recipient) = contributor_shares.get_mut(&j).unwrap();
recipient.commitment(dealer.clone(), commitment).unwrap();
}
}
let mut p2 = HashMap::new();
for i in 0..n {
let (_, shares, contributor) = contributor_shares.remove(&i).unwrap();
let contributor = contributor.finalize().unwrap();
p2.insert(i, (shares, contributor));
}
let mut contributor_shares = p2;
for i in 0..dealers {
let dealer = contributors[i as usize].clone();
for j in 0..n {
let (shares, _) = contributor_shares.get(&i).unwrap();
let share = shares[j as usize];
let (_, recipient) = contributor_shares.get_mut(&j).unwrap();
recipient.share(dealer.clone(), share).unwrap();
}
}
let t = threshold(n).expect("insufficient participants");
let included_commitments = (0..t).collect::<Vec<_>>();
let commitments = commitments[0..t as usize].to_vec();
let mut group: Option<poly::Public> = None;
for i in 0..n {
let (_, contributor) = contributor_shares.remove(&i).unwrap();
let output = contributor
.finalize(included_commitments.clone())
.expect("unable to finalize");
assert_eq!(output.commitments, commitments);
match &group {
Some(group) => {
assert_eq!(output.public, *group);
}
None => {
group = Some(output.public);
}
}
}
}
#[test]
fn test_simple_dkg() {
create_and_verify_shares(5, 5, 4);
}
#[test]
fn test_large_dkg() {
create_and_verify_shares(100, 80, 4);
}
#[test]
fn test_too_many_commitments() {
let n = 10;
let dealers = 8;
let concurrency = 1;
let mut contributors = (0..n)
.map(|i| Ed25519::from_seed(i as u64).public_key())
.collect::<Vec<_>>();
contributors.sort();
let mut contributor_shares = HashMap::new();
let mut commitments = Vec::new();
for i in 0..n {
let me = contributors[i as usize].clone();
let contributor = P0::new(
me,
None,
contributors.clone(),
contributors.clone(),
concurrency,
);
let (contributor, public, shares) = contributor.finalize();
contributor_shares.insert(i, (public.clone(), shares, contributor.unwrap()));
commitments.push(public);
}
for i in 0..dealers {
let dealer = contributors[i as usize].clone();
for j in 0..n {
let (commitment, _, _) = contributor_shares.get(&i).unwrap();
let commitment = commitment.clone();
let (_, _, ref mut recipient) = contributor_shares.get_mut(&j).unwrap();
recipient.commitment(dealer.clone(), commitment).unwrap();
}
}
let mut p2 = HashMap::new();
for i in 0..n {
let (_, shares, contributor) = contributor_shares.remove(&i).unwrap();
let contributor = contributor.finalize().unwrap();
p2.insert(i, (shares, contributor));
}
let mut contributor_shares = p2;
for i in 0..dealers {
let dealer = contributors[i as usize].clone();
for j in 0..n {
let (shares, _) = contributor_shares.get(&i).unwrap();
let share = shares[j as usize];
let (_, recipient) = contributor_shares.get_mut(&j).unwrap();
recipient.share(dealer.clone(), share).unwrap();
}
}
let included_commitments = (0..dealers).collect::<Vec<_>>();
for i in 0..n {
let (_, contributor) = contributor_shares.remove(&i).unwrap();
let result = contributor.finalize(included_commitments.clone());
assert!(matches!(result, Err(Error::TooManyCommitments)));
}
}
#[test]
fn test_insufficient_commitments() {
let n = 10;
let concurrency = 1;
let active_dealers = 4;
let mut contributors = (0..n)
.map(|i| Ed25519::from_seed(i as u64).public_key())
.collect::<Vec<_>>();
contributors.sort();
let mut contributor_shares = HashMap::new();
let mut commitments = Vec::new();
for i in 0..n {
let me = contributors[i as usize].clone();
let contributor = P0::new(
me,
None,
contributors.clone(),
contributors.clone(),
concurrency,
);
let (contributor, public, shares) = contributor.finalize();
contributor_shares.insert(i, (public.clone(), shares, contributor.unwrap()));
commitments.push(public);
}
for i in 0..active_dealers {
let dealer = contributors[i as usize].clone();
for j in 0..n {
let (commitment, _, _) = contributor_shares.get(&i).unwrap();
let commitment = commitment.clone();
let (_, _, ref mut recipient) = contributor_shares.get_mut(&j).unwrap();
recipient.commitment(dealer.clone(), commitment).unwrap();
}
}
for i in 0..n {
let (_, _, contributor) = contributor_shares.remove(&i).unwrap();
assert!(contributor.finalize().is_none());
}
}
#[test]
fn test_share_from_only_threshold() {
let n = 10;
let dealers = 8;
let share_dealers = 4;
let concurrency = 1;
let mut contributors = (0..n)
.map(|i| Ed25519::from_seed(i as u64).public_key())
.collect::<Vec<_>>();
contributors.sort();
let mut contributor_shares = HashMap::new();
let mut commitments = Vec::new();
for i in 0..n {
let me = contributors[i as usize].clone();
let contributor = P0::new(
me,
None,
contributors.clone(),
contributors.clone(),
concurrency,
);
let (contributor, public, shares) = contributor.finalize();
contributor_shares.insert(i, (public.clone(), shares, contributor.unwrap()));
commitments.push(public);
}
for i in 0..dealers {
let dealer = contributors[i as usize].clone();
for j in 0..n {
let (commitment, _, _) = contributor_shares.get(&i).unwrap();
let commitment = commitment.clone();
let (_, _, ref mut recipient) = contributor_shares.get_mut(&j).unwrap();
recipient.commitment(dealer.clone(), commitment).unwrap();
}
}
let mut p2 = HashMap::new();
for i in 0..n {
let (_, shares, contributor) = contributor_shares.remove(&i).unwrap();
let contributor = contributor.finalize().unwrap();
p2.insert(i, (shares, contributor));
}
let mut contributor_shares = p2;
for i in 0..share_dealers {
let dealer = contributors[i as usize].clone();
for j in 0..n {
let (shares, _) = contributor_shares.get(&i).unwrap();
let share = shares[j as usize];
let (_, recipient) = contributor_shares.get_mut(&j).unwrap();
recipient.share(dealer.clone(), share).unwrap();
}
}
let included_commitments = (0..share_dealers).collect::<Vec<_>>();
let commitments = commitments[0..share_dealers as usize].to_vec();
let mut group: Option<poly::Public> = None;
for i in 0..n {
let (_, contributor) = contributor_shares.remove(&i).unwrap();
let output = contributor
.finalize(included_commitments.clone())
.expect("unable to finalize");
assert_eq!(output.commitments, commitments);
match &group {
Some(group) => {
assert_eq!(output.public, *group);
}
None => {
group = Some(output.public);
}
}
}
}
#[test]
fn test_share_insufficient() {
let (n, t) = (10, 4);
let dealers = 8;
let share_dealers = 2;
let concurrency = 1;
let mut contributors = (0..n)
.map(|i| Ed25519::from_seed(i as u64).public_key())
.collect::<Vec<_>>();
contributors.sort();
let mut contributor_shares = HashMap::new();
let mut commitments = Vec::new();
for i in 0..n {
let me = contributors[i as usize].clone();
let contributor = P0::new(
me,
None,
contributors.clone(),
contributors.clone(),
concurrency,
);
let (contributor, public, shares) = contributor.finalize();
contributor_shares.insert(i, (public.clone(), shares, contributor.unwrap()));
commitments.push(public);
}
for i in 0..dealers {
let dealer = contributors[i as usize].clone();
for j in 0..n {
let (commitment, _, _) = contributor_shares.get(&i).unwrap();
let commitment = commitment.clone();
let (_, _, ref mut recipient) = contributor_shares.get_mut(&j).unwrap();
recipient.commitment(dealer.clone(), commitment).unwrap();
}
}
let mut p2 = HashMap::new();
for i in 0..n {
let (_, shares, contributor) = contributor_shares.remove(&i).unwrap();
let contributor = contributor.finalize().unwrap();
p2.insert(i, (shares, contributor));
}
let mut contributor_shares = p2;
for i in 0..share_dealers {
let dealer = contributors[i as usize].clone();
for j in 0..n {
let (shares, _) = contributor_shares.get(&i).unwrap();
let share = shares[j as usize];
let (_, recipient) = contributor_shares.get_mut(&j).unwrap();
recipient.share(dealer.clone(), share).unwrap();
}
}
let included_commitments = (0..t).collect::<Vec<_>>();
for i in 0..n {
let (_, contributor) = contributor_shares.remove(&i).unwrap();
assert!(matches!(
contributor.finalize(included_commitments.clone()),
Err(Error::MissingShare)
));
}
}
}