From 6fd65bd506a0502da39ea4fa292e5fc1669abc27 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Wed, 31 Jan 2024 13:08:56 +0100
Subject: [PATCH] test: update tests for dkgs with relaxed constraints
---
.../examples/server_api_precomputed.py | 12 +-
ferveo-python/examples/server_api_simple.py | 9 +-
ferveo-python/ferveo/__init__.py | 7 +-
ferveo-python/ferveo/__init__.pyi | 32 ++-
ferveo-python/test/test_ferveo.py | 94 ++++++--
ferveo-wasm/examples/node/src/main.test.ts | 173 +++++++-------
ferveo-wasm/tests/node.rs | 217 ++++++++++--------
ferveo/src/api.rs | 156 +++++++++----
ferveo/src/bindings_python.rs | 53 ++++-
ferveo/src/bindings_wasm.rs | 8 +-
ferveo/src/lib.rs | 10 +-
11 files changed, 474 insertions(+), 297 deletions(-)
diff --git a/ferveo-python/examples/server_api_precomputed.py b/ferveo-python/examples/server_api_precomputed.py
index 72263405..a9b98001 100644
--- a/ferveo-python/examples/server_api_precomputed.py
+++ b/ferveo-python/examples/server_api_precomputed.py
@@ -16,18 +16,16 @@ def gen_eth_addr(i: int) -> str:
tau = 1
shares_num = 4
+validators_num = shares_num + 2
# In precomputed variant, security threshold must be equal to shares_num
security_threshold = shares_num
-validator_keypairs = [Keypair.random() for _ in range(0, shares_num)]
+validator_keypairs = [Keypair.random() for _ in range(0, validators_num)]
validators = [
Validator(gen_eth_addr(i), keypair.public_key(), i)
for i, keypair in enumerate(validator_keypairs)
]
-# Validators must be sorted by their public key
-validators.sort(key=lambda v: v.address)
-
# Each validator holds their own DKG instance and generates a transcript every
# validator, including themselves
messages = []
@@ -52,11 +50,11 @@ def gen_eth_addr(i: int) -> str:
# Server can aggregate the transcripts
server_aggregate = dkg.aggregate_transcripts(messages)
-assert server_aggregate.verify(shares_num, messages)
+assert server_aggregate.verify(validators_num, messages)
# And the client can also aggregate and verify the transcripts
client_aggregate = AggregatedTranscript(messages)
-assert client_aggregate.verify(shares_num, messages)
+assert client_aggregate.verify(validators_num, messages)
# In the meantime, the client creates a ciphertext and decryption request
msg = "abc".encode()
@@ -76,7 +74,7 @@ def gen_eth_addr(i: int) -> str:
# We can also obtain the aggregated transcript from the side-channel (deserialize)
aggregate = AggregatedTranscript(messages)
- assert aggregate.verify(shares_num, messages)
+ assert aggregate.verify(validators_num, messages)
# The ciphertext is obtained from the client
diff --git a/ferveo-python/examples/server_api_simple.py b/ferveo-python/examples/server_api_simple.py
index 5fd2c8e5..44fb69c4 100644
--- a/ferveo-python/examples/server_api_simple.py
+++ b/ferveo-python/examples/server_api_simple.py
@@ -17,7 +17,8 @@ def gen_eth_addr(i: int) -> str:
tau = 1
security_threshold = 3
shares_num = 4
-validator_keypairs = [Keypair.random() for _ in range(0, shares_num)]
+validators_num = shares_num + 2
+validator_keypairs = [Keypair.random() for _ in range(0, validators_num)]
validators = [
Validator(gen_eth_addr(i), keypair.public_key(), i)
for i, keypair in enumerate(validator_keypairs)
@@ -52,11 +53,11 @@ def gen_eth_addr(i: int) -> str:
# Server can aggregate the transcripts
server_aggregate = dkg.aggregate_transcripts(messages)
-assert server_aggregate.verify(shares_num, messages)
+assert server_aggregate.verify(validators_num, messages)
# And the client can also aggregate and verify the transcripts
client_aggregate = AggregatedTranscript(messages)
-assert client_aggregate.verify(shares_num, messages)
+assert client_aggregate.verify(validators_num, messages)
# In the meantime, the client creates a ciphertext and decryption request
msg = "abc".encode()
@@ -79,7 +80,7 @@ def gen_eth_addr(i: int) -> str:
# We can also obtain the aggregated transcript from the side-channel (deserialize)
aggregate = AggregatedTranscript(messages)
- assert aggregate.verify(shares_num, messages)
+ assert aggregate.verify(validators_num, messages)
# The ciphertext is obtained from the client
diff --git a/ferveo-python/ferveo/__init__.py b/ferveo-python/ferveo/__init__.py
index 478628b1..fd906e54 100644
--- a/ferveo-python/ferveo/__init__.py
+++ b/ferveo-python/ferveo/__init__.py
@@ -30,8 +30,13 @@
InvalidDkgPublicKey,
InsufficientValidators,
InvalidTranscriptAggregate,
- ValidatorsNotSorted,
ValidatorPublicKeyMismatch,
SerializationError,
InvalidVariant,
+ InvalidDkgParameters,
+ InvalidDkgParametersForPrecomputedVariant,
+ InvalidShareIndex,
+ DuplicatedShareIndex,
+ NoTranscriptsToAggregate,
+ InvalidAggregateVerificationParameters,
)
diff --git a/ferveo-python/ferveo/__init__.pyi b/ferveo-python/ferveo/__init__.pyi
index a7c5abb6..ba7e7403 100644
--- a/ferveo-python/ferveo/__init__.pyi
+++ b/ferveo-python/ferveo/__init__.pyi
@@ -25,12 +25,14 @@ class FerveoPublicKey:
@final
class Validator:
- def __init__(self, address: str, public_key: FerveoPublicKey): ...
+ def __init__(self, address: str, public_key: FerveoPublicKey, share_index: int): ...
address: str
public_key: FerveoPublicKey
+ share_index: int
+
@final
class Transcript:
@staticmethod
@@ -104,7 +106,9 @@ class DecryptionSharePrecomputed:
@final
class AggregatedTranscript:
def __init__(self, messages: Sequence[ValidatorMessage]): ...
- def verify(self, shares_num: int, messages: Sequence[ValidatorMessage]) -> bool: ...
+ def verify(
+ self, validators_num: int, messages: Sequence[ValidatorMessage]
+ ) -> bool: ...
def create_decryption_share_simple(
self,
dkg: Dkg,
@@ -189,11 +193,29 @@ class InsufficientValidators(Exception):
class InvalidTranscriptAggregate(Exception):
pass
-class ValidatorsNotSorted(Exception):
- pass
-
class ValidatorPublicKeyMismatch(Exception):
pass
class SerializationError(Exception):
pass
+
+class InvalidVariant(Exception):
+ pass
+
+class InvalidDkgParameters(Exception):
+ pass
+
+class InvalidDkgParametersForPrecomputedVariant(Exception):
+ pass
+
+class InvalidShareIndex(Exception):
+ pass
+
+class DuplicatedShareIndex(Exception):
+ pass
+
+class NoTranscriptsToAggregate(Exception):
+ pass
+
+class InvalidAggregateVerificationParameters(Exception):
+ pass
diff --git a/ferveo-python/test/test_ferveo.py b/ferveo-python/test/test_ferveo.py
index b3496d3e..51af3867 100644
--- a/ferveo-python/test/test_ferveo.py
+++ b/ferveo-python/test/test_ferveo.py
@@ -5,6 +5,7 @@
combine_decryption_shares_simple,
combine_decryption_shares_precomputed,
decrypt_with_shared_secret,
+ AggregatedTranscript,
Keypair,
Validator,
ValidatorMessage,
@@ -37,18 +38,29 @@ def combine_shares_for_variant(v: FerveoVariant, decryption_shares):
raise ValueError("Unknown variant")
-def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_to_use):
+def scenario_for_variant(
+ variant: FerveoVariant, shares_num, validators_num, threshold, shares_to_use
+):
if variant not in [FerveoVariant.Simple, FerveoVariant.Precomputed]:
raise ValueError("Unknown variant: " + variant)
+ if validators_num < shares_num:
+ raise ValueError("validators_num must be >= shares_num")
+
+ if variant == FerveoVariant.Precomputed and shares_to_use != validators_num:
+ raise ValueError(
+ "In precomputed variant, shares_to_use must be equal to validators_num"
+ )
+
tau = 1
- validator_keypairs = [Keypair.random() for _ in range(0, shares_num)]
+ validator_keypairs = [Keypair.random() for _ in range(0, validators_num)]
validators = [
Validator(gen_eth_addr(i), keypair.public_key(), i)
for i, keypair in enumerate(validator_keypairs)
]
- validators.sort(key=lambda v: v.address)
+ # Each validator holds their own DKG instance and generates a transcript every
+ # validator, including themselves
messages = []
for sender in validators:
dkg = Dkg(
@@ -60,6 +72,7 @@ def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_t
)
messages.append(ValidatorMessage(sender, dkg.generate_transcript()))
+ # Both client and server should be able to verify the aggregated transcript
dkg = Dkg(
tau=tau,
shares_num=shares_num,
@@ -67,18 +80,23 @@ def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_t
validators=validators,
me=validators[0],
)
- pvss_aggregated = dkg.aggregate_transcripts(messages)
- assert pvss_aggregated.verify(shares_num, messages)
+ server_aggregate = dkg.aggregate_transcripts(messages)
+ assert server_aggregate.verify(validators_num, messages)
- dkg_pk_bytes = bytes(dkg.public_key)
- dkg_pk = DkgPublicKey.from_bytes(dkg_pk_bytes)
+ client_aggregate = AggregatedTranscript(messages)
+ assert client_aggregate.verify(validators_num, messages)
+ # Client creates a ciphertext and requests decryption shares from validators
msg = "abc".encode()
aad = "my-aad".encode()
- ciphertext = encrypt(msg, aad, dkg_pk)
+ ciphertext = encrypt(msg, aad, dkg.public_key)
+ # Having aggregated the transcripts, the validators can now create decryption shares
decryption_shares = []
for validator, validator_keypair in zip(validators, validator_keypairs):
+ assert validator.public_key == validator_keypair.public_key()
+ print("validator: ", validator.share_index)
+
dkg = Dkg(
tau=tau,
shares_num=shares_num,
@@ -87,15 +105,17 @@ def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_t
me=validator,
)
pvss_aggregated = dkg.aggregate_transcripts(messages)
- assert pvss_aggregated.verify(shares_num, messages)
+ assert pvss_aggregated.verify(validators_num, messages)
decryption_share = decryption_share_for_variant(variant, pvss_aggregated)(
dkg, ciphertext.header, aad, validator_keypair
)
decryption_shares.append(decryption_share)
- decryption_shares = decryption_shares[:shares_to_use]
+ # We are limiting the number of decryption shares to use for testing purposes
+ # decryption_shares = decryption_shares[:shares_to_use]
+ # Client combines the decryption shares and decrypts the ciphertext
shared_secret = combine_shares_for_variant(variant, decryption_shares)
if variant == FerveoVariant.Simple and len(decryption_shares) < threshold:
@@ -103,7 +123,7 @@ def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_t
decrypt_with_shared_secret(ciphertext, aad, shared_secret)
return
- if variant == FerveoVariant.Precomputed and len(decryption_shares) < shares_num:
+ if variant == FerveoVariant.Precomputed and len(decryption_shares) < threshold:
with pytest.raises(ThresholdEncryptionError):
decrypt_with_shared_secret(ciphertext, aad, shared_secret)
return
@@ -113,27 +133,55 @@ def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_t
def test_simple_tdec_has_enough_messages():
- scenario_for_variant(
- FerveoVariant.Simple, shares_num=4, threshold=3, shares_to_use=3
- )
+ shares_num = 4
+ threshold = shares_num - 1
+ for validators_num in [shares_num, shares_num + 2]:
+ scenario_for_variant(
+ FerveoVariant.Simple,
+ shares_num=shares_num,
+ validators_num=validators_num,
+ threshold=threshold,
+ shares_to_use=threshold,
+ )
def test_simple_tdec_doesnt_have_enough_messages():
- scenario_for_variant(
- FerveoVariant.Simple, shares_num=4, threshold=3, shares_to_use=2
- )
+ shares_num = 4
+ threshold = shares_num - 1
+ for validators_num in [shares_num, shares_num + 2]:
+ scenario_for_variant(
+ FerveoVariant.Simple,
+ shares_num=shares_num,
+ validators_num=validators_num,
+ threshold=threshold,
+ shares_to_use=validators_num - 1,
+ )
def test_precomputed_tdec_has_enough_messages():
- scenario_for_variant(
- FerveoVariant.Precomputed, shares_num=4, threshold=4, shares_to_use=4
- )
+ shares_num = 4
+ threshold = shares_num # in precomputed variant, we need all shares
+ for validators_num in [shares_num, shares_num + 2]:
+ scenario_for_variant(
+ FerveoVariant.Precomputed,
+ shares_num=shares_num,
+ validators_num=validators_num,
+ threshold=threshold,
+ shares_to_use=validators_num,
+ )
def test_precomputed_tdec_doesnt_have_enough_messages():
- scenario_for_variant(
- FerveoVariant.Precomputed, shares_num=4, threshold=4, shares_to_use=3
- )
+ shares_num = 4
+ threshold = shares_num # in precomputed variant, we need all shares
+ for validators_num in [shares_num, shares_num + 2]:
+ scenario_for_variant(
+ FerveoVariant.Simple,
+ shares_num=shares_num,
+ validators_num=validators_num,
+ threshold=threshold,
+ shares_to_use=threshold - 1,
+ )
PARAMS = [
diff --git a/ferveo-wasm/examples/node/src/main.test.ts b/ferveo-wasm/examples/node/src/main.test.ts
index 5814a53d..00da665b 100644
--- a/ferveo-wasm/examples/node/src/main.test.ts
+++ b/ferveo-wasm/examples/node/src/main.test.ts
@@ -22,11 +22,16 @@ const genEthAddr = (i: number) => {
return EthereumAddress.fromString(ethAddr);
};
-const tau = 1;
-function setupTest(sharesNum :number, threshold: number) {
+const TAU = 1;
+
+function setupTest(
+ sharesNum: number,
+ validatorsNum: number,
+ threshold: number
+) {
const validatorKeypairs: Keypair[] = [];
const validators: Validator[] = [];
- for (let i = 0; i < sharesNum; i++) {
+ for (let i = 0; i < validatorsNum; i++) {
const keypair = Keypair.random();
validatorKeypairs.push(keypair);
const validator = new Validator(genEthAddr(i), keypair.publicKey, i);
@@ -37,7 +42,7 @@ function setupTest(sharesNum :number, threshold: number) {
// validator, including themselves
const messages: ValidatorMessage[] = [];
validators.forEach((sender) => {
- const dkg = new Dkg(tau, sharesNum, threshold, validators, sender);
+ const dkg = new Dkg(TAU, sharesNum, threshold, validators, sender);
const transcript = dkg.generateTranscript();
const message = new ValidatorMessage(sender, transcript);
messages.push(message);
@@ -45,16 +50,16 @@ function setupTest(sharesNum :number, threshold: number) {
// 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, threshold, validators, validators[0]);
const serverAggregate = dkg.aggregateTranscript(messages);
- expect(serverAggregate.verify(sharesNum, messages)).toBe(true);
+ expect(serverAggregate.verify(validatorsNum, messages)).toBe(true);
// Client can also aggregate the transcripts and verify them
const clientAggregate = new AggregatedTranscript(messages);
- expect(clientAggregate.verify(sharesNum, messages)).toBe(true);
+ expect(clientAggregate.verify(validatorsNum, messages)).toBe(true);
- // In the meantime, the client creates a ciphertext and decryption request
+ // Client creates a ciphertext and requests decryption shares from validators
const msg = Buffer.from("my-msg");
const aad = Buffer.from("my-aad");
const ciphertext = ferveoEncrypt(msg, aad, dkg.publicKey());
@@ -73,94 +78,78 @@ function setupTest(sharesNum :number, threshold: number) {
// This test suite replicates tests from ferveo-wasm/tests/node.rs
describe("ferveo-wasm", () => {
it("simple tdec variant", () => {
- const sharesNum = 4;
- const threshold = 3;
- const {
- validatorKeypairs,
- validators,
- messages,
- msg,
- aad,
- ciphertext,
- } = setupTest(sharesNum, threshold);
-
- // Having aggregated the transcripts, the validators can now create decryption shares
- const decryptionShares: DecryptionShareSimple[] = [];
- zip(validators, validatorKeypairs).forEach(([validator, keypair]) => {
- expect(validator.publicKey.equals(keypair.publicKey)).toBe(true);
-
- const dkg = new Dkg(tau, sharesNum, threshold, validators, validator);
- const aggregate = dkg.aggregateTranscript(messages);
- const isValid = aggregate.verify(sharesNum, messages);
- expect(isValid).toBe(true);
-
- const decryptionShare = aggregate.createDecryptionShareSimple(
- dkg,
- ciphertext.header,
- aad,
- keypair
- );
- decryptionShares.push(decryptionShare);
+ const sharesNum = 4;
+ const threshold = sharesNum - 1;
+ [sharesNum, sharesNum + 2].forEach((validatorsNum) => {
+ const { validatorKeypairs, validators, messages, msg, aad, ciphertext } =
+ setupTest(sharesNum, validatorsNum, threshold);
+
+ // Having aggregated the transcripts, the validators can now create decryption shares
+ const decryptionShares: DecryptionShareSimple[] = [];
+ zip(validators, validatorKeypairs).forEach(([validator, keypair]) => {
+ expect(validator.publicKey.equals(keypair.publicKey)).toBe(true);
+
+ const dkg = new Dkg(TAU, sharesNum, threshold, validators, validator);
+ const aggregate = dkg.aggregateTranscript(messages);
+ const isValid = aggregate.verify(validatorsNum, messages);
+ expect(isValid).toBe(true);
+
+ const decryptionShare = aggregate.createDecryptionShareSimple(
+ dkg,
+ ciphertext.header,
+ aad,
+ keypair
+ );
+ decryptionShares.push(decryptionShare);
+ });
+
+ // Now, the decryption share can be used to decrypt the ciphertext
+ // This part is in the client API
+
+ const sharedSecret = combineDecryptionSharesSimple(decryptionShares);
+
+ // The client should have access to the public parameters of the DKG
+
+ const plaintext = decryptWithSharedSecret(ciphertext, aad, sharedSecret);
+ expect(Buffer.from(plaintext)).toEqual(msg);
});
-
- // Now, the decryption share can be used to decrypt the ciphertext
- // This part is in the client API
-
- const sharedSecret = combineDecryptionSharesSimple(
- decryptionShares,
- );
-
- // The client should have access to the public parameters of the DKG
-
- const plaintext = decryptWithSharedSecret(
- ciphertext,
- aad,
- sharedSecret,
- );
- expect(Buffer.from(plaintext)).toEqual(msg);
});
it("precomputed tdec variant", () => {
- const sharesNum = 4;
- const threshold = sharesNum; // threshold is equal to sharesNum in precomputed variant
- const {
- validatorKeypairs,
- validators,
- messages,
- msg,
- aad,
- ciphertext,
- } = setupTest(sharesNum, threshold);
-
- // 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 aggregate = dkg.aggregateTranscript(messages);
- const isValid = aggregate.verify(sharesNum, messages);
- expect(isValid).toBe(true);
-
- const decryptionShare = aggregate.createDecryptionSharePrecomputed(
- dkg,
- ciphertext.header,
- aad,
- keypair
- );
- decryptionShares.push(decryptionShare);
+ const sharesNum = 4;
+ const threshold = sharesNum; // threshold is equal to sharesNum in precomputed variant
+ [sharesNum, sharesNum + 2].forEach((validatorsNum) => {
+ const { validatorKeypairs, validators, messages, msg, aad, ciphertext } =
+ setupTest(sharesNum, validatorsNum, threshold);
+
+ // Having aggregated the transcripts, the validators can now create decryption shares
+ const decryptionShares: DecryptionSharePrecomputed[] = [];
+ zip(validators, validatorKeypairs).forEach(([validator, keypair]) => {
+ expect(validator.publicKey.equals(keypair.publicKey)).toBe(true);
+
+ const dkg = new Dkg(TAU, sharesNum, threshold, validators, validator);
+ const aggregate = dkg.aggregateTranscript(messages);
+ const isValid = aggregate.verify(validatorsNum, messages);
+ expect(isValid).toBe(true);
+
+ const decryptionShare = aggregate.createDecryptionSharePrecomputed(
+ dkg,
+ ciphertext.header,
+ aad,
+ keypair
+ );
+ decryptionShares.push(decryptionShare);
+ });
+
+ // Now, the decryption share can be used to decrypt the ciphertext
+ // This part is in the client API
+
+ const sharedSecret = combineDecryptionSharesPrecomputed(decryptionShares);
+
+ // The client should have access to the public parameters of the DKG
+
+ const plaintext = decryptWithSharedSecret(ciphertext, aad, sharedSecret);
+ expect(Buffer.from(plaintext)).toEqual(msg);
});
-
- // Now, the decryption share can be used to decrypt the ciphertext
- // This part is in the client API
-
- const sharedSecret = combineDecryptionSharesPrecomputed(decryptionShares);
-
- // The client should have access to the public parameters of the DKG
-
- const plaintext = decryptWithSharedSecret(
- ciphertext,
- aad,
- sharedSecret,
- );
- expect(Buffer.from(plaintext)).toEqual(msg);
});
});
diff --git a/ferveo-wasm/tests/node.rs b/ferveo-wasm/tests/node.rs
index bae5750b..7b35efa5 100644
--- a/ferveo-wasm/tests/node.rs
+++ b/ferveo-wasm/tests/node.rs
@@ -18,8 +18,12 @@ type TestSetup = (
const TAU: u32 = 0;
-fn setup_dkg(shares_num: u32, security_threshold: u32) -> TestSetup {
- let validator_keypairs = (0..shares_num as usize)
+fn setup_dkg(
+ shares_num: u32,
+ validators_num: u32,
+ security_threshold: u32,
+) -> TestSetup {
+ let validator_keypairs = (0..validators_num as usize)
.map(gen_keypair)
.collect::>();
let validators = validator_keypairs
@@ -32,7 +36,7 @@ fn setup_dkg(shares_num: u32, security_threshold: u32) -> TestSetup {
// Each validator holds their own DKG instance and generates a transcript every
// validator, including themselves
let messages = validators.iter().map(|sender| {
- let mut dkg = Dkg::new(
+ let mut validator_dkg = Dkg::new(
TAU,
shares_num,
security_threshold,
@@ -40,7 +44,7 @@ fn setup_dkg(shares_num: u32, security_threshold: u32) -> TestSetup {
sender,
)
.unwrap();
- let transcript = dkg.generate_transcript().unwrap();
+ let transcript = validator_dkg.generate_transcript().unwrap();
ValidatorMessage::new(sender, &transcript).unwrap()
});
@@ -61,12 +65,16 @@ fn setup_dkg(shares_num: u32, security_threshold: u32) -> TestSetup {
// Server can aggregate the transcripts and verify them
let server_aggregate = dkg.aggregate_transcripts(&messages_js).unwrap();
- let is_valid = server_aggregate.verify(shares_num, &messages_js).unwrap();
+ let is_valid = server_aggregate
+ .verify(validators_num, &messages_js)
+ .unwrap();
assert!(is_valid);
// Client can also aggregate the transcripts and verify them
let client_aggregate = AggregatedTranscript::new(&messages_js).unwrap();
- let is_valid = client_aggregate.verify(shares_num, &messages_js).unwrap();
+ let is_valid = client_aggregate
+ .verify(validators_num, &messages_js)
+ .unwrap();
assert!(is_valid);
// In the meantime, the client creates a ciphertext and decryption request
@@ -88,105 +96,116 @@ fn setup_dkg(shares_num: u32, security_threshold: u32) -> TestSetup {
#[wasm_bindgen_test]
fn tdec_simple() {
let shares_num = 16;
- let security_threshold = 10;
- let (
- validator_keypairs,
- validators,
- validators_js,
- messages_js,
- msg,
- aad,
- ciphertext,
- ) = setup_dkg(shares_num, security_threshold);
-
- // Having aggregated the transcripts, the validators can now create decryption shares
- let decryption_shares = zip_eq(validators, validator_keypairs)
- .map(|(validator, keypair)| {
- let mut dkg = Dkg::new(
- TAU,
- shares_num,
- security_threshold,
- &validators_js,
- &validator,
- )
- .unwrap();
- let aggregate = dkg.aggregate_transcripts(&messages_js).unwrap();
- let is_valid = aggregate.verify(shares_num, &messages_js).unwrap();
- assert!(is_valid);
-
- aggregate
- .create_decryption_share_simple(
- &dkg,
- &ciphertext.header().unwrap(),
- &aad,
- &keypair,
+ let security_threshold = shares_num / 2;
+ for validators_num in [shares_num, shares_num + 2] {
+ let (
+ validator_keypairs,
+ validators,
+ validators_js,
+ messages_js,
+ msg,
+ aad,
+ ciphertext,
+ ) = setup_dkg(shares_num, validators_num, security_threshold);
+
+ // Having aggregated the transcripts, the validators can now create decryption shares
+ let decryption_shares = zip_eq(validators, validator_keypairs)
+ .map(|(validator, keypair)| {
+ let mut dkg = Dkg::new(
+ TAU,
+ shares_num,
+ security_threshold,
+ &validators_js,
+ &validator,
)
- .unwrap()
- })
- .collect::>();
- let decryption_shares_js = into_js_array(decryption_shares);
-
- // Now, the decryption share can be used to decrypt the ciphertext
- // This part is in the client API
-
- let shared_secret =
- combine_decryption_shares_simple(&decryption_shares_js).unwrap();
-
- // The client should have access to the public parameters of the DKG
- let plaintext =
- decrypt_with_shared_secret(&ciphertext, &aad, &shared_secret).unwrap();
- assert_eq!(msg, plaintext);
+ .unwrap();
+ let aggregate =
+ dkg.aggregate_transcripts(&messages_js).unwrap();
+ let is_valid =
+ aggregate.verify(validators_num, &messages_js).unwrap();
+ assert!(is_valid);
+
+ aggregate
+ .create_decryption_share_simple(
+ &dkg,
+ &ciphertext.header().unwrap(),
+ &aad,
+ &keypair,
+ )
+ .unwrap()
+ })
+ .collect::>();
+ let decryption_shares_js = into_js_array(decryption_shares);
+
+ // Now, the decryption share can be used to decrypt the ciphertext
+ // This part is in the client API
+
+ let shared_secret =
+ combine_decryption_shares_simple(&decryption_shares_js).unwrap();
+
+ // The client should have access to the public parameters of the DKG
+ let plaintext =
+ decrypt_with_shared_secret(&ciphertext, &aad, &shared_secret)
+ .unwrap();
+ assert_eq!(msg, plaintext);
+ }
}
#[wasm_bindgen_test]
fn tdec_precomputed() {
let shares_num = 16;
let security_threshold = shares_num; // Must be equal to shares_num in precomputed variant
- let (
- validator_keypairs,
- validators,
- validators_js,
- messages_js,
- msg,
- aad,
- ciphertext,
- ) = setup_dkg(shares_num, security_threshold);
-
- // Having aggregated the transcripts, the validators can now create decryption shares
- let decryption_shares = zip_eq(validators, validator_keypairs)
- .map(|(validator, keypair)| {
- let mut dkg = Dkg::new(
- TAU,
- shares_num,
- security_threshold,
- &validators_js,
- &validator,
- )
- .unwrap();
- let aggregate = dkg.aggregate_transcripts(&messages_js).unwrap();
- let is_valid = aggregate.verify(shares_num, &messages_js).unwrap();
- assert!(is_valid);
-
- aggregate
- .create_decryption_share_precomputed(
- &dkg,
- &ciphertext.header().unwrap(),
- &aad,
- &keypair,
+ for validators_num in [shares_num, shares_num + 2] {
+ let (
+ validator_keypairs,
+ validators,
+ validators_js,
+ messages_js,
+ msg,
+ aad,
+ ciphertext,
+ ) = setup_dkg(shares_num, validators_num, security_threshold);
+
+ // Having aggregated the transcripts, the validators can now create decryption shares
+ let decryption_shares = zip_eq(validators, validator_keypairs)
+ .map(|(validator, keypair)| {
+ let mut dkg = Dkg::new(
+ TAU,
+ shares_num,
+ security_threshold,
+ &validators_js,
+ &validator,
)
- .unwrap()
- })
- .collect::>();
- let decryption_shares_js = into_js_array(decryption_shares);
-
- // Now, the decryption share can be used to decrypt the ciphertext
- // This part is in the client API
-
- let shared_secret =
- combine_decryption_shares_precomputed(&decryption_shares_js).unwrap();
-
- // The client should have access to the public parameters of the DKG
- let plaintext =
- decrypt_with_shared_secret(&ciphertext, &aad, &shared_secret).unwrap();
- assert_eq!(msg, plaintext);
+ .unwrap();
+ let aggregate =
+ dkg.aggregate_transcripts(&messages_js).unwrap();
+ let is_valid =
+ aggregate.verify(validators_num, &messages_js).unwrap();
+ assert!(is_valid);
+
+ aggregate
+ .create_decryption_share_precomputed(
+ &dkg,
+ &ciphertext.header().unwrap(),
+ &aad,
+ &keypair,
+ )
+ .unwrap()
+ })
+ .collect::>();
+ let decryption_shares_js = into_js_array(decryption_shares);
+
+ // Now, the decryption share can be used to decrypt the ciphertext
+ // This part is in the client API
+
+ let shared_secret =
+ combine_decryption_shares_precomputed(&decryption_shares_js)
+ .unwrap();
+
+ // The client should have access to the public parameters of the DKG
+ let plaintext =
+ decrypt_with_shared_secret(&ciphertext, &aad, &shared_secret)
+ .unwrap();
+ assert_eq!(msg, plaintext);
+ }
}
diff --git a/ferveo/src/api.rs b/ferveo/src/api.rs
index 80ab58d6..caa0d9b4 100644
--- a/ferveo/src/api.rs
+++ b/ferveo/src/api.rs
@@ -286,12 +286,20 @@ impl AggregatedTranscript {
pub fn verify(
&self,
- shares_num: u32,
+ validators_num: u32,
messages: &[ValidatorMessage],
) -> Result {
+ if validators_num < messages.len() as u32 {
+ return Err(Error::InvalidAggregateVerificationParameters(
+ validators_num,
+ messages.len() as u32,
+ ));
+ }
+
let pvss_params = PubliclyVerifiableParams::::default();
- let domain = GeneralEvaluationDomain::::new(shares_num as usize)
- .expect("Unable to construct an evaluation domain");
+ let domain =
+ GeneralEvaluationDomain::::new(validators_num as usize)
+ .expect("Unable to construct an evaluation domain");
let is_valid_optimistic = self.0.verify_optimistic();
if !is_valid_optimistic {
@@ -428,8 +436,9 @@ mod test_ferveo_api {
tau: u32,
security_threshold: u32,
shares_num: u32,
+ validators_num: u32,
) -> TestInputs {
- let validator_keypairs = gen_keypairs(shares_num);
+ let validator_keypairs = gen_keypairs(validators_num);
let validators = validator_keypairs
.iter()
.enumerate()
@@ -469,16 +478,22 @@ mod test_ferveo_api {
assert_eq!(dkg_pk, deserialized);
}
- #[test_case(4; "number of shares (validators) is a power of 2")]
- #[test_case(7; "number of shares (validators) is not a power of 2")]
- fn test_server_api_tdec_precomputed(shares_num: u32) {
+ #[test_case(4, 4; "number of shares (validators) is a power of 2")]
+ #[test_case(7, 7; "number of shares (validators) is not a power of 2")]
+ #[test_case(4, 6; "number of validators greater than the number of shares")]
+ fn test_server_api_tdec_precomputed(shares_num: u32, validators_num: u32) {
let rng = &mut StdRng::seed_from_u64(0);
// In precomputed variant, the security threshold is equal to the number of shares
let security_threshold = 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,
+ shares_num,
+ validators_num,
+ );
// Now that every validator holds a dkg instance and a transcript for every other validator,
// every validator can aggregate the transcripts
@@ -488,7 +503,7 @@ mod test_ferveo_api {
.unwrap();
let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap();
- assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());
+ assert!(pvss_aggregated.verify(validators_num, &messages).unwrap());
// At this point, any given validator should be able to provide a DKG public key
let dkg_public_key = dkg.public_key();
@@ -511,7 +526,9 @@ mod test_ferveo_api {
)
.unwrap();
let aggregate = dkg.aggregate_transcripts(&messages).unwrap();
- assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());
+ assert!(pvss_aggregated
+ .verify(validators_num, &messages)
+ .unwrap());
// And then each validator creates their own decryption share
aggregate
@@ -551,15 +568,21 @@ mod test_ferveo_api {
assert!(result.is_err());
}
- #[test_case(4; "number of shares (validators) is a power of 2")]
- #[test_case(7; "number of shares (validators) is not a power of 2")]
- fn test_server_api_tdec_simple(shares_num: u32) {
+ #[test_case(4, 4; "number of shares (validators) is a power of 2")]
+ #[test_case(7, 7; "number of shares (validators) is not a power of 2")]
+ #[test_case(4, 6; "number of validators greater than the number of shares")]
+ fn test_server_api_tdec_simple(shares_num: u32, validators_num: u32) {
let rng = &mut StdRng::seed_from_u64(0);
let security_threshold = shares_num / 2 + 1;
- 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,
+ shares_num,
+ validators_num,
+ );
// Now that every validator holds a dkg instance and a transcript for every other validator,
// every validator can aggregate the transcripts
@@ -573,7 +596,7 @@ mod test_ferveo_api {
.unwrap();
let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap();
- assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());
+ assert!(pvss_aggregated.verify(validators_num, &messages).unwrap());
// At this point, any given validator should be able to provide a DKG public key
let public_key = dkg.public_key();
@@ -595,7 +618,7 @@ mod test_ferveo_api {
)
.unwrap();
let aggregate = dkg.aggregate_transcripts(&messages).unwrap();
- assert!(aggregate.verify(shares_num, &messages).unwrap());
+ assert!(aggregate.verify(validators_num, &messages).unwrap());
aggregate
.create_decryption_share_simple(
&dkg,
@@ -635,104 +658,137 @@ mod test_ferveo_api {
// implementation for aggregation and aggregate verification.
// Here, we focus on testing user-facing APIs for server and client users.
- #[test]
- fn server_side_local_verification() {
+ #[test_case(4, 4; "number of shares (validators) is a power of 2")]
+ #[test_case(7, 7; "number of shares (validators) is not a power of 2")]
+ #[test_case(4, 6; "number of validators greater than the number of shares")]
+ fn server_side_local_verification(shares_num: u32, validators_num: u32) {
let rng = &mut StdRng::seed_from_u64(0);
+ let security_threshold = shares_num / 2 + 1;
- let (messages, validators, _) =
- make_test_inputs(rng, TAU, SECURITY_THRESHOLD, SHARES_NUM);
+ let (messages, validators, _) = make_test_inputs(
+ rng,
+ TAU,
+ security_threshold,
+ shares_num,
+ validators_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();
- assert!(local_aggregate
- .verify(dkg.0.dkg_params.shares_num(), &messages)
- .is_ok());
+ let good_aggregate = dkg.aggregate_transcripts(&messages).unwrap();
+ assert!(good_aggregate.verify(validators_num, &messages).is_ok());
// Test negative cases
// Notice that the dkg instance is mutable, so we need to get a fresh one
// for every test case
+ // Should fail if the number of validators is less than the number of messages
+ assert!(good_aggregate
+ .verify(messages.len() as u32 - 1, &messages)
+ .is_err());
+
// Should fail if no transcripts are provided
let mut dkg =
- Dkg::new(TAU, SHARES_NUM, SECURITY_THRESHOLD, &validators, &me)
+ Dkg::new(TAU, shares_num, security_threshold, &validators, &me)
.unwrap();
let result = dkg.aggregate_transcripts(&[]);
assert!(result.is_err());
// Not enough transcripts
let mut dkg =
- Dkg::new(TAU, SHARES_NUM, SECURITY_THRESHOLD, &validators, &me)
+ Dkg::new(TAU, shares_num, security_threshold, &validators, &me)
.unwrap();
- let not_enough_messages = &messages[..SECURITY_THRESHOLD as usize - 1];
- 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 =
dkg.aggregate_transcripts(not_enough_messages).unwrap();
- let result = insufficient_aggregate.verify(SHARES_NUM, &messages);
+ let result = insufficient_aggregate.verify(validators_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 mut dkg =
- Dkg::new(TAU, SHARES_NUM, SECURITY_THRESHOLD, &validators, &me)
+ Dkg::new(TAU, shares_num, security_threshold, &validators, &me)
.unwrap();
- let (bad_messages, _, _) =
- make_test_inputs(rng, TAU, SECURITY_THRESHOLD, SHARES_NUM);
+ let (bad_messages, _, _) = make_test_inputs(
+ rng,
+ TAU,
+ security_threshold,
+ shares_num,
+ validators_num,
+ );
let mixed_messages = [&messages[..2], &bad_messages[..1]].concat();
let bad_aggregate = dkg.aggregate_transcripts(&mixed_messages).unwrap();
- let result = bad_aggregate.verify(SHARES_NUM, &messages);
+ let result = bad_aggregate.verify(validators_num, &messages);
assert!(result.is_err());
}
- #[test]
- fn client_side_local_verification() {
+ #[test_case(4, 4; "number of shares (validators) is a power of 2")]
+ #[test_case(7, 7; "number of shares (validators) is not a power of 2")]
+ #[test_case(4, 6; "number of validators greater than the number of shares")]
+ fn client_side_local_verification(shares_num: u32, validators_num: u32) {
let rng = &mut StdRng::seed_from_u64(0);
+ let security_threshold = shares_num / 2 + 1;
- let (messages, _, _) =
- make_test_inputs(rng, TAU, SECURITY_THRESHOLD, SHARES_NUM);
+ let (messages, _, _) = make_test_inputs(
+ rng,
+ TAU,
+ security_threshold,
+ shares_num,
+ validators_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).unwrap();
+ let good_aggregate = AggregatedTranscript::new(messages).unwrap();
// We are separating the verification from the aggregation since the client may fetch
// 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 = good_aggregate.verify(validators_num, messages);
assert!(result.is_ok());
assert!(result.unwrap());
// Test negative cases
+ // Should fail if the number of validators is less than the number of messages
+ assert!(good_aggregate
+ .verify(messages.len() as u32 - 1, messages)
+ .is_err());
+
// Should fail if no transcripts are provided
let result = AggregatedTranscript::new(&[]);
assert!(result.is_err());
// Not enough transcripts
- let not_enough_messages = &messages[..SECURITY_THRESHOLD as usize - 1];
- 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).unwrap();
- let result = insufficient_aggregate.verify(SHARES_NUM, messages);
+ let result = insufficient_aggregate.verify(validators_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);
+ let (bad_messages, _, _) = make_test_inputs(
+ rng,
+ TAU,
+ security_threshold,
+ shares_num,
+ validators_num,
+ );
let mixed_messages = [&messages[..2], &bad_messages[..1]].concat();
let bad_aggregate = AggregatedTranscript::new(&mixed_messages).unwrap();
- let result = bad_aggregate.verify(SHARES_NUM, messages);
+ let result = bad_aggregate.verify(validators_num, messages);
assert!(result.is_err());
}
}
diff --git a/ferveo/src/bindings_python.rs b/ferveo/src/bindings_python.rs
index bd29dc0d..fe534af8 100644
--- a/ferveo/src/bindings_python.rs
+++ b/ferveo/src/bindings_python.rs
@@ -104,7 +104,7 @@ impl From for PyErr {
))
}
Error::InvalidDkgParametersForPrecomputedVariant(shares_num, security_threshold) => {
- InvalidDkgParameters::new_err(format!(
+ InvalidDkgParametersForPrecomputedVariant::new_err(format!(
"shares_num: {shares_num}, security_threshold: {security_threshold}"
))
}
@@ -116,6 +116,11 @@ impl From for PyErr {
Error::NoTranscriptsToAggregate => {
NoTranscriptsToAggregate::new_err("")
}
+ Error::InvalidAggregateVerificationParameters(validators_num, messages_num) => {
+ InvalidAggregateVerificationParameters::new_err(format!(
+ "validators_num: {validators_num}, messages_num: {messages_num}"
+ ))
+ }
},
_ => default(),
}
@@ -145,15 +150,24 @@ create_exception!(exceptions, InsufficientTranscriptsForAggregate, PyException);
create_exception!(exceptions, InvalidDkgPublicKey, PyValueError);
create_exception!(exceptions, InsufficientValidators, PyValueError);
create_exception!(exceptions, InvalidTranscriptAggregate, PyValueError);
-create_exception!(exceptions, ValidatorsNotSorted, PyValueError);
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);
+create_exception!(
+ exceptions,
+ InvalidDkgParametersForPrecomputedVariant,
+ PyValueError
+);
create_exception!(exceptions, InvalidShareIndex, PyValueError);
create_exception!(exceptions, DuplicatedShareIndex, PyValueError);
create_exception!(exceptions, NoTranscriptsToAggregate, PyValueError);
+create_exception!(
+ exceptions,
+ InvalidAggregateVerificationParameters,
+ PyValueError
+);
fn from_py_bytes(bytes: &[u8]) -> PyResult {
T::from_bytes(bytes)
@@ -421,6 +435,11 @@ impl Validator {
pub fn public_key(&self) -> FerveoPublicKey {
FerveoPublicKey(self.0.public_key)
}
+
+ #[getter]
+ pub fn share_index(&self) -> u32 {
+ self.0.share_index
+ }
}
#[pyclass(module = "ferveo")]
@@ -595,14 +614,14 @@ impl AggregatedTranscript {
pub fn verify(
&self,
- shares_num: u32,
+ validators_num: u32,
messages: Vec,
) -> PyResult {
let messages: Vec<_> =
messages.into_iter().map(|vm| vm.to_inner()).collect();
let is_valid = self
.0
- .verify(shares_num, &messages)
+ .verify(validators_num, &messages)
.map_err(FerveoPythonError::FerveoError)?;
Ok(is_valid)
}
@@ -736,13 +755,33 @@ pub fn make_ferveo_py_module(py: Python<'_>, m: &PyModule) -> PyResult<()> {
"InvalidTranscriptAggregate",
py.get_type::(),
)?;
- m.add("ValidatorsNotSorted", py.get_type::())?;
m.add(
"ValidatorPublicKeyMismatch",
py.get_type::(),
)?;
m.add("SerializationError", py.get_type::())?;
m.add("InvalidVariant", py.get_type::())?;
+ m.add(
+ "InvalidDkgParameters",
+ py.get_type::(),
+ )?;
+ m.add(
+ "InvalidDkgParametersForPrecomputedVariant",
+ py.get_type::(),
+ )?;
+ m.add("InvalidShareIndex", py.get_type::())?;
+ m.add(
+ "DuplicatedShareIndex",
+ py.get_type::(),
+ )?;
+ m.add(
+ "NoTranscriptsToAggregate",
+ py.get_type::(),
+ )?;
+ m.add(
+ "InvalidAggregateVerificationParameters",
+ py.get_type::(),
+ )?;
Ok(())
}
@@ -832,7 +871,6 @@ mod test_ferveo_python {
let messages = messages[..security_threshold as usize].to_vec();
let pvss_aggregated =
dkg.aggregate_transcripts(messages.clone()).unwrap();
- // TODO: Redo how verification API works;
assert!(pvss_aggregated
.verify(validators_num, messages.clone())
.unwrap());
@@ -859,7 +897,6 @@ mod test_ferveo_python {
let aggregate = validator_dkg
.aggregate_transcripts(messages.clone())
.unwrap();
- // TODO: Redo how verification API works;
assert!(pvss_aggregated
.verify(validators_num, messages.clone())
.is_ok());
@@ -912,7 +949,6 @@ mod test_ferveo_python {
let messages = messages[..security_threshold as usize].to_vec();
let pvss_aggregated =
dkg.aggregate_transcripts(messages.clone()).unwrap();
- // TODO: Redo how verification API works;
assert!(pvss_aggregated
.verify(validators_num, messages.clone())
.unwrap());
@@ -940,7 +976,6 @@ mod test_ferveo_python {
.aggregate_transcripts(messages.clone())
.unwrap();
- // TODO: Redo how verification API works;
assert!(aggregate
.verify(validators_num, messages.clone())
.unwrap());
diff --git a/ferveo/src/bindings_wasm.rs b/ferveo/src/bindings_wasm.rs
index 1396de13..07e22e3f 100644
--- a/ferveo/src/bindings_wasm.rs
+++ b/ferveo/src/bindings_wasm.rs
@@ -515,13 +515,15 @@ impl AggregatedTranscript {
#[wasm_bindgen]
pub fn verify(
&self,
- shares_num: u32,
+ validators_num: u32,
messages: &ValidatorMessageArray,
) -> JsResult {
set_panic_hook();
let messages = unwrap_messages_js(messages)?;
- let is_valid =
- self.0.verify(shares_num, &messages).map_err(map_js_err)?;
+ let is_valid = self
+ .0
+ .verify(validators_num, &messages)
+ .map_err(map_js_err)?;
Ok(is_valid)
}
diff --git a/ferveo/src/lib.rs b/ferveo/src/lib.rs
index d5f1fb11..f9d6c1a5 100644
--- a/ferveo/src/lib.rs
+++ b/ferveo/src/lib.rs
@@ -117,6 +117,10 @@ pub enum Error {
/// Creating a transcript aggregate requires at least one transcript
#[error("No transcripts to aggregate")]
NoTranscriptsToAggregate,
+
+ /// The number of messages may not be greater than the number of validators
+ #[error("Invalid aggregate verification parameters: number of validators {0}, number of messages: {1}")]
+ InvalidAggregateVerificationParameters(u32, u32),
}
pub type Result = std::result::Result;
@@ -557,15 +561,13 @@ mod test_dkg_full {
validator_keypairs.as_slice(),
);
- let domain_points = dkg.domain_points();
-
// Each participant prepares an update for each other participant
let share_updates = dkg
.validators
.keys()
.map(|v_addr| {
let deltas_i = prepare_share_updates_for_refresh::(
- &domain_points,
+ &dkg.domain_points(),
&dkg.pvss_params.h.into_affine(),
dkg.dkg_params.security_threshold() as usize,
rng,
@@ -628,7 +630,7 @@ mod test_dkg_full {
.collect();
let lagrange = ferveo_tdec::prepare_combine_simple::(
- &domain_points[..SECURITY_THRESHOLD as usize],
+ &dkg.domain_points()[..SECURITY_THRESHOLD as usize],
);
let new_shared_secret = ferveo_tdec::share_combine_simple::(
&decryption_shares[..SECURITY_THRESHOLD as usize],