From caf4dd7a60da4d24a7f1be4171f12bddcdf51374 Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Thu, 5 Mar 2026 23:11:20 +0300 Subject: [PATCH 01/11] feat(stm): snark clerk no dedup --- .../src/proof_system/halo2_snark/clerk.rs | 41 ++ .../src/proof_system/halo2_snark/mod.rs | 2 + mithril-stm/src/proof_system/mod.rs | 2 +- .../src/protocol/aggregate_signature/clerk.rs | 39 +- .../protocol/single_signature/signature.rs | 397 +++++++++--------- 5 files changed, 269 insertions(+), 212 deletions(-) create mode 100644 mithril-stm/src/proof_system/halo2_snark/clerk.rs 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..d3ffc1ec810 --- /dev/null +++ b/mithril-stm/src/proof_system/halo2_snark/clerk.rs @@ -0,0 +1,41 @@ +use crate::{ClosedKeyRegistration, MembershipDigest, Parameters, Signer}; + +use super::AggregateVerificationKeyForSnark; + +/// 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, +} + +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) + } +} diff --git a/mithril-stm/src/proof_system/halo2_snark/mod.rs b/mithril-stm/src/proof_system/halo2_snark/mod.rs index f6c5e345be7..acf0e4bacd8 100644 --- a/mithril-stm/src/proof_system/halo2_snark/mod.rs +++ b/mithril-stm/src/proof_system/halo2_snark/mod.rs @@ -1,10 +1,12 @@ mod aggregate_key; +mod clerk; mod eligibility; mod message; mod signer; mod single_signature; 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; 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/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:?}" + ); + } } } From bc889701fcebec0fe6938201f8469938206bcfe7 Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Thu, 5 Mar 2026 23:19:52 +0300 Subject: [PATCH 02/11] feat(stm): witness submodule - draft --- .../src/proof_system/halo2_snark/mod.rs | 1 + .../halo2_snark/witness/instance.rs | 15 +++++++++++ .../proof_system/halo2_snark/witness/mod.rs | 7 +++++ .../proof_system/halo2_snark/witness/proof.rs | 16 +++++++++++ .../halo2_snark/witness/signer_witness.rs | 27 +++++++++++++++++++ 5 files changed, 66 insertions(+) create mode 100644 mithril-stm/src/proof_system/halo2_snark/witness/instance.rs create mode 100644 mithril-stm/src/proof_system/halo2_snark/witness/mod.rs create mode 100644 mithril-stm/src/proof_system/halo2_snark/witness/proof.rs create mode 100644 mithril-stm/src/proof_system/halo2_snark/witness/signer_witness.rs diff --git a/mithril-stm/src/proof_system/halo2_snark/mod.rs b/mithril-stm/src/proof_system/halo2_snark/mod.rs index acf0e4bacd8..2732b2ff0a5 100644 --- a/mithril-stm/src/proof_system/halo2_snark/mod.rs +++ b/mithril-stm/src/proof_system/halo2_snark/mod.rs @@ -4,6 +4,7 @@ mod eligibility; mod message; mod signer; mod single_signature; +mod witness; pub(crate) use aggregate_key::AggregateVerificationKeyForSnark; pub(crate) use clerk::SnarkClerk; 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..55cd800764c --- /dev/null +++ b/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs @@ -0,0 +1,15 @@ +use crate::BaseFieldElement; + +pub(crate) struct Instance { + merkle_tree_root: BaseFieldElement, + message: BaseFieldElement, +} + +impl Instance { + 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..3b5869d8412 --- /dev/null +++ b/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs @@ -0,0 +1,7 @@ +mod instance; +mod proof; +mod signer_witness; + +pub(super) use instance::Instance; +pub(crate) use proof::SnarkProof; +pub(super) use signer_witness::SignerWitness; 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..24bf167d934 --- /dev/null +++ b/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs @@ -0,0 +1,16 @@ +use crate::{MembershipDigest, SingleSignature, StmResult}; + +use super::{Instance, SignerWitness}; +pub struct SnarkProof { + instance: Instance, + witness: Vec>, +} + +impl SnarkProof { + pub fn aggregate_signatures( + signatures: &[SingleSignature], + msg: &[u8], + ) -> StmResult> { + todo!("Implement signature aggregation and proof generation logic here") + } +} diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/signer_witness.rs b/mithril-stm/src/proof_system/halo2_snark/witness/signer_witness.rs new file mode 100644 index 00000000000..ad28a933207 --- /dev/null +++ b/mithril-stm/src/proof_system/halo2_snark/witness/signer_witness.rs @@ -0,0 +1,27 @@ +use crate::{ + LotteryIndex, MembershipDigest, UniqueSchnorrSignature, + membership_commitment::{MerklePath, MerkleTreeSnarkLeaf}, +}; + +pub(crate) struct SignerWitness { + merkle_tree_leaf: MerkleTreeSnarkLeaf, + merkle_path: MerklePath, + unique_schnorr_signature: UniqueSchnorrSignature, + lottery_index: LotteryIndex, +} + +impl SignerWitness { + 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, + } + } +} From 6a30e9d12704f973bf432d4ef3171a06d7938985 Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Fri, 6 Mar 2026 21:25:35 +0300 Subject: [PATCH 03/11] fix(stm): from initializer for closed reg entry fixed as try from --- mithril-stm/benches/size_benches.rs | 2 +- mithril-stm/benches/stm.rs | 4 ++-- mithril-stm/src/proof_system/concatenation/clerk.rs | 2 +- mithril-stm/src/protocol/aggregate_signature/mod.rs | 2 +- mithril-stm/src/protocol/key_registration/register.rs | 8 ++++++-- .../protocol/key_registration/registration_entry.rs | 10 ++++++---- mithril-stm/tests/test_extensions/protocol_phase.rs | 2 +- 7 files changed, 18 insertions(+), 12 deletions(-) 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/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/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/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/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, From d84ce0e0f32b8b887abd40569812dd92d9c49a06 Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Fri, 6 Mar 2026 22:34:05 +0300 Subject: [PATCH 04/11] feat(stm): aggregate sigs filter and validate signatures --- .../merkle_tree/error.rs | 6 + .../membership_commitment/merkle_tree/tree.rs | 11 ++ .../src/proof_system/halo2_snark/clerk.rs | 16 +- .../halo2_snark/witness/instance.rs | 3 +- .../proof_system/halo2_snark/witness/mod.rs | 1 - .../proof_system/halo2_snark/witness/proof.rs | 178 +++++++++++++++++- .../halo2_snark/witness/signer_witness.rs | 2 + mithril-stm/src/protocol/error.rs | 5 + 8 files changed, 216 insertions(+), 6 deletions(-) diff --git a/mithril-stm/src/membership_commitment/merkle_tree/error.rs b/mithril-stm/src/membership_commitment/merkle_tree/error.rs index 451859d30bf..cda88de684d 100644 --- a/mithril-stm/src/membership_commitment/merkle_tree/error.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/error.rs @@ -15,4 +15,10 @@ 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")] + #[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..9443ddc5501 100644 --- a/mithril-stm/src/membership_commitment/merkle_tree/tree.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/tree.rs @@ -189,6 +189,17 @@ 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")] + #[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/halo2_snark/clerk.rs b/mithril-stm/src/proof_system/halo2_snark/clerk.rs index d3ffc1ec810..1b53842c7fe 100644 --- a/mithril-stm/src/proof_system/halo2_snark/clerk.rs +++ b/mithril-stm/src/proof_system/halo2_snark/clerk.rs @@ -1,4 +1,7 @@ -use crate::{ClosedKeyRegistration, MembershipDigest, Parameters, Signer}; +use crate::{ + ClosedKeyRegistration, LotteryIndex, MembershipDigest, Parameters, RegistrationEntryForSnark, + Signer, StmResult, +}; use super::AggregateVerificationKeyForSnark; @@ -38,4 +41,15 @@ impl SnarkClerk { ) -> 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()) + } } diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs b/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs index 55cd800764c..ba6260ae739 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs @@ -1,10 +1,11 @@ use crate::BaseFieldElement; +#[allow(dead_code)] pub(crate) struct Instance { merkle_tree_root: BaseFieldElement, message: BaseFieldElement, } - +#[allow(dead_code)] impl Instance { pub(crate) fn new(merkle_tree_root: BaseFieldElement, message: BaseFieldElement) -> Self { Self { diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs b/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs index 3b5869d8412..2dcf4640828 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs @@ -3,5 +3,4 @@ mod proof; mod signer_witness; pub(super) use instance::Instance; -pub(crate) use proof::SnarkProof; pub(super) use signer_witness::SignerWitness; diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs b/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs index 24bf167d934..6fca69e595e 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs @@ -1,16 +1,188 @@ -use crate::{MembershipDigest, SingleSignature, StmResult}; +use crate::{ + MembershipDigest, RegisterError, SingleSignature, StmResult, + proof_system::{ + AggregateVerificationKeyForSnark, SnarkClerk, + halo2_snark::{build_snark_message, compute_winning_lottery_indices}, + }, +}; use super::{Instance, SignerWitness}; + +#[allow(dead_code)] pub struct SnarkProof { instance: Instance, witness: Vec>, } +#[allow(dead_code)] impl SnarkProof { pub fn aggregate_signatures( + clerk: &SnarkClerk, signatures: &[SingleSignature], - msg: &[u8], + message: &[u8], ) -> StmResult> { - todo!("Implement signature aggregation and proof generation logic here") + let avk: AggregateVerificationKeyForSnark = + clerk.compute_aggregate_verification_key_for_snark(); + let message_to_sign = build_snark_message(&avk.get_merkle_tree_commitment().root, message)?; + + // // Print the signatures (bls sig and snark sig) before verification + // println!("Input signatures: {} total", signatures.len()); + // for (i, sig) in signatures.iter().enumerate() { + // println!( + // " [{}] signer_index: {}, has_snark_sig: {}", + // i, + // sig.signer_index, + // sig.snark_signature.is_some() + // ); + // } + + // Collect the snark signatures and their registration entries by filtering + // the snark signatures and mapping them to their corresponding registration entries + let mut snark_sig_reg_list: Vec<_> = signatures + .iter() + .filter_map(|sig| { + sig.snark_signature + .clone() + .map(|snark_sig| (sig.signer_index, snark_sig)) + }) + .map(|(signer_index, snark_sig)| { + let reg_entry = clerk + .get_snark_registration_entry(signer_index)? + .ok_or(RegisterError::MissingSnarkRegistrationEntry(signer_index))?; + Ok((snark_sig, reg_entry)) + }) + .collect::>()?; + + // println!("After collect: {} entries", snark_sig_reg_list.len()); + + // Verify each SNARK signature against its registration entry. + // If valid, compute the winning lottery indices and set them in the signature. + // Retain only the valid signatures and their corresponding registration entries in the list. + snark_sig_reg_list.retain_mut(|(snark_sig, reg_entry)| { + if snark_sig.verify(®_entry.0, message, &avk).is_ok() { + if let Ok(indices) = compute_winning_lottery_indices( + clerk.parameters.m, + &message_to_sign, + &snark_sig.get_schnorr_signature(), + reg_entry.1, + ) { + snark_sig.set_indices(&indices); + return true; + } + } + false + }); + + // // Print verified signatures with computed indices + // println!("After retain_mut: {} entries", snark_sig_reg_list.len()); + // for (i, (sig, reg)) in snark_sig_reg_list.iter().enumerate() { + // println!(" [{}] indices: {:?}", i, sig.get_indices(),); + // } + + // TODO: build Instance and SignerWitness entries from snark_sig_reg_list + 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::{ + BlsVerificationKeyProofOfPossession, Initializer, KeyRegistration, MithrilMembershipDigest, + Parameters, RegistrationEntry, Signer, SingleSignature, + proof_system::{ConcatenationProofSigner, SnarkClerk}, + signature_scheme::BlsSigningKey, + }; + + use super::SnarkProof; + + type D = MithrilMembershipDigest; + + #[test] + fn aggregate_signatures_with_mixed_entries() { + let mut rng = ChaCha20Rng::from_seed([0u8; 32]); + let params = Parameters { + m: 100, + k: 1, + phi_f: 0.95, + }; + + let message = [0u8; 32]; + + let mut initializers = Vec::new(); + let mut entries = Vec::new(); + + let mut key_reg = KeyRegistration::initialize(); + + let stakes = [1, 5, 10, 20]; + + // 4 full initializers + for &stake in &stakes { + let init = Initializer::new(params, stake, &mut rng); + initializers.push(init.clone()); + let entry = RegistrationEntry::try_from(init).unwrap(); + entries.push(entry); + } + + // Initializer without snark + let sk = BlsSigningKey::generate(&mut rng); + let vk_pop = BlsVerificationKeyProofOfPossession::from(&sk); + let init = Initializer { + stake: 40, + parameters: params, + bls_signing_key: sk.clone(), + bls_verification_key_proof_of_possession: vk_pop.clone(), + #[cfg(feature = "future_snark")] + schnorr_signing_key: None, + #[cfg(feature = "future_snark")] + schnorr_verification_key: None, + }; + initializers.push(init.clone()); + let entry = RegistrationEntry::try_from(init).unwrap(); + entries.push(entry); + + // Register all entries + for entry in &entries { + key_reg.register_by_entry(entry).unwrap(); + } + + // Close the registration + let closed_key_reg = key_reg.close_registration(); + + // Create signatures for the first 4 initializers (with snark) and the last initializer (without snark) + let mut signatures: Vec = Vec::new(); + for i in 0..4 { + let signer: Signer = + initializers[i].clone().try_create_signer(&closed_key_reg).unwrap(); + let signature = signer.create_single_signature(&message).unwrap(); + signatures.push(signature); + } + let signer: Signer = Signer::new( + 4, + ConcatenationProofSigner::new( + 40, + closed_key_reg.total_stake, + params, + sk, + vk_pop.vk, + closed_key_reg.to_merkle_tree().to_merkle_tree_batch_commitment(), + ), + closed_key_reg.clone(), + params, + 40, + #[cfg(feature = "future_snark")] + None, + ); + let signature = signer.create_single_signature(&message).unwrap(); + signatures.push(signature); + + let clerk = SnarkClerk::new_clerk_from_closed_key_registration(¶ms, &closed_key_reg); + let _snark_proof: SnarkProof = + SnarkProof::aggregate_signatures(&clerk, &signatures, &message).unwrap(); } } diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/signer_witness.rs b/mithril-stm/src/proof_system/halo2_snark/witness/signer_witness.rs index ad28a933207..5824aca5058 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/signer_witness.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/signer_witness.rs @@ -3,6 +3,7 @@ use crate::{ membership_commitment::{MerklePath, MerkleTreeSnarkLeaf}, }; +#[allow(dead_code)] pub(crate) struct SignerWitness { merkle_tree_leaf: MerkleTreeSnarkLeaf, merkle_path: MerklePath, @@ -10,6 +11,7 @@ pub(crate) struct SignerWitness { lottery_index: LotteryIndex, } +#[allow(dead_code)] impl SignerWitness { pub(crate) fn new( merkle_tree_leaf: MerkleTreeSnarkLeaf, diff --git a/mithril-stm/src/protocol/error.rs b/mithril-stm/src/protocol/error.rs index 60dd9ed9241..09f1bd0d2b6 100644 --- a/mithril-stm/src/protocol/error.rs +++ b/mithril-stm/src/protocol/error.rs @@ -43,4 +43,9 @@ pub enum RegisterError { #[cfg(feature = "future_snark")] #[error("Unable to create SNARK proof signer.")] SnarkProofSignerCreation, + + /// Missing SNARK registration entry for the given signer index. + #[cfg(feature = "future_snark")] + #[error("Missing SNARK registration entry for signer index {0}.")] + MissingSnarkRegistrationEntry(u64), } From ec555427895a6ba011ed4ef7867c3e356515bbbe Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Fri, 6 Mar 2026 23:14:53 +0300 Subject: [PATCH 05/11] refactor(stm): eligibility computing lottery for hardcoded phi_f --- .../proof_system/halo2_snark/eligibility.rs | 21 +++++++++++++++++++ .../src/proof_system/halo2_snark/mod.rs | 2 +- .../proof_system/halo2_snark/witness/proof.rs | 2 +- mithril-stm/src/proof_system/mod.rs | 1 + .../closed_registration_entry.rs | 9 ++++---- 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/mithril-stm/src/proof_system/halo2_snark/eligibility.rs b/mithril-stm/src/proof_system/halo2_snark/eligibility.rs index 8ebb6d99ec7..586ec15717e 100644 --- a/mithril-stm/src/proof_system/halo2_snark/eligibility.rs +++ b/mithril-stm/src/proof_system/halo2_snark/eligibility.rs @@ -16,6 +16,27 @@ 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. + #[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.2; + 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 2732b2ff0a5..3b6c99bfdbf 100644 --- a/mithril-stm/src/proof_system/halo2_snark/mod.rs +++ b/mithril-stm/src/proof_system/halo2_snark/mod.rs @@ -8,7 +8,7 @@ mod witness; pub(crate) use aggregate_key::AggregateVerificationKeyForSnark; pub(crate) use clerk::SnarkClerk; -pub(crate) use eligibility::compute_winning_lottery_indices; +pub(crate) use eligibility::{compute_lottery_target_value, compute_winning_lottery_indices}; pub(crate) use message::build_snark_message; pub(crate) use signer::SnarkProofSigner; pub(crate) use single_signature::SingleSignatureForSnark; diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs b/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs index 6fca69e595e..25838e021e8 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs @@ -109,7 +109,7 @@ mod tests { let params = Parameters { m: 100, k: 1, - phi_f: 0.95, + phi_f: 0.2, }; let message = [0u8; 32]; diff --git a/mithril-stm/src/proof_system/mod.rs b/mithril-stm/src/proof_system/mod.rs index e1d7f52f9b5..ce11ae2e2ea 100644 --- a/mithril-stm/src/proof_system/mod.rs +++ b/mithril-stm/src/proof_system/mod.rs @@ -28,4 +28,5 @@ pub(crate) use concatenation::{ConcatenationProofSigner, SingleSignatureForConca #[cfg(feature = "future_snark")] pub(crate) use halo2_snark::{ AggregateVerificationKeyForSnark, SingleSignatureForSnark, SnarkClerk, SnarkProofSigner, + compute_lottery_target_value, }; 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..05e950595a2 100644 --- a/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs +++ b/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs @@ -5,7 +5,9 @@ use std::hash::Hash; use crate::{RegisterError, RegistrationEntry, Stake, StmResult, VerificationKeyForConcatenation}; #[cfg(feature = "future_snark")] -use crate::{LotteryTargetValue, VerificationKeyForSnark}; +use crate::{ + LotteryTargetValue, VerificationKeyForSnark, proof_system::compute_lottery_target_value, +}; /// Represents a registration entry of a closed key registration. #[derive(PartialEq, Eq, Clone, Debug, Copy, Deserialize)] @@ -166,12 +168,11 @@ impl Serialize for ClosedRegistrationEntry { /// TODO: Compute the lottery target value based on the total stake and the entry's stake. impl From<(RegistrationEntry, Stake)> for ClosedRegistrationEntry { fn from(entry_total_stake: (RegistrationEntry, Stake)) -> Self { - let (entry, _total_stake) = entry_total_stake; + let (entry, total_stake) = entry_total_stake; #[cfg(feature = "future_snark")] let (schnorr_verification_key, target_value) = { let vk = entry.get_verification_key_for_snark(); - let target = - vk.map(|_| &LotteryTargetValue::default() - &LotteryTargetValue::get_one()); + let target = vk.map(|_| compute_lottery_target_value(entry.get_stake(), total_stake)); (vk, target) }; From 17867162c70a73a03842c269e4b3caf37540317f Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Fri, 6 Mar 2026 23:16:25 +0300 Subject: [PATCH 06/11] feat(stm): deduplicate for snark NO EDIT --- .../src/proof_system/halo2_snark/clerk.rs | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/mithril-stm/src/proof_system/halo2_snark/clerk.rs b/mithril-stm/src/proof_system/halo2_snark/clerk.rs index 1b53842c7fe..ae92f731309 100644 --- a/mithril-stm/src/proof_system/halo2_snark/clerk.rs +++ b/mithril-stm/src/proof_system/halo2_snark/clerk.rs @@ -52,4 +52,96 @@ impl SnarkClerk { .get_registration_entry_for_index(&signer_index)?; Ok(closed_registration_entry.into()) } + + pub fn select_valid_signatures_for_k_indices( + params: &Parameters, + msg: &[u8], + sigs: &[SingleSignatureWithRegisteredParty], + avk: &AggregateVerificationKeyForConcatenation, + ) -> StmResult> { + let mut sig_by_index: BTreeMap = + BTreeMap::new(); + let mut removal_idx_by_vk: HashMap<&SingleSignatureWithRegisteredParty, Vec> = + HashMap::new(); + + for sig_reg in sigs.iter() { + if sig_reg + .sig + .concatenation_signature + .verify( + params, + &sig_reg.reg_party.get_verification_key_for_concatenation(), + &sig_reg.reg_party.get_stake(), + avk, + msg, + ) + .is_err() + { + continue; + } + for index in sig_reg.sig.get_concatenation_signature_indices().iter() { + let mut insert_this_sig = false; + if let Some(&previous_sig) = sig_by_index.get(index) { + let sig_to_remove_index = if sig_reg.sig.get_concatenation_signature_sigma() + < previous_sig.sig.get_concatenation_signature_sigma() + { + insert_this_sig = true; + previous_sig + } else { + sig_reg + }; + + if let Some(indexes) = removal_idx_by_vk.get_mut(sig_to_remove_index) { + indexes.push(*index); + } else { + removal_idx_by_vk.insert(sig_to_remove_index, vec![*index]); + } + } else { + insert_this_sig = true; + } + + if insert_this_sig { + sig_by_index.insert(*index, sig_reg); + } + } + } + + let mut dedup_sigs: HashSet = HashSet::new(); + let mut count: u64 = 0; + + for (_, &sig_reg) in sig_by_index.iter() { + if dedup_sigs.contains(sig_reg) { + continue; + } + let mut deduped_sig = sig_reg.clone(); + if let Some(indexes) = removal_idx_by_vk.get(sig_reg) { + let indices = deduped_sig + .sig + .get_concatenation_signature_indices() + .into_iter() + .filter(|i| !indexes.contains(i)) + .collect::>(); + deduped_sig.sig.set_concatenation_signature_indices(&indices); + } + + let size: Result = + deduped_sig.sig.get_concatenation_signature_indices().len().try_into(); + if let Ok(size) = size { + if dedup_sigs.contains(&deduped_sig) { + panic!( + "Invariant violation: duplicate signature encountered in deduplication set, which should not be possible." + ); + } + dedup_sigs.insert(deduped_sig); + count += size; + + if count >= params.k { + return Ok(dedup_sigs.into_iter().collect()); + } + } + } + Err(anyhow!(AggregationError::NotEnoughSignatures( + count, params.k + ))) + } } From a12162f3a7bfd8bd30ff27cd44179101537031f1 Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Sat, 7 Mar 2026 00:45:51 +0300 Subject: [PATCH 07/11] feat(stm): deduplicate for snark ADAPTED FOR SNARK --- .../src/proof_system/halo2_snark/clerk.rs | 181 +++++++++++++----- .../proof_system/halo2_snark/eligibility.rs | 2 +- .../halo2_snark/single_signature.rs | 6 +- .../jubjub/curve_points.rs | 20 ++ .../jubjub/field_elements.rs | 9 +- .../unique_schnorr_signature/signature.rs | 2 +- 6 files changed, 169 insertions(+), 51 deletions(-) diff --git a/mithril-stm/src/proof_system/halo2_snark/clerk.rs b/mithril-stm/src/proof_system/halo2_snark/clerk.rs index ae92f731309..0947ced8cc8 100644 --- a/mithril-stm/src/proof_system/halo2_snark/clerk.rs +++ b/mithril-stm/src/proof_system/halo2_snark/clerk.rs @@ -1,6 +1,8 @@ +use std::collections::{BTreeMap, HashMap, HashSet}; + use crate::{ - ClosedKeyRegistration, LotteryIndex, MembershipDigest, Parameters, RegistrationEntryForSnark, - Signer, StmResult, + AggregationError, ClosedKeyRegistration, LotteryIndex, MembershipDigest, Parameters, + RegistrationEntryForSnark, Signer, StmResult, proof_system::SingleSignatureForSnark, }; use super::AggregateVerificationKeyForSnark; @@ -15,6 +17,8 @@ pub struct SnarkClerk { 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( @@ -53,42 +57,35 @@ impl SnarkClerk { Ok(closed_registration_entry.into()) } - pub fn select_valid_signatures_for_k_indices( - params: &Parameters, - msg: &[u8], - sigs: &[SingleSignatureWithRegisteredParty], - avk: &AggregateVerificationKeyForConcatenation, - ) -> StmResult> { - let mut sig_by_index: BTreeMap = - BTreeMap::new(); - let mut removal_idx_by_vk: HashMap<&SingleSignatureWithRegisteredParty, Vec> = + /// Modifications: + /// Function inputs: remove `msg`, replace `sigs: &[SingleSignatureWithRegisteredParty]` with + /// `signatures: &[SingleSignatureForSnark]`, remove avk. + /// Return value: `StmResult`. + /// Remove signature verification loop -> already done in aggregation step. + /// Rename `sig_reg` as `signature` for the first loop, since we iterate over signatures now. + /// Rename `sig_reg` as `signature` for the second loop, since we iterate over signatures 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: &[SingleSignatureForSnark], + ) -> StmResult> { + let mut sig_by_index: BTreeMap = BTreeMap::new(); + let mut removal_idx_by_vk: HashMap<&SingleSignatureForSnark, Vec> = HashMap::new(); - for sig_reg in sigs.iter() { - if sig_reg - .sig - .concatenation_signature - .verify( - params, - &sig_reg.reg_party.get_verification_key_for_concatenation(), - &sig_reg.reg_party.get_stake(), - avk, - msg, - ) - .is_err() - { - continue; - } - for index in sig_reg.sig.get_concatenation_signature_indices().iter() { + for signature in signatures.iter() { + for index in signature.get_indices().iter() { let mut insert_this_sig = false; if let Some(&previous_sig) = sig_by_index.get(index) { - let sig_to_remove_index = if sig_reg.sig.get_concatenation_signature_sigma() - < previous_sig.sig.get_concatenation_signature_sigma() + let sig_to_remove_index = if signature.get_schnorr_signature() + < previous_sig.get_schnorr_signature() { insert_this_sig = true; previous_sig } else { - sig_reg + signature }; if let Some(indexes) = removal_idx_by_vk.get_mut(sig_to_remove_index) { @@ -101,31 +98,29 @@ impl SnarkClerk { } if insert_this_sig { - sig_by_index.insert(*index, sig_reg); + sig_by_index.insert(*index, signature); } } } - let mut dedup_sigs: HashSet = HashSet::new(); + let mut dedup_sigs: HashSet = HashSet::new(); let mut count: u64 = 0; - for (_, &sig_reg) in sig_by_index.iter() { - if dedup_sigs.contains(sig_reg) { + for (_, &signature) in sig_by_index.iter() { + if dedup_sigs.contains(signature) { continue; } - let mut deduped_sig = sig_reg.clone(); - if let Some(indexes) = removal_idx_by_vk.get(sig_reg) { + let mut deduped_sig = signature.clone(); + if let Some(indexes) = removal_idx_by_vk.get(signature) { let indices = deduped_sig - .sig - .get_concatenation_signature_indices() + .get_indices() .into_iter() .filter(|i| !indexes.contains(i)) .collect::>(); - deduped_sig.sig.set_concatenation_signature_indices(&indices); + deduped_sig.set_indices(&indices); } - let size: Result = - deduped_sig.sig.get_concatenation_signature_indices().len().try_into(); + let size: Result = deduped_sig.get_indices().len().try_into(); if let Ok(size) = size { if dedup_sigs.contains(&deduped_sig) { panic!( @@ -135,13 +130,109 @@ impl SnarkClerk { dedup_sigs.insert(deduped_sig); count += size; - if count >= params.k { + if count >= parameters.k { return Ok(dedup_sigs.into_iter().collect()); } } } - Err(anyhow!(AggregationError::NotEnoughSignatures( - count, params.k - ))) + Err(AggregationError::NotEnoughSignatures(count, parameters.k).into()) + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use rand_chacha::ChaCha20Rng; + use rand_core::SeedableRng; + + use crate::{ + Initializer, KeyRegistration, LotteryIndex, LotteryTargetValue, MithrilMembershipDigest, + Parameters, RegistrationEntry, Signer, + proof_system::{ + AggregateVerificationKeyForSnark, SingleSignatureForSnark, SnarkClerk, + halo2_snark::{build_snark_message, compute_winning_lottery_indices}, + }, + }; + + type D = MithrilMembershipDigest; + + #[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<(SingleSignatureForSnark, LotteryTargetValue)> = 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(); + let snark_sig = signature.snark_signature.unwrap(); + let lottery_target_value = signer.get_lottery_target_value().unwrap(); + signatures.push((snark_sig, lottery_target_value)); + } + + let clerk = + SnarkClerk::new_clerk_from_closed_key_registration(¶meters, &closed_key_reg); + + let avk: AggregateVerificationKeyForSnark = + clerk.compute_aggregate_verification_key_for_snark(); + let message_to_sign = + build_snark_message(&avk.get_merkle_tree_commitment().root, &message).unwrap(); + + let mut signatures_with_indices: Vec = Vec::new(); + for (sig, lottery_target_value) in signatures.clone() { + let indices = compute_winning_lottery_indices( + parameters.m, + &message_to_sign, + &sig.get_schnorr_signature(), + lottery_target_value, + ) + .unwrap(); + let mut new_sig = sig.clone(); + new_sig.set_indices(&indices); + signatures_with_indices.push(new_sig); + } + + let deduped_sigs = SnarkClerk::select_valid_signatures_for_k_indices( + ¶meters, + &signatures_with_indices, + ) + .unwrap(); + + let all_indices: Vec = + deduped_sigs.iter().flat_map(|s| s.get_indices()).collect(); + let unique_indices: HashSet = all_indices.iter().copied().collect(); + assert_eq!( + all_indices.len(), + unique_indices.len(), + "Duplicate indices found in deduplicated signatures" + ); + + assert!( + all_indices.len() as u64 >= parameters.k, + "Expected at least k={} indices, got {}", + parameters.k, + all_indices.len() + ); } } diff --git a/mithril-stm/src/proof_system/halo2_snark/eligibility.rs b/mithril-stm/src/proof_system/halo2_snark/eligibility.rs index 586ec15717e..607034caef6 100644 --- a/mithril-stm/src/proof_system/halo2_snark/eligibility.rs +++ b/mithril-stm/src/proof_system/halo2_snark/eligibility.rs @@ -22,7 +22,7 @@ cfg_num_integer! { // 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.2; + 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( 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..4b4ad1c98da 100644 --- a/mithril-stm/src/proof_system/halo2_snark/single_signature.rs +++ b/mithril-stm/src/proof_system/halo2_snark/single_signature.rs @@ -8,7 +8,7 @@ use crate::{ use super::{AggregateVerificationKeyForSnark, build_snark_message}; /// Single signature for the Snark proof system. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Hash)] pub(crate) struct SingleSignatureForSnark { /// The underlying Schnorr signature schnorr_signature: UniqueSchnorrSignature, @@ -52,8 +52,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 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, From 0568ea6e677c8468c3963d2f4a67d54c25509d8a Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Sat, 7 Mar 2026 02:22:30 +0300 Subject: [PATCH 08/11] feat(stm): dedup and aggregate corrected --- .../src/proof_system/halo2_snark/clerk.rs | 159 ++++-------------- .../src/proof_system/halo2_snark/mod.rs | 77 ++++----- .../halo2_snark/single_signature.rs | 10 +- .../proof_system/halo2_snark/witness/mod.rs | 2 + .../proof_system/halo2_snark/witness/proof.rs | 147 +++++----------- .../witness/signature_registration_entry.rs | 33 ++++ .../closed_registration_entry.rs | 4 +- .../src/protocol/key_registration/register.rs | 12 +- .../protocol/single_signature/signature.rs | 59 ++++--- .../signature_registered_party.rs | 32 ++-- 10 files changed, 217 insertions(+), 318 deletions(-) create mode 100644 mithril-stm/src/proof_system/halo2_snark/witness/signature_registration_entry.rs diff --git a/mithril-stm/src/proof_system/halo2_snark/clerk.rs b/mithril-stm/src/proof_system/halo2_snark/clerk.rs index 0947ced8cc8..0c602e4942a 100644 --- a/mithril-stm/src/proof_system/halo2_snark/clerk.rs +++ b/mithril-stm/src/proof_system/halo2_snark/clerk.rs @@ -2,10 +2,10 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use crate::{ AggregationError, ClosedKeyRegistration, LotteryIndex, MembershipDigest, Parameters, - RegistrationEntryForSnark, Signer, StmResult, proof_system::SingleSignatureForSnark, + RegistrationEntryForSnark, Signer, StmResult, }; -use super::AggregateVerificationKeyForSnark; +use super::{AggregateVerificationKeyForSnark, witness::SignatureRegistrationEntry}; /// The `SnarkClerk` is responsible for managing the proof system related to /// SNARK signatures. @@ -59,75 +59,76 @@ impl SnarkClerk { /// Modifications: /// Function inputs: remove `msg`, replace `sigs: &[SingleSignatureWithRegisteredParty]` with - /// `signatures: &[SingleSignatureForSnark]`, remove avk. - /// Return value: `StmResult`. + /// `signatures: &[SignatureRegistrationEntry]`, remove avk. + /// Return value: `StmResult`. /// Remove signature verification loop -> already done in aggregation step. - /// Rename `sig_reg` as `signature` for the first loop, since we iterate over signatures now. - /// Rename `sig_reg` as `signature` for the second loop, since we iterate over signatures now. + /// 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: &[SingleSignatureForSnark], - ) -> StmResult> { - let mut sig_by_index: BTreeMap = BTreeMap::new(); - let mut removal_idx_by_vk: HashMap<&SingleSignatureForSnark, Vec> = + signatures: &[SignatureRegistrationEntry], + ) -> StmResult> { + let mut sig_by_index: BTreeMap = BTreeMap::new(); + let mut removal_idx_by_vk: HashMap<&SignatureRegistrationEntry, Vec> = HashMap::new(); - for signature in signatures.iter() { - for index in signature.get_indices().iter() { + for entry in signatures.iter() { + for index in entry.get_signature().get_indices().iter() { let mut insert_this_sig = false; - if let Some(&previous_sig) = sig_by_index.get(index) { - let sig_to_remove_index = if signature.get_schnorr_signature() - < previous_sig.get_schnorr_signature() + 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_sig + previous_entry } else { - signature + entry }; - if let Some(indexes) = removal_idx_by_vk.get_mut(sig_to_remove_index) { + if let Some(indexes) = removal_idx_by_vk.get_mut(entry_to_remove_index) { indexes.push(*index); } else { - removal_idx_by_vk.insert(sig_to_remove_index, vec![*index]); + 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, signature); + sig_by_index.insert(*index, entry); } } } - let mut dedup_sigs: HashSet = HashSet::new(); + let mut dedup_sigs: HashSet = HashSet::new(); let mut count: u64 = 0; - for (_, &signature) in sig_by_index.iter() { - if dedup_sigs.contains(signature) { + for (_, &entry) in sig_by_index.iter() { + if dedup_sigs.contains(entry) { continue; } - let mut deduped_sig = signature.clone(); - if let Some(indexes) = removal_idx_by_vk.get(signature) { - let indices = deduped_sig + 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_sig.set_indices(&indices); + deduped_entry.set_indices(&indices); } - let size: Result = deduped_sig.get_indices().len().try_into(); + let size: Result = deduped_entry.get_signature().get_indices().len().try_into(); if let Ok(size) = size { - if dedup_sigs.contains(&deduped_sig) { + if dedup_sigs.contains(&deduped_entry) { panic!( "Invariant violation: duplicate signature encountered in deduplication set, which should not be possible." ); } - dedup_sigs.insert(deduped_sig); + dedup_sigs.insert(deduped_entry); count += size; if count >= parameters.k { @@ -138,101 +139,3 @@ impl SnarkClerk { Err(AggregationError::NotEnoughSignatures(count, parameters.k).into()) } } - -#[cfg(test)] -mod tests { - use std::collections::HashSet; - - use rand_chacha::ChaCha20Rng; - use rand_core::SeedableRng; - - use crate::{ - Initializer, KeyRegistration, LotteryIndex, LotteryTargetValue, MithrilMembershipDigest, - Parameters, RegistrationEntry, Signer, - proof_system::{ - AggregateVerificationKeyForSnark, SingleSignatureForSnark, SnarkClerk, - halo2_snark::{build_snark_message, compute_winning_lottery_indices}, - }, - }; - - type D = MithrilMembershipDigest; - - #[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<(SingleSignatureForSnark, LotteryTargetValue)> = 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(); - let snark_sig = signature.snark_signature.unwrap(); - let lottery_target_value = signer.get_lottery_target_value().unwrap(); - signatures.push((snark_sig, lottery_target_value)); - } - - let clerk = - SnarkClerk::new_clerk_from_closed_key_registration(¶meters, &closed_key_reg); - - let avk: AggregateVerificationKeyForSnark = - clerk.compute_aggregate_verification_key_for_snark(); - let message_to_sign = - build_snark_message(&avk.get_merkle_tree_commitment().root, &message).unwrap(); - - let mut signatures_with_indices: Vec = Vec::new(); - for (sig, lottery_target_value) in signatures.clone() { - let indices = compute_winning_lottery_indices( - parameters.m, - &message_to_sign, - &sig.get_schnorr_signature(), - lottery_target_value, - ) - .unwrap(); - let mut new_sig = sig.clone(); - new_sig.set_indices(&indices); - signatures_with_indices.push(new_sig); - } - - let deduped_sigs = SnarkClerk::select_valid_signatures_for_k_indices( - ¶meters, - &signatures_with_indices, - ) - .unwrap(); - - let all_indices: Vec = - deduped_sigs.iter().flat_map(|s| s.get_indices()).collect(); - let unique_indices: HashSet = all_indices.iter().copied().collect(); - assert_eq!( - all_indices.len(), - unique_indices.len(), - "Duplicate indices found in deduplicated signatures" - ); - - assert!( - all_indices.len() as u64 >= parameters.k, - "Expected at least k={} indices, got {}", - parameters.k, - all_indices.len() - ); - } -} diff --git a/mithril-stm/src/proof_system/halo2_snark/mod.rs b/mithril-stm/src/proof_system/halo2_snark/mod.rs index 3b6c99bfdbf..38ec3bc46c0 100644 --- a/mithril-stm/src/proof_system/halo2_snark/mod.rs +++ b/mithril-stm/src/proof_system/halo2_snark/mod.rs @@ -21,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, @@ -132,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); @@ -172,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); @@ -236,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); @@ -272,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); @@ -318,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); @@ -340,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); @@ -364,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(); @@ -388,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); @@ -419,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); @@ -439,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 4b4ad1c98da..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}; @@ -8,7 +10,7 @@ use crate::{ use super::{AggregateVerificationKeyForSnark, build_snark_message}; /// Single signature for the Snark proof system. -#[derive(Debug, Clone, Serialize, Deserialize, Hash)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct SingleSignatureForSnark { /// The underlying Schnorr signature schnorr_signature: UniqueSchnorrSignature, @@ -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/mod.rs b/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs index 2dcf4640828..00c704c20e4 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs @@ -1,6 +1,8 @@ mod instance; mod proof; +mod signature_registration_entry; mod signer_witness; pub(super) use instance::Instance; +pub(super) use signature_registration_entry::SignatureRegistrationEntry; pub(super) use signer_witness::SignerWitness; diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs b/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs index 25838e021e8..51503be9a6e 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs @@ -1,12 +1,12 @@ use crate::{ - MembershipDigest, RegisterError, SingleSignature, StmResult, + MembershipDigest, SingleSignature, StmResult, proof_system::{ AggregateVerificationKeyForSnark, SnarkClerk, halo2_snark::{build_snark_message, compute_winning_lottery_indices}, }, }; -use super::{Instance, SignerWitness}; +use super::{Instance, SignatureRegistrationEntry, SignerWitness}; #[allow(dead_code)] pub struct SnarkProof { @@ -25,61 +25,36 @@ impl SnarkProof { clerk.compute_aggregate_verification_key_for_snark(); let message_to_sign = build_snark_message(&avk.get_merkle_tree_commitment().root, message)?; - // // Print the signatures (bls sig and snark sig) before verification - // println!("Input signatures: {} total", signatures.len()); - // for (i, sig) in signatures.iter().enumerate() { - // println!( - // " [{}] signer_index: {}, has_snark_sig: {}", - // i, - // sig.signer_index, - // sig.snark_signature.is_some() - // ); - // } - - // Collect the snark signatures and their registration entries by filtering - // the snark signatures and mapping them to their corresponding registration entries - let mut snark_sig_reg_list: Vec<_> = signatures + let mut sig_reg_list: Vec = signatures .iter() .filter_map(|sig| { - sig.snark_signature - .clone() - .map(|snark_sig| (sig.signer_index, snark_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)) }) - .map(|(signer_index, snark_sig)| { - let reg_entry = clerk - .get_snark_registration_entry(signer_index)? - .ok_or(RegisterError::MissingSnarkRegistrationEntry(signer_index))?; - Ok((snark_sig, reg_entry)) - }) - .collect::>()?; - - // println!("After collect: {} entries", snark_sig_reg_list.len()); + .collect(); - // Verify each SNARK signature against its registration entry. - // If valid, compute the winning lottery indices and set them in the signature. - // Retain only the valid signatures and their corresponding registration entries in the list. - snark_sig_reg_list.retain_mut(|(snark_sig, reg_entry)| { - if snark_sig.verify(®_entry.0, message, &avk).is_ok() { - if let Ok(indices) = compute_winning_lottery_indices( + 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, - &snark_sig.get_schnorr_signature(), - reg_entry.1, - ) { - snark_sig.set_indices(&indices); - return true; - } + &entry.get_signature().get_schnorr_signature(), + reg.1, + ) + { + entry.set_indices(&indices); + return true; } false }); - // // Print verified signatures with computed indices - // println!("After retain_mut: {} entries", snark_sig_reg_list.len()); - // for (i, (sig, reg)) in snark_sig_reg_list.iter().enumerate() { - // println!(" [{}] indices: {:?}", i, sig.get_indices(),); - // } + let _deduped_signatures = + SnarkClerk::select_valid_signatures_for_k_indices(&clerk.parameters, &sig_reg_list)?; - // TODO: build Instance and SignerWitness entries from snark_sig_reg_list + // TODO: build Instance and SignerWitness entries from deduped_signatures Ok(SnarkProof { instance: Instance::new(message_to_sign[0], message_to_sign[1]), witness: Vec::new(), @@ -93,96 +68,50 @@ mod tests { use rand_core::SeedableRng; use crate::{ - BlsVerificationKeyProofOfPossession, Initializer, KeyRegistration, MithrilMembershipDigest, - Parameters, RegistrationEntry, Signer, SingleSignature, - proof_system::{ConcatenationProofSigner, SnarkClerk}, - signature_scheme::BlsSigningKey, + Initializer, KeyRegistration, MithrilMembershipDigest, Parameters, RegistrationEntry, + Signer, SingleSignature, proof_system::SnarkClerk, }; - use super::SnarkProof; + use super::*; type D = MithrilMembershipDigest; #[test] - fn aggregate_signatures_with_mixed_entries() { + fn deduplicate_indices() { let mut rng = ChaCha20Rng::from_seed([0u8; 32]); - let params = Parameters { + let parameters = Parameters { m: 100, - k: 1, - phi_f: 0.2, + k: 20, + phi_f: 0.50, }; let message = [0u8; 32]; let mut initializers = Vec::new(); - let mut entries = Vec::new(); - let mut key_reg = KeyRegistration::initialize(); - let stakes = [1, 5, 10, 20]; + let stakes = [10, 8, 13, 10]; - // 4 full initializers for &stake in &stakes { - let init = Initializer::new(params, stake, &mut rng); + let init = Initializer::new(parameters, stake, &mut rng); initializers.push(init.clone()); let entry = RegistrationEntry::try_from(init).unwrap(); - entries.push(entry); + key_reg.register_by_entry(&entry).unwrap(); } - // Initializer without snark - let sk = BlsSigningKey::generate(&mut rng); - let vk_pop = BlsVerificationKeyProofOfPossession::from(&sk); - let init = Initializer { - stake: 40, - parameters: params, - bls_signing_key: sk.clone(), - bls_verification_key_proof_of_possession: vk_pop.clone(), - #[cfg(feature = "future_snark")] - schnorr_signing_key: None, - #[cfg(feature = "future_snark")] - schnorr_verification_key: None, - }; - initializers.push(init.clone()); - let entry = RegistrationEntry::try_from(init).unwrap(); - entries.push(entry); - - // Register all entries - for entry in &entries { - key_reg.register_by_entry(entry).unwrap(); - } - - // Close the registration let closed_key_reg = key_reg.close_registration(); - // Create signatures for the first 4 initializers (with snark) and the last initializer (without snark) let mut signatures: Vec = Vec::new(); - for i in 0..4 { - let signer: Signer = - initializers[i].clone().try_create_signer(&closed_key_reg).unwrap(); + 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 signer: Signer = Signer::new( - 4, - ConcatenationProofSigner::new( - 40, - closed_key_reg.total_stake, - params, - sk, - vk_pop.vk, - closed_key_reg.to_merkle_tree().to_merkle_tree_batch_commitment(), - ), - closed_key_reg.clone(), - params, - 40, - #[cfg(feature = "future_snark")] - None, - ); - let signature = signer.create_single_signature(&message).unwrap(); - signatures.push(signature); - - let clerk = SnarkClerk::new_clerk_from_closed_key_registration(¶ms, &closed_key_reg); - let _snark_proof: SnarkProof = + + 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..6a195196512 --- /dev/null +++ b/mithril-stm/src/proof_system/halo2_snark/witness/signature_registration_entry.rs @@ -0,0 +1,33 @@ +use crate::{LotteryIndex, RegistrationEntryForSnark, proof_system::SingleSignatureForSnark}; + +#[allow(dead_code)] +#[derive(Clone, Hash, PartialEq, Eq)] +pub(crate) struct SignatureRegistrationEntry { + signature: SingleSignatureForSnark, + registration_entry: RegistrationEntryForSnark, +} + +#[allow(dead_code)] +impl SignatureRegistrationEntry { + pub(crate) fn new( + signature: SingleSignatureForSnark, + registration_entry: RegistrationEntryForSnark, + ) -> Self { + Self { + signature, + registration_entry, + } + } + + pub(crate) fn get_signature(&self) -> &SingleSignatureForSnark { + &self.signature + } + + pub(crate) fn set_indices(&mut self, indices: &[LotteryIndex]) { + self.signature.set_indices(indices); + } + + pub(crate) fn get_registration_entry(&self) -> &RegistrationEntryForSnark { + &self.registration_entry + } +} 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 05e950595a2..59abb4c4db1 100644 --- a/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs +++ b/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs @@ -168,11 +168,11 @@ impl Serialize for ClosedRegistrationEntry { /// TODO: Compute the lottery target value based on the total stake and the entry's stake. impl From<(RegistrationEntry, Stake)> for ClosedRegistrationEntry { fn from(entry_total_stake: (RegistrationEntry, Stake)) -> Self { - let (entry, total_stake) = entry_total_stake; + let (entry, _total_stake) = entry_total_stake; #[cfg(feature = "future_snark")] let (schnorr_verification_key, target_value) = { let vk = entry.get_verification_key_for_snark(); - let target = vk.map(|_| compute_lottery_target_value(entry.get_stake(), total_stake)); + let target = vk.map(|_| compute_lottery_target_value(entry.get_stake(), _total_stake)); (vk, target) }; diff --git a/mithril-stm/src/protocol/key_registration/register.rs b/mithril-stm/src/protocol/key_registration/register.rs index 6f25d14a007..b8d32a159b5 100644 --- a/mithril-stm/src/protocol/key_registration/register.rs +++ b/mithril-stm/src/protocol/key_registration/register.rs @@ -246,11 +246,19 @@ mod tests { fn golden_value() -> MerkleTreeBatchCommitment, MerkleTreeConcatenationLeaf> { let mut rng = ChaCha20Rng::from_seed([0u8; 32]); + #[cfg(not(feature = "future_snark"))] let params = Parameters { m: 10, k: 5, phi_f: 0.8, }; + + #[cfg(feature = "future_snark")] + let params = Parameters { + m: 10, + k: 5, + phi_f: 0.5, + }; let number_of_parties = 4; let mut key_reg = KeyRegistration::initialize(); @@ -291,7 +299,7 @@ mod tests { const GOLDEN_JSON: &str = r#" { - "root":[228,163,47,150,34,74,244,226,131,159,24,218,184,37,158,68,110,78,76,86,89,121,231,103,49,153,207,157,188,169,219,48], + "root":[14,47,36,200,0,186,74,223,0,131,30,25,150,157,54,61,89,102,26,188,96,60,0,101,43,209,187,215,127,180,103,105], "hasher":null }"#; @@ -300,7 +308,7 @@ mod tests { let params = Parameters { m: 10, k: 5, - phi_f: 0.8, + phi_f: 0.5, }; let number_of_parties = 4; diff --git a/mithril-stm/src/protocol/single_signature/signature.rs b/mithril-stm/src/protocol/single_signature/signature.rs index b0f37f5e825..2cd8f62d5fb 100644 --- a/mithril-stm/src/protocol/single_signature/signature.rs +++ b/mithril-stm/src/protocol/single_signature/signature.rs @@ -267,17 +267,17 @@ mod tests { ]; #[cfg(feature = "future_snark")] - const GOLDEN_BYTES: &[u8; 208] = &[ - 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, - 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 140, 18, 156, 86, 86, 16, 179, - 117, 148, 17, 195, 177, 207, 235, 93, 252, 78, 244, 112, 94, 47, 18, 158, 15, 78, 76, - 80, 43, 116, 242, 116, 205, 252, 21, 194, 58, 162, 117, 201, 62, 40, 190, 21, 183, 178, - 186, 196, 136, 0, 0, 0, 0, 0, 0, 0, 1, 198, 195, 131, 147, 143, 246, 147, 31, 112, 104, - 4, 197, 184, 150, 239, 16, 122, 195, 82, 217, 135, 174, 163, 231, 197, 102, 37, 57, - 253, 182, 126, 72, 116, 67, 192, 99, 53, 189, 46, 158, 53, 70, 174, 132, 144, 179, 25, - 203, 87, 11, 59, 253, 155, 114, 211, 22, 16, 29, 4, 233, 203, 127, 170, 6, 128, 135, - 196, 3, 229, 138, 6, 47, 81, 118, 6, 77, 1, 148, 175, 28, 88, 124, 103, 229, 155, 213, - 96, 68, 7, 94, 216, 151, 207, 157, 220, 67, 0, 0, 0, 0, 0, 0, 0, 0, + const GOLDEN_BYTES: &[u8; 192] = &[ + 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, + 0, 0, 7, 140, 18, 156, 86, 86, 16, 179, 117, 148, 17, 195, 177, 207, 235, 93, 252, 78, + 244, 112, 94, 47, 18, 158, 15, 78, 76, 80, 43, 116, 242, 116, 205, 252, 21, 194, 58, + 162, 117, 201, 62, 40, 190, 21, 183, 178, 186, 196, 136, 0, 0, 0, 0, 0, 0, 0, 1, 237, + 203, 118, 185, 163, 93, 169, 219, 56, 140, 82, 163, 201, 105, 102, 61, 53, 147, 176, + 103, 249, 235, 88, 4, 209, 93, 224, 213, 64, 203, 83, 39, 6, 10, 237, 123, 141, 133, + 58, 92, 27, 164, 59, 170, 175, 31, 170, 164, 3, 227, 222, 189, 72, 84, 247, 104, 74, + 238, 194, 75, 90, 225, 241, 4, 45, 201, 155, 247, 225, 138, 138, 175, 13, 86, 161, 49, + 113, 38, 201, 209, 121, 231, 164, 221, 194, 182, 117, 137, 175, 148, 101, 219, 176, + 159, 83, 63, 0, 0, 0, 0, 0, 0, 0, 0, ]; fn golden_value() -> SingleSignature { @@ -288,11 +288,20 @@ mod tests { #[cfg(feature = "future_snark")] let message = [0u8; 32]; + #[cfg(not(feature = "future_snark"))] let params = Parameters { m: 10, k: 5, phi_f: 0.8, }; + + #[cfg(feature = "future_snark")] + let params = Parameters { + m: 10, + k: 5, + phi_f: 0.5, + }; + let sk_1 = BlsSigningKey::generate(&mut rng); let sk_2 = BlsSigningKey::generate(&mut rng); let pk_1 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_1); @@ -411,24 +420,24 @@ mod tests { 78, 244, 112, 94, 47, 18, 158, 15, 78, 76, 80, 43, 116, 242, 116, 205, 252, 21, 194, 58, 162, 117, 201, 62, 40, 190, 21, 183, 178, 186, 196, 136 ], - "indexes": [3, 4, 5, 6, 7], + "indexes": [4, 6, 7], "signer_index": 1, "snark_signature": { "schnorr_signature": { "commitment_point": [ - 198, 195, 131, 147, 143, 246, 147, 31, 112, 104, 4, 197, 184, 150, - 239, 16, 122, 195, 82, 217, 135, 174, 163, 231, 197, 102, 37, 57, - 253, 182, 126, 72 + 237, 203, 118, 185, 163, 93, 169, 219, 56, 140, 82, 163, 201, 105, + 102, 61, 53, 147, 176, 103, 249, 235, 88, 4, 209, 93, 224, 213, + 64, 203, 83, 39 ], "response": [ - 116, 67, 192, 99, 53, 189, 46, 158, 53, 70, 174, 132, 144, 179, 25, - 203, 87, 11, 59, 253, 155, 114, 211, 22, 16, 29, 4, 233, 203, 127, - 170, 6 + 6, 10, 237, 123, 141, 133, 58, 92, 27, 164, 59, 170, 175, 31, + 170, 164, 3, 227, 222, 189, 72, 84, 247, 104, 74, 238, 194, 75, + 90, 225, 241, 4 ], "challenge": [ - 128, 135, 196, 3, 229, 138, 6, 47, 81, 118, 6, 77, 1, 148, 175, 28, - 88, 124, 103, 229, 155, 213, 96, 68, 7, 94, 216, 151, 207, 157, - 220, 67 + 45, 201, 155, 247, 225, 138, 138, 175, 13, 86, 161, 49, 113, 38, + 201, 209, 121, 231, 164, 221, 194, 182, 117, 137, 175, 148, 101, + 219, 176, 159, 83, 63 ] }, "indices": [] @@ -443,11 +452,19 @@ mod tests { #[cfg(feature = "future_snark")] let message = [0u8; 32]; + #[cfg(not(feature = "future_snark"))] let params = Parameters { m: 10, k: 5, phi_f: 0.8, }; + + #[cfg(feature = "future_snark")] + let params = Parameters { + m: 10, + k: 5, + phi_f: 0.5, + }; let sk_1 = BlsSigningKey::generate(&mut rng); let sk_2 = BlsSigningKey::generate(&mut rng); let pk_1 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_1); diff --git a/mithril-stm/src/protocol/single_signature/signature_registered_party.rs b/mithril-stm/src/protocol/single_signature/signature_registered_party.rs index 68da1372a4b..4535ec963fe 100644 --- a/mithril-stm/src/protocol/single_signature/signature_registered_party.rs +++ b/mithril-stm/src/protocol/single_signature/signature_registered_party.rs @@ -129,24 +129,24 @@ mod tests { 78, 244, 112, 94, 47, 18, 158, 15, 78, 76, 80, 43, 116, 242, 116, 205, 252, 21, 194, 58, 162, 117, 201, 62, 40, 190, 21, 183, 178, 186, 196, 136 ], - "indexes": [3, 4, 5, 6, 7], + "indexes": [4, 6, 7], "signer_index": 1, "snark_signature": { "schnorr_signature": { "commitment_point": [ - 198, 195, 131, 147, 143, 246, 147, 31, 112, 104, 4, 197, 184, 150, - 239, 16, 122, 195, 82, 217, 135, 174, 163, 231, 197, 102, 37, 57, - 253, 182, 126, 72 + 237, 203, 118, 185, 163, 93, 169, 219, 56, 140, 82, 163, 201, 105, + 102, 61, 53, 147, 176, 103, 249, 235, 88, 4, 209, 93, 224, 213, + 64, 203, 83, 39 ], "response": [ - 116, 67, 192, 99, 53, 189, 46, 158, 53, 70, 174, 132, 144, 179, - 25, 203, 87, 11, 59, 253, 155, 114, 211, 22, 16, 29, 4, 233, 203, - 127, 170, 6 + 6, 10, 237, 123, 141, 133, 58, 92, 27, 164, 59, 170, 175, 31, + 170, 164, 3, 227, 222, 189, 72, 84, 247, 104, 74, 238, 194, 75, + 90, 225, 241, 4 ], "challenge": [ - 128, 135, 196, 3, 229, 138, 6, 47, 81, 118, 6, 77, 1, 148, 175, - 28, 88, 124, 103, 229, 155, 213, 96, 68, 7, 94, 216, 151, 207, - 157, 220, 67 + 45, 201, 155, 247, 225, 138, 138, 175, 13, 86, 161, 49, 113, 38, + 201, 209, 121, 231, 164, 221, 194, 182, 117, 137, 175, 148, 101, + 219, 176, 159, 83, 63 ] }, "indices": [] @@ -167,8 +167,8 @@ mod tests { 173, 141, 223, 53, 86, 104, 169, 168, 82, 136, 67, 233, 108, 18, 229, 93 ], [ - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, - 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + 71, 50, 212, 197, 127, 171, 19, 11, 101, 174, 40, 236, 218, 206, 75, 84, + 214, 210, 184, 151, 200, 8, 21, 12, 40, 105, 111, 155, 44, 98, 244, 33 ] ] ] @@ -182,11 +182,19 @@ mod tests { #[cfg(feature = "future_snark")] let message = [0u8; 32]; + #[cfg(not(feature = "future_snark"))] let params = Parameters { m: 10, k: 5, phi_f: 0.8, }; + + #[cfg(feature = "future_snark")] + let params = Parameters { + m: 10, + k: 5, + phi_f: 0.5, + }; let sk_1 = BlsSigningKey::generate(&mut rng); let sk_2 = BlsSigningKey::generate(&mut rng); let pk_1 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_1); From 66a26f104af40d620dd556e17c15bb6dbd12ff1f Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Sat, 7 Mar 2026 02:46:39 +0300 Subject: [PATCH 09/11] fix(stm): add todo comments for dead code --- mithril-stm/src/membership_commitment/merkle_tree/error.rs | 1 + mithril-stm/src/membership_commitment/merkle_tree/tree.rs | 1 + mithril-stm/src/proof_system/halo2_snark/witness/instance.rs | 2 ++ mithril-stm/src/proof_system/halo2_snark/witness/proof.rs | 2 ++ .../halo2_snark/witness/signature_registration_entry.rs | 2 ++ .../src/proof_system/halo2_snark/witness/signer_witness.rs | 2 ++ mithril-stm/src/protocol/error.rs | 5 ----- 7 files changed, 10 insertions(+), 5 deletions(-) diff --git a/mithril-stm/src/membership_commitment/merkle_tree/error.rs b/mithril-stm/src/membership_commitment/merkle_tree/error.rs index cda88de684d..9a32219b2d6 100644 --- a/mithril-stm/src/membership_commitment/merkle_tree/error.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/error.rs @@ -18,6 +18,7 @@ pub enum MerkleTreeError { /// 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 9443ddc5501..5f07d1158d8 100644 --- a/mithril-stm/src/membership_commitment/merkle_tree/tree.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/tree.rs @@ -192,6 +192,7 @@ 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(); diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs b/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs index ba6260ae739..e60fcc4ae66 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs @@ -1,10 +1,12 @@ use crate::BaseFieldElement; +// TODO: remove this allow dead_code directive when function is called or future_snark is activated #[allow(dead_code)] pub(crate) struct Instance { merkle_tree_root: BaseFieldElement, message: BaseFieldElement, } +// TODO: remove this allow dead_code directive when function is called or future_snark is activated #[allow(dead_code)] impl Instance { pub(crate) fn new(merkle_tree_root: BaseFieldElement, message: BaseFieldElement) -> Self { diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs b/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs index 51503be9a6e..c4f8220815a 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs @@ -8,12 +8,14 @@ use crate::{ use super::{Instance, SignatureRegistrationEntry, SignerWitness}; +// TODO: remove this allow dead_code directive when function is called or future_snark is activated #[allow(dead_code)] pub struct SnarkProof { instance: Instance, witness: Vec>, } +// TODO: remove this allow dead_code directive when function is called or future_snark is activated #[allow(dead_code)] impl SnarkProof { pub fn aggregate_signatures( 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 index 6a195196512..971be5fe795 100644 --- 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 @@ -1,5 +1,6 @@ use crate::{LotteryIndex, RegistrationEntryForSnark, proof_system::SingleSignatureForSnark}; +// 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 { @@ -7,6 +8,7 @@ pub(crate) struct SignatureRegistrationEntry { registration_entry: RegistrationEntryForSnark, } +// TODO: remove this allow dead_code directive when function is called or future_snark is activated #[allow(dead_code)] impl SignatureRegistrationEntry { pub(crate) fn new( diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/signer_witness.rs b/mithril-stm/src/proof_system/halo2_snark/witness/signer_witness.rs index 5824aca5058..4cf94dc1fe7 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/signer_witness.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/signer_witness.rs @@ -3,6 +3,7 @@ use crate::{ membership_commitment::{MerklePath, MerkleTreeSnarkLeaf}, }; +// TODO: remove this allow dead_code directive when function is called or future_snark is activated #[allow(dead_code)] pub(crate) struct SignerWitness { merkle_tree_leaf: MerkleTreeSnarkLeaf, @@ -11,6 +12,7 @@ pub(crate) struct SignerWitness { lottery_index: LotteryIndex, } +// TODO: remove this allow dead_code directive when function is called or future_snark is activated #[allow(dead_code)] impl SignerWitness { pub(crate) fn new( diff --git a/mithril-stm/src/protocol/error.rs b/mithril-stm/src/protocol/error.rs index 09f1bd0d2b6..60dd9ed9241 100644 --- a/mithril-stm/src/protocol/error.rs +++ b/mithril-stm/src/protocol/error.rs @@ -43,9 +43,4 @@ pub enum RegisterError { #[cfg(feature = "future_snark")] #[error("Unable to create SNARK proof signer.")] SnarkProofSignerCreation, - - /// Missing SNARK registration entry for the given signer index. - #[cfg(feature = "future_snark")] - #[error("Missing SNARK registration entry for signer index {0}.")] - MissingSnarkRegistrationEntry(u64), } From 68856969cb8bd7a548fde7a77dd1f63b98859f55 Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Sat, 7 Mar 2026 03:08:57 +0300 Subject: [PATCH 10/11] doc(stm): revise documentation --- .../proof_system/halo2_snark/eligibility.rs | 1 + .../halo2_snark/witness/instance.rs | 8 ++++++++ .../proof_system/halo2_snark/witness/mod.rs | 4 ++-- .../proof_system/halo2_snark/witness/proof.rs | 20 ++++++++++++++++--- .../witness/signature_registration_entry.rs | 10 ++++++++++ .../{signer_witness.rs => witness_entry.rs} | 15 ++++++++++++-- .../closed_registration_entry.rs | 5 +++-- 7 files changed, 54 insertions(+), 9 deletions(-) rename mithril-stm/src/proof_system/halo2_snark/witness/{signer_witness.rs => witness_entry.rs} (56%) diff --git a/mithril-stm/src/proof_system/halo2_snark/eligibility.rs b/mithril-stm/src/proof_system/halo2_snark/eligibility.rs index 607034caef6..46f14e1e1ac 100644 --- a/mithril-stm/src/proof_system/halo2_snark/eligibility.rs +++ b/mithril-stm/src/proof_system/halo2_snark/eligibility.rs @@ -18,6 +18,7 @@ cfg_num_integer! { /// 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)] diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs b/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs index e60fcc4ae66..e05933dc013 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/instance.rs @@ -1,14 +1,22 @@ 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, diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs b/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs index 00c704c20e4..aa1dba98598 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/mod.rs @@ -1,8 +1,8 @@ mod instance; mod proof; mod signature_registration_entry; -mod signer_witness; +mod witness_entry; pub(super) use instance::Instance; pub(super) use signature_registration_entry::SignatureRegistrationEntry; -pub(super) use signer_witness::SignerWitness; +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 index c4f8220815a..09f4195c2b8 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs @@ -6,18 +6,32 @@ use crate::{ }, }; -use super::{Instance, SignatureRegistrationEntry, SignerWitness}; +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, - witness: Vec>, + /// 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], @@ -56,7 +70,7 @@ impl SnarkProof { let _deduped_signatures = SnarkClerk::select_valid_signatures_for_k_indices(&clerk.parameters, &sig_reg_list)?; - // TODO: build Instance and SignerWitness entries from deduped_signatures + // TODO: build WitnessEntry entries from deduped_signatures Ok(SnarkProof { instance: Instance::new(message_to_sign[0], message_to_sign[1]), witness: Vec::new(), 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 index 971be5fe795..0ab89eb18cb 100644 --- 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 @@ -1,16 +1,23 @@ 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, @@ -21,14 +28,17 @@ impl SignatureRegistrationEntry { } } + /// 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/signer_witness.rs b/mithril-stm/src/proof_system/halo2_snark/witness/witness_entry.rs similarity index 56% rename from mithril-stm/src/proof_system/halo2_snark/witness/signer_witness.rs rename to mithril-stm/src/proof_system/halo2_snark/witness/witness_entry.rs index 4cf94dc1fe7..45d8e3ae8e9 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/signer_witness.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/witness_entry.rs @@ -3,18 +3,29 @@ use crate::{ 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 SignerWitness { +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 SignerWitness { +impl WitnessEntry { + /// Create a new `WitnessEntry` from its components. pub(crate) fn new( merkle_tree_leaf: MerkleTreeSnarkLeaf, merkle_path: MerklePath, 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 59abb4c4db1..821a06d53b2 100644 --- a/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs +++ b/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs @@ -164,8 +164,9 @@ 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. +/// `LotteryTargetValue` is computed based on the stake of the entry and the total stake of all +/// entries, using a hardcoded phi_f value for testing purposes. +/// TODO: Compute the lottery target value without hardcoded phi_f. impl From<(RegistrationEntry, Stake)> for ClosedRegistrationEntry { fn from(entry_total_stake: (RegistrationEntry, Stake)) -> Self { let (entry, _total_stake) = entry_total_stake; From b49aad1f8554d37a207a74e2b6f15e745d93375c Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Sat, 7 Mar 2026 03:34:35 +0300 Subject: [PATCH 11/11] fix(stm): restore lottery target value in building closed reg entry --- .../src/proof_system/halo2_snark/mod.rs | 2 +- .../proof_system/halo2_snark/witness/proof.rs | 5 ++ mithril-stm/src/proof_system/mod.rs | 1 - .../closed_registration_entry.rs | 13 ++-- .../src/protocol/key_registration/register.rs | 12 +--- .../protocol/single_signature/signature.rs | 59 +++++++------------ .../signature_registered_party.rs | 32 ++++------ 7 files changed, 47 insertions(+), 77 deletions(-) diff --git a/mithril-stm/src/proof_system/halo2_snark/mod.rs b/mithril-stm/src/proof_system/halo2_snark/mod.rs index 38ec3bc46c0..78b1ed8f68d 100644 --- a/mithril-stm/src/proof_system/halo2_snark/mod.rs +++ b/mithril-stm/src/proof_system/halo2_snark/mod.rs @@ -8,7 +8,7 @@ mod witness; pub(crate) use aggregate_key::AggregateVerificationKeyForSnark; pub(crate) use clerk::SnarkClerk; -pub(crate) use eligibility::{compute_lottery_target_value, compute_winning_lottery_indices}; +pub(crate) use eligibility::compute_winning_lottery_indices; pub(crate) use message::build_snark_message; pub(crate) use signer::SnarkProofSigner; pub(crate) use single_signature::SingleSignatureForSnark; diff --git a/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs b/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs index 09f4195c2b8..ae3a674baf2 100644 --- a/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs +++ b/mithril-stm/src/proof_system/halo2_snark/witness/proof.rs @@ -92,6 +92,11 @@ mod tests { 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]); diff --git a/mithril-stm/src/proof_system/mod.rs b/mithril-stm/src/proof_system/mod.rs index ce11ae2e2ea..e1d7f52f9b5 100644 --- a/mithril-stm/src/proof_system/mod.rs +++ b/mithril-stm/src/proof_system/mod.rs @@ -28,5 +28,4 @@ pub(crate) use concatenation::{ConcatenationProofSigner, SingleSignatureForConca #[cfg(feature = "future_snark")] pub(crate) use halo2_snark::{ AggregateVerificationKeyForSnark, SingleSignatureForSnark, SnarkClerk, SnarkProofSigner, - compute_lottery_target_value, }; 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 821a06d53b2..912057e8c08 100644 --- a/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs +++ b/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs @@ -5,9 +5,7 @@ use std::hash::Hash; use crate::{RegisterError, RegistrationEntry, Stake, StmResult, VerificationKeyForConcatenation}; #[cfg(feature = "future_snark")] -use crate::{ - LotteryTargetValue, VerificationKeyForSnark, proof_system::compute_lottery_target_value, -}; +use crate::{LotteryTargetValue, VerificationKeyForSnark}; /// Represents a registration entry of a closed key registration. #[derive(PartialEq, Eq, Clone, Debug, Copy, Deserialize)] @@ -164,16 +162,17 @@ 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 computed based on the stake of the entry and the total stake of all -/// entries, using a hardcoded phi_f value for testing purposes. -/// TODO: Compute the lottery target value without hardcoded phi_f. +/// `LotteryTargetValue` is set to (modulus - 1) for now. +/// 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; #[cfg(feature = "future_snark")] let (schnorr_verification_key, target_value) = { let vk = entry.get_verification_key_for_snark(); - let target = vk.map(|_| compute_lottery_target_value(entry.get_stake(), _total_stake)); + let target = + vk.map(|_| &LotteryTargetValue::default() - &LotteryTargetValue::get_one()); (vk, target) }; diff --git a/mithril-stm/src/protocol/key_registration/register.rs b/mithril-stm/src/protocol/key_registration/register.rs index b8d32a159b5..6f25d14a007 100644 --- a/mithril-stm/src/protocol/key_registration/register.rs +++ b/mithril-stm/src/protocol/key_registration/register.rs @@ -246,19 +246,11 @@ mod tests { fn golden_value() -> MerkleTreeBatchCommitment, MerkleTreeConcatenationLeaf> { let mut rng = ChaCha20Rng::from_seed([0u8; 32]); - #[cfg(not(feature = "future_snark"))] let params = Parameters { m: 10, k: 5, phi_f: 0.8, }; - - #[cfg(feature = "future_snark")] - let params = Parameters { - m: 10, - k: 5, - phi_f: 0.5, - }; let number_of_parties = 4; let mut key_reg = KeyRegistration::initialize(); @@ -299,7 +291,7 @@ mod tests { const GOLDEN_JSON: &str = r#" { - "root":[14,47,36,200,0,186,74,223,0,131,30,25,150,157,54,61,89,102,26,188,96,60,0,101,43,209,187,215,127,180,103,105], + "root":[228,163,47,150,34,74,244,226,131,159,24,218,184,37,158,68,110,78,76,86,89,121,231,103,49,153,207,157,188,169,219,48], "hasher":null }"#; @@ -308,7 +300,7 @@ mod tests { let params = Parameters { m: 10, k: 5, - phi_f: 0.5, + phi_f: 0.8, }; let number_of_parties = 4; diff --git a/mithril-stm/src/protocol/single_signature/signature.rs b/mithril-stm/src/protocol/single_signature/signature.rs index 2cd8f62d5fb..b0f37f5e825 100644 --- a/mithril-stm/src/protocol/single_signature/signature.rs +++ b/mithril-stm/src/protocol/single_signature/signature.rs @@ -267,17 +267,17 @@ mod tests { ]; #[cfg(feature = "future_snark")] - const GOLDEN_BYTES: &[u8; 192] = &[ - 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, - 0, 0, 7, 140, 18, 156, 86, 86, 16, 179, 117, 148, 17, 195, 177, 207, 235, 93, 252, 78, - 244, 112, 94, 47, 18, 158, 15, 78, 76, 80, 43, 116, 242, 116, 205, 252, 21, 194, 58, - 162, 117, 201, 62, 40, 190, 21, 183, 178, 186, 196, 136, 0, 0, 0, 0, 0, 0, 0, 1, 237, - 203, 118, 185, 163, 93, 169, 219, 56, 140, 82, 163, 201, 105, 102, 61, 53, 147, 176, - 103, 249, 235, 88, 4, 209, 93, 224, 213, 64, 203, 83, 39, 6, 10, 237, 123, 141, 133, - 58, 92, 27, 164, 59, 170, 175, 31, 170, 164, 3, 227, 222, 189, 72, 84, 247, 104, 74, - 238, 194, 75, 90, 225, 241, 4, 45, 201, 155, 247, 225, 138, 138, 175, 13, 86, 161, 49, - 113, 38, 201, 209, 121, 231, 164, 221, 194, 182, 117, 137, 175, 148, 101, 219, 176, - 159, 83, 63, 0, 0, 0, 0, 0, 0, 0, 0, + const GOLDEN_BYTES: &[u8; 208] = &[ + 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, + 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 140, 18, 156, 86, 86, 16, 179, + 117, 148, 17, 195, 177, 207, 235, 93, 252, 78, 244, 112, 94, 47, 18, 158, 15, 78, 76, + 80, 43, 116, 242, 116, 205, 252, 21, 194, 58, 162, 117, 201, 62, 40, 190, 21, 183, 178, + 186, 196, 136, 0, 0, 0, 0, 0, 0, 0, 1, 198, 195, 131, 147, 143, 246, 147, 31, 112, 104, + 4, 197, 184, 150, 239, 16, 122, 195, 82, 217, 135, 174, 163, 231, 197, 102, 37, 57, + 253, 182, 126, 72, 116, 67, 192, 99, 53, 189, 46, 158, 53, 70, 174, 132, 144, 179, 25, + 203, 87, 11, 59, 253, 155, 114, 211, 22, 16, 29, 4, 233, 203, 127, 170, 6, 128, 135, + 196, 3, 229, 138, 6, 47, 81, 118, 6, 77, 1, 148, 175, 28, 88, 124, 103, 229, 155, 213, + 96, 68, 7, 94, 216, 151, 207, 157, 220, 67, 0, 0, 0, 0, 0, 0, 0, 0, ]; fn golden_value() -> SingleSignature { @@ -288,20 +288,11 @@ mod tests { #[cfg(feature = "future_snark")] let message = [0u8; 32]; - #[cfg(not(feature = "future_snark"))] let params = Parameters { m: 10, k: 5, phi_f: 0.8, }; - - #[cfg(feature = "future_snark")] - let params = Parameters { - m: 10, - k: 5, - phi_f: 0.5, - }; - let sk_1 = BlsSigningKey::generate(&mut rng); let sk_2 = BlsSigningKey::generate(&mut rng); let pk_1 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_1); @@ -420,24 +411,24 @@ mod tests { 78, 244, 112, 94, 47, 18, 158, 15, 78, 76, 80, 43, 116, 242, 116, 205, 252, 21, 194, 58, 162, 117, 201, 62, 40, 190, 21, 183, 178, 186, 196, 136 ], - "indexes": [4, 6, 7], + "indexes": [3, 4, 5, 6, 7], "signer_index": 1, "snark_signature": { "schnorr_signature": { "commitment_point": [ - 237, 203, 118, 185, 163, 93, 169, 219, 56, 140, 82, 163, 201, 105, - 102, 61, 53, 147, 176, 103, 249, 235, 88, 4, 209, 93, 224, 213, - 64, 203, 83, 39 + 198, 195, 131, 147, 143, 246, 147, 31, 112, 104, 4, 197, 184, 150, + 239, 16, 122, 195, 82, 217, 135, 174, 163, 231, 197, 102, 37, 57, + 253, 182, 126, 72 ], "response": [ - 6, 10, 237, 123, 141, 133, 58, 92, 27, 164, 59, 170, 175, 31, - 170, 164, 3, 227, 222, 189, 72, 84, 247, 104, 74, 238, 194, 75, - 90, 225, 241, 4 + 116, 67, 192, 99, 53, 189, 46, 158, 53, 70, 174, 132, 144, 179, 25, + 203, 87, 11, 59, 253, 155, 114, 211, 22, 16, 29, 4, 233, 203, 127, + 170, 6 ], "challenge": [ - 45, 201, 155, 247, 225, 138, 138, 175, 13, 86, 161, 49, 113, 38, - 201, 209, 121, 231, 164, 221, 194, 182, 117, 137, 175, 148, 101, - 219, 176, 159, 83, 63 + 128, 135, 196, 3, 229, 138, 6, 47, 81, 118, 6, 77, 1, 148, 175, 28, + 88, 124, 103, 229, 155, 213, 96, 68, 7, 94, 216, 151, 207, 157, + 220, 67 ] }, "indices": [] @@ -452,19 +443,11 @@ mod tests { #[cfg(feature = "future_snark")] let message = [0u8; 32]; - #[cfg(not(feature = "future_snark"))] let params = Parameters { m: 10, k: 5, phi_f: 0.8, }; - - #[cfg(feature = "future_snark")] - let params = Parameters { - m: 10, - k: 5, - phi_f: 0.5, - }; let sk_1 = BlsSigningKey::generate(&mut rng); let sk_2 = BlsSigningKey::generate(&mut rng); let pk_1 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_1); diff --git a/mithril-stm/src/protocol/single_signature/signature_registered_party.rs b/mithril-stm/src/protocol/single_signature/signature_registered_party.rs index 4535ec963fe..68da1372a4b 100644 --- a/mithril-stm/src/protocol/single_signature/signature_registered_party.rs +++ b/mithril-stm/src/protocol/single_signature/signature_registered_party.rs @@ -129,24 +129,24 @@ mod tests { 78, 244, 112, 94, 47, 18, 158, 15, 78, 76, 80, 43, 116, 242, 116, 205, 252, 21, 194, 58, 162, 117, 201, 62, 40, 190, 21, 183, 178, 186, 196, 136 ], - "indexes": [4, 6, 7], + "indexes": [3, 4, 5, 6, 7], "signer_index": 1, "snark_signature": { "schnorr_signature": { "commitment_point": [ - 237, 203, 118, 185, 163, 93, 169, 219, 56, 140, 82, 163, 201, 105, - 102, 61, 53, 147, 176, 103, 249, 235, 88, 4, 209, 93, 224, 213, - 64, 203, 83, 39 + 198, 195, 131, 147, 143, 246, 147, 31, 112, 104, 4, 197, 184, 150, + 239, 16, 122, 195, 82, 217, 135, 174, 163, 231, 197, 102, 37, 57, + 253, 182, 126, 72 ], "response": [ - 6, 10, 237, 123, 141, 133, 58, 92, 27, 164, 59, 170, 175, 31, - 170, 164, 3, 227, 222, 189, 72, 84, 247, 104, 74, 238, 194, 75, - 90, 225, 241, 4 + 116, 67, 192, 99, 53, 189, 46, 158, 53, 70, 174, 132, 144, 179, + 25, 203, 87, 11, 59, 253, 155, 114, 211, 22, 16, 29, 4, 233, 203, + 127, 170, 6 ], "challenge": [ - 45, 201, 155, 247, 225, 138, 138, 175, 13, 86, 161, 49, 113, 38, - 201, 209, 121, 231, 164, 221, 194, 182, 117, 137, 175, 148, 101, - 219, 176, 159, 83, 63 + 128, 135, 196, 3, 229, 138, 6, 47, 81, 118, 6, 77, 1, 148, 175, + 28, 88, 124, 103, 229, 155, 213, 96, 68, 7, 94, 216, 151, 207, + 157, 220, 67 ] }, "indices": [] @@ -167,8 +167,8 @@ mod tests { 173, 141, 223, 53, 86, 104, 169, 168, 82, 136, 67, 233, 108, 18, 229, 93 ], [ - 71, 50, 212, 197, 127, 171, 19, 11, 101, 174, 40, 236, 218, 206, 75, 84, - 214, 210, 184, 151, 200, 8, 21, 12, 40, 105, 111, 155, 44, 98, 244, 33 + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, + 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 ] ] ] @@ -182,19 +182,11 @@ mod tests { #[cfg(feature = "future_snark")] let message = [0u8; 32]; - #[cfg(not(feature = "future_snark"))] let params = Parameters { m: 10, k: 5, phi_f: 0.8, }; - - #[cfg(feature = "future_snark")] - let params = Parameters { - m: 10, - k: 5, - phi_f: 0.5, - }; let sk_1 = BlsSigningKey::generate(&mut rng); let sk_2 = BlsSigningKey::generate(&mut rng); let pk_1 = VerificationKeyProofOfPossessionForConcatenation::from(&sk_1);