Skip to content

Commit

Permalink
Merge pull request #172 from piotr-roslaniec/external-share-idx
Browse files Browse the repository at this point in the history
Validator ordering changes
  • Loading branch information
cygnusv authored Feb 5, 2024
2 parents de9cf36 + 72b8484 commit 15776e7
Show file tree
Hide file tree
Showing 18 changed files with 258 additions and 250 deletions.
2 changes: 1 addition & 1 deletion ferveo-python/examples/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def gen_eth_addr(i: int) -> str:

validator_keypairs = [Keypair.random() for _ in range(0, shares_num)]
validators = [
Validator(gen_eth_addr(i), keypair.public_key())
Validator(gen_eth_addr(i), keypair.public_key(), i)
for i, keypair in enumerate(validator_keypairs)
]

Expand Down
2 changes: 1 addition & 1 deletion ferveo-python/examples/server_api_precomputed.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def gen_eth_addr(i: int) -> str:

validator_keypairs = [Keypair.random() for _ in range(0, shares_num)]
validators = [
Validator(gen_eth_addr(i), keypair.public_key())
Validator(gen_eth_addr(i), keypair.public_key(), i)
for i, keypair in enumerate(validator_keypairs)
]

Expand Down
2 changes: 1 addition & 1 deletion ferveo-python/examples/server_api_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def gen_eth_addr(i: int) -> str:
shares_num = 4
validator_keypairs = [Keypair.random() for _ in range(0, shares_num)]
validators = [
Validator(gen_eth_addr(i), keypair.public_key())
Validator(gen_eth_addr(i), keypair.public_key(), i)
for i, keypair in enumerate(validator_keypairs)
]

