diff --git a/Cargo.lock b/Cargo.lock index 58c1823374f..59dedd64623 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4294,7 +4294,7 @@ dependencies = [ [[package]] name = "mithril-stm" -version = "0.6.4" +version = "0.7.0" dependencies = [ "anyhow", "blake2 0.10.6", diff --git a/mithril-stm/CHANGELOG.md b/mithril-stm/CHANGELOG.md index 642fc4e5893..496d8ad1cc8 100644 --- a/mithril-stm/CHANGELOG.md +++ b/mithril-stm/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.7.0 (12-16-2025) + +### Removed + +- Basic verifier functionality is removed. + ## 0.6.4 (12-12-2025) ### Changed diff --git a/mithril-stm/Cargo.toml b/mithril-stm/Cargo.toml index a7d22bcb28a..5eb5be43f32 100644 --- a/mithril-stm/Cargo.toml +++ b/mithril-stm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-stm" -version = "0.6.4" +version = "0.7.0" edition = { workspace = true } authors = { workspace = true } homepage = { workspace = true } diff --git a/mithril-stm/benches/size_benches.rs b/mithril-stm/benches/size_benches.rs index 6ecc0377392..e259c7f55f3 100644 --- a/mithril-stm/benches/size_benches.rs +++ b/mithril-stm/benches/size_benches.rs @@ -9,8 +9,8 @@ use rayon::iter::ParallelIterator; use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator}; use mithril_stm::{ - AggregateSignatureType, BasicVerifier, Clerk, Initializer, KeyRegistration, Parameters, Signer, - SingleSignature, SingleSignatureWithRegisteredParty, Stake, VerificationKey, + AggregateSignatureType, Clerk, Initializer, KeyRegistration, Parameters, Signer, + SingleSignature, }; fn size(k: u64, m: u64, nparties: usize, hash_name: &str) @@ -66,68 +66,6 @@ where ); } -fn core_size(k: u64, m: u64, nparties: usize) -where - H: Digest + Clone + Sync + Send + Default + FixedOutput, -{ - let mut rng = ChaCha20Rng::from_seed([0u8; 32]); - let mut msg = [0u8; 16]; - rng.fill_bytes(&mut msg); - - let mut public_signers: Vec<(VerificationKey, Stake)> = Vec::with_capacity(nparties); - let mut initializers: Vec = Vec::with_capacity(nparties); - - let parties = (0..nparties).map(|_| 1 + (rng.next_u64() % 9999)).collect::>(); - - let params = Parameters { k, m, phi_f: 0.2 }; - - for stake in parties { - let initializer = Initializer::new(params, stake, &mut rng); - initializers.push(initializer.clone()); - public_signers.push(( - initializer.get_verification_key_proof_of_possession().vk, - initializer.stake, - )); - } - - let core_verifier = BasicVerifier::new(&public_signers); - - let signers: Vec> = initializers - .into_iter() - .filter_map(|s| s.create_basic_signer(&core_verifier.eligible_parties)) - .collect(); - - let mut signatures: Vec = Vec::with_capacity(nparties); - for s in signers { - if let Some(sig) = s.basic_sign(&msg, core_verifier.total_stake) { - signatures.push(sig); - } - } - - let sig_reg_list = signatures - .iter() - .map(|sig| SingleSignatureWithRegisteredParty { - sig: sig.clone(), - reg_party: core_verifier.eligible_parties[sig.signer_index as usize], - }) - .collect::>(); - - let dedup_sigs = BasicVerifier::select_valid_signatures_for_k_indices( - &core_verifier.total_stake, - ¶ms, - &msg, - &sig_reg_list, - ) - .unwrap(); - - let mut size_sigs: usize = 0; - for sig in dedup_sigs { - size_sigs += sig.to_bytes().len(); - } - - println!("k = {k} | m = {m} | nr parties = {nparties}; {size_sigs} bytes",); -} - fn main() { println!("+-------------------+"); println!("| Size of proofs |"); @@ -140,14 +78,5 @@ fn main() { size::>(k, m, nparties, "Blake2b 512"); size::>(k, m, nparties, "Blake2b 256"); } - - println!("\n+-------------------------+"); - println!("| Size of core signatures |"); - println!("+-------------------------+"); - println!("+-------------------------+"); - - for (k, m, nparties) in params { - core_size::>(k, m, nparties); - } println!("+-------------------------+"); } diff --git a/mithril-stm/benches/stm.rs b/mithril-stm/benches/stm.rs index d4274d170e0..3edda900f1a 100644 --- a/mithril-stm/benches/stm.rs +++ b/mithril-stm/benches/stm.rs @@ -8,8 +8,8 @@ use rand_core::{RngCore, SeedableRng}; use rayon::prelude::*; use mithril_stm::{ - AggregateSignature, AggregateSignatureType, BasicVerifier, Clerk, Initializer, KeyRegistration, - Parameters, Signer, Stake, VerificationKey, + AggregateSignature, AggregateSignatureType, Clerk, Initializer, KeyRegistration, Parameters, + Signer, }; /// This benchmark framework is not ideal. We really have to think what is the best mechanism for @@ -155,59 +155,6 @@ fn batch_benches( } } -fn basic_verifier_benches(c: &mut Criterion, nr_parties: usize, params: Parameters) -where - H: Clone + Debug + Digest + Send + Sync + FixedOutput + Default, -{ - let mut group = c.benchmark_group("Core verifier"); - let mut rng = ChaCha20Rng::from_seed([0u8; 32]); - let mut msg = [0u8; 16]; - rng.fill_bytes(&mut msg); - - let mut public_signers: Vec<(VerificationKey, Stake)> = Vec::with_capacity(nr_parties); - let mut initializers: Vec = Vec::with_capacity(nr_parties); - - let param_string = format!( - "k: {}, m: {}, nr_parties: {}", - params.k, params.m, nr_parties - ); - - let stakes = (0..nr_parties) - .map(|_| 1 + (rng.next_u64() % 9999)) - .collect::>(); - - for stake in stakes { - let initializer = Initializer::new(params, stake, &mut rng); - initializers.push(initializer.clone()); - public_signers.push(( - initializer.get_verification_key_proof_of_possession().vk, - initializer.stake, - )); - } - - let core_verifier = BasicVerifier::new(&public_signers); - - let signers: Vec> = initializers - .into_iter() - .filter_map(|s| s.create_basic_signer(&core_verifier.eligible_parties)) - .collect(); - - group.bench_function(BenchmarkId::new("Play all lotteries", ¶m_string), |b| { - b.iter(|| { - signers[0].basic_sign(&msg, core_verifier.total_stake); - }) - }); - - let signatures = signers - .par_iter() - .filter_map(|p| p.basic_sign(&msg, core_verifier.total_stake)) - .collect::>(); - - group.bench_function(BenchmarkId::new("Core verification", ¶m_string), |b| { - b.iter(|| core_verifier.verify(&signatures, ¶ms, &msg)) - }); -} - fn batch_stm_benches_blake_300(c: &mut Criterion) { batch_benches::>( c, @@ -235,18 +182,6 @@ fn stm_benches_blake_300(c: &mut Criterion) { ); } -fn core_verifier_benches_blake_300(c: &mut Criterion) { - basic_verifier_benches::>( - c, - 300, - Parameters { - m: 150, - k: 25, - phi_f: 0.2, - }, - ); -} - fn batch_stm_benches_blake_2000(c: &mut Criterion) { batch_benches::>( c, @@ -274,23 +209,9 @@ fn stm_benches_blake_2000(c: &mut Criterion) { ); } -fn core_verifier_benches_blake_2000(c: &mut Criterion) { - basic_verifier_benches::>( - c, - 2000, - Parameters { - m: 1523, - k: 250, - phi_f: 0.2, - }, - ); -} - criterion_group!(name = benches; config = Criterion::default().nresamples(1000); targets = - core_verifier_benches_blake_300, - core_verifier_benches_blake_2000, stm_benches_blake_300, stm_benches_blake_2000, batch_stm_benches_blake_300, diff --git a/mithril-stm/src/proof_system/concatenation.rs b/mithril-stm/src/proof_system/concatenation.rs index 84dc64711c4..aacb37a4339 100644 --- a/mithril-stm/src/proof_system/concatenation.rs +++ b/mithril-stm/src/proof_system/concatenation.rs @@ -1,9 +1,10 @@ -use anyhow::Context; +use anyhow::{Context, anyhow}; use blake2::digest::{Digest, FixedOutput}; use serde::{Deserialize, Serialize}; +use std::collections::HashSet; use crate::{ - AggregateSignatureError, AggregateVerificationKey, BasicVerifier, Clerk, Parameters, + AggregateSignatureError, AggregateVerificationKey, AggregationError, Clerk, Parameters, RegisteredParty, SingleSignature, SingleSignatureWithRegisteredParty, StmResult, membership_commitment::MerkleBatchPath, signature_scheme::{BlsSignature, BlsVerificationKey}, @@ -43,17 +44,12 @@ impl ConcatenationProof { }) .collect::>(); - let avk = AggregateVerificationKey::from(&clerk.closed_reg); - let msgp = avk.get_merkle_tree_batch_commitment().concatenate_with_message(msg); - let mut unique_sigs = BasicVerifier::select_valid_signatures_for_k_indices( - &clerk.closed_reg.total_stake, - &clerk.params, - &msgp, - &sig_reg_list, - ) - .with_context( - || "Failed to aggregate unique signatures during selection for the k indices.", - )?; + let avk = clerk.compute_aggregate_verification_key(); + let mut unique_sigs = + Clerk::select_valid_signatures_for_k_indices(&clerk.params, msg, &sig_reg_list, &avk) + .with_context( + || "Failed to aggregate unique signatures during selection for the k indices.", + )?; unique_sigs.sort_unstable(); @@ -86,13 +82,35 @@ impl ConcatenationProof { parameters: &Parameters, ) -> StmResult<(Vec, Vec)> { let msgp = avk.get_merkle_tree_batch_commitment().concatenate_with_message(msg); - BasicVerifier::preliminary_verify( - &avk.get_total_stake(), - &self.signatures, - parameters, - &msgp, - ) - .with_context(|| "Preliminary verification of aggregate signatures failed.")?; + + let mut nr_indices = 0; + let mut unique_indices = HashSet::new(); + + for sig_reg in self.signatures.clone() { + sig_reg + .sig + .check_indices( + parameters, + &sig_reg.reg_party.1, + &msgp, + &avk.get_total_stake(), + ) + .with_context(|| "Preliminary verification for basic verifier failed.")?; + for &index in &sig_reg.sig.indexes { + unique_indices.insert(index); + nr_indices += 1; + } + } + + if nr_indices != unique_indices.len() { + return Err(anyhow!(AggregationError::IndexNotUnique)); + } + if (nr_indices as u64) < parameters.k { + return Err(anyhow!(AggregationError::NotEnoughSignatures( + nr_indices as u64, + parameters.k + ))); + } let leaves = self .signatures @@ -104,9 +122,7 @@ impl ConcatenationProof { .verify_leaves_membership_from_batch_path(&leaves, &self.batch_proof) .with_context(|| "Batch proof is invalid in preliminary verification.")?; - Ok(BasicVerifier::collect_signatures_verification_keys( - &self.signatures, - )) + Ok(self.collect_signatures_verification_keys()) } /// Verify concatenation proof, by checking that @@ -243,4 +259,23 @@ impl ConcatenationProof { batch_proof, }) } + + /// Collect and return `Vec, Vec` which will be used + /// by the aggregate verification. + pub(crate) fn collect_signatures_verification_keys( + &self, + ) -> (Vec, Vec) { + let sigs = self + .signatures + .iter() + .map(|sig_reg| sig_reg.sig.sigma) + .collect::>(); + let vks = self + .signatures + .iter() + .map(|sig_reg| sig_reg.reg_party.0) + .collect::>(); + + (sigs, vks) + } } diff --git a/mithril-stm/src/protocol/aggregate_signature/basic_verifier.rs b/mithril-stm/src/protocol/aggregate_signature/basic_verifier.rs deleted file mode 100644 index 0b14021123d..00000000000 --- a/mithril-stm/src/protocol/aggregate_signature/basic_verifier.rs +++ /dev/null @@ -1,257 +0,0 @@ -use std::collections::{BTreeMap, HashMap, HashSet}; - -use anyhow::{Context, anyhow}; - -use crate::{ - Index, Parameters, RegisteredParty, SingleSignature, SingleSignatureWithRegisteredParty, Stake, - StmResult, - membership_commitment::MerkleTreeLeaf, - signature_scheme::{BlsSignature, BlsVerificationKey}, -}; - -use super::AggregationError; - -/// Full node verifier including the list of eligible signers and the total stake of the system. -pub struct BasicVerifier { - /// List of registered parties. - pub eligible_parties: Vec, - /// Total stake of registered parties. - pub total_stake: Stake, -} - -impl BasicVerifier { - /// Setup a basic verifier for given list of signers. - /// * Collect the unique signers in a hash set, - /// * Calculate the total stake of the eligible signers, - /// * Sort the eligible signers. - pub fn new(public_signers: &[(BlsVerificationKey, Stake)]) -> Self { - let mut total_stake: Stake = 0; - let mut unique_parties = HashSet::new(); - for signer in public_signers.iter() { - let (res, overflow) = total_stake.overflowing_add(signer.1); - if overflow { - panic!("Total stake overflow"); - } - total_stake = res; - unique_parties.insert(MerkleTreeLeaf(signer.0, signer.1)); - } - - let mut eligible_parties: Vec<_> = unique_parties.into_iter().collect(); - eligible_parties.sort_unstable(); - BasicVerifier { - eligible_parties, - total_stake, - } - } - - /// Setup a basic verifier for given list of signers. - /// * Collect the unique signers in a hash set, - /// * Calculate the total stake of the eligible signers, - /// * Sort the eligible signers. - #[deprecated(since = "0.5.0", note = "Use `new` instead")] - pub fn setup(public_signers: &[(BlsVerificationKey, Stake)]) -> Self { - Self::new(public_signers) - } - - /// Preliminary verification that checks whether indices are unique and the quorum is achieved. - pub(crate) fn preliminary_verify( - total_stake: &Stake, - signatures: &[SingleSignatureWithRegisteredParty], - parameters: &Parameters, - msg: &[u8], - ) -> StmResult<()> { - let mut nr_indices = 0; - let mut unique_indices = HashSet::new(); - - for sig_reg in signatures { - sig_reg - .sig - .check_indices(parameters, &sig_reg.reg_party.1, msg, total_stake) - .with_context(|| "Preliminary verification for basic verifier failed.")?; - for &index in &sig_reg.sig.indexes { - unique_indices.insert(index); - nr_indices += 1; - } - } - - if nr_indices != unique_indices.len() { - return Err(anyhow!(AggregationError::IndexNotUnique)); - } - if (nr_indices as u64) < parameters.k { - return Err(anyhow!(AggregationError::NotEnoughSignatures( - nr_indices as u64, - parameters.k - ))); - } - - Ok(()) - } - - /// Given a slice of `sig_reg_list`, this function returns a new list of `sig_reg_list` with only valid indices. - /// In case of conflict (having several signatures for the same index) - /// it selects the smallest signature (i.e. takes the signature with the smallest scalar). - /// The function selects at least `self.k` indexes. - /// # Error - /// If there is no sufficient signatures, then the function fails. - // todo: We need to agree on a criteria to dedup (by default we use a BTreeMap that guarantees keys order) - // todo: not good, because it only removes index if there is a conflict (see benches) - pub fn select_valid_signatures_for_k_indices( - total_stake: &Stake, - params: &Parameters, - msg: &[u8], - sigs: &[SingleSignatureWithRegisteredParty], - ) -> 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 - .basic_verify( - params, - &sig_reg.reg_party.0, - &sig_reg.reg_party.1, - msg, - total_stake, - ) - .is_err() - { - continue; - } - for index in sig_reg.sig.indexes.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.sigma < previous_sig.sig.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) { - deduped_sig.sig.indexes = deduped_sig - .sig - .indexes - .clone() - .into_iter() - .filter(|i| !indexes.contains(i)) - .collect(); - } - - let size: Result = deduped_sig.sig.indexes.len().try_into(); - if let Ok(size) = size { - if dedup_sigs.contains(&deduped_sig) { - panic!("Should not reach!"); - } - 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 - ))) - } - - /// Given a slice of `sig_reg_list`, this function returns a new list of `sig_reg_list` with only valid indices. - /// In case of conflict (having several signatures for the same index) - /// it selects the smallest signature (i.e. takes the signature with the smallest scalar). - /// The function selects at least `self.k` indexes. - /// # Error - /// If there is no sufficient signatures, then the function fails. - // todo: We need to agree on a criteria to dedup (by default we use a BTreeMap that guarantees keys order) - // todo: not good, because it only removes index if there is a conflict (see benches) - #[deprecated( - since = "0.5.0", - note = "Use `select_valid_signatures_for_k_indices` instead" - )] - pub fn dedup_sigs_for_indices( - total_stake: &Stake, - params: &Parameters, - msg: &[u8], - sigs: &[SingleSignatureWithRegisteredParty], - ) -> StmResult> { - Self::select_valid_signatures_for_k_indices(total_stake, params, msg, sigs) - } - - /// Collect and return `Vec, Vec` which will be used - /// by the aggregate verification. - pub(crate) fn collect_signatures_verification_keys( - sig_reg_list: &[SingleSignatureWithRegisteredParty], - ) -> (Vec, Vec) { - let sigs = sig_reg_list - .iter() - .map(|sig_reg| sig_reg.sig.sigma) - .collect::>(); - let vks = sig_reg_list - .iter() - .map(|sig_reg| sig_reg.reg_party.0) - .collect::>(); - - (sigs, vks) - } - - /// Core verification - /// - /// Verify a list of signatures with respect to given message with given parameters. - pub fn verify( - &self, - signatures: &[SingleSignature], - parameters: &Parameters, - msg: &[u8], - ) -> StmResult<()> { - let sig_reg_list = signatures - .iter() - .map(|sig| SingleSignatureWithRegisteredParty { - sig: sig.clone(), - reg_party: self.eligible_parties[sig.signer_index as usize], - }) - .collect::>(); - - let unique_sigs = Self::select_valid_signatures_for_k_indices( - &self.total_stake, - parameters, - msg, - &sig_reg_list, - ) - .with_context(|| "Basic verification failed during selection of unique k indices.")?; - - Self::preliminary_verify(&self.total_stake, &unique_sigs, parameters, msg)?; - - let (sigs, vks) = Self::collect_signatures_verification_keys(&unique_sigs); - - BlsSignature::verify_aggregate(msg.to_vec().as_slice(), &vks, &sigs).with_context( - || "Basic verifier failed during BLS aggregate signature verification.", - )?; - - Ok(()) - } -} diff --git a/mithril-stm/src/protocol/aggregate_signature/clerk.rs b/mithril-stm/src/protocol/aggregate_signature/clerk.rs index 8a76709c2b6..f3627f4b252 100644 --- a/mithril-stm/src/protocol/aggregate_signature/clerk.rs +++ b/mithril-stm/src/protocol/aggregate_signature/clerk.rs @@ -1,18 +1,16 @@ -use anyhow::Context; +use anyhow::{Context, anyhow}; use blake2::digest::{Digest, FixedOutput}; - -#[cfg(feature = "future_snark")] -use anyhow::anyhow; - -#[cfg(feature = "future_snark")] -use super::AggregationError; +use std::collections::{BTreeMap, HashMap, HashSet}; use crate::{ - ClosedKeyRegistration, Index, Parameters, Signer, SingleSignature, Stake, StmResult, - VerificationKey, proof_system::ConcatenationProof, + ClosedKeyRegistration, Index, Parameters, Signer, SingleSignature, + SingleSignatureWithRegisteredParty, Stake, StmResult, VerificationKey, + proof_system::ConcatenationProof, }; -use super::{AggregateSignature, AggregateSignatureType, AggregateVerificationKey}; +use super::{ + AggregateSignature, AggregateSignatureType, AggregateVerificationKey, AggregationError, +}; /// `Clerk` can verify and aggregate `SingleSignature`s and verify `AggregateSignature`s. /// Clerks can only be generated with the registration closed. @@ -143,4 +141,210 @@ impl Clerk { pub fn get_reg_party(&self, party_index: &Index) -> Option<(VerificationKey, Stake)> { Self::get_registered_party_for_index(self, party_index) } + + /// Given a slice of `sig_reg_list`, this function returns a new list of `sig_reg_list` with only valid indices. + /// In case of conflict (having several signatures for the same index) + /// it selects the smallest signature (i.e. takes the signature with the smallest scalar). + /// The function selects at least `self.k` indexes. + /// # Error + /// If there is no sufficient signatures, then the function fails. + // todo: We need to agree on a criteria to dedup (by default we use a BTreeMap that guarantees keys order) + // todo: not good, because it only removes index if there is a conflict (see benches) + pub fn select_valid_signatures_for_k_indices( + params: &Parameters, + msg: &[u8], + sigs: &[SingleSignatureWithRegisteredParty], + avk: &AggregateVerificationKey, + ) -> 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 + .verify(params, &sig_reg.reg_party.0, &sig_reg.reg_party.1, avk, msg) + .is_err() + { + continue; + } + for index in sig_reg.sig.indexes.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.sigma < previous_sig.sig.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) { + deduped_sig.sig.indexes = deduped_sig + .sig + .indexes + .clone() + .into_iter() + .filter(|i| !indexes.contains(i)) + .collect(); + } + + let size: Result = deduped_sig.sig.indexes.len().try_into(); + if let Ok(size) = size { + if dedup_sigs.contains(&deduped_sig) { + panic!("Should not reach!"); + } + 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 + ))) + } +} + +#[cfg(test)] +mod tests { + use blake2::{Blake2b, digest::consts::U32}; + use proptest::prelude::*; + use rand_chacha::ChaCha20Rng; + use rand_core::{RngCore, SeedableRng}; + + use crate::{ + Clerk, ClosedKeyRegistration, Initializer, KeyRegistration, Parameters, + SingleSignatureWithRegisteredParty, + }; + + use super::AggregationError; + + type D = Blake2b; + + proptest! { + #![proptest_config(ProptestConfig::with_cases(50))] + + #[test] + fn test_dedup( + seed in any::<[u8; 32]>(), + msg in any::<[u8;16]>(), + nparties in 1_usize..10, + m in 1_u64..20, + k in 1_u64..10, + phi_f in 0.1_f64..1.0, + num_invalid_sigs_per_party in 0_usize..3, + ) { + let params = Parameters { m, k, phi_f }; + let mut rng = ChaCha20Rng::from_seed(seed); + + // False messages + let mut false_msgs = Vec::new(); + for _ in 0..num_invalid_sigs_per_party { + let mut false_msg = vec![0u8; 32]; + rng.fill_bytes(&mut false_msg); + if false_msg == msg { + false_msg[0] = msg[0].wrapping_add(1); + } + false_msgs.push(false_msg); + } + + let mut key_registration = KeyRegistration::init(); + let mut initializers = Vec::new(); + + for i in 0..nparties { + let stake = (i as u64 + 1) * 10; + let initializer = Initializer::new(params, stake, &mut rng); + key_registration.register(initializer.stake, initializer.pk).unwrap(); + initializers.push(initializer); + } + + let closed_registration: ClosedKeyRegistration = key_registration.close(); + + let signers: Vec<_> = initializers + .into_iter() + .map(|init| init.create_signer(closed_registration.clone()).unwrap()) + .collect(); + + let clerk = Clerk::new_clerk_from_signer(&signers[0]); + let avk = clerk.compute_aggregate_verification_key(); + + let mut all_sigs = Vec::new(); + for signer in &signers { + // Add invalid signatures + for false_msg in &false_msgs { + if let Some(sig) = signer.sign(false_msg) { + all_sigs.push(sig); + } + } + // Add valid signatures + if let Some(sig) = signer.sign(&msg) { + all_sigs.push(sig); + } + } + + let sig_reg_list = all_sigs + .iter() + .map(|sig| SingleSignatureWithRegisteredParty { + sig: sig.clone(), + reg_party: clerk.closed_reg.reg_parties[sig.signer_index as usize], + }) + .collect::>(); + + let dedup_result = + Clerk::select_valid_signatures_for_k_indices(¶ms, &msg, &sig_reg_list, &avk); + + match dedup_result { + Ok(valid_sigs) => { + assert!(!valid_sigs.is_empty(), "Should have at least one valid signature"); + + for passed_sigs in valid_sigs { + let signer = &signers[passed_sigs.sig.signer_index as usize]; + let verify_result = passed_sigs.sig.verify( + ¶ms, + &signer.get_verification_key(), + &signer.get_stake(), + &avk, + &msg, + ); + assert!(verify_result.is_ok(), "All returned signatures should verify: {:?}", verify_result); + } + } + Err(error) => { + assert!( + matches!( + error.downcast_ref::(), + Some(AggregationError::NotEnoughSignatures(..)) + ), + "Expected NotEnoughSignatures, got: {:?}", error + ); + } + } + } + } } diff --git a/mithril-stm/src/protocol/aggregate_signature/mod.rs b/mithril-stm/src/protocol/aggregate_signature/mod.rs index 3a59f190d0e..ee96c2c6be0 100644 --- a/mithril-stm/src/protocol/aggregate_signature/mod.rs +++ b/mithril-stm/src/protocol/aggregate_signature/mod.rs @@ -1,11 +1,9 @@ mod aggregate_key; -mod basic_verifier; mod clerk; mod error; mod signature; pub use aggregate_key::*; -pub use basic_verifier::*; pub use clerk::*; pub use error::*; pub use signature::*; @@ -23,14 +21,11 @@ mod tests { use std::collections::{HashMap, HashSet}; use crate::{ - Initializer, KeyRegistration, Parameters, Signer, SingleSignature, - SingleSignatureWithRegisteredParty, Stake, StmResult, - membership_commitment::MerkleBatchPath, signature_scheme::BlsVerificationKey, + Initializer, KeyRegistration, Parameters, Signer, SingleSignature, Stake, StmResult, + membership_commitment::MerkleBatchPath, }; - use super::{ - AggregateSignature, AggregateSignatureType, AggregationError, BasicVerifier, Clerk, - }; + use super::{AggregateSignature, AggregateSignatureType, AggregationError, Clerk}; type Sig = AggregateSignature; type D = Blake2b; @@ -196,50 +191,6 @@ mod tests { } } - proptest! { - #![proptest_config(ProptestConfig::with_cases(50))] - - #[test] - /// Test that `dedup_sigs_for_indices` only takes valid signatures. - fn test_dedup(msg in any::<[u8; 16]>()) { - let false_msg = [1u8; 20]; - let params = Parameters { m: 1, k: 1, phi_f: 1.0 }; - let ps = setup_equal_parties(params, 1); - let clerk = Clerk::new_clerk_from_signer(&ps[0]); - let avk = clerk.compute_aggregate_verification_key(); - let mut sigs = Vec::with_capacity(2); - - if let Some(sig) = ps[0].sign(&false_msg) { - sigs.push(sig); - } - - if let Some(sig) = ps[0].sign(&msg) { - sigs.push(sig); - } - - let sig_reg_list = sigs - .iter() - .map(|sig| SingleSignatureWithRegisteredParty { - sig: sig.clone(), - reg_party: clerk.closed_reg.reg_parties[sig.signer_index as usize], - }) - .collect::>(); - - let msgp = avk.get_merkle_tree_batch_commitment().concatenate_with_message(&msg); - let dedup_result = BasicVerifier::select_valid_signatures_for_k_indices( - &clerk.closed_reg.total_stake, - ¶ms, - &msgp, - &sig_reg_list, - ); - assert!(dedup_result.is_ok(), "dedup failure {dedup_result:?}"); - for passed_sigs in dedup_result.unwrap() { - let verify_result = passed_sigs.sig.verify(¶ms, &ps[0].get_verification_key(), &ps[0].get_stake(), &avk, &msg); - assert!(verify_result.is_ok(), "verify {verify_result:?}"); - } - } - } - proptest! { #![proptest_config(ProptestConfig::with_cases(50))] @@ -339,6 +290,8 @@ mod tests { } proptest! { + #![proptest_config(ProptestConfig::with_cases(50))] + #[test] /// Test that when a party creates a signature it can be verified fn test_sig(msg in any::<[u8;16]>()) { @@ -354,7 +307,7 @@ mod tests { } proptest! { - #![proptest_config(ProptestConfig::with_cases(10))] + #![proptest_config(ProptestConfig::with_cases(50))] #[test] fn test_parameters_serialize_deserialize(m in any::(), k in any::(), phi_f in any::()) { let params = Parameters { m, k, phi_f }; @@ -409,7 +362,7 @@ mod tests { } proptest! { - #![proptest_config(ProptestConfig::with_cases(10))] + #![proptest_config(ProptestConfig::with_cases(50))] #[test] /// Test that when the adversaries do not hold sufficient stake, they can not form a quorum @@ -449,6 +402,8 @@ mod tests { } proptest! { + #![proptest_config(ProptestConfig::with_cases(50))] + // Each of the tests below corresponds to falsifying a conjunct in the // definition of a valid signature #[test] @@ -496,99 +451,4 @@ mod tests { }) } } - - // --------------------------------------------------------------------- - // Basic verifier - // --------------------------------------------------------------------- - fn setup_equal_core_parties( - params: Parameters, - nparties: usize, - ) -> (Vec, Vec<(BlsVerificationKey, Stake)>) { - let stake = vec![1; nparties]; - setup_core_parties(params, stake) - } - - fn setup_core_parties( - params: Parameters, - stake: Vec, - ) -> (Vec, Vec<(BlsVerificationKey, Stake)>) { - let mut trng = TestRng::deterministic_rng(ChaCha); - let mut rng = ChaCha20Rng::from_seed(trng.random()); - - let ps = stake - .into_iter() - .map(|stake| Initializer::new(params, stake, &mut rng)) - .collect::>(); - - let public_signers = ps - .iter() - .map(|s| (s.pk.vk, s.stake)) - .collect::>(); - - (ps, public_signers) - } - - fn find_core_signatures( - msg: &[u8], - ps: &[Signer], - total_stake: Stake, - is: &[usize], - ) -> Vec { - let mut sigs = Vec::new(); - for i in is { - if let Some(sig) = ps[*i].basic_sign(msg, total_stake) { - sigs.push(sig); - } - } - sigs - } - - proptest! { - #![proptest_config(ProptestConfig::with_cases(50))] - - #[test] - fn test_core_verifier(nparties in 2_usize..30, - m in 10_u64..20, - k in 1_u64..5, - msg in any::<[u8;16]>()) { - - let params = Parameters { m, k, phi_f: 0.2 }; - let (initializers, public_signers) = setup_equal_core_parties(params, nparties); - let all_ps: Vec = (0..nparties).collect(); - - let core_verifier = BasicVerifier::new(&public_signers); - - let signers = initializers - .into_iter() - .filter_map(|s| s.create_basic_signer(&core_verifier.eligible_parties)) - .collect::>>(); - - let signatures = find_core_signatures(&msg, &signers, core_verifier.total_stake, &all_ps); - - let verify_result = core_verifier.verify(&signatures, ¶ms, &msg); - - match verify_result{ - Ok(_) => { - assert!(verify_result.is_ok(), "Verification failed: {verify_result:?}"); - } - Err(error) => { assert!( - matches!( - error.downcast_ref::(), - Some(AggregationError::NotEnoughSignatures{..}) - ), - "Unexpected error: {error:?}"); - } - } - } - - #[test] - fn test_total_stake_core_verifier(nparties in 2_usize..30, - m in 10_u64..20, - k in 1_u64..5,) { - let params = Parameters { m, k, phi_f: 0.2 }; - let (_initializers, public_signers) = setup_equal_core_parties(params, nparties); - let core_verifier = BasicVerifier::new(&public_signers); - assert_eq!(nparties as u64, core_verifier.total_stake, "Total stake expected: {}, got: {}.", nparties, core_verifier.total_stake); - } - } } diff --git a/mithril-stm/src/protocol/mod.rs b/mithril-stm/src/protocol/mod.rs index 7975dda24a2..d7603ab591e 100644 --- a/mithril-stm/src/protocol/mod.rs +++ b/mithril-stm/src/protocol/mod.rs @@ -8,7 +8,7 @@ mod single_signature; pub use aggregate_signature::{ AggregateSignature, AggregateSignatureError, AggregateSignatureType, AggregateVerificationKey, - AggregationError, BasicVerifier, Clerk, + AggregationError, Clerk, }; pub(crate) use eligibility_check::is_lottery_won; pub use error::RegisterError; @@ -27,9 +27,6 @@ pub use aggregate_signature::AggregateVerificationKey as StmAggrVerificationKey; #[deprecated(since = "0.5.0", note = "Use `Clerk` instead")] pub use aggregate_signature::Clerk as StmClerk; -#[deprecated(since = "0.5.0", note = "Use `BasicVerifier` instead")] -pub use aggregate_signature::BasicVerifier as CoreVerifier; - #[deprecated(since = "0.5.0", note = "Use `Parameters` instead")] pub use parameters::Parameters as StmParameters; diff --git a/mithril-stm/src/protocol/participant/initializer.rs b/mithril-stm/src/protocol/participant/initializer.rs index f2c16ef7e55..00e59d7149f 100644 --- a/mithril-stm/src/protocol/participant/initializer.rs +++ b/mithril-stm/src/protocol/participant/initializer.rs @@ -5,7 +5,7 @@ use rand_core::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; use crate::{ - ClosedKeyRegistration, Parameters, RegisterError, RegisteredParty, Stake, StmResult, + ClosedKeyRegistration, Parameters, RegisterError, Stake, StmResult, signature_scheme::{BlsSigningKey, BlsVerificationKeyProofOfPossession}, }; @@ -121,48 +121,6 @@ impl Initializer { Self::create_signer(self, closed_reg) } - /// Creates a new basic signer that does not include closed registration. - /// Takes `eligible_parties` as a parameter and determines the signer's index in the parties. - /// `eligible_parties` is verified and trusted which is only run by a full-node - /// that has already verified the parties. - pub fn create_basic_signer( - self, - eligible_parties: &[RegisteredParty], - ) -> Option> { - let mut parties = eligible_parties.to_vec(); - parties.sort_unstable(); - let mut my_index = None; - for (i, rp) in parties.iter().enumerate() { - if rp.0 == self.pk.vk { - my_index = Some(i as u64); - break; - } - } - if let Some(index) = my_index { - Some(Signer::set_basic_signer( - index, - self.stake, - self.params, - self.sk, - self.pk.vk, - )) - } else { - None - } - } - - /// Creates a new basic signer that does not include closed registration. - /// Takes `eligible_parties` as a parameter and determines the signer's index in the parties. - /// `eligible_parties` is verified and trusted which is only run by a full-node - /// that has already verified the parties. - #[deprecated(since = "0.5.0", note = "Use `create_basic_signer` instead")] - pub fn new_core_signer( - self, - eligible_parties: &[RegisteredParty], - ) -> Option> { - Self::create_basic_signer(self, eligible_parties) - } - /// Convert to bytes /// # Layout /// * Stake (u64) diff --git a/mithril-stm/src/protocol/participant/signer.rs b/mithril-stm/src/protocol/participant/signer.rs index a0f845d780d..2dcc14f5877 100644 --- a/mithril-stm/src/protocol/participant/signer.rs +++ b/mithril-stm/src/protocol/participant/signer.rs @@ -58,36 +58,6 @@ impl Signer { Self::set_signer(signer_index, stake, params, sk, vk, closed_reg) } - /// Create a basic signer (no registration data) for given input - pub(crate) fn set_basic_signer( - signer_index: u64, - stake: Stake, - params: Parameters, - sk: BlsSigningKey, - vk: VerificationKey, - ) -> Signer { - Self { - signer_index, - stake, - params, - sk, - vk, - closed_reg: None, - } - } - - /// Create a core signer (no registration data) for given input - #[deprecated(since = "0.5.0", note = "Use `set_basic_signer` instead")] - pub fn set_core_signer( - signer_index: u64, - stake: Stake, - params: Parameters, - sk: BlsSigningKey, - vk: VerificationKey, - ) -> Signer { - Self::set_basic_signer(signer_index, stake, params, sk, vk) - } - /// This function produces a signature following the description of Section 2.4. /// Once the signature is produced, this function checks whether any index in `[0,..,self.params.m]` /// wins the lottery by evaluating the dense mapping. @@ -100,13 +70,19 @@ impl Signer { .merkle_tree .to_merkle_tree_batch_commitment() .concatenate_with_message(msg); - let signature = self.basic_sign(&msgp, closed_reg.total_stake)?; + let sigma = self.sk.sign(&msgp); + + let indexes = self.check_lottery(&msgp, &sigma, closed_reg.total_stake); - Some(SingleSignature { - sigma: signature.sigma, - signer_index: self.signer_index, - indexes: signature.indexes, - }) + if !indexes.is_empty() { + Some(SingleSignature { + sigma, + indexes, + signer_index: self.signer_index, + }) + } else { + None + } } /// Extract the verification key. @@ -125,36 +101,6 @@ impl Signer { self.stake } - /// A basic signature generated without closed key registration. - /// The basic signature can be verified by basic verifier. - /// Once the signature is produced, this function checks whether any index in `[0,..,self.params.m]` - /// wins the lottery by evaluating the dense mapping. - /// It records all the winning indexes in `Self.indexes`. - pub fn basic_sign(&self, msg: &[u8], total_stake: Stake) -> Option { - let sigma = self.sk.sign(msg); - - let indexes = self.check_lottery(msg, &sigma, total_stake); - if !indexes.is_empty() { - Some(SingleSignature { - sigma, - indexes, - signer_index: self.signer_index, - }) - } else { - None - } - } - - /// A basic signature generated without closed key registration. - /// The basic signature can be verified by basic verifier. - /// Once the signature is produced, this function checks whether any index in `[0,..,self.params.m]` - /// wins the lottery by evaluating the dense mapping. - /// It records all the winning indexes in `Self.indexes`. - #[deprecated(since = "0.5.0", note = "Use `basic_sign` instead")] - pub fn core_sign(&self, msg: &[u8], total_stake: Stake) -> Option { - Self::basic_sign(self, msg, total_stake) - } - /// Collects and returns the winning indices. pub fn check_lottery(&self, msg: &[u8], sigma: &BlsSignature, total_stake: Stake) -> Vec { let mut indexes = Vec::new(); diff --git a/mithril-stm/src/protocol/single_signature/signature.rs b/mithril-stm/src/protocol/single_signature/signature.rs index f477497ff43..67abcc1560d 100644 --- a/mithril-stm/src/protocol/single_signature/signature.rs +++ b/mithril-stm/src/protocol/single_signature/signature.rs @@ -37,7 +37,13 @@ impl SingleSignature { msg: &[u8], ) -> StmResult<()> { let msgp = avk.get_merkle_tree_batch_commitment().concatenate_with_message(msg); - self.basic_verify(params, pk, stake, &msgp, &avk.get_total_stake()) + self.sigma.verify(&msgp, pk).with_context(|| { + format!( + "Single signature verification failed for signer index {}.", + self.signer_index + ) + })?; + self.check_indices(params, stake, &msgp, &avk.get_total_stake()) .with_context(|| { format!( "Single signature verification failed for signer index {}.", @@ -141,38 +147,6 @@ impl SingleSignature { pub fn cmp_stm_sig(&self, other: &Self) -> Ordering { Self::compare_signer_index(self, other) } - - /// Verify a basic signature by checking that the lottery was won, - /// the indexes are in the desired range and the underlying multi signature validates. - pub(crate) fn basic_verify( - &self, - params: &Parameters, - pk: &VerificationKey, - stake: &Stake, - msg: &[u8], - total_stake: &Stake, - ) -> StmResult<()> { - self.sigma - .verify(msg, pk) - .with_context(|| "Basic verification of single signature failed.")?; - self.check_indices(params, stake, msg, total_stake) - .with_context(|| "Basic verification of single signature failed.")?; - - Ok(()) - } - - /// Will be deprecated. Use `basic_verify` instead. - #[deprecated(since = "0.5.0", note = "Use `basic_verify` instead")] - pub fn core_verify( - &self, - params: &Parameters, - pk: &VerificationKey, - stake: &Stake, - msg: &[u8], - total_stake: &Stake, - ) -> StmResult<()> { - Self::basic_verify(self, params, pk, stake, msg, total_stake) - } } impl Hash for SingleSignature { diff --git a/mithril-stm/tests/stm_basic.rs b/mithril-stm/tests/stm_basic.rs deleted file mode 100644 index 1c37026143f..00000000000 --- a/mithril-stm/tests/stm_basic.rs +++ /dev/null @@ -1,76 +0,0 @@ -use blake2::Blake2b; -use digest::consts::U32; -use mithril_stm::{ - AggregationError, BasicVerifier, Initializer, Parameters, Signer, SingleSignature, Stake, - VerificationKey, -}; -use rand_chacha::ChaCha20Rng; -use rand_core::{RngCore, SeedableRng}; -type D = Blake2b; - -#[test] -fn test_core_verifier() { - let nparties: usize = 32; - let mut rng = ChaCha20Rng::from_seed([0u8; 32]); - let mut msg = [0u8; 32]; - rng.fill_bytes(&mut msg); - let mut public_signers: Vec<(VerificationKey, Stake)> = Vec::with_capacity(nparties); - let mut initializers: Vec = Vec::with_capacity(nparties); - - ////////////////////////// - // initialization phase // - ////////////////////////// - - let params = Parameters { - k: 357, - m: 2642, - phi_f: 0.2, - }; - - let parties = (0..nparties).map(|_| 1 + (rng.next_u64() % 9999)).collect::>(); - - for stake in parties { - let initializer = Initializer::new(params, stake, &mut rng); - initializers.push(initializer.clone()); - public_signers.push(( - initializer.get_verification_key_proof_of_possession().vk, - initializer.stake, - )); - } - - let core_verifier = BasicVerifier::new(&public_signers); - - let signers: Vec> = initializers - .into_iter() - .filter_map(|s| s.create_basic_signer(&core_verifier.eligible_parties)) - .collect(); - - ////////////////////////// - ///// operation phase //// - ////////////////////////// - - let mut signatures: Vec = Vec::with_capacity(nparties); - for s in signers { - if let Some(sig) = s.basic_sign(&msg, core_verifier.total_stake) { - signatures.push(sig); - } - } - let verify_result = core_verifier.verify(&signatures, ¶ms, &msg); - - match verify_result { - Ok(_) => { - assert!( - verify_result.is_ok(), - "Verification failed: {verify_result:?}" - ); - } - Err(error) => match error.downcast_ref::() { - Some(AggregationError::NotEnoughSignatures(nr_indices, _k)) => { - assert!((nr_indices) < ¶ms.k) - } - _ => { - panic!("Unexpected error: {:?}", error); - } - }, - } -}