From 52f441cae1ac8ed6c88743082bd2434f3cad9012 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Fri, 19 Jan 2024 12:54:29 +0100
Subject: [PATCH] refactor(test): deduplicate test utils
---
ferveo/src/api.rs | 65 ++++++++----------
ferveo/src/bindings_python.rs | 64 ++++++++----------
ferveo/src/bindings_wasm.rs | 1 -
ferveo/src/dkg.rs | 124 ++++------------------------------
ferveo/src/lib.rs | 99 +++++++++++++--------------
ferveo/src/pvss.rs | 6 +-
ferveo/src/refresh.rs | 40 +++++------
ferveo/src/test_common.rs | 110 ++++++++++++++++++++++++++++++
8 files changed, 242 insertions(+), 267 deletions(-)
create mode 100644 ferveo/src/test_common.rs
diff --git a/ferveo/src/api.rs b/ferveo/src/api.rs
index 0e610658..0a8bf2aa 100644
--- a/ferveo/src/api.rs
+++ b/ferveo/src/api.rs
@@ -9,7 +9,10 @@ pub use ferveo_tdec::api::{
prepare_combine_simple, share_combine_precomputed, share_combine_simple,
Fr, G1Affine, G1Prepared, G2Affine, SecretBox, E,
};
-use generic_array::{typenum::U48, GenericArray};
+use generic_array::{
+ typenum::{Unsigned, U48},
+ GenericArray,
+};
use rand::RngCore;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
@@ -19,8 +22,6 @@ pub type Keypair = ferveo_common::Keypair;
pub type Validator = crate::Validator;
pub type Transcript = PubliclyVerifiableSS;
-// pub type ShareIndex = u32;
-// pub type ValidatorMessage = (ShareIndex, Validator, Transcript);
pub type ValidatorMessage = (Validator, Transcript);
#[cfg(feature = "bindings-python")]
@@ -167,7 +168,7 @@ impl DkgPublicKey {
}
pub fn serialized_size() -> usize {
- 48
+ U48::to_usize()
}
/// Generate a random DKG public key.
@@ -399,12 +400,10 @@ mod test_ferveo_api {
use rand::{prelude::StdRng, SeedableRng};
use test_case::test_case;
- use crate::{api::*, dkg::test_common::*};
+ use crate::{api::*, test_common::*};
type TestInputs = (Vec, Vec, Vec);
- const TAU: u32 = 1;
-
fn make_test_inputs(
rng: &mut StdRng,
tau: u32,
@@ -446,6 +445,7 @@ mod test_ferveo_api {
let dkg_pk = DkgPublicKey::random();
let serialized = dkg_pk.to_bytes().unwrap();
let deserialized = DkgPublicKey::from_bytes(&serialized).unwrap();
+ assert_eq!(serialized.len(), 48_usize);
assert_eq!(dkg_pk, deserialized);
}
@@ -476,10 +476,9 @@ mod test_ferveo_api {
let dkg_public_key = dkg.public_key();
// In the meantime, the client creates a ciphertext and decryption request
- let msg = "my-msg".as_bytes().to_vec();
- let aad: &[u8] = "my-aad".as_bytes();
let ciphertext =
- encrypt(SecretBox::new(msg.clone()), aad, &dkg_public_key).unwrap();
+ encrypt(SecretBox::new(MSG.to_vec()), AAD, &dkg_public_key)
+ .unwrap();
// Having aggregated the transcripts, the validators can now create decryption shares
let decryption_shares: Vec<_> = izip!(&validators, &validator_keypairs)
@@ -501,7 +500,7 @@ mod test_ferveo_api {
.create_decryption_share_precomputed(
&dkg,
&ciphertext.header().unwrap(),
- aad,
+ AAD,
validator_keypair,
)
.unwrap()
@@ -514,11 +513,11 @@ mod test_ferveo_api {
let shared_secret = share_combine_precomputed(&decryption_shares);
let plaintext = decrypt_with_shared_secret(
&ciphertext,
- aad,
+ AAD,
&SharedSecret(shared_secret),
)
.unwrap();
- assert_eq!(plaintext, msg);
+ assert_eq!(plaintext, MSG);
// Since we're using a precomputed variant, we need all the shares to be able to decrypt
// So if we remove one share, we should not be able to decrypt
@@ -528,7 +527,7 @@ mod test_ferveo_api {
let shared_secret = share_combine_precomputed(&decryption_shares);
let result = decrypt_with_shared_secret(
&ciphertext,
- aad,
+ AAD,
&SharedSecret(shared_secret),
);
assert!(result.is_err());
@@ -562,10 +561,8 @@ mod test_ferveo_api {
let public_key = dkg.public_key();
// In the meantime, the client creates a ciphertext and decryption request
- let msg = "my-msg".as_bytes().to_vec();
- let aad: &[u8] = "my-aad".as_bytes();
let ciphertext =
- encrypt(SecretBox::new(msg.clone()), aad, &public_key).unwrap();
+ encrypt(SecretBox::new(MSG.to_vec()), AAD, &public_key).unwrap();
// Having aggregated the transcripts, the validators can now create decryption shares
let decryption_shares: Vec<_> = izip!(&validators, &validator_keypairs)
@@ -585,7 +582,7 @@ mod test_ferveo_api {
.create_decryption_share_simple(
&dkg,
&ciphertext.header().unwrap(),
- aad,
+ AAD,
validator_keypair,
)
.unwrap()
@@ -601,9 +598,9 @@ mod test_ferveo_api {
let shared_secret = combine_shares_simple(&decryption_shares);
let plaintext =
- decrypt_with_shared_secret(&ciphertext, aad, &shared_secret)
+ decrypt_with_shared_secret(&ciphertext, AAD, &shared_secret)
.unwrap();
- assert_eq!(plaintext, msg);
+ assert_eq!(plaintext, MSG);
// Let's say that we've only received `security_threshold - 1` shares
// In this case, we should not be able to decrypt
@@ -612,7 +609,7 @@ mod test_ferveo_api {
let shared_secret = combine_shares_simple(&decryption_shares);
let result =
- decrypt_with_shared_secret(&ciphertext, aad, &shared_secret);
+ decrypt_with_shared_secret(&ciphertext, AAD, &shared_secret);
assert!(result.is_err());
}
@@ -620,17 +617,14 @@ mod test_ferveo_api {
fn server_side_local_verification() {
let rng = &mut StdRng::seed_from_u64(0);
- let security_threshold = 3;
- let shares_num = 4;
-
let (messages, validators, _) =
- make_test_inputs(rng, TAU, security_threshold, shares_num);
+ make_test_inputs(rng, TAU, SECURITY_THRESHOLD, SHARES_NUM);
// Now that every validator holds a dkg instance and a transcript for every other validator,
// every validator can aggregate the transcripts
let me = validators[0].clone();
let mut dkg =
- Dkg::new(TAU, shares_num, security_threshold, &validators, &me)
+ Dkg::new(TAU, SHARES_NUM, SECURITY_THRESHOLD, &validators, &me)
.unwrap();
let local_aggregate = dkg.aggregate_transcripts(&messages).unwrap();
@@ -643,14 +637,11 @@ mod test_ferveo_api {
fn client_side_local_verification() {
let rng = &mut StdRng::seed_from_u64(0);
- let security_threshold = 3;
- let shares_num = 4;
-
let (messages, _, _) =
- make_test_inputs(rng, TAU, security_threshold, shares_num);
+ make_test_inputs(rng, TAU, SECURITY_THRESHOLD, SHARES_NUM);
// We only need `security_threshold` transcripts to aggregate
- let messages = &messages[..security_threshold as usize];
+ let messages = &messages[..SECURITY_THRESHOLD as usize];
// Create an aggregated transcript on the client side
let aggregated_transcript = AggregatedTranscript::new(messages);
@@ -659,27 +650,27 @@ mod test_ferveo_api {
// the aggregate from a side-channel or decide to persist it and verify it later
// Now, the client can verify the aggregated transcript
- let result = aggregated_transcript.verify(shares_num, messages);
+ let result = aggregated_transcript.verify(SHARES_NUM, messages);
assert!(result.is_ok());
assert!(result.unwrap());
// Test negative cases
// Not enough transcripts
- let not_enough_messages = &messages[..2];
- assert!(not_enough_messages.len() < security_threshold as usize);
+ let not_enough_messages = &messages[..SECURITY_THRESHOLD as usize - 1];
+ assert!(not_enough_messages.len() < SECURITY_THRESHOLD as usize);
let insufficient_aggregate =
AggregatedTranscript::new(not_enough_messages);
- let result = insufficient_aggregate.verify(shares_num, messages);
+ let result = insufficient_aggregate.verify(SHARES_NUM, messages);
assert!(result.is_err());
// Unexpected transcripts in the aggregate or transcripts from a different ritual
// Using same DKG parameters, but different DKG instances and validators
let (bad_messages, _, _) =
- make_test_inputs(rng, TAU, security_threshold, shares_num);
+ make_test_inputs(rng, TAU, SECURITY_THRESHOLD, SHARES_NUM);
let mixed_messages = [&messages[..2], &bad_messages[..1]].concat();
let bad_aggregate = AggregatedTranscript::new(&mixed_messages);
- let result = bad_aggregate.verify(shares_num, messages);
+ let result = bad_aggregate.verify(SHARES_NUM, messages);
assert!(result.is_err());
}
}
diff --git a/ferveo/src/bindings_python.rs b/ferveo/src/bindings_python.rs
index 00c455a3..9b472d34 100644
--- a/ferveo/src/bindings_python.rs
+++ b/ferveo/src/bindings_python.rs
@@ -739,7 +739,7 @@ pub fn make_ferveo_py_module(py: Python<'_>, m: &PyModule) -> PyResult<()> {
mod test_ferveo_python {
use itertools::izip;
- use crate::bindings_python::*;
+ use crate::{bindings_python::*, test_common::*};
type TestInputs = (Vec, Vec, Vec);
@@ -785,21 +785,19 @@ mod test_ferveo_python {
#[test]
fn test_server_api_tdec_precomputed() {
- let tau = 1;
- let shares_num = 4;
// In precomputed variant, the security threshold is equal to the number of shares
- let security_threshold = shares_num;
+ let security_threshold = SHARES_NUM;
let (messages, validators, validator_keypairs) =
- make_test_inputs(tau, security_threshold, shares_num);
+ make_test_inputs(TAU, security_threshold, SHARES_NUM);
// Now that every validator holds a dkg instance and a transcript for every other validator,
// every validator can aggregate the transcripts
let me = validators[0].clone();
let mut dkg = Dkg::new(
- tau,
- shares_num,
+ TAU,
+ SHARES_NUM,
security_threshold,
validators.clone(),
&me,
@@ -811,24 +809,22 @@ mod test_ferveo_python {
let pvss_aggregated =
dkg.aggregate_transcripts(messages.clone()).unwrap();
assert!(pvss_aggregated
- .verify(shares_num, messages.clone())
+ .verify(SHARES_NUM, messages.clone())
.unwrap());
// At this point, any given validator should be able to provide a DKG public key
let dkg_public_key = dkg.public_key();
// In the meantime, the client creates a ciphertext and decryption request
- let msg: &[u8] = "my-msg".as_bytes();
- let aad: &[u8] = "my-aad".as_bytes();
- let ciphertext = encrypt(msg, aad, &dkg_public_key).unwrap();
+ let ciphertext = encrypt(MSG, AAD, &dkg_public_key).unwrap();
// Having aggregated the transcripts, the validators can now create decryption shares
let decryption_shares: Vec<_> = izip!(&validators, &validator_keypairs)
.map(|(validator, validator_keypair)| {
// Each validator holds their own instance of DKG and creates their own aggregate
let mut dkg = Dkg::new(
- tau,
- shares_num,
+ TAU,
+ SHARES_NUM,
security_threshold,
validators.clone(),
validator,
@@ -837,13 +833,13 @@ mod test_ferveo_python {
let aggregate =
dkg.aggregate_transcripts(messages.clone()).unwrap();
assert!(pvss_aggregated
- .verify(shares_num, messages.clone())
+ .verify(SHARES_NUM, messages.clone())
.is_ok());
aggregate
.create_decryption_share_precomputed(
&dkg,
&ciphertext.header().unwrap(),
- aad,
+ AAD,
validator_keypair,
)
.unwrap()
@@ -857,56 +853,50 @@ mod test_ferveo_python {
combine_decryption_shares_precomputed(decryption_shares);
let plaintext =
- decrypt_with_shared_secret(&ciphertext, aad, &shared_secret)
+ decrypt_with_shared_secret(&ciphertext, AAD, &shared_secret)
.unwrap();
- assert_eq!(plaintext, msg);
+ assert_eq!(plaintext, MSG);
}
#[test]
fn test_server_api_tdec_simple() {
- let tau = 1;
- let shares_num = 4;
- let security_threshold = 3;
-
let (messages, validators, validator_keypairs) =
- make_test_inputs(tau, security_threshold, shares_num);
+ make_test_inputs(TAU, SECURITY_THRESHOLD, SHARES_NUM);
// Now that every validator holds a dkg instance and a transcript for every other validator,
// every validator can aggregate the transcripts
let me = validators[0].clone();
let mut dkg = Dkg::new(
- tau,
- shares_num,
- security_threshold,
+ TAU,
+ SHARES_NUM,
+ SECURITY_THRESHOLD,
validators.clone(),
&me,
)
.unwrap();
// Lets say that we've only receives `security_threshold` transcripts
- let messages = messages[..security_threshold as usize].to_vec();
+ let messages = messages[..SECURITY_THRESHOLD as usize].to_vec();
let pvss_aggregated =
dkg.aggregate_transcripts(messages.clone()).unwrap();
assert!(pvss_aggregated
- .verify(shares_num, messages.clone())
+ .verify(SHARES_NUM, messages.clone())
.unwrap());
// At this point, any given validator should be able to provide a DKG public key
let dkg_public_key = dkg.public_key();
// In the meantime, the client creates a ciphertext and decryption request
- let msg: &[u8] = "my-msg".as_bytes();
- let aad: &[u8] = "my-aad".as_bytes();
- let ciphertext = encrypt(msg, aad, &dkg_public_key).unwrap();
+ let ciphertext = encrypt(MSG, AAD, &dkg_public_key).unwrap();
// Having aggregated the transcripts, the validators can now create decryption shares
let decryption_shares: Vec<_> = izip!(&validators, &validator_keypairs)
.map(|(validator, validator_keypair)| {
// Each validator holds their own instance of DKG and creates their own aggregate
let mut dkg = Dkg::new(
- tau,
- shares_num,
- security_threshold,
+ TAU,
+ SHARES_NUM,
+ SECURITY_THRESHOLD,
validators.clone(),
validator,
)
@@ -914,13 +904,13 @@ mod test_ferveo_python {
let aggregate =
dkg.aggregate_transcripts(messages.clone()).unwrap();
assert!(aggregate
- .verify(shares_num, messages.clone())
+ .verify(SHARES_NUM, messages.clone())
.unwrap());
aggregate
.create_decryption_share_simple(
&dkg,
&ciphertext.header().unwrap(),
- aad,
+ AAD,
validator_keypair,
)
.unwrap()
@@ -933,8 +923,8 @@ mod test_ferveo_python {
let shared_secret = combine_decryption_shares_simple(decryption_shares);
let plaintext =
- decrypt_with_shared_secret(&ciphertext, aad, &shared_secret)
+ decrypt_with_shared_secret(&ciphertext, AAD, &shared_secret)
.unwrap();
- assert_eq!(plaintext, msg);
+ assert_eq!(plaintext, MSG);
}
}
diff --git a/ferveo/src/bindings_wasm.rs b/ferveo/src/bindings_wasm.rs
index 5b03f188..a1310277 100644
--- a/ferveo/src/bindings_wasm.rs
+++ b/ferveo/src/bindings_wasm.rs
@@ -595,7 +595,6 @@ impl Keypair {
}
}
-/// Factory functions for testing
pub mod test_common {
use crate::bindings_wasm::*;
diff --git a/ferveo/src/dkg.rs b/ferveo/src/dkg.rs
index dbab8e03..e13c4894 100644
--- a/ferveo/src/dkg.rs
+++ b/ferveo/src/dkg.rs
@@ -361,110 +361,14 @@ pub enum Message {
Aggregate(Aggregation),
}
-/// Factory functions for testing
-#[cfg(test)]
-pub(crate) mod test_common {
- use std::str::FromStr;
-
- pub use ark_bls12_381::Bls12_381 as E;
- use ferveo_common::Keypair;
-
- pub use super::*;
-
- pub type G1 = ::G1Affine;
-
- pub fn gen_keypairs(n: u32) -> Vec> {
- let rng = &mut ark_std::test_rng();
- (0..n).map(|_| Keypair::::new(rng)).collect()
- }
-
- pub fn gen_address(i: usize) -> EthereumAddress {
- EthereumAddress::from_str(&format!("0x{i:040}")).unwrap()
- }
-
- pub fn gen_validators(keypairs: &[Keypair]) -> Vec> {
- keypairs
- .iter()
- .enumerate()
- .map(|(i, keypair)| Validator {
- address: gen_address(i),
- public_key: keypair.public_key(),
- })
- .collect()
- }
-
- pub type TestSetup = (PubliclyVerifiableDkg, Vec>);
-
- pub fn setup_dkg_for_n_validators(
- security_threshold: u32,
- shares_num: u32,
- my_index: usize,
- ) -> TestSetup {
- let keypairs = gen_keypairs(shares_num);
- let mut validators = gen_validators(keypairs.as_slice());
- validators.sort();
- let me = validators[my_index].clone();
- let dkg = PubliclyVerifiableDkg::new(
- &validators,
- &DkgParams {
- tau: 0,
- security_threshold,
- shares_num,
- },
- &me,
- )
- .expect("Setup failed");
- (dkg, keypairs)
- }
-
- /// Create a test dkg
- ///
- /// The [`test_dkg_init`] module checks correctness of this setup
- pub fn setup_dkg(validator: usize) -> TestSetup {
- setup_dkg_for_n_validators(2, 4, validator)
- }
-
- /// Set up a dkg with enough pvss transcripts to meet the threshold
- ///
- /// The correctness of this function is tested in the module [`test_dealing`]
- pub fn setup_dealt_dkg() -> TestSetup {
- setup_dealt_dkg_with_n_validators(2, 4)
- }
-
- pub fn setup_dealt_dkg_with_n_validators(
- security_threshold: u32,
- shares_num: u32,
- ) -> TestSetup {
- let rng = &mut ark_std::test_rng();
-
- // Gather everyone's transcripts
- let messages: Vec<_> = (0..shares_num)
- .map(|my_index| {
- let (mut dkg, _) = setup_dkg_for_n_validators(
- security_threshold,
- shares_num,
- my_index as usize,
- );
- let me = dkg.me.validator.clone();
- let message = dkg.share(rng).unwrap();
- (me, message)
- })
- .collect();
-
- // Create a test DKG instance
- let (mut dkg, keypairs) =
- setup_dkg_for_n_validators(security_threshold, shares_num, 0);
- messages.iter().for_each(|(sender, message)| {
- dkg.apply_message(sender, message).expect("Setup failed");
- });
- (dkg, keypairs)
- }
-}
-
/// Test initializing DKG
#[cfg(test)]
mod test_dkg_init {
- use super::test_common::*;
+ use crate::{
+ dkg::{PubliclyVerifiableDkg, Validator},
+ test_common::*,
+ DkgParams,
+ };
/// Test that dkg fails to start if the `me` input
/// is not in the validator set
@@ -480,11 +384,7 @@ mod test_dkg_init {
};
let err = PubliclyVerifiableDkg::::new(
&gen_validators(&known_keypairs),
- &DkgParams {
- tau: 0,
- security_threshold: shares_num / 2,
- shares_num,
- },
+ &DkgParams::new(TAU, SECURITY_THRESHOLD, SHARES_NUM).unwrap(),
&unknown_validator,
)
.unwrap_err();
@@ -498,8 +398,7 @@ mod test_dkg_init {
mod test_dealing {
use ark_ec::AffineRepr;
- use super::test_common::*;
- use crate::DkgState::Dealt;
+ use crate::{test_common::*, DkgState, DkgState::Dealt, Validator};
/// Test that dealing correct PVSS transcripts
/// pass verification an application and that
@@ -716,7 +615,7 @@ mod test_dealing {
mod test_aggregation {
use ark_ec::AffineRepr;
- use super::test_common::*;
+ use crate::{dkg::*, test_common::*, DkgState, Message};
/// Test that if the security threshold is
/// met, we can create a final key
@@ -802,17 +701,18 @@ mod test_aggregation {
/// Test DKG parameters
#[cfg(test)]
mod test_dkg_params {
- const TAU: u32 = 0;
+ use crate::test_common::*;
#[test]
fn test_shares_num_less_than_security_threshold() {
- let dkg_params = super::DkgParams::new(TAU, 4, 3);
+ let dkg_params = super::DkgParams::new(TAU, SHARES_NUM + 1, SHARES_NUM);
assert!(dkg_params.is_err());
}
#[test]
fn test_valid_dkg_params() {
- let dkg_params = super::DkgParams::new(TAU, 2, 3);
+ let dkg_params =
+ super::DkgParams::new(TAU, SECURITY_THRESHOLD, SHARES_NUM);
assert!(dkg_params.is_ok());
}
}
diff --git a/ferveo/src/lib.rs b/ferveo/src/lib.rs
index 605dc0d7..394afb1a 100644
--- a/ferveo/src/lib.rs
+++ b/ferveo/src/lib.rs
@@ -21,6 +21,9 @@ pub mod validator;
mod utils;
+#[cfg(test)]
+mod test_common;
+
pub use dkg::*;
pub use primitives::*;
pub use pvss::*;
@@ -96,15 +99,19 @@ pub enum Error {
#[error(transparent)]
ArkSerializeError(#[from] ark_serialize::SerializationError),
+ /// Invalid byte length
#[error("Invalid byte length. Expected {0}, got {1}")]
InvalidByteLength(usize, usize),
+ /// Invalid variant
#[error("Invalid variant: {0}")]
InvalidVariant(String),
+ /// DKG parameters validaiton failed
#[error("Invalid DKG parameters: number of shares {0}, threshold {1}")]
InvalidDkgParameters(u32, u32),
+ /// Failed to access a share for a given share index
#[error("Invalid share index: {0}")]
InvalidShareIndex(u32),
}
@@ -127,7 +134,7 @@ mod test_dkg_full {
use std::collections::HashMap;
use ark_bls12_381::{Bls12_381 as E, Fr, G1Affine};
- use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup};
+ use ark_ec::{AffineRepr, CurveGroup};
use ark_ff::{UniformRand, Zero};
use ark_poly::EvaluationDomain;
use ark_std::test_rng;
@@ -137,12 +144,11 @@ mod test_dkg_full {
SharedSecret,
};
use itertools::izip;
+ use rand::seq::SliceRandom;
use test_case::test_case;
use super::*;
- use crate::dkg::test_common::*;
-
- type TargetField = ::TargetField;
+ use crate::test_common::*;
fn make_shared_secret_simple_tdec(
dkg: &PubliclyVerifiableDkg,
@@ -203,13 +209,12 @@ mod test_dkg_full {
let threshold = shares_num / 2 + 1;
let (dkg, validator_keypairs) =
- setup_dealt_dkg_with_n_validators(threshold, shares_num);
- let msg = "my-msg".as_bytes().to_vec();
- let aad: &[u8] = "my-aad".as_bytes();
+ setup_dealt_dkg_with(threshold, shares_num);
+
let public_key = dkg.public_key();
let ciphertext = ferveo_tdec::encrypt::(
- SecretBox::new(msg.clone()),
- aad,
+ SecretBox::new(MSG.to_vec()),
+ AAD,
&public_key,
rng,
)
@@ -217,19 +222,19 @@ mod test_dkg_full {
let (_, _, shared_secret) = make_shared_secret_simple_tdec(
&dkg,
- aad,
+ AAD,
&ciphertext.header().unwrap(),
validator_keypairs.as_slice(),
);
let plaintext = ferveo_tdec::decrypt_with_shared_secret(
&ciphertext,
- aad,
+ AAD,
&shared_secret,
&dkg.pvss_params.g_inv(),
)
.unwrap();
- assert_eq!(plaintext, msg);
+ assert_eq!(plaintext, MSG);
}
#[test_case(4; "number of shares (validators) is a power of 2")]
@@ -240,13 +245,11 @@ mod test_dkg_full {
// In precomputed variant, threshold must be equal to shares_num
let threshold = shares_num;
let (dkg, validator_keypairs) =
- setup_dealt_dkg_with_n_validators(threshold, shares_num);
- let msg = "my-msg".as_bytes().to_vec();
- let aad: &[u8] = "my-aad".as_bytes();
+ setup_dealt_dkg_with(threshold, shares_num);
let public_key = dkg.public_key();
let ciphertext = ferveo_tdec::encrypt::(
- SecretBox::new(msg.clone()),
- aad,
+ SecretBox::new(MSG.to_vec()),
+ AAD,
&public_key,
rng,
)
@@ -260,7 +263,7 @@ mod test_dkg_full {
.take(validator_keypairs.len())
.collect::>();
- let decryption_shares: Vec> =
+ let mut decryption_shares: Vec> =
validator_keypairs
.iter()
.map(|validator_keypair| {
@@ -270,7 +273,7 @@ mod test_dkg_full {
pvss_aggregated
.make_decryption_share_simple_precomputed(
&ciphertext.header().unwrap(),
- aad,
+ AAD,
&validator_keypair.decryption_key,
validator.share_index,
&domain_points,
@@ -279,6 +282,7 @@ mod test_dkg_full {
.unwrap()
})
.collect();
+ decryption_shares.shuffle(rng);
assert_eq!(domain_points.len(), decryption_shares.len());
let shared_secret =
@@ -287,25 +291,24 @@ mod test_dkg_full {
// Combination works, let's decrypt
let plaintext = ferveo_tdec::decrypt_with_shared_secret(
&ciphertext,
- aad,
+ AAD,
&shared_secret,
&dkg.pvss_params.g_inv(),
)
.unwrap();
- assert_eq!(plaintext, msg);
+ assert_eq!(plaintext, MSG);
}
#[test]
fn test_dkg_simple_tdec_share_verification() {
let rng = &mut test_rng();
- let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators(3, 4);
- let msg = "my-msg".as_bytes().to_vec();
- let aad: &[u8] = "my-aad".as_bytes();
+ let (dkg, validator_keypairs) =
+ setup_dealt_dkg_with(SECURITY_THRESHOLD, SHARES_NUM);
let public_key = dkg.public_key();
let ciphertext = ferveo_tdec::encrypt::(
- SecretBox::new(msg),
- aad,
+ SecretBox::new(MSG.to_vec()),
+ AAD,
&public_key,
rng,
)
@@ -314,7 +317,7 @@ mod test_dkg_full {
let (pvss_aggregated, decryption_shares, _) =
make_shared_secret_simple_tdec(
&dkg,
- aad,
+ AAD,
&ciphertext.header().unwrap(),
validator_keypairs.as_slice(),
);
@@ -363,16 +366,12 @@ mod test_dkg_full {
fn test_dkg_simple_tdec_share_recovery() {
let rng = &mut test_rng();
- let security_threshold = 3;
- let shares_num = 4;
let (dkg, validator_keypairs) =
- setup_dealt_dkg_with_n_validators(security_threshold, shares_num);
- let msg = "my-msg".as_bytes().to_vec();
- let aad: &[u8] = "my-aad".as_bytes();
+ setup_dealt_dkg_with(SECURITY_THRESHOLD, SHARES_NUM);
let public_key = &dkg.public_key();
let ciphertext = ferveo_tdec::encrypt::(
- SecretBox::new(msg),
- aad,
+ SecretBox::new(MSG.to_vec()),
+ AAD,
public_key,
rng,
)
@@ -381,7 +380,7 @@ mod test_dkg_full {
// Create an initial shared secret
let (_, _, old_shared_secret) = make_shared_secret_simple_tdec(
&dkg,
- aad,
+ AAD,
&ciphertext.header().unwrap(),
validator_keypairs.as_slice(),
);
@@ -476,7 +475,7 @@ mod test_dkg_full {
pvss_aggregated
.make_decryption_share_simple(
&ciphertext.header().unwrap(),
- aad,
+ AAD,
&validator_keypair.decryption_key,
share_index,
&dkg.pvss_params.g_inv(),
@@ -492,21 +491,21 @@ mod test_dkg_full {
&new_validator_decryption_key,
&new_private_key_share,
&ciphertext.header().unwrap(),
- aad,
+ AAD,
&dkg.pvss_params.g_inv(),
)
.unwrap(),
);
domain_points.push(x_r);
- assert_eq!(domain_points.len(), shares_num as usize);
- assert_eq!(decryption_shares.len(), shares_num as usize);
+ assert_eq!(domain_points.len(), SHARES_NUM as usize);
+ assert_eq!(decryption_shares.len(), SHARES_NUM as usize);
// Maybe parametrize this test with [1..] and [..threshold]
let domain_points = &domain_points[1..];
let decryption_shares = &decryption_shares[1..];
- assert_eq!(domain_points.len(), security_threshold as usize);
- assert_eq!(decryption_shares.len(), security_threshold as usize);
+ assert_eq!(domain_points.len(), SECURITY_THRESHOLD as usize);
+ assert_eq!(decryption_shares.len(), SECURITY_THRESHOLD as usize);
let lagrange = ferveo_tdec::prepare_combine_simple::(domain_points);
let new_shared_secret = ferveo_tdec::share_combine_simple::(
@@ -524,16 +523,12 @@ mod test_dkg_full {
fn test_dkg_simple_tdec_share_refreshing() {
let rng = &mut test_rng();
- let security_threshold = 3;
- let shares_num = 4;
let (dkg, validator_keypairs) =
- setup_dealt_dkg_with_n_validators(security_threshold, shares_num);
- let msg = "my-msg".as_bytes().to_vec();
- let aad: &[u8] = "my-aad".as_bytes();
+ setup_dealt_dkg_with(SECURITY_THRESHOLD, SHARES_NUM);
let public_key = &dkg.public_key();
let ciphertext = ferveo_tdec::encrypt::(
- SecretBox::new(msg),
- aad,
+ SecretBox::new(MSG.to_vec()),
+ AAD,
public_key,
rng,
)
@@ -542,7 +537,7 @@ mod test_dkg_full {
// Create an initial shared secret
let (_, _, old_shared_secret) = make_shared_secret_simple_tdec(
&dkg,
- aad,
+ AAD,
&ciphertext.header().unwrap(),
validator_keypairs.as_slice(),
);
@@ -607,7 +602,7 @@ mod test_dkg_full {
&validator_keypair.decryption_key,
updated_shares.get(share_index).unwrap(),
&ciphertext.header().unwrap(),
- aad,
+ AAD,
&dkg.pvss_params.g_inv(),
)
.unwrap()
@@ -615,10 +610,10 @@ mod test_dkg_full {
.collect();
let lagrange = ferveo_tdec::prepare_combine_simple::(
- &domain_points[..security_threshold as usize],
+ &domain_points[..SECURITY_THRESHOLD as usize],
);
let new_shared_secret = ferveo_tdec::share_combine_simple::(
- &decryption_shares[..security_threshold as usize],
+ &decryption_shares[..SECURITY_THRESHOLD as usize],
&lagrange,
);
diff --git a/ferveo/src/pvss.rs b/ferveo/src/pvss.rs
index 495018bf..c8498bb7 100644
--- a/ferveo/src/pvss.rs
+++ b/ferveo/src/pvss.rs
@@ -462,11 +462,7 @@ mod test_pvss {
use rand::seq::SliceRandom;
use super::*;
- use crate::{dkg::test_common::*, utils::is_sorted};
-
- type ScalarField = ::ScalarField;
- type G1 = ::G1Affine;
- type G2 = ::G2Affine;
+ use crate::{test_common::*, utils::is_sorted, DkgParams};
/// Test the happy flow that a pvss with the correct form is created
/// and that appropriate validations pass
diff --git a/ferveo/src/refresh.rs b/ferveo/src/refresh.rs
index c9e692d4..524e6569 100644
--- a/ferveo/src/refresh.rs
+++ b/ferveo/src/refresh.rs
@@ -122,22 +122,18 @@ mod tests_refresh {
use std::collections::HashMap;
use ark_bls12_381::Fr;
- use ark_ec::pairing::Pairing;
use ark_std::{test_rng, UniformRand, Zero};
- use rand_core::RngCore;
-
- type E = ark_bls12_381::Bls12_381;
- type ScalarField = ::ScalarField;
-
use ferveo_tdec::{
test_common::setup_simple, PrivateDecryptionContextSimple,
PrivateKeyShare,
};
+ use rand_core::RngCore;
+ use test_case::test_matrix;
use crate::{
apply_updates_to_private_share, prepare_share_updates_for_recovery,
prepare_share_updates_for_refresh,
- recover_share_from_updated_private_shares,
+ recover_share_from_updated_private_shares, test_common::*,
};
fn make_new_share_fragments_for_recovery(
@@ -191,14 +187,13 @@ mod tests_refresh {
/// Ñ parties (where t <= Ñ <= N) jointly execute a "share recovery" algorithm, and the output is 1 new share.
/// The new share is intended to restore a previously existing share, e.g., due to loss or corruption.
- #[test]
- fn tdec_simple_variant_share_recovery_at_selected_point() {
+ #[test_matrix([4, 7, 11, 16])]
+ fn tdec_simple_variant_share_recovery_at_selected_point(shares_num: usize) {
let rng = &mut test_rng();
- let shares_num = 16;
- let threshold = shares_num * 2 / 3;
+ let security_threshold = shares_num * 2 / 3;
let (_, _, mut contexts) =
- setup_simple::(threshold, shares_num, rng);
+ setup_simple::(security_threshold, shares_num, rng);
// Prepare participants
@@ -220,7 +215,7 @@ mod tests_refresh {
// Each participant prepares an update for each other participant, and uses it to create a new share fragment
let new_share_fragments = make_new_share_fragments_for_recovery(
rng,
- threshold,
+ security_threshold,
&x_r,
&remaining_participants,
);
@@ -233,8 +228,8 @@ mod tests_refresh {
.collect::>();
let new_private_key_share = recover_share_from_updated_private_shares(
&x_r,
- &domain_points[..threshold],
- &new_share_fragments[..threshold],
+ &domain_points[..security_threshold],
+ &new_share_fragments[..security_threshold],
);
assert_eq!(new_private_key_share, original_private_key_share);
@@ -244,8 +239,8 @@ mod tests_refresh {
let incorrect_private_key_share =
recover_share_from_updated_private_shares(
&x_r,
- &domain_points[..(threshold - 1)],
- &new_share_fragments[..(threshold - 1)],
+ &domain_points[..(security_threshold - 1)],
+ &new_share_fragments[..(security_threshold - 1)],
);
assert_ne!(incorrect_private_key_share, original_private_key_share);
@@ -253,10 +248,9 @@ mod tests_refresh {
/// Ñ parties (where t <= Ñ <= N) jointly execute a "share recovery" algorithm, and the output is 1 new share.
/// The new share is independent from the previously existing shares. We can use this to on-board a new participant into an existing cohort.
- #[test]
- fn tdec_simple_variant_share_recovery_at_random_point() {
+ #[test_matrix([4, 7, 11, 16])]
+ fn tdec_simple_variant_share_recovery_at_random_point(shares_num: usize) {
let rng = &mut test_rng();
- let shares_num = 16;
let threshold = shares_num * 2 / 3;
let (_, shared_private_key, mut contexts) =
@@ -321,10 +315,10 @@ mod tests_refresh {
/// Ñ parties (where t <= Ñ <= N) jointly execute a "share refresh" algorithm.
/// The output is M new shares (with M <= Ñ), with each of the M new shares substituting the
/// original share (i.e., the original share is deleted).
- #[test]
- fn tdec_simple_variant_share_refreshing() {
+ #[test_matrix([4, 7, 11, 16])]
+
+ fn tdec_simple_variant_share_refreshing(shares_num: usize) {
let rng = &mut test_rng();
- let shares_num = 16;
let threshold = shares_num * 2 / 3;
let (_, shared_private_key, contexts) =
diff --git a/ferveo/src/test_common.rs b/ferveo/src/test_common.rs
new file mode 100644
index 00000000..22d072a2
--- /dev/null
+++ b/ferveo/src/test_common.rs
@@ -0,0 +1,110 @@
+/// Factory functions and variables for testing
+use std::str::FromStr;
+
+pub use ark_bls12_381::Bls12_381 as E;
+use ark_ec::pairing::Pairing;
+use ferveo_common::Keypair;
+use rand::seq::SliceRandom;
+
+use crate::{DkgParams, EthereumAddress, PubliclyVerifiableDkg, Validator};
+
+pub type ScalarField = ::ScalarField;
+pub type G1 = ::G1Affine;
+pub type G2 = ::G2Affine;
+pub type TargetField = ::TargetField;
+
+pub const TAU: u32 = 0;
+pub const MSG: &[u8] = b"my-msg";
+pub const AAD: &[u8] = b"my-aad";
+pub const SECURITY_THRESHOLD: u32 = 3;
+pub const SHARES_NUM: u32 = 4;
+
+pub fn gen_keypairs(n: u32) -> Vec> {
+ let rng = &mut ark_std::test_rng();
+ (0..n).map(|_| Keypair::::new(rng)).collect()
+}
+
+pub fn gen_address(i: usize) -> EthereumAddress {
+ EthereumAddress::from_str(&format!("0x{i:040}")).unwrap()
+}
+
+pub fn gen_validators(keypairs: &[Keypair]) -> Vec> {
+ keypairs
+ .iter()
+ .enumerate()
+ .map(|(i, keypair)| Validator {
+ address: gen_address(i),
+ public_key: keypair.public_key(),
+ })
+ .collect()
+}
+
+pub type TestSetup = (PubliclyVerifiableDkg, Vec>);
+
+pub fn setup_dkg_for_n_validators(
+ security_threshold: u32,
+ shares_num: u32,
+ my_validator_index: usize,
+) -> TestSetup {
+ let keypairs = gen_keypairs(shares_num);
+ let mut validators = gen_validators(keypairs.as_slice());
+ validators.sort();
+ let me = validators[my_validator_index].clone();
+ let dkg = PubliclyVerifiableDkg::new(
+ &validators,
+ &DkgParams::new(TAU, security_threshold, shares_num).unwrap(),
+ &me,
+ )
+ .expect("Setup failed");
+ (dkg, keypairs)
+}
+
+/// Create a test dkg
+///
+/// The [`crate::dkg::test_dkg_init`] module checks correctness of this setup
+pub fn setup_dkg(my_validator_index: usize) -> TestSetup {
+ setup_dkg_for_n_validators(
+ SECURITY_THRESHOLD,
+ SHARES_NUM,
+ my_validator_index,
+ )
+}
+
+/// 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 {
+ setup_dealt_dkg_with(SECURITY_THRESHOLD, SHARES_NUM)
+}
+
+pub fn setup_dealt_dkg_with(
+ security_threshold: u32,
+ shares_num: u32,
+) -> TestSetup {
+ let rng = &mut ark_std::test_rng();
+
+ // Gather everyone's transcripts
+ let mut messages: Vec<_> = (0..shares_num)
+ .map(|my_index| {
+ let (mut dkg, _) = setup_dkg_for_n_validators(
+ security_threshold,
+ shares_num,
+ my_index as usize,
+ );
+ let me = dkg.me.validator.clone();
+ let message = dkg.share(rng).unwrap();
+ (me, message)
+ })
+ .collect();
+
+ // Create a test DKG instance
+ let (mut dkg, keypairs) =
+ setup_dkg_for_n_validators(security_threshold, shares_num, 0);
+
+ // The ordering of messages should not matter
+ messages.shuffle(rng);
+ messages.iter().for_each(|(sender, message)| {
+ dkg.apply_message(sender, message).expect("Setup failed");
+ });
+ (dkg, keypairs)
+}