Expand Down
2 changes: 1 addition & 1 deletion ferveo-python/test/test_ferveo.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_t
tau = 1
validator_keypairs = [Keypair.random() for _ in range(0, shares_num)]
validators = [
Validator(gen_eth_addr(i), keypair.public_key())
Validator(gen_eth_addr(i), keypair.public_key(), i)
for i, keypair in enumerate(validator_keypairs)
]
validators.sort(key=lambda v: v.address)
Expand Down
2 changes: 1 addition & 1 deletion ferveo-python/test/test_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def gen_eth_addr(i: int) -> str:
shares_num = 4
validator_keypairs = [Keypair.random() for _ in range(shares_num)]
validators = [
Validator(gen_eth_addr(i), keypair.public_key())
Validator(gen_eth_addr(i), keypair.public_key(), i)
for i, keypair in enumerate(validator_keypairs)
]
validators.sort(key=lambda v: v.address)
Expand Down
2 changes: 1 addition & 1 deletion ferveo-wasm/examples/node/src/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function setupTest(sharesNum :number, threshold: number) {
for (let i = 0; i < sharesNum; i++) {
const keypair = Keypair.random();
validatorKeypairs.push(keypair);
const validator = new Validator(genEthAddr(i), keypair.publicKey);
const validator = new Validator(genEthAddr(i), keypair.publicKey, i);
validators.push(validator);
}

Expand Down
2 changes: 1 addition & 1 deletion ferveo-wasm/tests/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,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 dkg = Dkg::new(
let mut dkg = Dkg::new(
TAU,
shares_num,
security_threshold,
Expand Down
1 change: 1 addition & 0 deletions ferveo/benches/benchmarks/validity_checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ fn gen_validators(
.map(|i| Validator {
address: gen_address(i),
public_key: keypairs[i].public_key(),
share_index: i as u32,
})
.collect()
}
Expand Down
6 changes: 3 additions & 3 deletions ferveo/examples/bench_primitives_size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ fn gen_validators(
.map(|i| Validator {
address: gen_address(i),
public_key: keypairs[i].public_key(),
share_index: i as u32,
})
.collect()
}
Expand Down Expand Up @@ -95,14 +96,13 @@ fn setup(
for i in 0..shares_num {
let mut dkg = setup_dkg(i as usize, shares_num, security_threshold);
let message = dkg.share(rng).expect("Test failed");
let sender = dkg.get_validator(&dkg.me.validator.public_key).unwrap();
let sender = dkg.get_validator(&dkg.me.public_key).unwrap();
transcripts.push((sender.clone(), message.clone()));
}

let mut dkg = setup_dkg(0, shares_num, security_threshold);
for (sender, pvss) in transcripts.into_iter() {
dkg.apply_message(&sender.validator, &pvss)
.expect("Setup failed");
dkg.apply_message(&sender, &pvss).expect("Setup failed");
}
dkg
}
Expand Down
89 changes: 75 additions & 14 deletions ferveo/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::bindings_python;
use crate::bindings_wasm;
pub use crate::EthereumAddress;
use crate::{
do_verify_aggregation, Error, PVSSMap, PubliclyVerifiableParams,
do_verify_aggregation, Error, Message, PVSSMap, PubliclyVerifiableParams,
PubliclyVerifiableSS, Result,
};

Expand Down Expand Up @@ -222,15 +222,21 @@ impl Dkg {
}

pub fn generate_transcript<R: RngCore>(
&self,
&mut self,
rng: &mut R,
// TODO: Replace with Message::Deal?
) -> Result<Transcript> {
self.0.create_share(rng)
match self.0.share(rng) {
Ok(Message::Deal(transcript)) => Ok(transcript),
Err(e) => Err(e),
_ => Err(Error::InvalidDkgStateToDeal),
}
}

pub fn aggregate_transcripts(
&mut self,
messages: &[ValidatorMessage],
// TODO: Replace with Message::Aggregate?
) -> Result<AggregatedTranscript> {
// We must use `deal` here instead of to produce AggregatedTranscript instead of simply
// creating an AggregatedTranscript from the messages, because `deal` also updates the
Expand All @@ -242,7 +248,12 @@ impl Dkg {
for (validator, transcript) in messages {
self.0.deal(validator, transcript)?;
}
Ok(AggregatedTranscript(crate::pvss::aggregate(&self.0.vss)))
let pvss = messages
.iter()
.map(|(_, t)| t)
.cloned()
.collect::<Vec<PubliclyVerifiableSS<E>>>();
Ok(AggregatedTranscript(crate::pvss::aggregate(&pvss)?))
}

pub fn public_params(&self) -> DkgPublicParameters {
Expand All @@ -264,9 +275,13 @@ fn make_pvss_map(messages: &[ValidatorMessage]) -> PVSSMap<E> {
pub struct AggregatedTranscript(PubliclyVerifiableSS<E, crate::Aggregated>);

impl AggregatedTranscript {
pub fn new(messages: &[ValidatorMessage]) -> Self {
let pvss_map = make_pvss_map(messages);
AggregatedTranscript(crate::pvss::aggregate(&pvss_map))
pub fn new(messages: &[ValidatorMessage]) -> Result<Self> {
let pvss_list = messages
.iter()
.map(|(_, t)| t)
.cloned()
.collect::<Vec<PubliclyVerifiableSS<E>>>();
Ok(AggregatedTranscript(crate::pvss::aggregate(&pvss_list)?))
}

pub fn verify(
Expand Down Expand Up @@ -327,7 +342,7 @@ impl AggregatedTranscript {
&ciphertext_header.0,
aad,
&validator_keypair.decryption_key,
dkg.0.me.share_index,
dkg.0.me.share_index as usize,
&domain_points,
&dkg.0.pvss_params.g_inv(),
)
Expand All @@ -344,12 +359,13 @@ impl AggregatedTranscript {
&ciphertext_header.0,
aad,
&validator_keypair.decryption_key,
dkg.0.me.share_index,
dkg.0.me.share_index as usize,
&dkg.0.pvss_params.g_inv(),
)?;
let domain_point = dkg.0.domain.element(dkg.0.me.share_index as usize);
Ok(DecryptionShareSimple {
share,
domain_point: dkg.0.domain.element(dkg.0.me.share_index),
domain_point,
})
}
}
Expand Down Expand Up @@ -425,6 +441,7 @@ mod test_ferveo_api {
.map(|(i, keypair)| Validator {
address: gen_address(i),
public_key: keypair.public_key(),
share_index: i as u32,
})
.collect::<Vec<_>>();

Expand All @@ -433,7 +450,7 @@ mod test_ferveo_api {
let messages: Vec<_> = validators
.iter()
.map(|sender| {
let dkg = Dkg::new(
let mut dkg = Dkg::new(
tau,
shares_num,
security_threshold,
Expand Down Expand Up @@ -619,6 +636,10 @@ mod test_ferveo_api {
assert!(result.is_err());
}

// Note that the server and client code are using the same underlying
// 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() {
let rng = &mut StdRng::seed_from_u64(0);
Expand All @@ -637,6 +658,41 @@ mod test_ferveo_api {
assert!(local_aggregate
.verify(dkg.0.dkg_params.shares_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 no transcripts are provided
let mut dkg =
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)
.unwrap();
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);
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)
.unwrap();
let (bad_messages, _, _) =
make_test_inputs(rng, TAU, SECURITY_THRESHOLD, SHARES_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);
assert!(result.is_err());
}

#[test]
Expand All @@ -650,7 +706,8 @@ mod test_ferveo_api {
let messages = &messages[..SECURITY_THRESHOLD as usize];

// Create an aggregated transcript on the client side
let aggregated_transcript = AggregatedTranscript::new(messages);
let aggregated_transcript =
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
Expand All @@ -662,11 +719,15 @@ mod test_ferveo_api {

// Test negative cases

// 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 insufficient_aggregate =
AggregatedTranscript::new(not_enough_messages);
AggregatedTranscript::new(not_enough_messages).unwrap();
let result = insufficient_aggregate.verify(SHARES_NUM, messages);
assert!(result.is_err());

Expand All @@ -675,7 +736,7 @@ mod test_ferveo_api {
let (bad_messages, _, _) =
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 bad_aggregate = AggregatedTranscript::new(&mixed_messages).unwrap();
let result = bad_aggregate.verify(SHARES_NUM, messages);
assert!(result.is_err());
}
Expand Down
31 changes: 23 additions & 8 deletions ferveo/src/bindings_python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ impl From<FerveoPythonError> for PyErr {
Error::InvalidTranscriptAggregate => {
InvalidTranscriptAggregate::new_err("")
}
Error::ValidatorsNotSorted => ValidatorsNotSorted::new_err(""),
Error::ValidatorPublicKeyMismatch => {
ValidatorPublicKeyMismatch::new_err("")
}
Expand Down Expand Up @@ -109,6 +108,14 @@ impl From<FerveoPythonError> for PyErr {
"num_shares: {num_shares}, security_threshold: {security_threshold}"
))
},
Error::DuplicatedShareIndex(index) => {
InvalidShareIndex::new_err(format!(
"{index}"
))
},
Error::NoTranscriptsToAggregate => {
NoTranscriptsToAggregate::new_err("")
},
},
_ => default(),
}
Expand Down Expand Up @@ -145,6 +152,7 @@ create_exception!(exceptions, InvalidByteLength, PyValueError);
create_exception!(exceptions, InvalidVariant, PyValueError);
create_exception!(exceptions, InvalidDkgParameters, PyValueError);
create_exception!(exceptions, InvalidShareIndex, PyValueError);
create_exception!(exceptions, NoTranscriptsToAggregate, PyValueError);

fn from_py_bytes<T: FromBytes>(bytes: &[u8]) -> PyResult<T> {
T::from_bytes(bytes)
Expand Down Expand Up @@ -396,8 +404,9 @@ impl Validator {
pub fn new(
address: String,
public_key: &FerveoPublicKey,
share_index: u32,
) -> PyResult<Self> {
let validator = api::Validator::new(address, public_key.0)
let validator = api::Validator::new(address, public_key.0, share_index)
.map_err(|err| FerveoPythonError::Other(err.to_string()))?;
Ok(Self(validator))
}
Expand Down Expand Up @@ -486,7 +495,7 @@ impl Dkg {
DkgPublicKey(self.0.public_key())
}

pub fn generate_transcript(&self) -> PyResult<Transcript> {
pub fn generate_transcript(&mut self) -> PyResult<Transcript> {
let rng = &mut thread_rng();
let transcript = self
.0
Expand Down Expand Up @@ -575,10 +584,12 @@ generate_bytes_serialization!(AggregatedTranscript);
#[pymethods]
impl AggregatedTranscript {
#[new]
pub fn new(messages: Vec<ValidatorMessage>) -> Self {
pub fn new(messages: Vec<ValidatorMessage>) -> PyResult<Self> {
let messages: Vec<_> =
messages.into_iter().map(|vm| vm.to_inner()).collect();
Self(api::AggregatedTranscript::new(&messages))
let inner = api::AggregatedTranscript::new(&messages)
.map_err(FerveoPythonError::FerveoError)?;
Ok(Self(inner))
}

pub fn verify(
Expand Down Expand Up @@ -756,8 +767,12 @@ mod test_ferveo_python {
.iter()
.enumerate()
.map(|(i, keypair)| {
Validator::new(format!("0x{i:040}"), &keypair.public_key())
.unwrap()
Validator::new(
format!("0x{i:040}"),
&keypair.public_key(),
i as u32,
)
.unwrap()
})
.collect();

Expand All @@ -767,7 +782,7 @@ mod test_ferveo_python {
.iter()
.cloned()
.map(|sender| {
let dkg = Dkg::new(
let mut dkg = Dkg::new(
tau,
shares_num,
security_threshold,
Expand Down
Loading

0 comments on commit 15776e7

Please sign in to comment.