Skip to content

Commit

Permalink
feature!: remove state from dkg, part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
piotr-roslaniec committed Feb 19, 2024
1 parent 2e7835c commit f746edf
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 212 deletions.
1 change: 1 addition & 0 deletions ferveo-python/ferveo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,5 @@
InvalidAggregateVerificationParameters,
UnknownValidator,
TooManyTranscripts,
DuplicateTranscript,
)
3 changes: 3 additions & 0 deletions ferveo-python/ferveo/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,6 @@ class UnknownValidator(Exception):

class TooManyTranscripts(Exception):
pass

class DuplicateTranscript(Exception):
pass
139 changes: 46 additions & 93 deletions ferveo/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ use crate::bindings_python;
use crate::bindings_wasm;
pub use crate::EthereumAddress;
use crate::{
do_verify_aggregation, DomainPoint, Error, PVSSMap,
PubliclyVerifiableParams, PubliclyVerifiableSS, Result,
do_verify_aggregation, DomainPoint, Error, PubliclyVerifiableParams,
PubliclyVerifiableSS, Result,
};

pub type PublicKey = ferveo_common::PublicKey<E>;
Expand Down Expand Up @@ -66,12 +66,12 @@ pub fn decrypt_with_shared_secret(
aad: &[u8],
shared_secret: &SharedSecret,
) -> Result<Vec<u8>> {
let dkg_public_params = DkgPublicParameters::default();
let g_inv = PubliclyVerifiableParams::<E>::default().g_inv();
ferveo_tdec::api::decrypt_with_shared_secret(
&ciphertext.0,
aad,
&shared_secret.0,
&dkg_public_params.g1_inv,
&g_inv,
)
.map_err(Error::from)
}
Expand Down Expand Up @@ -235,13 +235,6 @@ impl Dkg {
.map(AggregatedTranscript)
}

// TODO: Unused?
pub fn public_params(&self) -> DkgPublicParameters {
DkgPublicParameters {
g1_inv: self.0.pvss_params.g_inv(),
}
}

pub fn me(&self) -> &Validator {
&self.0.me
}
Expand All @@ -251,14 +244,6 @@ impl Dkg {
}
}

fn make_pvss_map(messages: &[ValidatorMessage]) -> PVSSMap<E> {
let mut pvss_map: PVSSMap<E> = PVSSMap::new();
messages.iter().for_each(|(validator, transcript)| {
pvss_map.insert(validator.address.clone(), transcript.clone());
});
pvss_map
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct AggregatedTranscript(crate::AggregatedTranscript<E>);

Expand All @@ -285,33 +270,34 @@ impl AggregatedTranscript {
));
}

let pvss_params = PubliclyVerifiableParams::<E>::default();
let domain =
GeneralEvaluationDomain::<Fr>::new(validators_num as usize)
.expect("Unable to construct an evaluation domain");

let is_valid_optimistic = self.0.aggregate.verify_optimistic();
if !is_valid_optimistic {
return Err(Error::InvalidTranscriptAggregate);
}

let pvss_map = make_pvss_map(messages);
let pvss_params = PubliclyVerifiableParams::<E>::default();
let validators: Vec<_> = messages
.iter()
.map(|(validator, _)| validator)
.cloned()
.collect();

let pvss_list = messages
.iter()
.map(|(_validator, transcript)| transcript)
.cloned()
.collect::<Vec<_>>();
// This check also includes `verify_full`. See impl. for details.
let is_valid = do_verify_aggregation(
do_verify_aggregation(
&self.0.aggregate.coeffs,
&self.0.aggregate.shares,
&pvss_params,
&validators,
&domain,
&pvss_map,
)?;
Ok(is_valid)
&pvss_list,
)
}

// TODO: Consider deprecating in favor of PrivateKeyShare::create_decryption_share_simple
Expand Down Expand Up @@ -354,7 +340,6 @@ impl AggregatedTranscript {
aad,
validator_keypair,
dkg.0.me.share_index,
&dkg.0.pvss_params.g_inv(),
)?;
let domain_point = dkg.0.get_domain_point(dkg.0.me.share_index)?;
Ok(DecryptionShareSimple {
Expand Down Expand Up @@ -390,32 +375,6 @@ pub struct DecryptionShareSimple {
domain_point: DomainPoint<E>,
}

// TODO: Deprecate?
#[serde_as]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct DkgPublicParameters {
#[serde_as(as = "serialization::SerdeAs")]
pub(crate) g1_inv: G1Prepared,
}

impl Default for DkgPublicParameters {
fn default() -> Self {
DkgPublicParameters {
g1_inv: PubliclyVerifiableParams::<E>::default().g_inv(),
}
}
}

impl DkgPublicParameters {
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
bincode::deserialize(bytes).map_err(|e| e.into())
}

pub fn to_bytes(&self) -> Result<Vec<u8>> {
bincode::serialize(self).map_err(|e| e.into())
}
}

