diff --git a/mithril-stm/benches/size_benches.rs b/mithril-stm/benches/size_benches.rs index 4b1fb1d75d8..79204b1f446 100644 --- a/mithril-stm/benches/size_benches.rs +++ b/mithril-stm/benches/size_benches.rs @@ -31,7 +31,7 @@ where let mut key_reg = KeyRegistration::initialize(); for stake in parties { let p = Initializer::new(params, stake, &mut rng); - key_reg.register_by_entry(&p.clone().into()).unwrap(); + key_reg.register_by_entry(&p.clone().try_into().unwrap()).unwrap(); ps.push(p); } diff --git a/mithril-stm/benches/stm.rs b/mithril-stm/benches/stm.rs index 0705378cd9a..dba26494172 100644 --- a/mithril-stm/benches/stm.rs +++ b/mithril-stm/benches/stm.rs @@ -44,7 +44,7 @@ fn stm_benches( // We need to initialise the key_reg at each iteration key_reg = KeyRegistration::initialize(); for p in initializers.iter() { - key_reg.register_by_entry(&p.clone().into()).unwrap(); + key_reg.register_by_entry(&p.clone().try_into().unwrap()).unwrap(); } }) }); @@ -115,7 +115,7 @@ fn batch_benches( } let mut key_reg = KeyRegistration::initialize(); for p in initializers.iter() { - key_reg.register_by_entry(&p.clone().into()).unwrap(); + key_reg.register_by_entry(&p.clone().try_into().unwrap()).unwrap(); } let closed_reg = key_reg.close_registration(); diff --git a/mithril-stm/src/membership_commitment/merkle_tree/error.rs b/mithril-stm/src/membership_commitment/merkle_tree/error.rs index 451859d30bf..9a32219b2d6 100644 --- a/mithril-stm/src/membership_commitment/merkle_tree/error.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/error.rs @@ -15,4 +15,11 @@ pub enum MerkleTreeError { /// Invalid merkle batch path #[error("Batch path does not verify against root")] BatchPathInvalid(Vec), + + /// Leaf not found in the merkle tree + #[cfg(feature = "future_snark")] + // TODO: remove this allow dead_code directive when function is called or future_snark is activated + #[allow(dead_code)] + #[error("Leaf not found in the merkle tree")] + LeafNotFound, } diff --git a/mithril-stm/src/membership_commitment/merkle_tree/tree.rs b/mithril-stm/src/membership_commitment/merkle_tree/tree.rs index 0773f36231d..5f07d1158d8 100644 --- a/mithril-stm/src/membership_commitment/merkle_tree/tree.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/tree.rs @@ -189,6 +189,18 @@ impl MerkleTree { }) } + /// Find the index of a leaf in the Merkle tree. + /// Returns the index if the leaf is found, or an error otherwise. + #[cfg(feature = "future_snark")] + // TODO: remove this allow dead_code directive when function is called or future_snark is activated + #[allow(dead_code)] + pub(crate) fn find_leaf_index(&self, leaf: &L) -> StmResult { + let leaf_hash = D::digest(leaf.as_bytes_for_merkle_tree()).to_vec(); + (0..self.n) + .find(|&i| self.nodes[self.leaf_off + i] == leaf_hash) + .ok_or_else(|| MerkleTreeError::LeafNotFound.into()) + } + #[cfg(feature = "future_snark")] // TODO: remove this allow dead_code directive when function is called or future_snark is activated #[allow(dead_code)] diff --git a/mithril-stm/src/proof_system/concatenation/clerk.rs b/mithril-stm/src/proof_system/concatenation/clerk.rs index 922f260a7a1..a580829b124 100644 --- a/mithril-stm/src/proof_system/concatenation/clerk.rs +++ b/mithril-stm/src/proof_system/concatenation/clerk.rs @@ -205,7 +205,7 @@ mod tests { for i in 0..nparties { let stake = (i as u64 + 1) * 10; let initializer = Initializer::new(params, stake, &mut rng); - key_registration.register_by_entry(&initializer.clone().into()).unwrap(); + key_registration.register_by_entry(&initializer.clone().try_into().unwrap()).unwrap(); initializers.push(initializer); } diff --git a/mithril-stm/src/proof_system/halo2_snark/clerk.rs b/mithril-stm/src/proof_system/halo2_snark/clerk.rs new file mode 100644 index 00000000000..0c602e4942a --- /dev/null +++ b/mithril-stm/src/proof_system/halo2_snark/clerk.rs @@ -0,0 +1,141 @@ +use std::collections::{BTreeMap, HashMap, HashSet}; + +use crate::{ + AggregationError, ClosedKeyRegistration, LotteryIndex, MembershipDigest, Parameters, + RegistrationEntryForSnark, Signer, StmResult, +}; + +use super::{AggregateVerificationKeyForSnark, witness::SignatureRegistrationEntry}; + +/// The `SnarkClerk` is responsible for managing the proof system related to +/// SNARK signatures. +#[derive(Debug, Clone)] +pub struct SnarkClerk { + /// The closed key registration associated with this clerk. + pub(crate) closed_key_registration: ClosedKeyRegistration, + /// Protocol parameters + pub(crate) parameters: Parameters, +} + +// TODO: remove this allow dead_code directive when function is called or future_snark is activated +#[allow(dead_code)] +impl SnarkClerk { + /// Create a new `SnarkClerk` from a closed registration instance. + pub fn new_clerk_from_closed_key_registration( + parameters: &Parameters, + closed_key_registration: &ClosedKeyRegistration, + ) -> Self { + Self { + parameters: *parameters, + closed_key_registration: closed_key_registration.clone(), + } + } + + /// Create a `SnarkClerk` from a signer. + pub fn new_clerk_from_signer(signer: &Signer) -> Self { + Self { + parameters: signer.parameters, + closed_key_registration: signer.closed_key_registration.clone(), + } + } + + /// Compute the `AggregateVerificationKeyForSnark` related to the used registration. + pub fn compute_aggregate_verification_key_for_snark( + &self, + ) -> AggregateVerificationKeyForSnark { + AggregateVerificationKeyForSnark::from(&self.closed_key_registration) + } + + /// Get the SNARK registration entry for a given signer index. + pub fn get_snark_registration_entry( + &self, + signer_index: LotteryIndex, + ) -> StmResult> { + let closed_registration_entry = self + .closed_key_registration + .get_registration_entry_for_index(&signer_index)?; + Ok(closed_registration_entry.into()) + } + + /// Modifications: + /// Function inputs: remove `msg`, replace `sigs: &[SingleSignatureWithRegisteredParty]` with + /// `signatures: &[SignatureRegistrationEntry]`, remove avk. + /// Return value: `StmResult`. + /// Remove signature verification loop -> already done in aggregation step. + /// Rename `sig_reg` as `entry` for the first loop, since we iterate over entries now. + /// Rename `sig_reg` as `entry` for the second loop, since we iterate over entries now. + /// + /// Note: schnorr sig, scalar field element, projective point are updated to satisfy `Hash`, + /// `Ord`, `PartialOrd`. + pub(crate) fn select_valid_signatures_for_k_indices( + parameters: &Parameters, + signatures: &[SignatureRegistrationEntry], + ) -> StmResult> { + let mut sig_by_index: BTreeMap = BTreeMap::new(); + let mut removal_idx_by_vk: HashMap<&SignatureRegistrationEntry, Vec> = + HashMap::new(); + + for entry in signatures.iter() { + for index in entry.get_signature().get_indices().iter() { + let mut insert_this_sig = false; + if let Some(&previous_entry) = sig_by_index.get(index) { + let entry_to_remove_index = if entry.get_signature().get_schnorr_signature() + < previous_entry.get_signature().get_schnorr_signature() + { + insert_this_sig = true; + previous_entry + } else { + entry + }; + + if let Some(indexes) = removal_idx_by_vk.get_mut(entry_to_remove_index) { + indexes.push(*index); + } else { + removal_idx_by_vk.insert(entry_to_remove_index, vec![*index]); + } + } else { + insert_this_sig = true; + } + + if insert_this_sig { + sig_by_index.insert(*index, entry); + } + } + } + + let mut dedup_sigs: HashSet = HashSet::new(); + let mut count: u64 = 0; + + for (_, &entry) in sig_by_index.iter() { + if dedup_sigs.contains(entry) { + continue; + } + let mut deduped_entry = entry.clone(); + if let Some(indexes) = removal_idx_by_vk.get(entry) { + let indices = deduped_entry + .get_signature() + .get_indices() + .into_iter() + .filter(|i| !indexes.contains(i)) + .collect::>(); + deduped_entry.set_indices(&indices); + } + + let size: Result = deduped_entry.get_signature().get_indices().len().try_into(); + if let Ok(size) = size { + if dedup_sigs.contains(&deduped_entry) { + panic!( + "Invariant violation: duplicate signature encountered in deduplication set, which should not be possible." + ); + } + dedup_sigs.insert(deduped_entry); + count += size; + + if count >= parameters.k { + return Ok(dedup_sigs.into_iter().collect()); + } + } + } + Err(AggregationError::NotEnoughSignatures(count, parameters.k).into()) + } +} diff --git a/mithril-stm/src/proof_system/halo2_snark/eligibility.rs b/mithril-stm/src/proof_system/halo2_snark/eligibility.rs index 8ebb6d99ec7..46f14e1e1ac 100644 --- a/mithril-stm/src/proof_system/halo2_snark/eligibility.rs +++ b/mithril-stm/src/proof_system/halo2_snark/eligibility.rs @@ -16,6 +16,28 @@ cfg_num_integer! { /// the value is. A value of 30 provides ~69 bits precision for phi_f=0.2 const TAYLOR_EXPANSION_ITERATIONS: usize = 30; + /// Computes the lottery target value for a given stake and total stake. + /// Phi_f is hardcoded for testing. + // TODO: pass phi_f as an argument instead of hardcoding it. + #[cfg(feature = "future_snark")] + // TODO: remove this allow dead_code directive when function is called or future_snark is activated + #[allow(dead_code)] + pub fn compute_lottery_target_value(stake: Stake, total_stake: Stake) -> LotteryTargetValue{ + let phi_f = 0.5; + let phi_f_ratio_int: Ratio = + Ratio::approximate_float(phi_f).expect("Only fails if the float is infinite or NaN."); + let phi_f_ratio = Ratio::new_raw( + BigInt::from(*phi_f_ratio_int.numer()), + BigInt::from(*phi_f_ratio_int.denom()), + ); + let ln_one_minus_phi_f = ln_1p_taylor_expansion( + TAYLOR_EXPANSION_ITERATIONS, + phi_f_ratio.numer(), + phi_f_ratio.denom(), + ); + compute_target_value(&ln_one_minus_phi_f, stake, total_stake) + } + #[cfg(feature = "future_snark")] // TODO: remove this allow dead_code directive when function is called or future_snark is activated #[allow(dead_code)] diff --git a/mithril-stm/src/proof_system/halo2_snark/mod.rs b/mithril-stm/src/proof_system/halo2_snark/mod.rs index f6c5e345be7..78b1ed8f68d 100644 --- a/mithril-stm/src/proof_system/halo2_snark/mod.rs +++ b/mithril-stm/src/proof_system/halo2_snark/mod.rs @@ -1,10 +1,13 @@ mod aggregate_key; +mod clerk; mod eligibility; mod message; mod signer; mod single_signature; +mod witness; pub(crate) use aggregate_key::AggregateVerificationKeyForSnark; +pub(crate) use clerk::SnarkClerk; pub(crate) use eligibility::compute_winning_lottery_indices; pub(crate) use message::build_snark_message; pub(crate) use signer::SnarkProofSigner; @@ -18,7 +21,7 @@ mod tests { use crate::{ ClosedRegistrationEntry, KeyRegistration, MithrilMembershipDigest, Parameters, - RegistrationEntry, SignatureError, VerificationKeyForSnark, + RegistrationEntry, VerificationKeyForSnark, VerificationKeyProofOfPossessionForConcatenation, proof_system::halo2_snark::eligibility::{check_lottery_for_index, compute_lottery_prefix}, protocol::RegistrationEntryForSnark, @@ -129,9 +132,9 @@ mod tests { ) { let mut rng = ChaCha20Rng::from_seed(seed); let params = Parameters { - m: 10, - k: 5, - phi_f: 0.2, + m: 100, + k: 20, + phi_f: 0.5, }; let (_signer, avk) = setup_snark_signer(params, 3, &mut rng); @@ -169,9 +172,9 @@ mod tests { fn schnorr_challenge_matches_circuit_ordering() { let mut rng = ChaCha20Rng::from_seed([0u8; 32]); let params = Parameters { - m: 10, - k: 5, - phi_f: 0.2, + m: 100, + k: 20, + phi_f: 0.5, }; let (signer, avk) = setup_snark_signer(params, 3, &mut rng); @@ -233,9 +236,9 @@ mod tests { fn lottery_prefix_structure_matches_circuit() { let mut rng = ChaCha20Rng::from_seed([0u8; 32]); let params = Parameters { - m: 10, - k: 5, - phi_f: 0.2, + m: 100, + k: 20, + phi_f: 0.5, }; let (_signer, avk) = setup_snark_signer(params, 3, &mut rng); @@ -269,9 +272,9 @@ mod tests { fn lottery_evaluation_structure_matches_circuit() { let mut rng = ChaCha20Rng::from_seed([0u8; 32]); let params = Parameters { - m: 10, - k: 5, - phi_f: 0.2, + m: 100, + k: 20, + phi_f: 0.5, }; let (signer, avk) = setup_snark_signer(params, 3, &mut rng); @@ -315,12 +318,12 @@ mod tests { #[test] fn sign_then_verify_roundtrip( nparties in 2_usize..10, - m in 10_u64..20, - k in 1_u64..5, + m in 100_u64..120, + k in 20_u64..25, msg in any::<[u8; 32]>(), seed in any::<[u8; 32]>(), ) { - let params = Parameters { m, k, phi_f: 0.2 }; + let params = Parameters { m, k, phi_f: 0.5 }; let mut rng = ChaCha20Rng::from_seed(seed); let (signer, avk) = setup_snark_signer(params, nparties, &mut rng); @@ -337,14 +340,14 @@ mod tests { #[test] fn wrong_message_fails_verification( nparties in 2_usize..10, - m in 10_u64..20, - k in 1_u64..5, + m in 100_u64..120, + k in 20_u64..25, msg1 in any::<[u8; 32]>(), msg2 in any::<[u8; 32]>(), seed in any::<[u8; 32]>(), ) { prop_assume!(msg1 != msg2); - let params = Parameters { m, k, phi_f: 0.2 }; + let params = Parameters { m, k, phi_f: 0.5 }; let mut rng = ChaCha20Rng::from_seed(seed); let (signer, avk) = setup_snark_signer(params, nparties, &mut rng); @@ -361,12 +364,12 @@ mod tests { #[test] fn wrong_verification_key_fails( nparties in 2_usize..10, - m in 10_u64..20, - k in 1_u64..5, + m in 100_u64..120, + k in 20_u64..25, msg in any::<[u8; 32]>(), seed in any::<[u8; 32]>(), ) { - let params = Parameters { m, k, phi_f: 0.2 }; + let params = Parameters { m, k, phi_f: 0.5 }; let mut rng = ChaCha20Rng::from_seed(seed); let (signer, avk) = setup_snark_signer(params, nparties, &mut rng); let sig = signer.create_single_signature(&msg, &mut rng).unwrap(); @@ -385,12 +388,12 @@ mod tests { #[test] fn serde_roundtrip( nparties in 2_usize..10, - m in 10_u64..20, - k in 1_u64..5, + m in 100_u64..120, + k in 20_u64..25, msg in any::<[u8; 32]>(), seed in any::<[u8; 32]>(), ) { - let params = Parameters { m, k, phi_f: 0.2 }; + let params = Parameters { m, k, phi_f: 0.5 }; let mut rng = ChaCha20Rng::from_seed(seed); let (signer, _) = setup_snark_signer(params, nparties, &mut rng); @@ -416,9 +419,9 @@ mod tests { fn check_lottery_returns_all_winning_indices() { let mut rng = ChaCha20Rng::from_seed([0u8; 32]); let params = Parameters { - m: 10, - k: 5, - phi_f: 0.2, + m: 100, + k: 20, + phi_f: 0.5, }; let (signer, avk) = setup_snark_signer(params, 3, &mut rng); @@ -436,31 +439,22 @@ mod tests { let prefix = compute_lottery_prefix(&message_to_sign); - // Every returned index must pass verify_lottery_eligibility for &index in &winning_indices { assert!( index < params.m, "Winning index {index} should be less than m={}", params.m ); - assert!( - check_lottery_for_index(&schnorr, index, params.m, prefix, target).is_ok(), - "Winning index {index} should pass check_lottery_for_index" - ); + let result = check_lottery_for_index(&schnorr, index, params.m, prefix, target) + .expect("check_lottery_for_index should not error for valid index"); + assert!(result, "Winning index {index} should return true"); } - // Every index NOT in the winning set must fail check_lottery_for_index for index in 0..params.m { if !winning_indices.contains(&index) { - let err = check_lottery_for_index(&schnorr, index, params.m, prefix, target) - .expect_err(&format!("Non-winning index {index} should fail")); - assert!( - matches!( - err.downcast_ref::(), - Some(SignatureError::LotteryLost) - ), - "Expected LotteryLost for index {index}, got: {err:?}" - ); + let result = check_lottery_for_index(&schnorr, index, params.m, prefix, target) + .expect("check_lottery_for_index should not error for valid index"); + assert!(!result, "Non-winning index {index} should return false"); } } } diff --git a/mithril-stm/src/proof_system/halo2_snark/single_signature.rs b/mithril-stm/src/proof_system/halo2_snark/single_signature.rs index ec952263b5e..d705ff5c48b 100644 --- a/mithril-stm/src/proof_system/halo2_snark/single_signature.rs +++ b/mithril-stm/src/proof_system/halo2_snark/single_signature.rs @@ -1,3 +1,5 @@ +use std::hash::{Hash, Hasher}; + use anyhow::Context; use serde::{Deserialize, Serialize}; @@ -52,8 +54,8 @@ impl SingleSignatureForSnark { } /// Return `indices` of the single signature - pub(crate) fn get_indices(&self) -> &[LotteryIndex] { - &self.indices + pub(crate) fn get_indices(&self) -> Vec { + self.indices.to_vec() } /// Set `indices` of single signature to given value @@ -69,6 +71,12 @@ impl SingleSignatureForSnark { } } +impl Hash for SingleSignatureForSnark { + fn hash(&self, state: &mut H) { + self.schnorr_signature.hash(state); + } +} + impl PartialEq for SingleSignatureForSnark { fn eq(&self, other: &Self) -> bool { self.schnorr_signature == other.schnorr_signature diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs b/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs new file mode 100644 index 00000000000..e05933dc013 --- /dev/null +++ b/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs @@ -0,0 +1,26 @@ +use crate::BaseFieldElement; + +/// Public inputs to the SNARK circuit. +/// +/// Contains the Merkle tree root of the registration commitment and the +/// signed message, both represented as base field elements. +// TODO: remove this allow dead_code directive when function is called or future_snark is activated +#[allow(dead_code)] +pub(crate) struct Instance { + /// The root of the SNARK registration Merkle tree. + merkle_tree_root: BaseFieldElement, + /// The signed message as a base field element. + message: BaseFieldElement, +} + +// TODO: remove this allow dead_code directive when function is called or future_snark is activated +#[allow(dead_code)] +impl Instance { + /// Create a new `Instance` from the Merkle tree root and message. + pub(crate) fn new(merkle_tree_root: BaseFieldElement, message: BaseFieldElement) -> Self { + Self { + merkle_tree_root, + message, + } + } +} diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs b/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs new file mode 100644 index 00000000000..aa1dba98598 --- /dev/null +++ b/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs @@ -0,0 +1,8 @@ +mod instance; +mod proof; +mod signature_registration_entry; +mod witness_entry; + +pub(super) use instance::Instance; +pub(super) use signature_registration_entry::SignatureRegistrationEntry; +pub(super) use witness_entry::WitnessEntry; diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs b/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs new file mode 100644 index 00000000000..ae3a674baf2 --- /dev/null +++ b/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs @@ -0,0 +1,138 @@ +use crate::{ + MembershipDigest, SingleSignature, StmResult, + proof_system::{ + AggregateVerificationKeyForSnark, SnarkClerk, + halo2_snark::{build_snark_message, compute_winning_lottery_indices}, + }, +}; + +use super::{Instance, SignatureRegistrationEntry, WitnessEntry}; + +/// SNARK proof consisting of the public instance and a list of witness entries. +/// +/// The instance holds the Merkle tree root and message (public inputs to the circuit). +/// The witness holds one [`WitnessEntry`] per winning lottery index, each containing +/// the signature, Merkle leaf, and Merkle path needed by the circuit. +// TODO: remove this allow dead_code directive when function is called or future_snark is activated +#[allow(dead_code)] +pub struct SnarkProof { + /// Public inputs to the SNARK circuit. + instance: Instance, + /// Per-winning-lottery-index witness data. + witness: Vec>, +} + +// TODO: remove this allow dead_code directive when function is called or future_snark is activated +#[allow(dead_code)] +impl SnarkProof { + /// Aggregate single signatures into a `SnarkProof`. + /// + /// This function: + /// 1. Computes the aggregate verification key and SNARK message. + /// 2. Pairs each signature with its registration entry, filtering out invalid ones. + /// 3. Verifies each SNARK signature and computes its winning lottery indices. + /// 4. Deduplicates indices across signers to select at least `k` unique winning indices. + pub fn aggregate_signatures( + clerk: &SnarkClerk, + signatures: &[SingleSignature], + message: &[u8], + ) -> StmResult> { + let avk: AggregateVerificationKeyForSnark = + clerk.compute_aggregate_verification_key_for_snark(); + let message_to_sign = build_snark_message(&avk.get_merkle_tree_commitment().root, message)?; + + let mut sig_reg_list: Vec = signatures + .iter() + .filter_map(|sig| { + let snark_sig = sig.snark_signature.clone()?; + let reg_entry = + clerk.get_snark_registration_entry(sig.signer_index).ok().flatten()?; + Some(SignatureRegistrationEntry::new(snark_sig, reg_entry)) + }) + .collect(); + + sig_reg_list.retain_mut(|entry| { + let reg = entry.get_registration_entry(); + if entry.get_signature().verify(®.0, message, &avk).is_ok() + && let Ok(indices) = compute_winning_lottery_indices( + clerk.parameters.m, + &message_to_sign, + &entry.get_signature().get_schnorr_signature(), + reg.1, + ) + { + entry.set_indices(&indices); + return true; + } + false + }); + + let _deduped_signatures = + SnarkClerk::select_valid_signatures_for_k_indices(&clerk.parameters, &sig_reg_list)?; + + // TODO: build WitnessEntry entries from deduped_signatures + Ok(SnarkProof { + instance: Instance::new(message_to_sign[0], message_to_sign[1]), + witness: Vec::new(), + }) + } +} + +#[cfg(test)] +mod tests { + use rand_chacha::ChaCha20Rng; + use rand_core::SeedableRng; + + use crate::{ + Initializer, KeyRegistration, MithrilMembershipDigest, Parameters, RegistrationEntry, + Signer, SingleSignature, proof_system::SnarkClerk, + }; + + use super::*; + + type D = MithrilMembershipDigest; + + // TODO: To get meaningful test results, the lottery target value should be computed + // using `compute_lottery_target_value` instead of the current `p-1` default in the + // `From<(RegistrationEntry, Stake)>` impl for `ClosedRegistrationEntry`. + // This requires revising the `From` implementation to support testing with the + // hardcoded computation method. + #[test] + fn deduplicate_indices() { + let mut rng = ChaCha20Rng::from_seed([0u8; 32]); + let parameters = Parameters { + m: 100, + k: 20, + phi_f: 0.50, + }; + + let message = [0u8; 32]; + + let mut initializers = Vec::new(); + let mut key_reg = KeyRegistration::initialize(); + + let stakes = [10, 8, 13, 10]; + + for &stake in &stakes { + let init = Initializer::new(parameters, stake, &mut rng); + initializers.push(init.clone()); + let entry = RegistrationEntry::try_from(init).unwrap(); + key_reg.register_by_entry(&entry).unwrap(); + } + + let closed_key_reg = key_reg.close_registration(); + + let mut signatures: Vec = Vec::new(); + for init in initializers { + let signer: Signer = init.clone().try_create_signer(&closed_key_reg).unwrap(); + let signature = signer.create_single_signature(&message).unwrap(); + signatures.push(signature); + } + + let clerk = + SnarkClerk::new_clerk_from_closed_key_registration(¶meters, &closed_key_reg); + + let _proof: SnarkProof = + SnarkProof::aggregate_signatures(&clerk, &signatures, &message).unwrap(); + } +} diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/signature_registration_entry.rs b/mithril-stm/src/proof_system/halo2_snark/witness/signature_registration_entry.rs new file mode 100644 index 00000000000..0ab89eb18cb --- /dev/null +++ b/mithril-stm/src/proof_system/halo2_snark/witness/signature_registration_entry.rs @@ -0,0 +1,45 @@ +use crate::{LotteryIndex, RegistrationEntryForSnark, proof_system::SingleSignatureForSnark}; + +/// Pairs a SNARK single signature with its corresponding registration entry. +/// +/// Used during witness preparation to associate each verified signature +/// with the signer's registration data (verification key and lottery target value). +// TODO: remove this allow dead_code directive when function is called or future_snark is activated +#[allow(dead_code)] +#[derive(Clone, Hash, PartialEq, Eq)] +pub(crate) struct SignatureRegistrationEntry { + /// The SNARK single signature + signature: SingleSignatureForSnark, + /// The signer's registration entry for SNARK + registration_entry: RegistrationEntryForSnark, +} + +// TODO: remove this allow dead_code directive when function is called or future_snark is activated +#[allow(dead_code)] +impl SignatureRegistrationEntry { + /// Create a new `SignatureRegistrationEntry` from a signature and its registration entry. + pub(crate) fn new( + signature: SingleSignatureForSnark, + registration_entry: RegistrationEntryForSnark, + ) -> Self { + Self { + signature, + registration_entry, + } + } + + /// Return a reference to the SNARK single signature. + pub(crate) fn get_signature(&self) -> &SingleSignatureForSnark { + &self.signature + } + + /// Set the winning lottery indices on the inner signature. + pub(crate) fn set_indices(&mut self, indices: &[LotteryIndex]) { + self.signature.set_indices(indices); + } + + /// Return a reference to the signer's registration entry. + pub(crate) fn get_registration_entry(&self) -> &RegistrationEntryForSnark { + &self.registration_entry + } +} diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/witness_entry.rs b/mithril-stm/src/proof_system/halo2_snark/witness/witness_entry.rs new file mode 100644 index 00000000000..45d8e3ae8e9 --- /dev/null +++ b/mithril-stm/src/proof_system/halo2_snark/witness/witness_entry.rs @@ -0,0 +1,42 @@ +use crate::{ + LotteryIndex, MembershipDigest, UniqueSchnorrSignature, + membership_commitment::{MerklePath, MerkleTreeSnarkLeaf}, +}; + +/// Per-winning-lottery-index witness data for the SNARK proof. +/// +/// Each `WitnessEntry` corresponds to a single winning lottery index and contains +/// the signature along with the signer's Merkle tree leaf and path for membership +/// proof inside the SNARK circuit. +// TODO: add conversion function(s) to comply with the expected circuit input format +// TODO: remove this allow dead_code directive when function is called or future_snark is activated +#[allow(dead_code)] +pub(crate) struct WitnessEntry { + /// The signer's registration entry as a Merkle tree leaf + merkle_tree_leaf: MerkleTreeSnarkLeaf, + /// The Merkle path from the leaf to the root, + merkle_path: MerklePath, + /// The Schnorr signature corresponding to the winning lottery index + unique_schnorr_signature: UniqueSchnorrSignature, + /// The winning lottery index + lottery_index: LotteryIndex, +} + +// TODO: remove this allow dead_code directive when function is called or future_snark is activated +#[allow(dead_code)] +impl WitnessEntry { + /// Create a new `WitnessEntry` from its components. + pub(crate) fn new( + merkle_tree_leaf: MerkleTreeSnarkLeaf, + merkle_path: MerklePath, + unique_schnorr_signature: UniqueSchnorrSignature, + lottery_index: LotteryIndex, + ) -> Self { + Self { + merkle_tree_leaf, + merkle_path, + unique_schnorr_signature, + lottery_index, + } + } +} diff --git a/mithril-stm/src/proof_system/mod.rs b/mithril-stm/src/proof_system/mod.rs index 3d9d8170d1a..e1d7f52f9b5 100644 --- a/mithril-stm/src/proof_system/mod.rs +++ b/mithril-stm/src/proof_system/mod.rs @@ -27,5 +27,5 @@ pub(crate) use concatenation::{ConcatenationProofSigner, SingleSignatureForConca #[cfg(feature = "future_snark")] pub(crate) use halo2_snark::{ - AggregateVerificationKeyForSnark, SingleSignatureForSnark, SnarkProofSigner, + AggregateVerificationKeyForSnark, SingleSignatureForSnark, SnarkClerk, SnarkProofSigner, }; diff --git a/mithril-stm/src/protocol/aggregate_signature/clerk.rs b/mithril-stm/src/protocol/aggregate_signature/clerk.rs index 0f7f3435a2c..df001ee14fc 100644 --- a/mithril-stm/src/protocol/aggregate_signature/clerk.rs +++ b/mithril-stm/src/protocol/aggregate_signature/clerk.rs @@ -10,6 +10,9 @@ use crate::{ proof_system::{ConcatenationClerk, ConcatenationProof}, }; +#[cfg(feature = "future_snark")] +use crate::{LotteryTargetValue, VerificationKeyForSnark, proof_system::SnarkClerk}; + use super::{AggregateSignature, AggregateSignatureType}; #[cfg(feature = "future_snark")] @@ -19,6 +22,8 @@ use super::AggregationError; #[derive(Debug, Clone)] pub struct Clerk { concatenation_proof_clerk: ConcatenationClerk, + #[cfg(feature = "future_snark")] + snark_proof_clerk: Option, phantom_data: PhantomData, } @@ -27,6 +32,8 @@ impl Clerk { pub fn new_clerk_from_signer(signer: &Signer) -> Self { Self { concatenation_proof_clerk: ConcatenationClerk::new_clerk_from_signer(signer), + #[cfg(feature = "future_snark")] + snark_proof_clerk: Some(SnarkClerk::new_clerk_from_signer(signer)), phantom_data: PhantomData, } } @@ -40,6 +47,10 @@ impl Clerk { concatenation_proof_clerk: ConcatenationClerk::new_clerk_from_closed_key_registration( parameters, closed_reg, ), + #[cfg(feature = "future_snark")] + snark_proof_clerk: Some(SnarkClerk::new_clerk_from_closed_key_registration( + parameters, closed_reg, + )), phantom_data: PhantomData, } } @@ -72,6 +83,12 @@ impl Clerk { &self.concatenation_proof_clerk } + /// Get the SNARK clerk. + #[cfg(feature = "future_snark")] + pub fn get_snark_clerk(&self) -> Option<&SnarkClerk> { + self.snark_proof_clerk.as_ref() + } + /// Compute the aggregate verification key. /// It computes only the concatenation aggregate verification key for now. // TODO: Replace None with the actual SNARK verification key when implementing @@ -81,7 +98,9 @@ impl Clerk { self.concatenation_proof_clerk .compute_aggregate_verification_key_for_concatenation(), #[cfg(feature = "future_snark")] - None, + self.snark_proof_clerk + .as_ref() + .map(|clerk| clerk.compute_aggregate_verification_key_for_snark()), ) } @@ -100,6 +119,24 @@ impl Clerk { )) } + /// Get the SNARK registered party for a given index. + #[cfg(feature = "future_snark")] + pub fn get_snark_registered_party_for_index( + &self, + party_index: &LotteryIndex, + ) -> StmResult> { + if let Some(snark_clerk) = self.get_snark_clerk() { + let entry = snark_clerk + .closed_key_registration + .get_registration_entry_for_index(party_index)?; + Ok(entry + .get_verification_key_for_snark() + .zip(entry.get_lottery_target_value())) + } else { + Ok(None) + } + } + #[cfg(test)] pub fn update_k(&mut self, k: u64) { self.concatenation_proof_clerk.update_k(k); diff --git a/mithril-stm/src/protocol/aggregate_signature/mod.rs b/mithril-stm/src/protocol/aggregate_signature/mod.rs index b0f6a7e367d..d0953c90f91 100644 --- a/mithril-stm/src/protocol/aggregate_signature/mod.rs +++ b/mithril-stm/src/protocol/aggregate_signature/mod.rs @@ -47,7 +47,7 @@ mod tests { .into_iter() .map(|stake| { let p = Initializer::new(params, stake, &mut rng); - let entry: RegistrationEntry = p.clone().into(); + let entry: RegistrationEntry = p.clone().try_into().unwrap(); kr.register_by_entry(&entry).unwrap(); p }) diff --git a/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs b/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs index ad41c8b2a8b..912057e8c08 100644 --- a/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs +++ b/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs @@ -163,7 +163,8 @@ impl Serialize for ClosedRegistrationEntry { /// Converts the registration entry into a closed registration entry for given total stake. /// This is where we will compute the lottery target value in the future. /// `LotteryTargetValue` is set to (modulus - 1) for now. -/// TODO: Compute the lottery target value based on the total stake and the entry's stake. +/// TODO: Compute the lottery target value based on the total stake and the entry's stake +/// using `compute_lottery_target_value(entry.get_stake(), total_stake)`. impl From<(RegistrationEntry, Stake)> for ClosedRegistrationEntry { fn from(entry_total_stake: (RegistrationEntry, Stake)) -> Self { let (entry, _total_stake) = entry_total_stake; diff --git a/mithril-stm/src/protocol/key_registration/register.rs b/mithril-stm/src/protocol/key_registration/register.rs index 5396f4c8ec4..6f25d14a007 100644 --- a/mithril-stm/src/protocol/key_registration/register.rs +++ b/mithril-stm/src/protocol/key_registration/register.rs @@ -256,7 +256,9 @@ mod tests { let mut key_reg = KeyRegistration::initialize(); for stake in 0..number_of_parties { let initializer = Initializer::new(params, stake, &mut rng); - key_reg.register_by_entry(&initializer.clone().into()).unwrap(); + key_reg + .register_by_entry(&initializer.clone().try_into().unwrap()) + .unwrap(); } let closed_key_reg: ClosedKeyRegistration = key_reg.close_registration(); @@ -305,7 +307,9 @@ mod tests { let mut key_reg = KeyRegistration::initialize(); for stake in 0..number_of_parties { let initializer = Initializer::new(params, stake, &mut rng); - key_reg.register_by_entry(&initializer.clone().into()).unwrap(); + key_reg + .register_by_entry(&initializer.clone().try_into().unwrap()) + .unwrap(); } let closed_key_reg: ClosedKeyRegistration = key_reg.close_registration(); diff --git a/mithril-stm/src/protocol/key_registration/registration_entry.rs b/mithril-stm/src/protocol/key_registration/registration_entry.rs index f4a065d96bd..7eb3f1dac1f 100644 --- a/mithril-stm/src/protocol/key_registration/registration_entry.rs +++ b/mithril-stm/src/protocol/key_registration/registration_entry.rs @@ -83,10 +83,12 @@ impl From for RegistrationEntry { } } -impl From for RegistrationEntry { - fn from(initializer: Initializer) -> Self { - Self( - initializer.bls_verification_key_proof_of_possession.vk, +impl TryFrom for RegistrationEntry { + type Error = anyhow::Error; + + fn try_from(initializer: Initializer) -> StmResult { + Self::new( + initializer.bls_verification_key_proof_of_possession, initializer.stake, #[cfg(feature = "future_snark")] initializer.schnorr_verification_key, diff --git a/mithril-stm/src/protocol/single_signature/signature.rs b/mithril-stm/src/protocol/single_signature/signature.rs index 819a6f47192..b0f37f5e825 100644 --- a/mithril-stm/src/protocol/single_signature/signature.rs +++ b/mithril-stm/src/protocol/single_signature/signature.rs @@ -247,107 +247,11 @@ mod tests { }; use crate::{ - AggregateVerificationKey, BlsSignatureError, Clerk, KeyRegistration, - MithrilMembershipDigest, Parameters, RegistrationEntry, Signer, SingleSignature, + KeyRegistration, MithrilMembershipDigest, Parameters, RegistrationEntry, SingleSignature, VerificationKeyProofOfPossessionForConcatenation, proof_system::ConcatenationProofSigner, signature_scheme::BlsSigningKey, }; - use super::SignatureError; - - type D = MithrilMembershipDigest; - - const TEST_MESSAGE: [u8; 16] = [42u8; 16]; - - fn test_parameters() -> Parameters { - Parameters { - m: 10, - k: 5, - phi_f: 0.8, - } - } - - struct SingleSignatureTestContext { - signer_1: Signer, - vk_1: VerificationKeyProofOfPossessionForConcatenation, - vk_2: VerificationKeyProofOfPossessionForConcatenation, - avk: AggregateVerificationKey, - } - - fn build_single_signature_context( - number_of_signers: usize, - rng_seed: [u8; 32], - ) -> SingleSignatureTestContext { - assert!( - number_of_signers >= 2, - "at least 2 signers are required for these tests" - ); - - let mut rng = ChaCha20Rng::from_seed(rng_seed); - let params = test_parameters(); - - let mut signing_keys = Vec::with_capacity(number_of_signers); - let mut verification_keys = Vec::with_capacity(number_of_signers); - for _ in 0..number_of_signers { - let signing_key = BlsSigningKey::generate(&mut rng); - let verification_key = - VerificationKeyProofOfPossessionForConcatenation::from(&signing_key); - signing_keys.push(signing_key); - verification_keys.push(verification_key); - } - - let mut registration = KeyRegistration::initialize(); - for verification_key in &verification_keys { - let entry = RegistrationEntry::new( - *verification_key, - 1, - #[cfg(feature = "future_snark")] - None, - ) - .unwrap(); - registration.register_by_entry(&entry).unwrap(); - } - - let closed_key_registration = registration.close_registration(); - let mut signing_keys = signing_keys.into_iter(); - let sk_1 = signing_keys.next().expect("at least one signer exists"); - let mut verification_keys = verification_keys.into_iter(); - let vk_1 = verification_keys.next().expect( - "internal test setup invariant violated: missing first verification key (vk_1)", - ); - let vk_2 = verification_keys.next().expect( - "internal test setup invariant violated: missing second verification key (vk_2)", - ); - let signer_1: Signer = Signer::new( - 1, - ConcatenationProofSigner::new( - 1, - 2, - params, - sk_1, - vk_1.vk, - closed_key_registration - .to_merkle_tree() - .to_merkle_tree_batch_commitment(), - ), - closed_key_registration, - params, - 1, - #[cfg(feature = "future_snark")] - None, - ); - - let clerk = Clerk::new_clerk_from_signer(&signer_1); - let avk = clerk.compute_aggregate_verification_key(); - - SingleSignatureTestContext { - signer_1, - vk_1, - vk_2, - avk, - } - } - mod golden { use super::*; @@ -640,123 +544,196 @@ mod tests { } } - #[test] - fn verify_fails_with_wrong_verification_key() { - let ctx = build_single_signature_context(2, [0u8; 32]); - let signature = ctx - .signer_1 - .create_single_signature(&TEST_MESSAGE) - .expect("signature should be created"); - - let params = test_parameters(); - let error = signature - .verify( - ¶ms, - &ctx.vk_2.vk, - &1, - &ctx.avk, - &TEST_MESSAGE, - #[cfg(feature = "future_snark")] - None, - ) - .expect_err("Verification should fail with wrong verification key"); - assert!( - matches!( - error.downcast_ref::(), - Some(BlsSignatureError::SignatureInvalid(_)) - ), - "Unexpected error variant: {error:?}" - ); - } + #[cfg(not(feature = "future_snark"))] + mod verify_concatenation_only { + use super::*; + use crate::{AggregateVerificationKey, BlsSignatureError, Clerk, SignatureError, Signer}; - #[test] - fn verify_fails_with_out_of_bounds_index() { - let ctx = build_single_signature_context(2, [0u8; 32]); - let mut signature = ctx - .signer_1 - .create_single_signature(&TEST_MESSAGE) - .expect("signature should be created"); - - let params = test_parameters(); - signature.set_concatenation_signature_indices(&[params.m + 1]); - - let error = signature - .verify( - ¶ms, - &ctx.vk_1.vk, - &1, - &ctx.avk, - &TEST_MESSAGE, - #[cfg(feature = "future_snark")] - None, - ) - .expect_err("Verification should fail with invalid index"); - assert!( - matches!( - error.downcast_ref::(), - Some(SignatureError::IndexBoundFailed(_, _)) - ), - "Unexpected error variant: {error:?}" - ); - } + type D = MithrilMembershipDigest; - #[test] - fn verify_fails_with_wrong_message() { - let ctx = build_single_signature_context(2, [0u8; 32]); - let signature = ctx - .signer_1 - .create_single_signature(&TEST_MESSAGE) - .expect("signature should be created"); - let wrong_message = [43u8; 16]; - - let params = test_parameters(); - let error = signature - .verify( - ¶ms, - &ctx.vk_1.vk, - &1, - &ctx.avk, - &wrong_message, - #[cfg(feature = "future_snark")] - None, - ) - .expect_err("Verification should fail with wrong message"); - assert!( - matches!( - error.downcast_ref::(), - Some(BlsSignatureError::SignatureInvalid(_)) - ), - "Unexpected error variant: {error:?}" - ); - } + const TEST_MESSAGE: [u8; 16] = [42u8; 16]; - #[test] - fn verify_fails_with_different_registration_avk() { - let signing_ctx = build_single_signature_context(2, [0u8; 32]); - let different_registration_ctx = build_single_signature_context(3, [0u8; 32]); - let signature = signing_ctx - .signer_1 - .create_single_signature(&TEST_MESSAGE) - .expect("signature should be created"); - - let params = test_parameters(); - let error = signature - .verify( - ¶ms, - &signing_ctx.vk_1.vk, - &1, - &different_registration_ctx.avk, - &TEST_MESSAGE, + fn test_parameters() -> Parameters { + Parameters { + m: 10, + k: 5, + phi_f: 0.8, + } + } + + struct SingleSignatureTestContext { + signer_1: Signer, + vk_1: VerificationKeyProofOfPossessionForConcatenation, + vk_2: VerificationKeyProofOfPossessionForConcatenation, + avk: AggregateVerificationKey, + } + + fn build_single_signature_context( + number_of_signers: usize, + rng_seed: [u8; 32], + ) -> SingleSignatureTestContext { + assert!( + number_of_signers >= 2, + "at least 2 signers are required for these tests" + ); + + let mut rng = ChaCha20Rng::from_seed(rng_seed); + let params = test_parameters(); + + let mut signing_keys = Vec::with_capacity(number_of_signers); + let mut verification_keys = Vec::with_capacity(number_of_signers); + for _ in 0..number_of_signers { + let signing_key = BlsSigningKey::generate(&mut rng); + let verification_key = + VerificationKeyProofOfPossessionForConcatenation::from(&signing_key); + signing_keys.push(signing_key); + verification_keys.push(verification_key); + } + + let mut registration = KeyRegistration::initialize(); + for verification_key in &verification_keys { + let entry = RegistrationEntry::new( + *verification_key, + 1, + #[cfg(feature = "future_snark")] + None, + ) + .unwrap(); + registration.register_by_entry(&entry).unwrap(); + } + + let closed_key_registration = registration.close_registration(); + let mut signing_keys = signing_keys.into_iter(); + let sk_1 = signing_keys.next().expect("at least one signer exists"); + let mut verification_keys = verification_keys.into_iter(); + let vk_1 = verification_keys.next().expect( + "internal test setup invariant violated: missing first verification key (vk_1)", + ); + let vk_2 = verification_keys.next().expect( + "internal test setup invariant violated: missing second verification key (vk_2)", + ); + let signer_1: Signer = Signer::new( + 1, + ConcatenationProofSigner::new( + 1, + 2, + params, + sk_1, + vk_1.vk, + closed_key_registration + .to_merkle_tree() + .to_merkle_tree_batch_commitment(), + ), + closed_key_registration, + params, + 1, #[cfg(feature = "future_snark")] None, - ) - .expect_err("Verification should fail with a different registration AVK"); - assert!( - matches!( - error.downcast_ref::(), - Some(BlsSignatureError::SignatureInvalid(_)) - ), - "Unexpected error variant: {error:?}" - ); + ); + + let clerk = Clerk::new_clerk_from_signer(&signer_1); + let avk = clerk.compute_aggregate_verification_key(); + + SingleSignatureTestContext { + signer_1, + vk_1, + vk_2, + avk, + } + } + + #[test] + fn verify_fails_with_wrong_verification_key() { + let ctx = build_single_signature_context(2, [0u8; 32]); + let signature = ctx + .signer_1 + .create_single_signature(&TEST_MESSAGE) + .expect("signature should be created"); + + let params = test_parameters(); + let error = signature + .verify(¶ms, &ctx.vk_2.vk, &1, &ctx.avk, &TEST_MESSAGE) + .expect_err("Verification should fail with wrong verification key"); + assert!( + matches!( + error.downcast_ref::(), + Some(BlsSignatureError::SignatureInvalid(_)) + ), + "Unexpected error variant: {error:?}" + ); + } + + #[test] + fn verify_fails_with_out_of_bounds_index() { + let ctx = build_single_signature_context(2, [0u8; 32]); + let mut signature = ctx + .signer_1 + .create_single_signature(&TEST_MESSAGE) + .expect("signature should be created"); + + let params = test_parameters(); + signature.set_concatenation_signature_indices(&[params.m + 1]); + + let error = signature + .verify(¶ms, &ctx.vk_1.vk, &1, &ctx.avk, &TEST_MESSAGE) + .expect_err("Verification should fail with invalid index"); + assert!( + matches!( + error.downcast_ref::(), + Some(SignatureError::IndexBoundFailed(_, _)) + ), + "Unexpected error variant: {error:?}" + ); + } + + #[test] + fn verify_fails_with_wrong_message() { + let ctx = build_single_signature_context(2, [0u8; 32]); + let signature = ctx + .signer_1 + .create_single_signature(&TEST_MESSAGE) + .expect("signature should be created"); + let wrong_message = [43u8; 16]; + + let params = test_parameters(); + let error = signature + .verify(¶ms, &ctx.vk_1.vk, &1, &ctx.avk, &wrong_message) + .expect_err("Verification should fail with wrong message"); + assert!( + matches!( + error.downcast_ref::(), + Some(BlsSignatureError::SignatureInvalid(_)) + ), + "Unexpected error variant: {error:?}" + ); + } + + #[test] + fn verify_fails_with_different_registration_avk() { + let signing_ctx = build_single_signature_context(2, [0u8; 32]); + let different_registration_ctx = build_single_signature_context(3, [0u8; 32]); + let signature = signing_ctx + .signer_1 + .create_single_signature(&TEST_MESSAGE) + .expect("signature should be created"); + + let params = test_parameters(); + let error = signature + .verify( + ¶ms, + &signing_ctx.vk_1.vk, + &1, + &different_registration_ctx.avk, + &TEST_MESSAGE, + ) + .expect_err("Verification should fail with a different registration AVK"); + assert!( + matches!( + error.downcast_ref::(), + Some(BlsSignatureError::SignatureInvalid(_)) + ), + "Unexpected error variant: {error:?}" + ); + } } } diff --git a/mithril-stm/src/signature_scheme/unique_schnorr_signature/jubjub/curve_points.rs b/mithril-stm/src/signature_scheme/unique_schnorr_signature/jubjub/curve_points.rs index 2f40489bd12..c99bca44ce7 100644 --- a/mithril-stm/src/signature_scheme/unique_schnorr_signature/jubjub/curve_points.rs +++ b/mithril-stm/src/signature_scheme/unique_schnorr_signature/jubjub/curve_points.rs @@ -9,6 +9,8 @@ use midnight_circuits::{ use midnight_curves::{ EDWARDS_D, Fq as JubjubBase, JubjubAffine as JubjubAffinePoint, JubjubExtended, JubjubSubgroup, }; +use std::cmp::Ordering; +use std::hash::{Hash, Hasher}; use std::ops::{Add, Mul}; use crate::{StmResult, signature_scheme::UniqueSchnorrSignatureError}; @@ -127,6 +129,24 @@ impl From for ProjectivePoint { } } +impl Hash for ProjectivePoint { + fn hash(&self, state: &mut H) { + self.to_bytes().hash(state); + } +} + +impl PartialOrd for ProjectivePoint { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ProjectivePoint { + fn cmp(&self, other: &Self) -> Ordering { + self.to_bytes().cmp(&other.to_bytes()) + } +} + /// Represents a point of prime order in projective coordinates on the Jubjub curve #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] pub(crate) struct PrimeOrderProjectivePoint(pub(crate) JubjubSubgroup); diff --git a/mithril-stm/src/signature_scheme/unique_schnorr_signature/jubjub/field_elements.rs b/mithril-stm/src/signature_scheme/unique_schnorr_signature/jubjub/field_elements.rs index 7e1c407de9f..5967dcc3b46 100644 --- a/mithril-stm/src/signature_scheme/unique_schnorr_signature/jubjub/field_elements.rs +++ b/mithril-stm/src/signature_scheme/unique_schnorr_signature/jubjub/field_elements.rs @@ -3,6 +3,7 @@ use ff::Field; use midnight_curves::{Fq as JubjubBase, Fr as JubjubScalar}; use rand_core::{CryptoRng, RngCore}; use sha2::{Digest, Sha256}; +use std::hash::{Hash, Hasher}; use std::ops::{Add, Mul, Neg, Sub}; use crate::StmError; @@ -131,7 +132,7 @@ impl Mul for &BaseFieldElement { } /// Represents an element in the scalar field of the Jubjub curve -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub(crate) struct ScalarFieldElement(pub(crate) JubjubScalar); impl ScalarFieldElement { @@ -231,6 +232,12 @@ impl Sub for ScalarFieldElement { } } +impl Hash for ScalarFieldElement { + fn hash(&self, state: &mut H) { + self.to_bytes().hash(state); + } +} + #[cfg(test)] mod tests { use rand_chacha::ChaCha20Rng; diff --git a/mithril-stm/src/signature_scheme/unique_schnorr_signature/signature.rs b/mithril-stm/src/signature_scheme/unique_schnorr_signature/signature.rs index 1ba472edd30..3e7bb38a6ff 100644 --- a/mithril-stm/src/signature_scheme/unique_schnorr_signature/signature.rs +++ b/mithril-stm/src/signature_scheme/unique_schnorr_signature/signature.rs @@ -14,7 +14,7 @@ use super::{ /// This signature includes a value `commitment_point` which depends only on /// the message and the signing key. /// This value is used in the lottery process to determine the correct indices. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash)] pub struct UniqueSchnorrSignature { /// Deterministic value depending on the message and signing key pub(crate) commitment_point: ProjectivePoint, diff --git a/mithril-stm/tests/test_extensions/protocol_phase.rs b/mithril-stm/tests/test_extensions/protocol_phase.rs index 1545bfb7078..dcf6b39e940 100644 --- a/mithril-stm/tests/test_extensions/protocol_phase.rs +++ b/mithril-stm/tests/test_extensions/protocol_phase.rs @@ -40,7 +40,7 @@ pub fn initialization_phase( for stake in parties { let p = Initializer::new(params, stake, &mut rng); - key_reg.register_by_entry(&p.clone().into()).unwrap(); + key_reg.register_by_entry(&p.clone().try_into().unwrap()).unwrap(); reg_parties.push(( p.get_verification_key_proof_of_possession_for_concatenation().vk, stake,