use crate::bls12381::{
dkg::Error,
primitives::{
group::Share,
ops::msm_interpolate,
poly::{self, compute_weights},
variant::Variant,
},
};
use rand_core::CryptoRngCore;
use rayon::{prelude::*, ThreadPoolBuilder};
use std::collections::BTreeMap;
pub fn generate_shares<R: CryptoRngCore, V: Variant>(
rng: &mut R,
share: Option<Share>,
n: u32,
t: u32,
) -> (poly::Public<V>, Vec<Share>) {
let mut secret = poly::new_from(t - 1, rng);
if let Some(share) = share {
secret.set(0, share.private);
}
let commitment = poly::Public::<V>::commit(secret.clone());
let shares = (0..n)
.map(|i| {
let eval = secret.evaluate(i);
Share {
index: eval.index,
private: eval.value,
}
})
.collect::<Vec<_>>();
(commitment, shares)
}
pub fn evaluate_all<V: Variant>(polynomial: &poly::Public<V>, n: u32) -> Vec<V::Public> {
let mut evals = Vec::with_capacity(n as usize);
for index in 0..n {
evals.push(polynomial.evaluate(index).value);
}
evals
}
pub fn verify_commitment<V: Variant>(
previous: Option<&poly::Public<V>>,
commitment: &poly::Public<V>,
dealer: u32,
t: u32,
) -> Result<(), Error> {
if let Some(previous) = previous {
let expected = previous.evaluate(dealer).value;
if expected != *commitment.constant() {
return Err(Error::UnexpectedPolynomial);
}
}
if commitment.degree() != t - 1 {
return Err(Error::CommitmentWrongDegree);
}
Ok(())
}
pub fn verify_share<V: Variant>(
commitment: &poly::Public<V>,
recipient: u32,
share: &Share,
) -> Result<(), Error> {
if share.index != recipient {
return Err(Error::MisdirectedShare);
}
let expected = share.public::<V>();
let given = commitment.evaluate(share.index);
if given.value != expected {
return Err(Error::ShareWrongCommitment);
}
Ok(())
}
pub fn construct_public<'a, V: Variant>(
commitments: impl IntoIterator<Item = &'a poly::Public<V>>,
required: u32,
) -> Result<poly::Public<V>, Error> {
let mut count = 0;
let mut public = poly::Public::<V>::zero();
for commitment in commitments.into_iter() {
public.add(commitment);
count += 1;
}
if count < required {
return Err(Error::InsufficientDealings);
}
Ok(public)
}
pub fn recover_public_with_weights<V: Variant>(
previous: &poly::Public<V>,
commitments: &BTreeMap<u32, poly::Public<V>>,
weights: &BTreeMap<u32, poly::Weight>,
threshold: u32,
concurrency: usize,
) -> Result<poly::Public<V>, Error> {
let required = previous.required();
if commitments.len() < required as usize {
return Err(Error::InsufficientDealings);
}
let pool = ThreadPoolBuilder::new()
.num_threads(concurrency)
.build()
.expect("unable to build thread pool");
let new = match pool.install(|| {
(0..threshold)
.into_par_iter()
.map(|coeff| {
let evals = commitments
.iter()
.map(|(dealer, commitment)| poly::Eval {
index: *dealer,
value: commitment.get(coeff),
})
.collect::<Vec<_>>();
msm_interpolate(weights, &evals).map_err(|_| Error::PublicKeyInterpolationFailed)
})
.collect::<Result<Vec<_>, _>>()
}) {
Ok(points) => poly::Public::<V>::from(points),
Err(e) => return Err(e),
};
if previous.constant() != new.constant() {
return Err(Error::ReshareMismatch);
}
Ok(new)
}
pub fn recover_public<V: Variant>(
previous: &poly::Public<V>,
commitments: &BTreeMap<u32, poly::Public<V>>,
threshold: u32,
concurrency: usize,
) -> Result<poly::Public<V>, Error> {
let required = previous.required();
if commitments.len() < required as usize {
return Err(Error::InsufficientDealings);
}
let indices: Vec<u32> = commitments.keys().cloned().collect();
let weights = compute_weights(indices).map_err(|_| Error::PublicKeyInterpolationFailed)?;
recover_public_with_weights::<V>(previous, commitments, &weights, threshold, concurrency)
}