pub fn combine_shares_simple(shares: &[DecryptionShareSimple]) -> SharedSecret {
// Pick domain points that are corresponding to the shares we have.
let domain_points: Vec<_> = shares.iter().map(|s| s.domain_point).collect();
Expand Down Expand Up @@ -575,7 +534,6 @@ impl PrivateKeyShare {
&ciphertext_header.0,
aad,
validator_keypair,
&dkg.public_params().g1_inv,
)?;
let domain_point = dkg.0.get_domain_point(dkg.0.me.share_index)?;
Ok(DecryptionShareSimple {
Expand All @@ -593,15 +551,15 @@ impl PrivateKeyShare {
share_index: u32,
domain_points: &[DomainPoint<E>],
) -> Result<DecryptionSharePrecomputed> {
let dkg_public_params = DkgPublicParameters::default();
let g_inv = PubliclyVerifiableParams::<E>::default().g_inv();
let share = crate::PrivateKeyShare(self.0.clone())
.create_decryption_share_simple_precomputed(
&ciphertext_header.0,
aad,
validator_keypair,
share_index,
domain_points,
&dkg_public_params.g1_inv,
&g_inv,
)?;
Ok(share)
}
Expand Down Expand Up @@ -918,33 +876,33 @@ mod test_ferveo_api {
assert!(result.is_err());

// Duplicated transcripts
let message_with_duplicated_validator = (
// Duplicating the validator but with a different, valid transcript
messages[0].0.clone(),
messages[security_threshold as usize].1.clone(),
);
let mut messages_with_duplicates = messages.clone();
messages_with_duplicates.push(message_with_duplicated_validator);
// assert_eq!(messages_with_duplicates.len(), security_threshold as usize);
let messages_with_duplicated_transcript = [
(
validators[security_threshold as usize - 1].clone(),
messages[security_threshold as usize - 1].1.clone(),
),
(
validators[security_threshold as usize - 1].clone(),
messages[security_threshold as usize - 2].1.clone(),
),
];
assert!(dkg
.aggregate_transcripts(&messages_with_duplicates)
.aggregate_transcripts(&messages_with_duplicated_transcript)
.is_err());

// TODO: Transcripts are not hashable?
// let message_with_duplicated_transcript = (
// // Duplicating the transcript but with a different, valid validator
// validators[security_threshold as usize - 1].clone(),
// messages[security_threshold as usize - 1].1.clone(),
// );
// let messages_with_duplicates = [
// &messages[..(security_threshold - 1) as usize],
// &[message_with_duplicated_transcript],
// ]
// .concat();
// assert_eq!(messages_with_duplicates.len(), security_threshold as usize);
// assert!(dkg
// .aggregate_transcripts(&messages_with_duplicates)
// .is_err());
let messages_with_duplicated_transcript = [
(
validators[security_threshold as usize - 1].clone(),
messages[security_threshold as usize - 1].1.clone(),
),
(
validators[security_threshold as usize - 2].clone(),
messages[security_threshold as usize - 1].1.clone(),
),
];
assert!(dkg
.aggregate_transcripts(&messages_with_duplicated_transcript)
.is_err());

// Unexpected transcripts in the aggregate or transcripts from a different ritual
// Using same DKG parameters, but different DKG instances and validators
Expand Down Expand Up @@ -1065,7 +1023,7 @@ mod test_ferveo_api {
.collect::<Vec<_>>();

// Creating a copy to avoiding accidentally changing DKG state
let mut dkg = dkgs[0].clone();
let dkg = dkgs[0].clone();
let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap();
assert!(pvss_aggregated.verify(validators_num, &messages).unwrap());

Expand All @@ -1074,17 +1032,18 @@ mod test_ferveo_api {
let ciphertext =
encrypt(SecretBox::new(MSG.to_vec()), AAD, &public_key).unwrap();
let ciphertext_header = ciphertext.header().unwrap();
for (validator, transcript) in messages.iter() {
dkg.0
.vss
.insert(validator.address.clone(), transcript.clone());
}
let transcripts = messages
.iter()
.map(|(_, transcript)| transcript)
.cloned()
.collect::<Vec<_>>();
let (_, _, old_shared_secret) =
crate::test_dkg_full::create_shared_secret_simple_tdec(
&dkg.0,
AAD,
&ciphertext_header.0,
validator_keypairs.as_slice(),
&transcripts,
);

(
Expand Down Expand Up @@ -1142,13 +1101,7 @@ mod test_ferveo_api {
messages.pop().unwrap();
dkgs.pop();
validator_keypairs.pop().unwrap();

let removed_validator = validators.pop().unwrap();
for dkg in dkgs.iter_mut() {
dkg.0
.offboard_validator(&removed_validator.address)
.expect("Unable to off-board a validator from the DKG context");
}

// Now, we're going to recover a new share at a random point or at a specific point
// and check that the shared secret is still the same.
Expand Down
5 changes: 5 additions & 0 deletions ferveo/src/bindings_python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ impl From<FerveoPythonError> for PyErr {
"expected: {expected}, received: {received}"
))
}
Error::DuplicateTranscript(validator) => {
DuplicateTranscript::new_err(validator.to_string())
}
// Remember to create Python exceptions using `create_exception!` macro, and to register them in the
// `make_ferveo_py_module` function. You will have to update the `ferveo/__init__.{py, pyi}` files too.
},
Expand Down Expand Up @@ -180,6 +183,7 @@ create_exception!(
);
create_exception!(exceptions, UnknownValidator, PyValueError);
create_exception!(exceptions, TooManyTranscripts, PyValueError);
create_exception!(exceptions, DuplicateTranscript, PyValueError);

fn from_py_bytes<T: FromBytes>(bytes: &[u8]) -> PyResult<T> {
T::from_bytes(bytes)
Expand Down Expand Up @@ -796,6 +800,7 @@ pub fn make_ferveo_py_module(py: Python<'_>, m: &PyModule) -> PyResult<()> {
)?;
m.add("UnknownValidator", py.get_type::<UnknownValidator>())?;
m.add("TooManyTranscripts", py.get_type::<TooManyTranscripts>())?;
m.add("DuplicateTranscript", py.get_type::<DuplicateTranscript>())?;

Ok(())
}
Expand Down
Loading

0 comments on commit f746edf

Please sign in to comment.