From 6a539a40372a9b3b90d8cbd6cac8badcfb4be8f1 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Wed, 10 Jan 2024 11:36:40 +0100
Subject: [PATCH] feat(dkg): relax dkg ceremony constraints using new
min_shares_num parameter
---
.../examples/server_api_precomputed.py | 4 +
ferveo-python/examples/server_api_simple.py | 6 +-
ferveo-python/test/test_ferveo.py | 4 +
ferveo-wasm/examples/node/src/main.test.ts | 12 ++-
ferveo-wasm/tests/node.rs | 36 +++++---
ferveo/benches/benchmarks/validity_checks.rs | 6 +-
ferveo/examples/bench_primitives_size.rs | 6 +-
ferveo/src/api.rs | 92 ++++++++++++++-----
ferveo/src/bindings_python.rs | 34 ++++++-
ferveo/src/bindings_wasm.rs | 10 +-
ferveo/src/dkg.rs | 83 ++++++++++++++---
ferveo/src/lib.rs | 52 ++++++++---
ferveo/src/pvss.rs | 14 ++-
13 files changed, 266 insertions(+), 93 deletions(-)
diff --git a/ferveo-python/examples/server_api_precomputed.py b/ferveo-python/examples/server_api_precomputed.py
index 7c433e96..3adeb190 100644
--- a/ferveo-python/examples/server_api_precomputed.py
+++ b/ferveo-python/examples/server_api_precomputed.py
@@ -18,6 +18,7 @@ def gen_eth_addr(i: int) -> str:
shares_num = 4
# In precomputed variant, security threshold must be equal to shares_num
security_threshold = shares_num
+min_shares_num = shares_num
validator_keypairs = [Keypair.random() for _ in range(0, shares_num)]
validators = [
@@ -35,6 +36,7 @@ def gen_eth_addr(i: int) -> str:
dkg = Dkg(
tau=tau,
shares_num=shares_num,
+ min_shares_num=min_shares_num,
security_threshold=security_threshold,
validators=validators,
me=sender,
@@ -45,6 +47,7 @@ def gen_eth_addr(i: int) -> str:
dkg = Dkg(
tau=tau,
shares_num=shares_num,
+ min_shares_num=min_shares_num,
security_threshold=security_threshold,
validators=validators,
me=validators[0],
@@ -69,6 +72,7 @@ def gen_eth_addr(i: int) -> str:
dkg = Dkg(
tau=tau,
shares_num=shares_num,
+ min_shares_num=min_shares_num,
security_threshold=security_threshold,
validators=validators,
me=validator,
diff --git a/ferveo-python/examples/server_api_simple.py b/ferveo-python/examples/server_api_simple.py
index 4f9e8447..71a88673 100644
--- a/ferveo-python/examples/server_api_simple.py
+++ b/ferveo-python/examples/server_api_simple.py
@@ -15,8 +15,9 @@ def gen_eth_addr(i: int) -> str:
tau = 1
-security_threshold = 3
shares_num = 4
+security_threshold = shares_num - 1
+min_shares_num = shares_num
validator_keypairs = [Keypair.random() for _ in range(0, shares_num)]
validators = [
Validator(gen_eth_addr(i), keypair.public_key())
@@ -33,6 +34,7 @@ def gen_eth_addr(i: int) -> str:
dkg = Dkg(
tau=tau,
shares_num=shares_num,
+ min_shares_num=min_shares_num,
security_threshold=security_threshold,
validators=validators,
me=sender,
@@ -45,6 +47,7 @@ def gen_eth_addr(i: int) -> str:
dkg = Dkg(
tau=tau,
shares_num=shares_num,
+ min_shares_num=min_shares_num,
security_threshold=security_threshold,
validators=validators,
me=me,
@@ -72,6 +75,7 @@ def gen_eth_addr(i: int) -> str:
dkg = Dkg(
tau=tau,
shares_num=shares_num,
+ min_shares_num=min_shares_num,
security_threshold=security_threshold,
validators=validators,
me=validator,
diff --git a/ferveo-python/test/test_ferveo.py b/ferveo-python/test/test_ferveo.py
index 6f00b6df..959e02fc 100644
--- a/ferveo-python/test/test_ferveo.py
+++ b/ferveo-python/test/test_ferveo.py
@@ -42,6 +42,7 @@ def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_t
raise ValueError("Unknown variant: " + variant)
tau = 1
+ min_shares_num = shares_num
validator_keypairs = [Keypair.random() for _ in range(0, shares_num)]
validators = [
Validator(gen_eth_addr(i), keypair.public_key())
@@ -54,6 +55,7 @@ def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_t
dkg = Dkg(
tau=tau,
shares_num=shares_num,
+ min_shares_num=min_shares_num,
security_threshold=threshold,
validators=validators,
me=sender,
@@ -63,6 +65,7 @@ def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_t
dkg = Dkg(
tau=tau,
shares_num=shares_num,
+ min_shares_num=min_shares_num,
security_threshold=threshold,
validators=validators,
me=validators[0],
@@ -82,6 +85,7 @@ def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_t
dkg = Dkg(
tau=tau,
shares_num=shares_num,
+ min_shares_num=min_shares_num,
security_threshold=threshold,
validators=validators,
me=validator,
diff --git a/ferveo-wasm/examples/node/src/main.test.ts b/ferveo-wasm/examples/node/src/main.test.ts
index 28144861..e828d935 100644
--- a/ferveo-wasm/examples/node/src/main.test.ts
+++ b/ferveo-wasm/examples/node/src/main.test.ts
@@ -26,6 +26,7 @@ function setupTest() {
const tau = 1;
const sharesNum = 4;
const threshold = Math.floor((sharesNum * 2) / 3);
+ const minSharesNum = sharesNum;
const validatorKeypairs: Keypair[] = [];
const validators: Validator[] = [];
@@ -40,7 +41,7 @@ function setupTest() {
// validator, including themselves
const messages: ValidatorMessage[] = [];
validators.forEach((sender) => {
- const dkg = new Dkg(tau, sharesNum, threshold, validators, sender);
+ const dkg = new Dkg(tau, sharesNum, minSharesNum, threshold, validators, sender);
const transcript = dkg.generateTranscript();
const message = new ValidatorMessage(sender, transcript);
messages.push(message);
@@ -48,7 +49,7 @@ function setupTest() {
// Now that every validator holds a dkg instance and a transcript for every other validator,
// every validator can aggregate the transcripts
- const dkg = new Dkg(tau, sharesNum, threshold, validators, validators[0]);
+ const dkg = new Dkg(tau, sharesNum, minSharesNum, threshold, validators, validators[0]);
const serverAggregate = dkg.aggregateTranscript(messages);
expect(serverAggregate.verify(sharesNum, messages)).toBe(true);
@@ -65,6 +66,7 @@ function setupTest() {
return {
tau,
sharesNum,
+ minSharesNum,
threshold,
validatorKeypairs,
validators,
@@ -82,6 +84,7 @@ describe("ferveo-wasm", () => {
const {
tau,
sharesNum,
+ minSharesNum,
threshold,
validatorKeypairs,
validators,
@@ -96,7 +99,7 @@ describe("ferveo-wasm", () => {
zip(validators, validatorKeypairs).forEach(([validator, keypair]) => {
expect(validator.publicKey.equals(keypair.publicKey)).toBe(true);
- const dkg = new Dkg(tau, sharesNum, threshold, validators, validator);
+ const dkg = new Dkg(tau, sharesNum, minSharesNum, threshold, validators, validator);
const aggregate = dkg.aggregateTranscript(messages);
const isValid = aggregate.verify(sharesNum, messages);
expect(isValid).toBe(true);
@@ -131,6 +134,7 @@ describe("ferveo-wasm", () => {
const {
tau,
sharesNum,
+ minSharesNum,
threshold,
validatorKeypairs,
validators,
@@ -143,7 +147,7 @@ describe("ferveo-wasm", () => {
// Having aggregated the transcripts, the validators can now create decryption shares
const decryptionShares: DecryptionSharePrecomputed[] = [];
zip(validators, validatorKeypairs).forEach(([validator, keypair]) => {
- const dkg = new Dkg(tau, sharesNum, threshold, validators, validator);
+ const dkg = new Dkg(tau, sharesNum, minSharesNum, threshold, validators, validator);
const aggregate = dkg.aggregateTranscript(messages);
const isValid = aggregate.verify(sharesNum, messages);
expect(isValid).toBe(true);
diff --git a/ferveo-wasm/tests/node.rs b/ferveo-wasm/tests/node.rs
index b4234d07..25f24d9f 100644
--- a/ferveo-wasm/tests/node.rs
+++ b/ferveo-wasm/tests/node.rs
@@ -8,8 +8,9 @@ use wasm_bindgen_test::*;
type TestSetup = (
u32,
- usize,
- usize,
+ u32,
+ u32,
+ u32,
Vec,
Vec,
ValidatorArray,
@@ -21,11 +22,13 @@ type TestSetup = (
fn setup_dkg() -> TestSetup {
let tau = 1;
- let shares_num = 16;
+ let shares_num: u32 = 16;
let security_threshold = shares_num * 2 / 3;
+ let min_shares_num = shares_num;
- let validator_keypairs =
- (0..shares_num).map(gen_keypair).collect::>();
+ let validator_keypairs = (0..shares_num as usize)
+ .map(gen_keypair)
+ .collect::>();
let validators = validator_keypairs
.iter()
.enumerate()
@@ -38,8 +41,9 @@ fn setup_dkg() -> TestSetup {
let messages = validators.iter().map(|sender| {
let dkg = Dkg::new(
tau,
- shares_num as u32,
- security_threshold as u32,
+ shares_num,
+ min_shares_num,
+ security_threshold,
&validators_js,
sender,
)
@@ -54,8 +58,9 @@ fn setup_dkg() -> TestSetup {
let mut dkg = Dkg::new(
tau,
- shares_num as u32,
- security_threshold as u32,
+ shares_num,
+ min_shares_num,
+ security_threshold,
&validators_js,
&validators[0],
)
@@ -81,6 +86,7 @@ fn setup_dkg() -> TestSetup {
(
tau,
shares_num,
+ min_shares_num,
security_threshold,
validator_keypairs,
validators,
@@ -97,6 +103,7 @@ fn tdec_simple() {
let (
tau,
shares_num,
+ min_shares_num,
security_threshold,
validator_keypairs,
validators,
@@ -112,8 +119,9 @@ fn tdec_simple() {
.map(|(validator, keypair)| {
let mut dkg = Dkg::new(
tau,
- shares_num as u32,
- security_threshold as u32,
+ shares_num,
+ min_shares_num,
+ security_threshold,
&validators_js,
&validator,
)
@@ -151,6 +159,7 @@ fn tdec_precomputed() {
let (
tau,
shares_num,
+ min_shares_num,
security_threshold,
validator_keypairs,
validators,
@@ -166,8 +175,9 @@ fn tdec_precomputed() {
.map(|(validator, keypair)| {
let mut dkg = Dkg::new(
tau,
- shares_num as u32,
- security_threshold as u32,
+ shares_num,
+ min_shares_num,
+ security_threshold,
&validators_js,
&validator,
)
diff --git a/ferveo/benches/benchmarks/validity_checks.rs b/ferveo/benches/benchmarks/validity_checks.rs
index a6dd9f48..381f1070 100644
--- a/ferveo/benches/benchmarks/validity_checks.rs
+++ b/ferveo/benches/benchmarks/validity_checks.rs
@@ -45,11 +45,7 @@ fn setup_dkg(
let me = validators[validator].clone();
PubliclyVerifiableDkg::new(
&validators,
- &DkgParams {
- tau: 0,
- security_threshold: shares_num / 3,
- shares_num,
- },
+ &DkgParams::new(0, shares_num / 3, shares_num, shares_num).unwrap(),
&me,
)
.expect("Setup failed")
diff --git a/ferveo/examples/bench_primitives_size.rs b/ferveo/examples/bench_primitives_size.rs
index 18adf673..8f133200 100644
--- a/ferveo/examples/bench_primitives_size.rs
+++ b/ferveo/examples/bench_primitives_size.rs
@@ -80,11 +80,7 @@ fn setup_dkg(
let me = validators[validator].clone();
PubliclyVerifiableDkg::new(
&validators,
- &DkgParams {
- tau: 0,
- security_threshold,
- shares_num,
- },
+ &DkgParams::new(0, security_threshold, shares_num, shares_num).unwrap(),
&me,
)
.expect("Setup failed")
diff --git a/ferveo/src/api.rs b/ferveo/src/api.rs
index af3edcd4..e62a718a 100644
--- a/ferveo/src/api.rs
+++ b/ferveo/src/api.rs
@@ -199,15 +199,17 @@ impl Dkg {
pub fn new(
tau: u32,
shares_num: u32,
+ min_shares_num: u32,
security_threshold: u32,
validators: &[Validator],
me: &Validator,
) -> Result {
- let dkg_params = crate::DkgParams {
+ let dkg_params = crate::DkgParams::new(
tau,
security_threshold,
+ min_shares_num,
shares_num,
- };
+ )?;
let dkg = crate::PubliclyVerifiableDkg::::new(
validators,
&dkg_params,
@@ -312,7 +314,7 @@ impl AggregatedTranscript {
.0
.domain
.elements()
- .take(dkg.0.dkg_params.shares_num as usize)
+ .take(dkg.0.dkg_params.shares_num() as usize)
.collect();
self.0.make_decryption_share_simple_precomputed(
&ciphertext_header.0,
@@ -406,6 +408,7 @@ mod test_ferveo_api {
rng: &mut StdRng,
tau: u32,
security_threshold: u32,
+ min_shares_num: u32,
shares_num: u32,
) -> TestInputs {
let validator_keypairs = gen_keypairs(shares_num);
@@ -426,6 +429,7 @@ mod test_ferveo_api {
let dkg = Dkg::new(
tau,
shares_num,
+ min_shares_num,
security_threshold,
&validators,
sender,
@@ -456,16 +460,28 @@ mod test_ferveo_api {
// TODO: Refactor DKG constructor to not require security threshold or this case.
// Or figure out a different way to simplify the precomputed variant API.
let security_threshold = shares_num;
+ let min_shares_num = shares_num;
- let (messages, validators, validator_keypairs) =
- make_test_inputs(rng, tau, security_threshold, shares_num);
+ let (messages, validators, validator_keypairs) = make_test_inputs(
+ rng,
+ tau,
+ security_threshold,
+ min_shares_num,
+ 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)
- .unwrap();
+ let mut dkg = Dkg::new(
+ tau,
+ shares_num,
+ min_shares_num,
+ security_threshold,
+ &validators,
+ &me,
+ )
+ .unwrap();
let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap();
assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());
@@ -489,6 +505,7 @@ mod test_ferveo_api {
tau,
shares_num,
security_threshold,
+ min_shares_num,
&validators,
validator,
)
@@ -546,15 +563,22 @@ mod test_ferveo_api {
for shares_num in [4, 7] {
let tau = 1;
let security_threshold = shares_num / 2 + 1;
+ let min_shares_num = shares_num;
- let (messages, validators, validator_keypairs) =
- make_test_inputs(rng, tau, security_threshold, shares_num);
+ let (messages, validators, validator_keypairs) = make_test_inputs(
+ rng,
+ tau,
+ security_threshold,
+ min_shares_num,
+ shares_num,
+ );
// Now that every validator holds a dkg instance and a transcript for every other validator,
// every validator can aggregate the transcripts
let mut dkg = Dkg::new(
tau,
shares_num,
+ min_shares_num,
security_threshold,
&validators,
&validators[0],
@@ -581,6 +605,7 @@ mod test_ferveo_api {
let mut dkg = Dkg::new(
tau,
shares_num,
+ min_shares_num,
security_threshold,
&validators,
validator,
@@ -632,22 +657,34 @@ mod test_ferveo_api {
let rng = &mut StdRng::seed_from_u64(0);
let tau = 1;
- let security_threshold = 3;
let shares_num = 4;
+ let min_shares_num = shares_num;
+ let security_threshold = shares_num - 1;
- let (messages, validators, _) =
- make_test_inputs(rng, tau, security_threshold, shares_num);
+ let (messages, validators, _) = make_test_inputs(
+ rng,
+ tau,
+ security_threshold,
+ min_shares_num,
+ 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)
- .unwrap();
+ let mut dkg = Dkg::new(
+ tau,
+ shares_num,
+ min_shares_num,
+ security_threshold,
+ &validators,
+ &me,
+ )
+ .unwrap();
let local_aggregate = dkg.aggregate_transcripts(&messages).unwrap();
assert!(local_aggregate
- .verify(dkg.0.dkg_params.shares_num, &messages)
+ .verify(dkg.0.dkg_params.shares_num(), &messages)
.is_ok());
}
@@ -656,11 +693,17 @@ mod test_ferveo_api {
let rng = &mut StdRng::seed_from_u64(0);
let tau = 1;
- let security_threshold = 3;
let shares_num = 4;
+ let security_threshold = shares_num - 1;
+ let min_shares_num = shares_num;
- let (messages, _, _) =
- make_test_inputs(rng, tau, security_threshold, shares_num);
+ let (messages, _, _) = make_test_inputs(
+ rng,
+ tau,
+ security_threshold,
+ min_shares_num,
+ shares_num,
+ );
// We only need `security_threshold` transcripts to aggregate
let messages = &messages[..security_threshold as usize];
@@ -688,8 +731,13 @@ mod test_ferveo_api {
// 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);
+ let (bad_messages, _, _) = make_test_inputs(
+ rng,
+ tau,
+ security_threshold,
+ min_shares_num,
+ 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);
diff --git a/ferveo/src/bindings_python.rs b/ferveo/src/bindings_python.rs
index ed965f3e..09fa0753 100644
--- a/ferveo/src/bindings_python.rs
+++ b/ferveo/src/bindings_python.rs
@@ -93,6 +93,11 @@ impl From for PyErr {
}
Error::InvalidVariant(variant) => {
InvalidVariant::new_err(variant.to_string())
+ },
+ Error::InvalidDkgParameters(num_shares, security_threshold, min_shares_num) => {
+ InvalidDkgParameters::new_err(format!(
+ "num_shares: {num_shares}, security_threshold: {security_threshold}, min_shares_num: {min_shares_num}"
+ ))
}
},
_ => default(),
@@ -128,6 +133,7 @@ create_exception!(exceptions, ValidatorPublicKeyMismatch, PyValueError);
create_exception!(exceptions, SerializationError, PyValueError);
create_exception!(exceptions, InvalidByteLength, PyValueError);
create_exception!(exceptions, InvalidVariant, PyValueError);
+create_exception!(exceptions, InvalidDkgParameters, PyValueError);
fn from_py_bytes(bytes: &[u8]) -> PyResult {
T::from_bytes(bytes)
@@ -452,6 +458,7 @@ impl Dkg {
pub fn new(
tau: u32,
shares_num: u32,
+ min_shares_num: u32,
security_threshold: u32,
validators: Vec,
me: &Validator,
@@ -460,6 +467,7 @@ impl Dkg {
let dkg = api::Dkg::new(
tau,
shares_num,
+ min_shares_num,
security_threshold,
&validators,
&me.0,
@@ -734,6 +742,7 @@ mod test_ferveo_python {
fn make_test_inputs(
tau: u32,
security_threshold: u32,
+ min_shares_num: u32,
shares_num: u32,
) -> TestInputs {
let validator_keypairs = (0..shares_num)
@@ -757,6 +766,7 @@ mod test_ferveo_python {
let dkg = Dkg::new(
tau,
shares_num,
+ min_shares_num,
security_threshold,
validators.clone(),
&sender,
@@ -777,9 +787,14 @@ mod test_ferveo_python {
let shares_num = 4;
// In precomputed variant, the security threshold is equal to the number of shares
let security_threshold = shares_num;
+ let min_shares_num = shares_num;
- let (messages, validators, validator_keypairs) =
- make_test_inputs(tau, security_threshold, shares_num);
+ let (messages, validators, validator_keypairs) = make_test_inputs(
+ tau,
+ security_threshold,
+ min_shares_num,
+ shares_num,
+ );
// Now that every validator holds a dkg instance and a transcript for every other validator,
// every validator can aggregate the transcripts
@@ -788,6 +803,7 @@ mod test_ferveo_python {
let mut dkg = Dkg::new(
tau,
shares_num,
+ min_shares_num,
security_threshold,
validators.clone(),
&me,
@@ -817,6 +833,7 @@ mod test_ferveo_python {
let mut dkg = Dkg::new(
tau,
shares_num,
+ min_shares_num,
security_threshold,
validators.clone(),
validator,
@@ -854,10 +871,15 @@ mod test_ferveo_python {
fn test_server_api_tdec_simple() {
let tau = 1;
let shares_num = 4;
- let security_threshold = 3;
+ let security_threshold = shares_num - 1;
+ let min_shares_num = shares_num;
- let (messages, validators, validator_keypairs) =
- make_test_inputs(tau, security_threshold, shares_num);
+ let (messages, validators, validator_keypairs) = make_test_inputs(
+ tau,
+ security_threshold,
+ min_shares_num,
+ shares_num,
+ );
// Now that every validator holds a dkg instance and a transcript for every other validator,
// every validator can aggregate the transcripts
@@ -865,6 +887,7 @@ mod test_ferveo_python {
let mut dkg = Dkg::new(
tau,
shares_num,
+ min_shares_num,
security_threshold,
validators.clone(),
&me,
@@ -894,6 +917,7 @@ mod test_ferveo_python {
let mut dkg = Dkg::new(
tau,
shares_num,
+ min_shares_num,
security_threshold,
validators.clone(),
validator,
diff --git a/ferveo/src/bindings_wasm.rs b/ferveo/src/bindings_wasm.rs
index e412b6e0..5daae742 100644
--- a/ferveo/src/bindings_wasm.rs
+++ b/ferveo/src/bindings_wasm.rs
@@ -340,6 +340,7 @@ impl Dkg {
pub fn new(
tau: u32,
shares_num: u32,
+ min_shares_num: u32,
security_threshold: u32,
validators_js: &ValidatorArray,
me: &Validator,
@@ -352,6 +353,7 @@ impl Dkg {
let dkg = api::Dkg::new(
tau,
shares_num,
+ min_shares_num,
security_threshold,
&validators,
&me.to_inner()?,
@@ -510,15 +512,13 @@ impl AggregatedTranscript {
#[wasm_bindgen]
pub fn verify(
&self,
- shares_num: usize,
+ shares_num: u32,
messages: &ValidatorMessageArray,
) -> JsResult {
set_panic_hook();
let messages = unwrap_messages_js(messages)?;
- let is_valid = self
- .0
- .verify(shares_num as u32, &messages)
- .map_err(map_js_err)?;
+ let is_valid =
+ self.0.verify(shares_num, &messages).map_err(map_js_err)?;
Ok(is_valid)
}
diff --git a/ferveo/src/dkg.rs b/ferveo/src/dkg.rs
index 3f7fc09d..cf11acf8 100644
--- a/ferveo/src/dkg.rs
+++ b/ferveo/src/dkg.rs
@@ -15,9 +15,63 @@ use crate::{
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct DkgParams {
- pub tau: u32,
- pub security_threshold: u32,
- pub shares_num: u32,
+ tau: u32,
+ security_threshold: u32,
+ min_shares_num: u32,
+ shares_num: u32,
+}
+
+impl DkgParams {
+ pub fn new(
+ tau: u32,
+ security_threshold: u32,
+ min_shares_num: u32,
+ shares_num: u32,
+ ) -> Result {
+ if min_shares_num > shares_num {
+ return Err(Error::InvalidDkgParameters(
+ shares_num,
+ security_threshold,
+ min_shares_num,
+ ));
+ }
+ if shares_num < security_threshold {
+ return Err(Error::InvalidDkgParameters(
+ shares_num,
+ security_threshold,
+ min_shares_num,
+ ));
+ }
+ if min_shares_num < security_threshold {
+ return Err(Error::InvalidDkgParameters(
+ shares_num,
+ security_threshold,
+ min_shares_num,
+ ));
+ }
+ Ok(Self {
+ tau,
+ security_threshold,
+ min_shares_num,
+ shares_num,
+ })
+ }
+
+ pub fn tau(&self) -> u32 {
+ self.tau
+ }
+
+ pub fn security_threshold(&self) -> u32 {
+ self.security_threshold
+ }
+
+ pub fn min_shares_num(&self) -> u32 {
+ self.min_shares_num
+ }
+
+ pub fn shares_num(&self) -> u32 {
+ self.shares_num
+ }
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
@@ -350,6 +404,7 @@ pub(crate) mod test_common {
pub fn setup_dkg_for_n_validators(
security_threshold: u32,
+ min_shares_num: u32,
shares_num: u32,
my_index: usize,
) -> TestSetup {
@@ -359,11 +414,8 @@ pub(crate) mod test_common {
let me = validators[my_index].clone();
let dkg = PubliclyVerifiableDkg::new(
&validators,
- &DkgParams {
- tau: 0,
- security_threshold,
- shares_num,
- },
+ &DkgParams::new(0, security_threshold, min_shares_num, shares_num)
+ .unwrap(),
&me,
)
.expect("Setup failed");
@@ -374,18 +426,19 @@ pub(crate) mod test_common {
///
/// 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)
+ setup_dkg_for_n_validators(2, 4, 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)
+ setup_dealt_dkg_with_n_validators(2, 4, 4)
}
pub fn setup_dealt_dkg_with_n_validators(
security_threshold: u32,
+ min_shares_num: u32,
shares_num: u32,
) -> TestSetup {
let rng = &mut ark_std::test_rng();
@@ -395,6 +448,7 @@ pub(crate) mod test_common {
.map(|my_index| {
let (mut dkg, _) = setup_dkg_for_n_validators(
security_threshold,
+ min_shares_num,
shares_num,
my_index as usize,
);
@@ -405,8 +459,12 @@ pub(crate) mod test_common {
.collect();
// Create a test DKG instance
- let (mut dkg, keypairs) =
- setup_dkg_for_n_validators(security_threshold, shares_num, 0);
+ let (mut dkg, keypairs) = setup_dkg_for_n_validators(
+ security_threshold,
+ min_shares_num,
+ shares_num,
+ 0,
+ );
messages.iter().for_each(|(sender, message)| {
dkg.apply_message(sender, message).expect("Setup failed");
});
@@ -436,6 +494,7 @@ mod test_dkg_init {
&DkgParams {
tau: 0,
security_threshold: shares_num / 2,
+ min_shares_num: shares_num - 1,
shares_num,
},
&unknown_validator,
diff --git a/ferveo/src/lib.rs b/ferveo/src/lib.rs
index 59a44024..22f10f65 100644
--- a/ferveo/src/lib.rs
+++ b/ferveo/src/lib.rs
@@ -101,6 +101,9 @@ pub enum Error {
#[error("Invalid variant: {0}")]
InvalidVariant(String),
+
+ #[error("Invalid DKG parameters: number of shares {0}, threshold {1}, minimum number of shares {2}")]
+ InvalidDkgParameters(u32, u32, u32),
}
pub type Result = std::result::Result;
@@ -196,8 +199,12 @@ mod test_dkg_full {
// Works for both power of 2 and non-power of 2
for shares_num in [4, 7] {
let threshold = shares_num / 2 + 1;
- let (dkg, validator_keypairs) =
- setup_dealt_dkg_with_n_validators(threshold, shares_num);
+ let min_shares_num = shares_num - 1;
+ let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators(
+ threshold,
+ min_shares_num,
+ shares_num,
+ );
let msg = "my-msg".as_bytes().to_vec();
let aad: &[u8] = "my-aad".as_bytes();
let public_key = dkg.public_key();
@@ -235,8 +242,12 @@ mod test_dkg_full {
for shares_num in [4, 7] {
// 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 min_shares_num = shares_num;
+ let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators(
+ threshold,
+ min_shares_num,
+ shares_num,
+ );
let msg = "my-msg".as_bytes().to_vec();
let aad: &[u8] = "my-aad".as_bytes();
let public_key = dkg.public_key();
@@ -295,8 +306,15 @@ mod test_dkg_full {
#[test]
fn test_dkg_simple_tdec_share_verification() {
let rng = &mut test_rng();
+ let shares_num = 4;
+ let security_threshold = shares_num - 1;
+ let min_shares_num = shares_num - 1;
- let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators(3, 4);
+ let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators(
+ security_threshold,
+ min_shares_num,
+ shares_num,
+ );
let msg = "my-msg".as_bytes().to_vec();
let aad: &[u8] = "my-aad".as_bytes();
let public_key = dkg.public_key();
@@ -360,10 +378,14 @@ 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 security_threshold = shares_num - 1;
+ let min_shares_num = shares_num - 1;
+ let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators(
+ security_threshold,
+ min_shares_num,
+ shares_num,
+ );
let msg = "my-msg".as_bytes().to_vec();
let aad: &[u8] = "my-aad".as_bytes();
let public_key = &dkg.public_key();
@@ -410,7 +432,7 @@ mod test_dkg_full {
&domain_points,
&dkg.pvss_params.h.into_affine(),
&x_r,
- dkg.dkg_params.security_threshold as usize,
+ dkg.dkg_params.security_threshold() as usize,
rng,
);
(v_addr.clone(), deltas_i)
@@ -519,10 +541,14 @@ 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 min_shares_num = shares_num - 1;
+ let security_threshold = shares_num - 1;
+ let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators(
+ security_threshold,
+ min_shares_num,
+ shares_num,
+ );
let msg = "my-msg".as_bytes().to_vec();
let aad: &[u8] = "my-aad".as_bytes();
let public_key = &dkg.public_key();
@@ -552,7 +578,7 @@ mod test_dkg_full {
let deltas_i = prepare_share_updates_for_refresh::(
&domain_points,
&dkg.pvss_params.h.into_affine(),
- dkg.dkg_params.security_threshold as usize,
+ dkg.dkg_params.security_threshold() as usize,
rng,
);
(v_addr.clone(), deltas_i)
diff --git a/ferveo/src/pvss.rs b/ferveo/src/pvss.rs
index 4f63da82..db91f9d2 100644
--- a/ferveo/src/pvss.rs
+++ b/ferveo/src/pvss.rs
@@ -138,7 +138,7 @@ impl PubliclyVerifiableSS {
) -> Result {
let phi = SecretPolynomial::::new(
s,
- (dkg.dkg_params.security_threshold - 1) as usize,
+ (dkg.dkg_params.security_threshold() - 1) as usize,
rng,
);
@@ -482,7 +482,7 @@ mod test_pvss {
// Check that a polynomial of the correct degree was created
assert_eq!(
pvss.coeffs.len(),
- dkg.dkg_params.security_threshold as usize
+ dkg.dkg_params.security_threshold() as usize
);
// Check that the correct number of shares were created
assert_eq!(pvss.shares.len(), dkg.validators.len());
@@ -544,6 +544,7 @@ mod test_pvss {
let shares_num = 4;
let security_threshold = shares_num - 1;
+ let min_shares_num = shares_num - 1;
let keypairs = gen_keypairs(shares_num);
let mut validators = gen_validators(&keypairs);
let me = validators[0].clone();
@@ -555,11 +556,8 @@ mod test_pvss {
// And because of that the DKG should fail
let result = PubliclyVerifiableDkg::new(
&validators,
- &DkgParams {
- tau: 0,
- security_threshold,
- shares_num,
- },
+ &DkgParams::new(0, security_threshold, min_shares_num, shares_num)
+ .unwrap(),
&me,
);
assert!(result.is_err());
@@ -578,7 +576,7 @@ mod test_pvss {
// Check that a polynomial of the correct degree was created
assert_eq!(
aggregate.coeffs.len(),
- dkg.dkg_params.security_threshold as usize
+ dkg.dkg_params.security_threshold() as usize
);
// Check that the correct number of shares were created
assert_eq!(aggregate.shares.len(), dkg.validators.len());