use core::{cmp::PartialEq, fmt::Debug};
use hashbrown::{HashMap, HashSet};
use polynomial::Polynomial;
use rand_core::{CryptoRng, RngCore};
use serde::{Deserialize, Serialize};
use crate::{
common::{MerkleRoot, Nonce, PolyCommitment, PublicNonce, Signature, SignatureShare},
curve::{point::Point, scalar::Scalar},
errors::{AggregatorError, DkgError},
taproot::SchnorrProof,
};
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct PartyState {
pub polynomial: Option<Polynomial<Scalar>>,
pub private_keys: Vec<(u32, Scalar)>,
pub nonce: Nonce,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct SignerState {
pub id: u32,
pub key_ids: Vec<u32>,
pub num_keys: u32,
pub num_parties: u32,
pub threshold: u32,
pub group_key: Point,
pub parties: Vec<(u32, PartyState)>,
}
pub trait Signer: Clone + Debug + PartialEq {
fn new<RNG: RngCore + CryptoRng>(
party_id: u32,
key_ids: &[u32],
num_signers: u32,
num_keys: u32,
threshold: u32,
rng: &mut RNG,
) -> Self;
fn load(state: &SignerState) -> Self;
fn save(&self) -> SignerState;
fn get_id(&self) -> u32;
fn get_key_ids(&self) -> Vec<u32>;
fn get_num_parties(&self) -> u32;
fn get_poly_commitments<RNG: RngCore + CryptoRng>(&self, rng: &mut RNG) -> Vec<PolyCommitment>;
fn reset_polys<RNG: RngCore + CryptoRng>(&mut self, rng: &mut RNG);
fn clear_polys(&mut self);
fn get_shares(&self) -> HashMap<u32, HashMap<u32, Scalar>>;
fn compute_secrets(
&mut self,
shares: &HashMap<u32, HashMap<u32, Scalar>>,
polys: &HashMap<u32, PolyCommitment>,
) -> Result<(), HashMap<u32, DkgError>>;
fn gen_nonces<RNG: RngCore + CryptoRng>(&mut self, rng: &mut RNG) -> Vec<PublicNonce>;
fn compute_intermediate(
msg: &[u8],
signer_ids: &[u32],
key_ids: &[u32],
nonces: &[PublicNonce],
) -> (Vec<Point>, Point);
fn validate_party_id(
signer_id: u32,
party_id: u32,
signer_key_ids: &HashMap<u32, HashSet<u32>>,
) -> bool;
fn sign(
&self,
msg: &[u8],
signer_ids: &[u32],
key_ids: &[u32],
nonces: &[PublicNonce],
) -> Vec<SignatureShare>;
fn sign_schnorr(
&self,
msg: &[u8],
signer_ids: &[u32],
key_ids: &[u32],
nonces: &[PublicNonce],
) -> Vec<SignatureShare>;
fn sign_taproot(
&self,
msg: &[u8],
signer_ids: &[u32],
key_ids: &[u32],
nonces: &[PublicNonce],
merkle_root: Option<MerkleRoot>,
) -> Vec<SignatureShare>;
}
pub trait Aggregator: Clone + Debug + PartialEq {
fn new(num_keys: u32, threshold: u32) -> Self;
fn init(&mut self, poly_comms: &HashMap<u32, PolyCommitment>) -> Result<(), AggregatorError>;
fn sign(
&mut self,
msg: &[u8],
nonces: &[PublicNonce],
sig_shares: &[SignatureShare],
key_ids: &[u32],
) -> Result<Signature, AggregatorError>;
fn sign_schnorr(
&mut self,
msg: &[u8],
nonces: &[PublicNonce],
sig_shares: &[SignatureShare],
key_ids: &[u32],
) -> Result<SchnorrProof, AggregatorError>;
fn sign_taproot(
&mut self,
msg: &[u8],
nonces: &[PublicNonce],
sig_shares: &[SignatureShare],
key_ids: &[u32],
merkle_root: Option<MerkleRoot>,
) -> Result<SchnorrProof, AggregatorError>;
}
pub mod test_helpers {
use hashbrown::HashMap;
use rand_core::{CryptoRng, RngCore};
use crate::{common::PolyCommitment, errors::DkgError, traits::Scalar, util::create_rng};
pub fn dkg<RNG: RngCore + CryptoRng, Signer: super::Signer>(
signers: &mut [Signer],
rng: &mut RNG,
) -> Result<HashMap<u32, PolyCommitment>, HashMap<u32, DkgError>> {
let public_shares: HashMap<u32, PolyCommitment> = signers
.iter()
.flat_map(|s| s.get_poly_commitments(rng))
.map(|comm| (comm.id.id.get_u32(), comm))
.collect();
let mut private_shares = HashMap::new();
for signer in signers.iter() {
for (signer_id, signer_shares) in signer.get_shares() {
private_shares.insert(signer_id, signer_shares);
}
}
let mut secret_errors = HashMap::new();
for signer in signers.iter_mut() {
if let Err(signer_secret_errors) =
signer.compute_secrets(&private_shares, &public_shares)
{
secret_errors.extend(signer_secret_errors.into_iter());
}
}
if secret_errors.is_empty() {
Ok(public_shares)
} else {
Err(secret_errors)
}
}
fn compute_secrets_missing_private_shares<RNG: RngCore + CryptoRng, Signer: super::Signer>(
signers: &mut [Signer],
rng: &mut RNG,
missing_key_ids: &[u32],
) -> Result<HashMap<u32, PolyCommitment>, HashMap<u32, DkgError>> {
assert!(
!missing_key_ids.is_empty(),
"Cannot run a missing shares test without specificying at least one missing key id"
);
let polys: HashMap<u32, PolyCommitment> = signers
.iter()
.flat_map(|s| s.get_poly_commitments(rng))
.map(|comm| (comm.id.id.get_u32(), comm))
.collect();
let mut private_shares = HashMap::new();
for signer in signers.iter() {
for (signer_id, mut signer_shares) in signer.get_shares() {
for key_id in missing_key_ids {
if signer.get_key_ids().contains(key_id) {
signer_shares.remove(key_id);
}
}
private_shares.insert(signer_id, signer_shares);
}
}
let mut secret_errors = HashMap::new();
for signer in signers.iter_mut() {
if let Err(signer_secret_errors) = signer.compute_secrets(&private_shares, &polys) {
secret_errors.extend(signer_secret_errors.into_iter());
}
}
if secret_errors.is_empty() {
Ok(polys)
} else {
Err(secret_errors)
}
}
#[allow(non_snake_case)]
pub fn run_compute_secrets_missing_private_shares<Signer: super::Signer>() {
let Nk: u32 = 10;
let Np: u32 = 4;
let T: u32 = 7;
let signer_ids: Vec<Vec<u32>> = vec![vec![1, 2, 3], vec![4, 5], vec![6, 7, 8], vec![9, 10]];
let missing_key_ids = vec![1, 7];
let mut rng = create_rng();
let mut signers: Vec<Signer> = signer_ids
.iter()
.enumerate()
.map(|(id, ids)| Signer::new(id.try_into().unwrap(), ids, Nk, Np, T, &mut rng))
.collect();
match compute_secrets_missing_private_shares(&mut signers, &mut rng, &missing_key_ids) {
Ok(polys) => panic!("Got a result with missing public shares: {polys:?}"),
Err(secret_errors) => {
for (_, error) in secret_errors {
assert!(matches!(error, DkgError::MissingPrivateShares(_)));
}
}
}
}
pub fn bad_polynomial_length<Signer: super::Signer, F: Fn(u32) -> u32>(func: F) {
let num_keys: u32 = 10;
let num_signers: u32 = 4;
let threshold: u32 = 7;
let signer_ids: Vec<Vec<u32>> = vec![vec![1, 2, 3, 4], vec![5, 6, 7], vec![8, 9], vec![10]];
let mut rng = create_rng();
let mut signers: Vec<Signer> = signer_ids
.iter()
.enumerate()
.map(|(id, ids)| {
if *ids == vec![10] {
Signer::new(
id.try_into().unwrap(),
ids,
num_signers,
num_keys,
func(threshold),
&mut rng,
)
} else {
Signer::new(
id.try_into().unwrap(),
ids,
num_signers,
num_keys,
threshold,
&mut rng,
)
}
})
.collect();
if dkg(&mut signers, &mut rng).is_ok() {
panic!("DKG should have failed")
}
}
pub fn bad_polynomial_commitment<Signer: super::Signer>() {
let num_keys: u32 = 10;
let num_signers: u32 = 4;
let threshold: u32 = 7;
let signer_ids: Vec<Vec<u32>> = vec![vec![1, 2, 3, 4], vec![5, 6, 7], vec![8, 9], vec![10]];
let mut rng = create_rng();
let mut signers: Vec<Signer> = signer_ids
.iter()
.enumerate()
.map(|(id, ids)| {
Signer::new(
id.try_into().unwrap(),
ids,
num_signers,
num_keys,
threshold,
&mut rng,
)
})
.collect();
let bad_party_id = 2u32;
let public_shares: HashMap<u32, PolyCommitment> = signers
.iter()
.flat_map(|s| s.get_poly_commitments(&mut rng))
.map(|comm| {
let party_id = comm.id.id.get_u32();
if party_id == bad_party_id {
let mut bad_comm = comm.clone();
bad_comm.id.kca += Scalar::from(1);
(party_id, bad_comm)
} else {
(party_id, comm)
}
})
.collect();
let mut private_shares = HashMap::new();
for signer in signers.iter() {
for (signer_id, signer_shares) in signer.get_shares() {
private_shares.insert(signer_id, signer_shares);
}
}
let mut secret_errors = HashMap::new();
for signer in signers.iter_mut() {
if let Err(signer_secret_errors) =
signer.compute_secrets(&private_shares, &public_shares)
{
secret_errors.extend(signer_secret_errors.into_iter());
}
}
assert!(!secret_errors.is_empty());
}
}