diff --git a/ferveo-python/ferveo/__init__.py b/ferveo-python/ferveo/__init__.py index 5088675d..209741b2 100644 --- a/ferveo-python/ferveo/__init__.py +++ b/ferveo-python/ferveo/__init__.py @@ -41,4 +41,5 @@ InvalidAggregateVerificationParameters, UnknownValidator, TooManyTranscripts, + DuplicateTranscript, ) diff --git a/ferveo-python/ferveo/__init__.pyi b/ferveo-python/ferveo/__init__.pyi index f4b5fd9f..e738f2ab 100644 --- a/ferveo-python/ferveo/__init__.pyi +++ b/ferveo-python/ferveo/__init__.pyi @@ -226,3 +226,6 @@ class UnknownValidator(Exception): class TooManyTranscripts(Exception): pass + +class DuplicateTranscript(Exception): + pass diff --git a/ferveo/src/api.rs b/ferveo/src/api.rs index 57ae0694..32449257 100644 --- a/ferveo/src/api.rs +++ b/ferveo/src/api.rs @@ -25,8 +25,8 @@ use crate::bindings_python; use crate::bindings_wasm; pub use crate::EthereumAddress; use crate::{ - do_verify_aggregation, DomainPoint, Error, PVSSMap, - PubliclyVerifiableParams, PubliclyVerifiableSS, Result, + do_verify_aggregation, DomainPoint, Error, PubliclyVerifiableParams, + PubliclyVerifiableSS, Result, }; pub type PublicKey = ferveo_common::PublicKey; @@ -66,12 +66,12 @@ pub fn decrypt_with_shared_secret( aad: &[u8], shared_secret: &SharedSecret, ) -> Result> { - let dkg_public_params = DkgPublicParameters::default(); + let g_inv = PubliclyVerifiableParams::::default().g_inv(); ferveo_tdec::api::decrypt_with_shared_secret( &ciphertext.0, aad, &shared_secret.0, - &dkg_public_params.g1_inv, + &g_inv, ) .map_err(Error::from) } @@ -235,13 +235,6 @@ impl Dkg { .map(AggregatedTranscript) } - // TODO: Unused? - pub fn public_params(&self) -> DkgPublicParameters { - DkgPublicParameters { - g1_inv: self.0.pvss_params.g_inv(), - } - } - pub fn me(&self) -> &Validator { &self.0.me } @@ -251,14 +244,6 @@ impl Dkg { } } -fn make_pvss_map(messages: &[ValidatorMessage]) -> PVSSMap { - let mut pvss_map: PVSSMap = PVSSMap::new(); - messages.iter().for_each(|(validator, transcript)| { - pvss_map.insert(validator.address.clone(), transcript.clone()); - }); - pvss_map -} - #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct AggregatedTranscript(crate::AggregatedTranscript); @@ -285,33 +270,34 @@ impl AggregatedTranscript { )); } - let pvss_params = PubliclyVerifiableParams::::default(); let domain = GeneralEvaluationDomain::::new(validators_num as usize) .expect("Unable to construct an evaluation domain"); - let is_valid_optimistic = self.0.aggregate.verify_optimistic(); if !is_valid_optimistic { return Err(Error::InvalidTranscriptAggregate); } - let pvss_map = make_pvss_map(messages); + let pvss_params = PubliclyVerifiableParams::::default(); let validators: Vec<_> = messages .iter() .map(|(validator, _)| validator) .cloned() .collect(); - + let pvss_list = messages + .iter() + .map(|(_validator, transcript)| transcript) + .cloned() + .collect::>(); // This check also includes `verify_full`. See impl. for details. - let is_valid = do_verify_aggregation( + do_verify_aggregation( &self.0.aggregate.coeffs, &self.0.aggregate.shares, &pvss_params, &validators, &domain, - &pvss_map, - )?; - Ok(is_valid) + &pvss_list, + ) } // TODO: Consider deprecating in favor of PrivateKeyShare::create_decryption_share_simple @@ -354,7 +340,6 @@ impl AggregatedTranscript { aad, validator_keypair, dkg.0.me.share_index, - &dkg.0.pvss_params.g_inv(), )?; let domain_point = dkg.0.get_domain_point(dkg.0.me.share_index)?; Ok(DecryptionShareSimple { @@ -390,32 +375,6 @@ pub struct DecryptionShareSimple { domain_point: DomainPoint, } -// TODO: Deprecate? -#[serde_as] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct DkgPublicParameters { - #[serde_as(as = "serialization::SerdeAs")] - pub(crate) g1_inv: G1Prepared, -} - -impl Default for DkgPublicParameters { - fn default() -> Self { - DkgPublicParameters { - g1_inv: PubliclyVerifiableParams::::default().g_inv(), - } - } -} - -impl DkgPublicParameters { - pub fn from_bytes(bytes: &[u8]) -> Result { - bincode::deserialize(bytes).map_err(|e| e.into()) - } - - pub fn to_bytes(&self) -> Result> { - bincode::serialize(self).map_err(|e| e.into()) - } -} - pub fn combine_shares_simple(shares: &[DecryptionShareSimple]) -> SharedSecret { // Pick domain points that are corresponding to the shares we have. let domain_points: Vec<_> = shares.iter().map(|s| s.domain_point).collect(); @@ -575,7 +534,6 @@ impl PrivateKeyShare { &ciphertext_header.0, aad, validator_keypair, - &dkg.public_params().g1_inv, )?; let domain_point = dkg.0.get_domain_point(dkg.0.me.share_index)?; Ok(DecryptionShareSimple { @@ -593,7 +551,7 @@ impl PrivateKeyShare { share_index: u32, domain_points: &[DomainPoint], ) -> Result { - let dkg_public_params = DkgPublicParameters::default(); + let g_inv = PubliclyVerifiableParams::::default().g_inv(); let share = crate::PrivateKeyShare(self.0.clone()) .create_decryption_share_simple_precomputed( &ciphertext_header.0, @@ -601,7 +559,7 @@ impl PrivateKeyShare { validator_keypair, share_index, domain_points, - &dkg_public_params.g1_inv, + &g_inv, )?; Ok(share) } @@ -918,33 +876,33 @@ mod test_ferveo_api { assert!(result.is_err()); // Duplicated transcripts - let message_with_duplicated_validator = ( - // Duplicating the validator but with a different, valid transcript - messages[0].0.clone(), - messages[security_threshold as usize].1.clone(), - ); - let mut messages_with_duplicates = messages.clone(); - messages_with_duplicates.push(message_with_duplicated_validator); - // assert_eq!(messages_with_duplicates.len(), security_threshold as usize); + let messages_with_duplicated_transcript = [ + ( + validators[security_threshold as usize - 1].clone(), + messages[security_threshold as usize - 1].1.clone(), + ), + ( + validators[security_threshold as usize - 1].clone(), + messages[security_threshold as usize - 2].1.clone(), + ), + ]; assert!(dkg - .aggregate_transcripts(&messages_with_duplicates) + .aggregate_transcripts(&messages_with_duplicated_transcript) .is_err()); - // TODO: Transcripts are not hashable? - // let message_with_duplicated_transcript = ( - // // Duplicating the transcript but with a different, valid validator - // validators[security_threshold as usize - 1].clone(), - // messages[security_threshold as usize - 1].1.clone(), - // ); - // let messages_with_duplicates = [ - // &messages[..(security_threshold - 1) as usize], - // &[message_with_duplicated_transcript], - // ] - // .concat(); - // assert_eq!(messages_with_duplicates.len(), security_threshold as usize); - // assert!(dkg - // .aggregate_transcripts(&messages_with_duplicates) - // .is_err()); + let messages_with_duplicated_transcript = [ + ( + validators[security_threshold as usize - 1].clone(), + messages[security_threshold as usize - 1].1.clone(), + ), + ( + validators[security_threshold as usize - 2].clone(), + messages[security_threshold as usize - 1].1.clone(), + ), + ]; + assert!(dkg + .aggregate_transcripts(&messages_with_duplicated_transcript) + .is_err()); // Unexpected transcripts in the aggregate or transcripts from a different ritual // Using same DKG parameters, but different DKG instances and validators @@ -1065,7 +1023,7 @@ mod test_ferveo_api { .collect::>(); // Creating a copy to avoiding accidentally changing DKG state - let mut dkg = dkgs[0].clone(); + let dkg = dkgs[0].clone(); let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap(); assert!(pvss_aggregated.verify(validators_num, &messages).unwrap()); @@ -1074,17 +1032,18 @@ mod test_ferveo_api { let ciphertext = encrypt(SecretBox::new(MSG.to_vec()), AAD, &public_key).unwrap(); let ciphertext_header = ciphertext.header().unwrap(); - for (validator, transcript) in messages.iter() { - dkg.0 - .vss - .insert(validator.address.clone(), transcript.clone()); - } + let transcripts = messages + .iter() + .map(|(_, transcript)| transcript) + .cloned() + .collect::>(); let (_, _, old_shared_secret) = crate::test_dkg_full::create_shared_secret_simple_tdec( &dkg.0, AAD, &ciphertext_header.0, validator_keypairs.as_slice(), + &transcripts, ); ( @@ -1142,13 +1101,7 @@ mod test_ferveo_api { messages.pop().unwrap(); dkgs.pop(); validator_keypairs.pop().unwrap(); - let removed_validator = validators.pop().unwrap(); - for dkg in dkgs.iter_mut() { - dkg.0 - .offboard_validator(&removed_validator.address) - .expect("Unable to off-board a validator from the DKG context"); - } // Now, we're going to recover a new share at a random point or at a specific point // and check that the shared secret is still the same. diff --git a/ferveo/src/bindings_python.rs b/ferveo/src/bindings_python.rs index a229568c..8505ae42 100644 --- a/ferveo/src/bindings_python.rs +++ b/ferveo/src/bindings_python.rs @@ -129,6 +129,9 @@ impl From for PyErr { "expected: {expected}, received: {received}" )) } + Error::DuplicateTranscript(validator) => { + DuplicateTranscript::new_err(validator.to_string()) + } // Remember to create Python exceptions using `create_exception!` macro, and to register them in the // `make_ferveo_py_module` function. You will have to update the `ferveo/__init__.{py, pyi}` files too. }, @@ -180,6 +183,7 @@ create_exception!( ); create_exception!(exceptions, UnknownValidator, PyValueError); create_exception!(exceptions, TooManyTranscripts, PyValueError); +create_exception!(exceptions, DuplicateTranscript, PyValueError); fn from_py_bytes(bytes: &[u8]) -> PyResult { T::from_bytes(bytes) @@ -796,6 +800,7 @@ pub fn make_ferveo_py_module(py: Python<'_>, m: &PyModule) -> PyResult<()> { )?; m.add("UnknownValidator", py.get_type::())?; m.add("TooManyTranscripts", py.get_type::())?; + m.add("DuplicateTranscript", py.get_type::())?; Ok(()) } diff --git a/ferveo/src/dkg.rs b/ferveo/src/dkg.rs index fe1e18c2..087b3069 100644 --- a/ferveo/src/dkg.rs +++ b/ferveo/src/dkg.rs @@ -76,8 +76,6 @@ pub struct PubliclyVerifiableDkg { pub dkg_params: DkgParams, pub pvss_params: PubliclyVerifiableParams, pub validators: ValidatorsMap, - // TODO: Remove vss? - pub vss: PVSSMap, pub domain: ark_poly::GeneralEvaluationDomain, pub me: Validator, } @@ -117,7 +115,6 @@ impl PubliclyVerifiableDkg { Ok(Self { dkg_params: *dkg_params, pvss_params: PubliclyVerifiableParams::::default(), - vss: PVSSMap::::new(), domain, me: me.clone(), validators, @@ -170,42 +167,26 @@ impl PubliclyVerifiableDkg { self.domain.elements().take(self.validators.len()).collect() } - /// Remove a validator from the DKG - pub fn offboard_validator( - &mut self, - address: &EthereumAddress, - ) -> Result> { - if let Some(validator) = self.validators.remove(address) { - self.vss.remove(address); - Ok(validator) - } else { - Err(Error::UnknownValidator(address.clone())) - } - } - /// Verify PVSS transcripts against the set of validators in the DKG - // TODO: Make private? - pub fn verify_transcripts( + fn verify_transcripts( &self, messages: &[ValidatorMessage], ) -> Result<()> { let mut validator_set = HashSet::::new(); - // TODO: Transcripts are not hashable? - // let mut transcript_set = HashSet::>::new(); + let mut transcript_set = HashSet::>::new(); for (sender, transcript) in messages.iter() { let sender = &sender.address; if !self.validators.contains_key(sender) { return Err(Error::UnknownDealer(sender.clone())); } else if validator_set.contains(sender) { return Err(Error::DuplicateDealer(sender.clone())); - // } else if !transcript_set.contains(transcript) { - // return Err(Error::DuplicateTranscript(sender.clone())); + } else if transcript_set.contains(transcript) { + return Err(Error::DuplicateTranscript(sender.clone())); } else if !transcript.verify_optimistic() { return Err(Error::InvalidPvssTranscript(sender.clone())); - } else { - validator_set.insert(sender.clone()); - // transcript_set.insert(sender.clone()); } + validator_set.insert(sender.clone()); + transcript_set.insert(transcript.clone()); } if validator_set.len() > self.validators.len() { @@ -214,12 +195,12 @@ impl PubliclyVerifiableDkg { validator_set.len() as u32, )); } - // if transcript_set.len() > self.validators.len() { - // return Err(Error::TooManyTranscripts( - // self.validators.len() as u32, - // transcript_set.len() as u32, - // )); - // } + if transcript_set.len() > self.validators.len() { + return Err(Error::TooManyTranscripts( + self.validators.len() as u32, + transcript_set.len() as u32, + )); + } Ok(()) } @@ -360,8 +341,7 @@ mod test_aggregation { /// Test that if the security threshold is met, we can create a final key #[test_case(4, 4; "number of validators equal to the number of shares")] #[test_case(4, 6; "number of validators greater than the number of shares")] - fn test_aggregate(shares_num: u32, _validators_num: u32) { - let _security_threshold = shares_num - 1; + fn test_aggregate(_shares_num: u32, _validators_num: u32) { let rng = &mut ark_std::test_rng(); let (dkg, _) = setup_dkg(0); let all_messages = make_messages(rng, &dkg); diff --git a/ferveo/src/lib.rs b/ferveo/src/lib.rs index 94128a01..5d52a4db 100644 --- a/ferveo/src/lib.rs +++ b/ferveo/src/lib.rs @@ -129,10 +129,10 @@ pub enum Error { /// Too many transcripts received by the DKG #[error("Too many transcripts. Expected: {0}, got: {1}")] TooManyTranscripts(u32, u32), - // TODO: Transcripts are not hashable? - // /// Received a duplicated transcript from a validator - // #[error("Received a duplicated transcript from validator: {0}")] - // DuplicateTranscript(EthereumAddress), + + /// Received a duplicated transcript from a validator + #[error("Received a duplicated transcript from validator: {0}")] + DuplicateTranscript(EthereumAddress), } pub type Result = std::result::Result; @@ -163,15 +163,18 @@ mod test_dkg_full { aad: &[u8], ciphertext_header: &ferveo_tdec::CiphertextHeader, validator_keypairs: &[Keypair], + transcripts: &[PubliclyVerifiableSS], ) -> ( AggregatedTranscript, Vec>, SharedSecret, ) { - let transcripts = dkg.vss.values().cloned().collect::>(); let pvss_aggregated = - AggregatedTranscript::from_transcripts(&transcripts).unwrap(); - assert!(pvss_aggregated.aggregate.verify_aggregation(dkg).is_ok()); + AggregatedTranscript::from_transcripts(transcripts).unwrap(); + assert!(pvss_aggregated + .aggregate + .verify_aggregation(dkg, transcripts) + .is_ok()); let decryption_shares: Vec> = validator_keypairs @@ -187,7 +190,6 @@ mod test_dkg_full { aad, validator_keypair, validator.share_index, - &dkg.pvss_params.g_inv(), ) .unwrap() }) @@ -217,13 +219,14 @@ mod test_dkg_full { let rng = &mut test_rng(); let security_threshold = shares_num / 2 + 1; - let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators( - security_threshold, - shares_num, - validators_num, - ); - - let transcripts = dkg.vss.values().cloned().collect::>(); + let (dkg, validator_keypairs, messages) = + setup_dealt_dkg_with_n_validators( + security_threshold, + shares_num, + validators_num, + ); + let transcripts = + messages.iter().map(|m| m.1.clone()).collect::>(); let public_key = AggregatedTranscript::from_transcripts(&transcripts) .unwrap() .public_key; @@ -240,6 +243,7 @@ mod test_dkg_full { AAD, &ciphertext.header().unwrap(), validator_keypairs.as_slice(), + &transcripts, ); let plaintext = ferveo_tdec::decrypt_with_shared_secret( @@ -260,15 +264,20 @@ mod test_dkg_full { // In precomputed variant, threshold must be equal to shares_num let security_threshold = shares_num; - let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators( - security_threshold, - shares_num, - validators_num, - ); - let transcripts = dkg.vss.values().cloned().collect::>(); + let (dkg, validator_keypairs, messangers) = + setup_dealt_dkg_with_n_validators( + security_threshold, + shares_num, + validators_num, + ); + let transcripts = + messangers.iter().map(|m| m.1.clone()).collect::>(); let pvss_aggregated = AggregatedTranscript::from_transcripts(&transcripts).unwrap(); - pvss_aggregated.aggregate.verify_aggregation(&dkg).unwrap(); + pvss_aggregated + .aggregate + .verify_aggregation(&dkg, &transcripts) + .unwrap(); let public_key = pvss_aggregated.public_key; let ciphertext = ferveo_tdec::encrypt::( SecretBox::new(MSG.to_vec()), @@ -331,12 +340,14 @@ mod test_dkg_full { let rng = &mut test_rng(); let security_threshold = shares_num / 2 + 1; - let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators( - security_threshold, - shares_num, - validators_num, - ); - let transcripts = dkg.vss.values().cloned().collect::>(); + let (dkg, validator_keypairs, messages) = + setup_dealt_dkg_with_n_validators( + security_threshold, + shares_num, + validators_num, + ); + let transcripts = + messages.iter().map(|m| m.1.clone()).collect::>(); let public_key = AggregatedTranscript::from_transcripts(&transcripts) .unwrap() .public_key; @@ -354,6 +365,7 @@ mod test_dkg_full { AAD, &ciphertext.header().unwrap(), validator_keypairs.as_slice(), + &transcripts, ); izip!( @@ -406,12 +418,14 @@ mod test_dkg_full { let rng = &mut test_rng(); let security_threshold = shares_num / 2 + 1; - let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators( - security_threshold, - shares_num, - validators_num, - ); - let transcripts = dkg.vss.values().cloned().collect::>(); + let (dkg, validator_keypairs, messages) = + setup_dealt_dkg_with_n_validators( + security_threshold, + shares_num, + validators_num, + ); + let transcripts = + messages.iter().map(|m| m.1.clone()).collect::>(); let public_key = AggregatedTranscript::from_transcripts(&transcripts) .unwrap() .public_key; @@ -429,6 +443,7 @@ mod test_dkg_full { AAD, &ciphertext.header().unwrap(), validator_keypairs.as_slice(), + &transcripts, ); // Remove one participant from the contexts and all nested structure @@ -524,7 +539,6 @@ mod test_dkg_full { AAD, validator_keypair, share_index as u32, - &dkg.pvss_params.g_inv(), ) .unwrap() }) @@ -576,12 +590,14 @@ mod test_dkg_full { let rng = &mut test_rng(); let security_threshold = shares_num / 2 + 1; - let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators( - security_threshold, - shares_num, - validators_num, - ); - let transcripts = dkg.vss.values().cloned().collect::>(); + let (dkg, validator_keypairs, messages) = + setup_dealt_dkg_with_n_validators( + security_threshold, + shares_num, + validators_num, + ); + let transcripts = + messages.iter().map(|m| m.1.clone()).collect::>(); let public_key = AggregatedTranscript::from_transcripts(&transcripts) .unwrap() .public_key; @@ -599,6 +615,7 @@ mod test_dkg_full { AAD, &ciphertext.header().unwrap(), validator_keypairs.as_slice(), + &transcripts, ); // Each participant prepares an update for each other participant diff --git a/ferveo/src/pvss.rs b/ferveo/src/pvss.rs index 003a411e..cba7f92c 100644 --- a/ferveo/src/pvss.rs +++ b/ferveo/src/pvss.rs @@ -19,7 +19,7 @@ use zeroize::{self, Zeroize, ZeroizeOnDrop}; use crate::{ assert_no_share_duplicates, batch_to_projective_g1, batch_to_projective_g2, - DomainPoint, Error, PVSSMap, PrivateKeyShare, PrivateKeyShareUpdate, + DomainPoint, Error, PrivateKeyShare, PrivateKeyShareUpdate, PubliclyVerifiableDkg, Result, UpdatedPrivateKeyShare, Validator, }; @@ -28,7 +28,7 @@ use crate::{ pub type ShareEncryptions = ::G2Affine; /// Marker struct for unaggregated PVSS transcripts -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct Unaggregated; /// Marker struct for aggregated PVSS transcripts @@ -124,6 +124,15 @@ pub struct PubliclyVerifiableSS { phantom: PhantomData, } +// Manually implementing Hash trait because of the PhantomData +impl Hash for PubliclyVerifiableSS { + fn hash(&self, state: &mut H) { + self.coeffs.hash(state); + self.shares.hash(state); + self.sigma.hash(state); + } +} + impl PubliclyVerifiableSS { /// Create a new PVSS instance /// `s`: the secret constant coefficient to share @@ -254,7 +263,7 @@ pub fn do_verify_aggregation( pvss_params: &PubliclyVerifiableParams, validators: &[Validator], domain: &ark_poly::GeneralEvaluationDomain, - vss: &PVSSMap, + pvss: &[PubliclyVerifiableSS], ) -> Result { let is_valid = do_verify_full( pvss_agg_coefficients, @@ -268,10 +277,9 @@ pub fn do_verify_aggregation( } // Now, we verify that the aggregated PVSS transcript is a valid aggregation - let mut y = E::G1::zero(); - for pvss in vss.values() { - y += pvss.coeffs[0].into_group(); - } + let y = pvss + .iter() + .fold(E::G1::zero(), |acc, pvss| acc + pvss.coeffs[0].into_group()); if y.into_affine() == pvss_agg_coefficients[0] { Ok(true) } else { @@ -288,6 +296,7 @@ impl PubliclyVerifiableSS { pub fn verify_aggregation( &self, dkg: &PubliclyVerifiableDkg, + pvss: &[PubliclyVerifiableSS], ) -> Result { let validators = dkg.validators.values().cloned().collect::>(); do_verify_aggregation( @@ -296,7 +305,7 @@ impl PubliclyVerifiableSS { &dkg.pvss_params, &validators, &dkg.domain, - &dkg.vss, + pvss, ) } @@ -330,14 +339,12 @@ impl PubliclyVerifiableSS { aad: &[u8], validator_keypair: &Keypair, share_index: u32, - g_inv: &E::G1Prepared, ) -> Result> { self.decrypt_private_key_share(validator_keypair, share_index)? .create_decryption_share_simple( ciphertext_header, aad, validator_keypair, - g_inv, ) } @@ -463,16 +470,16 @@ mod test_pvss { /// security threshold is not met #[test] fn test_aggregate_wont_verify_if_under_threshold() { - let (_dkg, _) = setup_dealt_dkg_with_n_transcript_dealt( + let (dkg, _, messages) = setup_dealt_dkg_with_n_transcript_dealt( SECURITY_THRESHOLD, SHARES_NUM, VALIDATORS_NUM, SECURITY_THRESHOLD - 1, ); - // TODO: Fix after rewriting dkg.vss - // let messages = dkg.vss.iter().map(|(v, t)| (v.clone(), t.clone())).collect::>(); - // let aggregate = dkg.aggregate_transcripts(&messages).unwrap(); - // assert!(aggregate.aggregate.verify_aggregation(&dkg).unwrap()); + let pvss_list = + messages.iter().map(|(_, pvss)| pvss).cloned().collect_vec(); + let aggregate = aggregate(&pvss_list).unwrap(); + assert!(aggregate.verify_aggregation(&dkg, &pvss_list).unwrap()); } /// Test the happy flow such that the PVSS with the correct form is created @@ -483,7 +490,7 @@ mod test_pvss { let rng = &mut ark_std::test_rng(); let security_threshold = shares_num - 1; - let (dkg, _) = setup_dealt_dkg_with_n_validators( + let (dkg, _, _) = setup_dealt_dkg_with_n_validators( security_threshold, shares_num, validators_num, @@ -556,12 +563,13 @@ mod test_pvss { #[test_case(4, 6; "number of validators is greater than the number of shares")] fn test_aggregate_pvss(shares_num: u32, validators_num: u32) { let security_threshold = shares_num - 1; - let (dkg, _) = setup_dealt_dkg_with_n_validators( + let (dkg, _, messages) = setup_dealt_dkg_with_n_validators( security_threshold, shares_num, validators_num, ); - let pvss_list = dkg.vss.values().cloned().collect::>(); + let pvss_list = + messages.iter().map(|(_, pvss)| pvss).cloned().collect_vec(); let aggregate = aggregate(&pvss_list).unwrap(); // Check that a polynomial of the correct degree was created assert_eq!( @@ -575,25 +583,27 @@ mod test_pvss { // Check that the full verify returns true assert!(aggregate.verify_full(&dkg).unwrap()); // Check that the verification of aggregation passes - assert!(aggregate.verify_aggregation(&dkg).expect("Test failed")); + assert!(aggregate + .verify_aggregation(&dkg, &pvss_list) + .expect("Test failed")); } /// Check that if the aggregated PVSS transcript has an /// incorrect constant term, the verification fails #[test] fn test_verify_aggregation_fails_if_constant_term_wrong() { - let (dkg, _) = setup_dealt_dkg(); - let pvss_list = dkg.vss.values().cloned().collect::>(); + let (dkg, _, messages) = setup_dealt_dkg(); + let pvss_list = + messages.iter().map(|(_, pvss)| pvss).cloned().collect_vec(); let mut aggregated = aggregate(&pvss_list).unwrap(); while aggregated.coeffs[0] == G1::zero() { - let (dkg, _) = setup_dkg(0); - let pvss_list = dkg.vss.values().cloned().collect::>(); + let (_dkg, _) = setup_dkg(0); aggregated = aggregate(&pvss_list).unwrap(); } aggregated.coeffs[0] = G1::zero(); assert_eq!( aggregated - .verify_aggregation(&dkg) + .verify_aggregation(&dkg, &pvss_list) .expect_err("Test failed") .to_string(), "Transcript aggregate doesn't match the received PVSS instances" diff --git a/ferveo/src/refresh.rs b/ferveo/src/refresh.rs index 619eff1c..ddaa8215 100644 --- a/ferveo/src/refresh.rs +++ b/ferveo/src/refresh.rs @@ -13,7 +13,7 @@ use rand_core::RngCore; use serde::{Deserialize, Serialize}; use zeroize::ZeroizeOnDrop; -use crate::{DomainPoint, Error, Result}; +use crate::{DomainPoint, Error, PubliclyVerifiableParams, Result}; // TODO: Rename refresh.rs to key_share.rs? @@ -62,14 +62,14 @@ impl PrivateKeyShare { ciphertext_header: &CiphertextHeader, aad: &[u8], validator_keypair: &Keypair, - g_inv: &E::G1Prepared, ) -> Result> { + let g_inv = PubliclyVerifiableParams::::default().g_inv(); DecryptionShareSimple::create( &validator_keypair.decryption_key, &self.0, ciphertext_header, aad, - g_inv, + &g_inv, ) .map_err(|e| e.into()) } diff --git a/ferveo/src/test_common.rs b/ferveo/src/test_common.rs index d947317c..df28f553 100644 --- a/ferveo/src/test_common.rs +++ b/ferveo/src/test_common.rs @@ -9,7 +9,7 @@ use rand::{seq::SliceRandom, Rng}; use crate::{ DkgParams, EthereumAddress, PubliclyVerifiableDkg, PubliclyVerifiableSS, - Validator, + Validator, ValidatorMessage, }; pub type ScalarField = ::ScalarField; @@ -77,10 +77,16 @@ pub fn setup_dkg(my_validator_index: usize) -> TestSetup { ) } +pub type DealtTestSetup = ( + PubliclyVerifiableDkg, + Vec>, + Vec>, +); + /// Set up a dkg with enough pvss transcripts to meet the threshold /// /// The correctness of this function is tested in the module [`crate::dkg::test_dealing`] -pub fn setup_dealt_dkg() -> TestSetup { +pub fn setup_dealt_dkg() -> DealtTestSetup { setup_dealt_dkg_with(SECURITY_THRESHOLD, SHARES_NUM) } @@ -89,7 +95,7 @@ pub fn setup_dealt_dkg() -> TestSetup { pub fn setup_dealt_dkg_with( security_threshold: u32, shares_num: u32, -) -> TestSetup { +) -> DealtTestSetup { setup_dealt_dkg_with_n_validators( security_threshold, shares_num, @@ -101,7 +107,7 @@ pub fn setup_dealt_dkg_with_n_validators( security_threshold: u32, shares_num: u32, validators_num: u32, -) -> TestSetup { +) -> DealtTestSetup { setup_dealt_dkg_with_n_transcript_dealt( security_threshold, shares_num, @@ -129,11 +135,12 @@ pub fn setup_dealt_dkg_with_n_transcript_dealt( shares_num: u32, validators_num: u32, transcripts_to_use: u32, -) -> TestSetup { +) -> DealtTestSetup { let rng = &mut ark_std::test_rng(); // Gather everyone's transcripts - let mut transcripts: Vec<_> = (0..validators_num) + // Use only the first `transcripts_to_use` transcripts + let mut transcripts: Vec<_> = (0..transcripts_to_use) .map(|my_index| { let (dkg, _) = setup_dkg_for_n_validators( security_threshold, @@ -148,23 +155,13 @@ pub fn setup_dealt_dkg_with_n_transcript_dealt( .collect(); // Create a test DKG instance - let (mut dkg, keypairs) = setup_dkg_for_n_validators( + let (dkg, keypairs) = setup_dkg_for_n_validators( security_threshold, shares_num, 0, validators_num, ); - // The ordering of messages should not matter transcripts.shuffle(rng); - // Use only the first `transcripts_to_use` transcripts - transcripts - .iter() - .take(transcripts_to_use as usize) - .for_each(|(sender, message)| { - // TODO: How to do this in user-facing API? - // TODO: just return transcripts after getting rid of the dkg.vss - dkg.vss.insert(sender.address.clone(), message.clone()); - }); - (dkg, keypairs) + (dkg, keypairs, transcripts) }