fixed bug in DKG to allow for different sets of dealers and receivers

This commit is contained in:
Jędrzej Stuczyński
2024-01-30 15:47:31 +00:00
parent 4fe7ce0f12
commit 85d96deded
2 changed files with 150 additions and 54 deletions
+99 -18
View File
@@ -320,9 +320,17 @@ impl<'a> TryFrom<&'a nym_contracts_common::dealings::ContractSafeBytes> for Deal
}
}
// this assumes all dealings have been verified
/// Attempt to run the `VkCombine` algorithm to obtain the public master verification key, `VK`
/// alongside shares of the verification key, `shvk_{1}`, `shvk_{2}`, ... `svhk_{n}`, where n is the number of receivers.
///
/// # Arguments
///
/// * `dealings`: map of dealer indices to dealings they generated
/// * `threshold`: explicit threshold value of the associated dealings
/// * `receivers`:map of receiver indices to their public keys
// note: this function assumes all dealings have already been verified
pub fn try_recover_verification_keys(
dealings: &[Dealing],
dealings: &BTreeMap<NodeIndex, Dealing>,
threshold: Threshold,
receivers: &BTreeMap<NodeIndex, PublicKey>,
) -> Result<RecoveredVerificationKeys, DkgError> {
@@ -330,24 +338,31 @@ pub fn try_recover_verification_keys(
return Err(DkgError::NoDealingsAvailable);
}
let threshold_usize = threshold as usize;
let threshold = threshold as usize;
if dealings.len() < threshold {
return Err(DkgError::NotEnoughDealingsAvailable {
available: dealings.len(),
required: threshold,
});
}
if !dealings
.iter()
.all(|dealing| dealing.public_coefficients.size() == threshold_usize)
.values()
.all(|dealing| dealing.public_coefficients.size() == threshold)
{
return Err(DkgError::MismatchedDealings);
}
let indices = receivers.keys().collect::<Vec<_>>();
let dealer_indices = dealings.keys().collect::<Vec<_>>();
// Compute A0, ..., A_{t-1}
let mut interpolated_coefficients = Vec::with_capacity(threshold_usize);
for k in 0..threshold_usize {
let mut samples = Vec::with_capacity(indices.len());
for (j, dealing) in dealings.iter().enumerate() {
let mut interpolated_coefficients = Vec::with_capacity(threshold);
for k in 0..threshold {
let mut samples = Vec::with_capacity(dealer_indices.len());
for (dealer_index, dealing) in dealings.iter() {
samples.push((
Scalar::from(*indices[j]),
Scalar::from(*dealer_index),
*dealing.public_coefficients.nth(k),
))
}
@@ -364,7 +379,7 @@ pub fn try_recover_verification_keys(
// shvk_j = A0^{j^0} * A1^{j^1} * ... * A_{t-1}^{j^{t-1}}
let verification_key_shares = receivers
.keys()
.map(|index| interpolated_coefficients.evaluate_at(&Scalar::from(*index)))
.map(|receiver_index| interpolated_coefficients.evaluate_at(&Scalar::from(*receiver_index)))
.collect();
Ok(RecoveredVerificationKeys {
@@ -456,14 +471,17 @@ mod tests {
let dealings = node_indices
.iter()
.map(|&dealer_index| {
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0
(
dealer_index,
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0,
)
})
.collect::<Vec<_>>();
.collect::<BTreeMap<_, _>>();
let mut derived_secrets = Vec::new();
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
for (i, (ref dk, _)) in full_keys.iter().enumerate() {
let shares = dealings
.iter()
.values()
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
.collect();
derived_secrets.push(
@@ -512,9 +530,12 @@ mod tests {
let dealings = node_indices
.iter()
.map(|&dealer_index| {
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0
(
dealer_index,
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0,
)
})
.collect::<Vec<_>>();
.collect::<BTreeMap<_, _>>();
let RecoveredVerificationKeys {
recovered_master,
@@ -530,6 +551,66 @@ mod tests {
.is_ok())
}
#[test]
#[ignore] // expensive test
fn verifying_partial_verification_keys_with_different_dealers_and_receivers() {
let dummy_seed = [42u8; 32];
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
let params = setup();
let dealer_indices = [1, 2, 3, 8];
let receiver_indices = [3, 4, 5, 6, 7];
let threshold = 3;
let mut receivers = BTreeMap::new();
let mut full_keys = Vec::new();
for index in &receiver_indices {
let (dk, pk) = keygen(&params, &mut rng);
receivers.insert(*index, *pk.public_key());
full_keys.push((dk, pk))
}
let dealings = dealer_indices
.iter()
.map(|&dealer_index| {
(
dealer_index,
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0,
)
})
.collect::<BTreeMap<_, _>>();
let RecoveredVerificationKeys {
recovered_master,
recovered_partials,
} = try_recover_verification_keys(&dealings, threshold, &receivers).unwrap();
let g2 = G2Projective::generator();
let mut derived_secrets = Vec::new();
for (i, (dk, _)) in full_keys.iter().enumerate() {
let shares = dealings
.values()
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
.collect();
let recovered_secret = combine_shares(shares, &dealer_indices).unwrap();
// make sure it matches the associated vk
assert_eq!(recovered_partials[i], g2 * recovered_secret);
derived_secrets.push(recovered_secret)
}
assert!(verify_verification_keys(
&recovered_master,
&recovered_partials,
&receivers,
threshold
)
.is_ok())
}
#[test]
#[ignore] // expensive test
fn dealing_roundtrip() {
+51 -36
View File
@@ -52,7 +52,7 @@ fn single_sender() {
.unwrap();
// make sure each share is actually decryptable (even though proofs say they must be, perform this sanity check)
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
for (i, (ref dk, _)) in full_keys.iter().enumerate() {
let _recovered = decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap();
}
@@ -91,10 +91,13 @@ fn full_threshold_secret_sharing() {
let dealings = node_indices
.iter()
.map(|&dealer_index| {
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0
(
dealer_index,
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0,
)
})
.collect::<Vec<_>>();
for dealing in dealings.iter() {
.collect::<BTreeMap<_, _>>();
for dealing in dealings.values() {
dealing
.verify(&params, threshold, &receivers, None)
.unwrap();
@@ -109,9 +112,9 @@ fn full_threshold_secret_sharing() {
let g2 = G2Projective::generator();
let mut derived_secrets = Vec::new();
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
for (i, (ref dk, _)) in full_keys.iter().enumerate() {
let shares = dealings
.iter()
.values()
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
.collect();
@@ -169,9 +172,12 @@ fn full_threshold_secret_resharing() {
let first_dealings = node_indices
.iter()
.map(|&dealer_index| {
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0
(
dealer_index,
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0,
)
})
.collect::<Vec<_>>();
.collect::<BTreeMap<_, _>>();
// recover verification keys
let RecoveredVerificationKeys {
@@ -180,9 +186,9 @@ fn full_threshold_secret_resharing() {
} = try_recover_verification_keys(&first_dealings, threshold, &receivers).unwrap();
let mut derived_secrets = Vec::new();
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
for (i, (ref dk, _)) in full_keys.iter().enumerate() {
let shares = first_dealings
.iter()
.values()
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
.collect();
@@ -203,19 +209,22 @@ fn full_threshold_secret_resharing() {
.iter()
.zip(derived_secrets.iter())
.map(|(&dealer_index, prior_secret)| {
Dealing::create(
&mut rng,
&params,
(
dealer_index,
threshold,
&receivers,
Some(*prior_secret),
Dealing::create(
&mut rng,
&params,
dealer_index,
threshold,
&receivers,
Some(*prior_secret),
)
.0,
)
.0
})
.collect::<Vec<_>>();
.collect::<BTreeMap<_, _>>();
for (reshared_dealing, prior_vk) in resharing_dealings.iter().zip(recovered_partials.iter()) {
for (reshared_dealing, prior_vk) in resharing_dealings.values().zip(recovered_partials.iter()) {
reshared_dealing
.verify(&params, threshold, &receivers, Some(*prior_vk))
.unwrap();
@@ -228,9 +237,9 @@ fn full_threshold_secret_resharing() {
} = try_recover_verification_keys(&resharing_dealings, threshold, &receivers).unwrap();
let mut reshared_secrets = Vec::new();
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
for (i, (ref dk, _)) in full_keys.iter().enumerate() {
let shares = resharing_dealings
.iter()
.values()
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
.collect();
@@ -279,9 +288,12 @@ fn full_threshold_secret_resharing_left_party() {
let first_dealings = node_indices
.iter()
.map(|&dealer_index| {
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0
(
dealer_index,
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0,
)
})
.collect::<Vec<_>>();
.collect::<BTreeMap<_, _>>();
// recover verification keys
let RecoveredVerificationKeys {
@@ -290,9 +302,9 @@ fn full_threshold_secret_resharing_left_party() {
} = try_recover_verification_keys(&first_dealings, threshold, &receivers).unwrap();
let mut derived_secrets = Vec::new();
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
for (i, (ref dk, _)) in full_keys.iter().enumerate() {
let shares = first_dealings
.iter()
.values()
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
.collect();
@@ -323,20 +335,23 @@ fn full_threshold_secret_resharing_left_party() {
.iter()
.zip(derived_secrets.iter().take(2))
.map(|(&dealer_index, prior_secret)| {
Dealing::create(
&mut rng,
&params,
(
dealer_index,
threshold,
&receivers,
Some(*prior_secret),
Dealing::create(
&mut rng,
&params,
dealer_index,
threshold,
&receivers,
Some(*prior_secret),
)
.0,
)
.0
})
.collect::<Vec<_>>();
.collect::<BTreeMap<_, _>>();
for (reshared_dealing, prior_vk) in resharing_dealings
.iter()
.values()
.zip(recovered_partials.iter().take(2))
{
reshared_dealing
@@ -351,9 +366,9 @@ fn full_threshold_secret_resharing_left_party() {
} = try_recover_verification_keys(&resharing_dealings, threshold, &receivers).unwrap();
let mut reshared_secrets = Vec::new();
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
for (i, (ref dk, _)) in full_keys.iter().enumerate() {
let shares = resharing_dealings
.iter()
.values()
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
.collect();