diff --git a/mithril-stm/benches/size_benches.rs b/mithril-stm/benches/size_benches.rs index 0af3ca80d4c..87cf9984bdd 100644 --- a/mithril-stm/benches/size_benches.rs +++ b/mithril-stm/benches/size_benches.rs @@ -32,7 +32,14 @@ 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().try_into().unwrap()).unwrap(); + key_reg + .register( + stake, + &p.get_verification_key_proof_of_possession_for_concatenation(), + #[cfg(feature = "future_snark")] + p.schnorr_verification_key, + ) + .unwrap(); ps.push(p); } diff --git a/mithril-stm/benches/stm.rs b/mithril-stm/benches/stm.rs index af792edbbad..e32a466c10e 100644 --- a/mithril-stm/benches/stm.rs +++ b/mithril-stm/benches/stm.rs @@ -44,7 +44,14 @@ 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().try_into().unwrap()).unwrap(); + key_reg + .register( + p.stake, + &p.get_verification_key_proof_of_possession_for_concatenation(), + #[cfg(feature = "future_snark")] + p.schnorr_verification_key, + ) + .unwrap(); } }) }); @@ -136,7 +143,14 @@ fn batch_benches( } let mut key_reg = KeyRegistration::initialize(); for p in initializers.iter() { - key_reg.register_by_entry(&p.clone().try_into().unwrap()).unwrap(); + key_reg + .register( + p.stake, + &p.get_verification_key_proof_of_possession_for_concatenation(), + #[cfg(feature = "future_snark")] + p.schnorr_verification_key, + ) + .unwrap(); } let closed_reg = key_reg.close_registration(¶ms).unwrap(); diff --git a/mithril-stm/examples/key_registration.rs b/mithril-stm/examples/key_registration.rs index 92f732e0f09..3f07117d407 100644 --- a/mithril-stm/examples/key_registration.rs +++ b/mithril-stm/examples/key_registration.rs @@ -7,7 +7,7 @@ use rand_core::{RngCore, SeedableRng}; use mithril_stm::{ AggregateSignature, AggregateSignatureType, AncillaryGenesisData, AncillaryProofInput, Clerk, ClosedKeyRegistration, Initializer, KeyRegistration, MithrilMembershipDigest, Parameters, - RegistrationEntry, Stake, VerificationKeyProofOfPossessionForConcatenation, + Stake, VerificationKeyProofOfPossessionForConcatenation, }; type D = MithrilMembershipDigest; @@ -236,14 +236,14 @@ fn local_reg( }; // data, such as the public key, stake and id. for (pk, _) in pks.iter().zip(ids.iter()) { - let entry = RegistrationEntry::new( - *pk, - 1, - #[cfg(feature = "future_snark")] - None, - ) - .unwrap(); - local_keyreg.register_by_entry(&entry).unwrap(); + local_keyreg + .register( + 1, + pk, + #[cfg(feature = "future_snark")] + None, + ) + .unwrap(); } local_keyreg.close_registration(¶ms).unwrap() } diff --git a/mithril-stm/src/lib.rs b/mithril-stm/src/lib.rs index a74415bff5a..9414b13208c 100644 --- a/mithril-stm/src/lib.rs +++ b/mithril-stm/src/lib.rs @@ -15,7 +15,7 @@ //! //! use mithril_stm::{ //! AggregateSignatureType, AggregationError, AncillaryGenesisData, AncillaryProofInput, Clerk, -//! Initializer, KeyRegistration, Parameters, RegistrationEntry, Signer, SingleSignature, +//! Initializer, KeyRegistration, Parameters, Signer, SingleSignature, //! MithrilMembershipDigest, //! }; //! @@ -59,13 +59,12 @@ //! // Create keys for this party //! let p = Initializer::new(params, stake, &mut rng); //! // Register keys with the KeyRegistration service -//! let entry = RegistrationEntry::new( -//! p.get_verification_key_proof_of_possession_for_concatenation(), +//! key_reg.register( //! p.stake, +//! &p.get_verification_key_proof_of_possession_for_concatenation(), //! #[cfg(feature = "future_snark")] p.schnorr_verification_key, //! ) //! .unwrap(); -//! key_reg.register_by_entry(&entry).unwrap(); //! ps.push(p); //! } //! @@ -152,11 +151,12 @@ pub use protocol::{ AggregateSignature, AggregateSignatureError, AggregateSignatureType, AggregateVerificationKey, AggregationError, AncillaryGenesisData, AncillaryProofInput, AncillaryProofOutput, AncillaryProverData, AncillaryVerifierData, Clerk, ClosedKeyRegistration, - ClosedRegistrationEntry, GenesisVerificationKeyBundle, Initializer, KeyRegistration, - Parameters, RegisterError, RegistrationEntry, RegistrationEntryForConcatenation, - SignatureError, Signer, SingleSignature, SingleSignatureWithRegisteredParty, - VerificationKeyForConcatenation, VerificationKeyProofOfPossessionForConcatenation, + GenesisVerificationKeyBundle, Initializer, KeyRegistration, Parameters, RegisterError, + RegistrationEntryForConcatenation, SignatureError, Signer, SingleSignature, + SingleSignatureWithRegisteredParty, VerificationKeyForConcatenation, + VerificationKeyProofOfPossessionForConcatenation, }; +pub(crate) use protocol::{ClosedRegistrationEntry, RegistrationEntry}; pub use signature_scheme::BlsSignatureError; use blake2::{Blake2b, digest::consts::U32}; diff --git a/mithril-stm/src/membership_commitment/merkle_tree/commitment.rs b/mithril-stm/src/membership_commitment/merkle_tree/commitment.rs index d24b9ff0a74..d287d65230d 100644 --- a/mithril-stm/src/membership_commitment/merkle_tree/commitment.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/commitment.rs @@ -176,12 +176,23 @@ impl MerkleTreeBatchCommitment ))); } - let nr_nodes = self.nr_leaves + self.nr_leaves.next_power_of_two() - 1; + let nr_nodes = self + .nr_leaves + .checked_next_power_of_two() + .and_then(|nr_nodes| nr_nodes.checked_add(self.nr_leaves)) + .and_then(|nr_nodes| nr_nodes.checked_sub(1)) + .ok_or(MerkleTreeError::SerializationError)?; ordered_indices = ordered_indices .into_iter() - .map(|i| i + self.nr_leaves.next_power_of_two() - 1) - .collect(); + .map(|i| { + self.nr_leaves + .checked_next_power_of_two() + .and_then(|idx| idx.checked_add(i)) + .and_then(|idx| idx.checked_sub(1)) + .ok_or(MerkleTreeError::SerializationError) + }) + .collect::, _>>()?; let mut idx = ordered_indices[0]; // First we need to hash the leave values diff --git a/mithril-stm/src/membership_commitment/merkle_tree/tree.rs b/mithril-stm/src/membership_commitment/merkle_tree/tree.rs index b71fa0cff92..34e8c3ce888 100644 --- a/mithril-stm/src/membership_commitment/merkle_tree/tree.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/tree.rs @@ -3,12 +3,14 @@ use std::marker::PhantomData; use digest::{Digest, FixedOutput}; use serde::{Deserialize, Serialize}; -use crate::StmResult; -use crate::codec; +#[cfg(test)] +use crate::{StmResult, codec}; +#[cfg(test)] +use super::MerkleTreeError; use super::{ - MerkleBatchPath, MerkleTreeBatchCommitment, MerkleTreeError, MerkleTreeLeaf, left_child, - parent, right_child, sibling, + MerkleBatchPath, MerkleTreeBatchCommitment, MerkleTreeLeaf, left_child, parent, right_child, + sibling, }; #[cfg(feature = "future_snark")] // TODO: remove this allow dead_code directive when function is called or future_snark is activated @@ -147,6 +149,7 @@ impl MerkleTree { } /// Convert a `MerkleTree` into a byte string. + #[cfg(test)] pub fn to_bytes(&self) -> StmResult> { codec::to_cbor_bytes(self) } @@ -154,6 +157,7 @@ impl MerkleTree { /// Try to convert a byte string into a `MerkleTree`. /// # Error /// It returns error if conversion fails. + #[cfg(test)] pub fn from_bytes(bytes: &[u8]) -> StmResult { codec::from_versioned_bytes(bytes, Self::from_bytes_legacy) } @@ -162,27 +166,38 @@ impl MerkleTree { /// # Layout /// * Number of leaves committed in the Merkle Tree (as u64) /// * All nodes of the merkle tree (starting with the root) + #[cfg(test)] fn from_bytes_legacy(bytes: &[u8]) -> StmResult { let mut u64_bytes = [0u8; 8]; u64_bytes.copy_from_slice(bytes.get(..8).ok_or(MerkleTreeError::SerializationError)?); let n = usize::try_from(u64::from_be_bytes(u64_bytes)) .map_err(|_| MerkleTreeError::SerializationError)?; - let num_nodes = n + n.next_power_of_two() - 1; - let mut nodes = Vec::with_capacity(num_nodes); + let num_nodes = n + .checked_next_power_of_two() + .and_then(|num_nodes| num_nodes.checked_add(n)) + .and_then(|num_nodes| num_nodes.checked_sub(1)) + .ok_or(MerkleTreeError::SerializationError)?; + let mut nodes = Vec::new(); for i in 0..num_nodes { + let range_low = i + .checked_mul(::output_size()) + .and_then(|rl| rl.checked_add(16)) + .ok_or(MerkleTreeError::SerializationError)?; + let range_high = i + .checked_add(1) + .and_then(|rh| rh.checked_mul(::output_size())) + .and_then(|rh| rh.checked_add(16)) + .ok_or(MerkleTreeError::SerializationError)?; nodes.push( bytes - .get( - 8 + i * ::output_size() - ..8 + (i + 1) * ::output_size(), - ) + .get(range_low..range_high) .ok_or(MerkleTreeError::SerializationError)? .to_vec(), ); } Ok(Self { nodes, - leaf_off: num_nodes - n, + leaf_off: num_nodes.checked_sub(n).ok_or(MerkleTreeError::SerializationError)?, n, hasher: PhantomData, leaves: PhantomData, diff --git a/mithril-stm/src/proof_system/concatenation/aggregate_key.rs b/mithril-stm/src/proof_system/concatenation/aggregate_key.rs index 26c9884f0fe..3b40f9dc4df 100644 --- a/mithril-stm/src/proof_system/concatenation/aggregate_key.rs +++ b/mithril-stm/src/proof_system/concatenation/aggregate_key.rs @@ -77,7 +77,7 @@ impl From<&ClosedKeyRegistration> mt_commitment: reg .to_merkle_tree::() .to_merkle_tree_batch_commitment(), - total_stake: reg.total_stake, + total_stake: reg.get_total_stake(), } } } diff --git a/mithril-stm/src/proof_system/concatenation/eligibility.rs b/mithril-stm/src/proof_system/concatenation/eligibility.rs index d05a3d6297c..f35b7fb1016 100644 --- a/mithril-stm/src/proof_system/concatenation/eligibility.rs +++ b/mithril-stm/src/proof_system/concatenation/eligibility.rs @@ -1,4 +1,6 @@ -use crate::{PhiFValue, Stake}; +use anyhow::anyhow; + +use crate::{PhiFValue, Stake, StmResult}; cfg_num_integer! { use num_bigint::{BigInt, Sign}; @@ -29,10 +31,12 @@ cfg_num_integer! { /// 1 - p 1 - (ev / evMax) (evMax - ev) /// /// Used to determine winning lottery tickets. - pub(crate) fn is_lottery_won(phi_f: PhiFValue, ev: [u8; 64], stake: Stake, total_stake: Stake) -> bool { + pub(crate) fn is_lottery_won(phi_f: PhiFValue, ev: [u8; 64], stake: Stake, total_stake: Stake) -> StmResult { // If phi_f = 1, then we automatically break with true if (phi_f - 1.0).abs() < PhiFValue::EPSILON { - return true; + return Ok(true); + } else if !(phi_f > 0.0 && phi_f <= 1.0) { + return Err(anyhow!("phi_f must be in the range (0, 1], got {phi_f}")); } let ev_max = BigInt::from(2u8).pow(512); @@ -40,12 +44,12 @@ cfg_num_integer! { let q = Ratio::new_raw(ev_max.clone(), ev_max - ev); let c = - Ratio::from_float((1.0 - phi_f).ln()).expect("Only fails if the float is infinite or NaN."); + Ratio::from_float((1.0 - phi_f).ln()).ok_or(anyhow!("phi_f must not be infinite or NaN, got {phi_f}"))?; let w = Ratio::new_raw(BigInt::from(stake), BigInt::from(total_stake)); let x = (w * c).neg(); // Now we compute a taylor function that breaks when the result is known. - taylor_comparison(1000, q, x) + Ok(taylor_comparison(1000, q, x)) } /// Checks if cmp < exp(x). Uses error approximation for an early stop. Whenever the value being @@ -92,10 +96,12 @@ cfg_rug! { /// order to keep the error in the 1e-17 range, we need to carry out the computations with 34 /// decimal digits (in order to represent the 4.5e16 ada without any rounding errors, we need /// double that precision). - pub(crate) fn is_lottery_won(phi_f: PhiFValue, ev: [u8; 64], stake: Stake, total_stake: Stake) -> bool { + pub(crate) fn is_lottery_won(phi_f: PhiFValue, ev: [u8; 64], stake: Stake, total_stake: Stake) -> StmResult { // If phi_f = 1, then we automatically break with true if (phi_f - 1.0).abs() < PhiFValue::EPSILON { - return true; + return Ok(true); + } else if !(phi_f > 0.0 && phi_f <= 1.0) { + return Err(anyhow!("phi_f must be in the range (0, 1], got {phi_f}")); } let ev = rug::Integer::from_digits(&ev, Order::LsfLe); let ev_max: Float = Float::with_val(117, 2).pow(512); @@ -104,7 +110,7 @@ cfg_rug! { let w = Float::with_val(117, stake) / Float::with_val(117, total_stake); let phi = Float::with_val(117, 1.0) - Float::with_val(117, 1.0 - phi_f).pow(w); - q < phi + Ok(q < phi) } } @@ -142,7 +148,7 @@ mod tests { ev.copy_from_slice(&[&ev_1[..], &ev_2[..]].concat()); let quick_result = trivial_is_lottery_won(phi_f, ev, stake, total_stake); - let result = is_lottery_won(phi_f, ev, stake, total_stake); + let result = is_lottery_won(phi_f, ev, stake, total_stake).unwrap(); assert_eq!(quick_result, result); } @@ -159,4 +165,18 @@ mod tests { assert!(!taylor_comparison(1000, cmp_p, Ratio::from_float(x).unwrap())); } } + + #[test] + fn is_lottery_won_rejects_out_of_range_phi_f() { + let ev = [0u8; 64]; + let stake = 100; + let total_stake = 1000; + + for invalid_phi_f in [-0.5, 0.0, 1.5, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!( + is_lottery_won(invalid_phi_f, ev, stake, total_stake).is_err(), + "phi_f = {invalid_phi_f} should be rejected" + ); + } + } } diff --git a/mithril-stm/src/proof_system/concatenation/proof.rs b/mithril-stm/src/proof_system/concatenation/proof.rs index bb77b67f746..98ae4be1afd 100644 --- a/mithril-stm/src/proof_system/concatenation/proof.rs +++ b/mithril-stm/src/proof_system/concatenation/proof.rs @@ -282,21 +282,27 @@ impl ConcatenationProof { .map_err(|_| AggregateSignatureError::SerializationError)?; bytes_index += 8; - let mut sig_reg_list = Vec::with_capacity(total_sigs); + let mut sig_reg_list = Vec::new(); for _ in 0..total_sigs { + let size_field_end = bytes_index + .checked_add(8) + .ok_or(AggregateSignatureError::SerializationError)?; u64_bytes.copy_from_slice( bytes - .get(bytes_index..bytes_index + 8) + .get(bytes_index..size_field_end) .ok_or(AggregateSignatureError::SerializationError)?, ); let sig_reg_size = usize::try_from(u64::from_be_bytes(u64_bytes)) .map_err(|_| AggregateSignatureError::SerializationError)?; + let sig_reg_end = size_field_end + .checked_add(sig_reg_size) + .ok_or(AggregateSignatureError::SerializationError)?; let sig_reg = SingleSignatureWithRegisteredParty::from_bytes::( bytes - .get(bytes_index + 8..bytes_index + 8 + sig_reg_size) + .get(size_field_end..sig_reg_end) .ok_or(AggregateSignatureError::SerializationError)?, )?; - bytes_index += 8 + sig_reg_size; + bytes_index = sig_reg_end; sig_reg_list.push(sig_reg); } diff --git a/mithril-stm/src/proof_system/concatenation/signer.rs b/mithril-stm/src/proof_system/concatenation/signer.rs index cc0171d87d3..d3fab14b3bd 100644 --- a/mithril-stm/src/proof_system/concatenation/signer.rs +++ b/mithril-stm/src/proof_system/concatenation/signer.rs @@ -60,7 +60,7 @@ impl ConcatenationProofSigner { self.key_registration_commitment.concatenate_with_message(message); let sigma = self.signing_key.sign(&message_with_commitment); - let indices = self.check_lottery(&message_with_commitment, &sigma); + let indices = self.check_lottery(&message_with_commitment, &sigma)?; if indices.is_empty() { Err(anyhow!(SignatureError::LotteryLost)) @@ -71,7 +71,11 @@ impl ConcatenationProofSigner { /// Checks the lottery for given `message_with_commitment` and the `sigma`. /// Returns a vector of winning indices. - pub fn check_lottery(&self, message_with_commitment: &[u8], sigma: &BlsSignature) -> Vec { + pub fn check_lottery( + &self, + message_with_commitment: &[u8], + sigma: &BlsSignature, + ) -> StmResult> { let mut indices = Vec::new(); for index in 0..self.parameters.m { if is_lottery_won( @@ -79,11 +83,11 @@ impl ConcatenationProofSigner { sigma.evaluate_dense_mapping(message_with_commitment, index), self.stake, self.total_stake, - ) { + )? { indices.push(index); } } - indices + Ok(indices) } pub fn get_verification_key(&self) -> VerificationKeyForConcatenation { diff --git a/mithril-stm/src/proof_system/concatenation/single_signature.rs b/mithril-stm/src/proof_system/concatenation/single_signature.rs index 065811cc24e..5afdea09f30 100644 --- a/mithril-stm/src/proof_system/concatenation/single_signature.rs +++ b/mithril-stm/src/proof_system/concatenation/single_signature.rs @@ -67,7 +67,7 @@ impl SingleSignatureForConcatenation { let ev = self.sigma.evaluate_dense_mapping(msg, index); - if !is_lottery_won(params.phi_f, ev, *stake, *total_stake) { + if !is_lottery_won(params.phi_f, ev, *stake, *total_stake)? { return Err(anyhow!(SignatureError::LotteryLost)); } } diff --git a/mithril-stm/src/proof_system/halo2_snark/aggregate_key.rs b/mithril-stm/src/proof_system/halo2_snark/aggregate_key.rs index c8674c5cfa2..ea949a76802 100644 --- a/mithril-stm/src/proof_system/halo2_snark/aggregate_key.rs +++ b/mithril-stm/src/proof_system/halo2_snark/aggregate_key.rs @@ -123,7 +123,7 @@ impl From<&ClosedKeyRegistration> for AggregateVerification merkle_tree_commitment: registration .to_merkle_tree::() .to_merkle_tree_commitment(), - total_stake: registration.total_stake, + total_stake: registration.get_total_stake(), } } } diff --git a/mithril-stm/src/proof_system/halo2_snark/eligibility.rs b/mithril-stm/src/proof_system/halo2_snark/eligibility.rs index 3800b5cb7be..a30e0b27385 100644 --- a/mithril-stm/src/proof_system/halo2_snark/eligibility.rs +++ b/mithril-stm/src/proof_system/halo2_snark/eligibility.rs @@ -36,9 +36,12 @@ cfg_num_integer! { /// 1. Rejects `total_stake == 0` with `RegisterError::ZeroTotalStake`. /// 2. Short-circuits `phi_f ≈ 1.0` to `p - 1` (all indices win), matching the concatenation /// proof system and avoiding `ln(0)`. - /// 3. Approximates `phi_f` as an exact `Ratio`, promotes to `Ratio`. - /// 4. Computes `ln(1 - phi_f)` via Taylor expansion (`ln_1p_taylor_expansion`). - /// 5. Delegates to `compute_target_value_for_snark_lottery_given_ln_approximation` for the final field-element computation. + /// 3. Rejects `phi_f` outside `(0, 1]`, since `Ratio::approximate_float` only rejects + /// infinite/NaN values and the Taylor expansion below silently diverges (or converges to a + /// value for a semantically invalid negative `phi_f`) otherwise. + /// 4. Approximates `phi_f` as an exact `Ratio`, promotes to `Ratio`. + /// 5. Computes `ln(1 - phi_f)` via Taylor expansion (`ln_1p_taylor_expansion`). + /// 6. Delegates to `compute_target_value_for_snark_lottery_given_ln_approximation` for the final field-element computation. #[cfg(feature = "future_snark")] pub fn compute_target_value_for_snark_lottery(phi_f: PhiFValue, stake: Stake, total_stake: Stake) -> StmResult { if total_stake == 0 { @@ -51,6 +54,10 @@ cfg_num_integer! { return Ok(&LotteryTargetValue::default() - &LotteryTargetValue::get_one()); } + if !(phi_f > 0.0 && phi_f <= 1.0) { + return Err(anyhow!("phi_f must be in the range (0, 1], got {phi_f}")); + } + let phi_f_ratio_int: Ratio = Ratio::approximate_float(phi_f).ok_or(anyhow!("Approximation of float as a Ratio failed because it is infinite or NaN."))?; let phi_f_ratio = Ratio::new_raw( @@ -333,6 +340,19 @@ mod tests { ); } + #[test] + fn compute_target_value_for_snark_lottery_rejects_out_of_range_phi_f() { + let total_stake = 10_000; + let stake = 5_000; + + for invalid_phi_f in [-0.5, 0.0, 1.5, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!( + compute_target_value_for_snark_lottery(invalid_phi_f, stake, total_stake).is_err(), + "phi_f = {invalid_phi_f} should be rejected" + ); + } + } + mod lottery_computations { use super::*; diff --git a/mithril-stm/src/proof_system/halo2_snark/mod.rs b/mithril-stm/src/proof_system/halo2_snark/mod.rs index ce2ba335ee5..d25b8b06ec9 100644 --- a/mithril-stm/src/proof_system/halo2_snark/mod.rs +++ b/mithril-stm/src/proof_system/halo2_snark/mod.rs @@ -113,7 +113,7 @@ mod tests { ) .to_merkle_tree_commitment(); let lottery_target_value = - ClosedRegistrationEntry::try_from((entry, closed_reg.total_stake, params.phi_f)) + ClosedRegistrationEntry::try_from((entry, closed_reg.get_total_stake(), params.phi_f)) .unwrap() .get_lottery_target_value() .unwrap(); diff --git a/mithril-stm/src/protocol/error.rs b/mithril-stm/src/protocol/error.rs index 412a6427925..7d79855dd4d 100644 --- a/mithril-stm/src/protocol/error.rs +++ b/mithril-stm/src/protocol/error.rs @@ -7,6 +7,7 @@ use crate::VerificationKeyForSnark; #[derive(Debug, Clone, thiserror::Error, PartialEq, Eq)] pub enum RegisterError { /// This key has already been registered by a participant + #[allow(private_interfaces)] #[error("This key has already been registered.")] EntryAlreadyRegistered(Box), 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 5d88044d15f..1e9bdd71ead 100644 --- a/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs +++ b/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs @@ -32,7 +32,7 @@ struct ClosedRegistrationEntryCborEnvelope { /// Represents a registration entry of a closed key registration. #[derive(PartialEq, Eq, Clone, Debug, Deserialize)] -pub struct ClosedRegistrationEntry { +pub(crate) struct ClosedRegistrationEntry { verification_key_for_concatenation: VerificationKeyForConcatenation, stake: Stake, #[cfg(feature = "future_snark")] @@ -45,7 +45,13 @@ pub struct ClosedRegistrationEntry { impl ClosedRegistrationEntry { /// Creates a new closed registration entry. - pub fn new( + /// + /// This is private rather than `pub`: it performs no verification of any kind (unlike + /// `RegistrationEntry::new`, which verifies proof of possession), so it must only be used + /// where the caller has already established the verification key is trustworthy — currently + /// only via `TryFrom<(RegistrationEntry, Stake, PhiFValue)>`, which derives from an + /// already-verified `RegistrationEntry`. + fn new( verification_key_for_concatenation: VerificationKeyForConcatenation, stake: Stake, #[cfg(feature = "future_snark")] verification_key_for_snark: Option< @@ -64,12 +70,12 @@ impl ClosedRegistrationEntry { } /// Gets the verification key for concatenation. - pub fn get_verification_key_for_concatenation(&self) -> VerificationKeyForConcatenation { + pub(crate) fn get_verification_key_for_concatenation(&self) -> VerificationKeyForConcatenation { self.verification_key_for_concatenation } /// Gets the stake. - pub fn get_stake(&self) -> Stake { + pub(crate) fn get_stake(&self) -> Stake { self.stake } @@ -79,7 +85,7 @@ impl ClosedRegistrationEntry { /// which do not need SNARK fields and must remain backward-compatible with /// clients that do not support the `future_snark` feature. #[cfg(feature = "future_snark")] - pub fn without_snark_fields(&self) -> Self { + pub(crate) fn without_snark_fields(&self) -> Self { ClosedRegistrationEntry { verification_key_for_concatenation: self.verification_key_for_concatenation, stake: self.stake, @@ -90,13 +96,13 @@ impl ClosedRegistrationEntry { #[cfg(feature = "future_snark")] /// Gets the verification key for snark. - pub fn get_verification_key_for_snark(&self) -> Option { + pub(crate) fn get_verification_key_for_snark(&self) -> Option { self.verification_key_for_snark } #[cfg(feature = "future_snark")] /// Gets the lottery target value. - pub fn get_lottery_target_value(&self) -> Option { + pub(crate) fn get_lottery_target_value(&self) -> Option { self.lottery_target_value } diff --git a/mithril-stm/src/protocol/key_registration/mod.rs b/mithril-stm/src/protocol/key_registration/mod.rs index 730522fc977..e15a2cf4678 100644 --- a/mithril-stm/src/protocol/key_registration/mod.rs +++ b/mithril-stm/src/protocol/key_registration/mod.rs @@ -6,9 +6,9 @@ mod registration_entry; #[cfg(feature = "future_snark")] mod snark_registration_entry; -pub use closed_registration_entry::ClosedRegistrationEntry; +pub(crate) use closed_registration_entry::ClosedRegistrationEntry; pub use concatenation_registration_entry::RegistrationEntryForConcatenation; pub use register::{ClosedKeyRegistration, KeyRegistration}; -pub use registration_entry::RegistrationEntry; +pub(crate) use registration_entry::RegistrationEntry; #[cfg(feature = "future_snark")] pub use snark_registration_entry::RegistrationEntryForSnark; diff --git a/mithril-stm/src/protocol/key_registration/register.rs b/mithril-stm/src/protocol/key_registration/register.rs index ff42604bcfa..727d8985340 100644 --- a/mithril-stm/src/protocol/key_registration/register.rs +++ b/mithril-stm/src/protocol/key_registration/register.rs @@ -36,9 +36,16 @@ impl KeyRegistration { /// Check whether the given `RegistrationEntry` is already registered. /// Insert the new entry if all checks pass. + /// + /// This is `pub(crate)` rather than `pub`: it trusts that `entry` was produced through a + /// path that already verified proof of possession (`RegistrationEntry::new`), which every + /// internal caller does. Exposing it publicly would let external callers register an entry + /// obtained without that verification (e.g. via deserialization), so external registration + /// must go through [`KeyRegistration::register`] instead, which always constructs the entry + /// itself via `RegistrationEntry::new`. /// # Error /// The function fails when the entry is already registered. - pub fn register_by_entry(&mut self, entry: &RegistrationEntry) -> StmResult<()> { + pub(crate) fn register_by_entry(&mut self, entry: &RegistrationEntry) -> StmResult<()> { let vk_concatenation = entry.get_verification_key_for_concatenation(); let is_already_registered = self.registered_keys_for_concatenation.contains(&vk_concatenation); @@ -104,26 +111,40 @@ impl KeyRegistration { .map(|entry| ClosedRegistrationEntry::try_from((*entry, total_stake, params.phi_f))) .collect(); - Ok(ClosedKeyRegistration { - closed_registration_entries: closed_registration_entries?, + Ok(ClosedKeyRegistration::new( + closed_registration_entries?, total_stake, - }) + )) } } /// Closed Key Registration #[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct ClosedKeyRegistration { - /// The closed key registration entries - pub closed_registration_entries: BTreeSet, - - /// The total stake registered - pub total_stake: Stake, + closed_registration_entries: BTreeSet, + total_stake: Stake, } impl ClosedKeyRegistration { + pub(crate) fn new( + closed_registration_entries: BTreeSet, + total_stake: Stake, + ) -> Self { + Self { + closed_registration_entries, + total_stake, + } + } + + /// Gets the total stake registered. + pub(crate) fn get_total_stake(&self) -> Stake { + self.total_stake + } + /// Creates a Merkle tree from the closed registration entries - pub fn to_merkle_tree(&self) -> MerkleTree + pub(crate) fn to_merkle_tree( + &self, + ) -> MerkleTree where Option: From, { @@ -137,7 +158,7 @@ impl ClosedKeyRegistration { } /// Gets the index of given closed registration entry. - pub fn get_signer_index_for_registration( + pub(crate) fn get_signer_index_for_registration( &self, entry: &ClosedRegistrationEntry, ) -> Option { @@ -149,19 +170,19 @@ impl ClosedKeyRegistration { /// Check if any registration entry has a SNARK verification key. #[cfg(feature = "future_snark")] - pub fn has_snark_verification_keys(&self) -> bool { + pub(crate) fn has_snark_verification_keys(&self) -> bool { self.closed_registration_entries .iter() .any(|entry| entry.get_verification_key_for_snark().is_some()) } /// Return the number of registered parties. - pub fn number_of_registered_parties(&self) -> usize { + pub(crate) fn number_of_registered_parties(&self) -> usize { self.closed_registration_entries.len() } /// Get the closed registration entry for a given signer index. - pub fn get_registration_entry_for_index( + pub(crate) fn get_registration_entry_for_index( &self, signer_index: &SignerIndex, ) -> StmResult { diff --git a/mithril-stm/src/protocol/key_registration/registration_entry.rs b/mithril-stm/src/protocol/key_registration/registration_entry.rs index 7eb3f1dac1f..c3686776c89 100644 --- a/mithril-stm/src/protocol/key_registration/registration_entry.rs +++ b/mithril-stm/src/protocol/key_registration/registration_entry.rs @@ -1,8 +1,6 @@ use std::cmp::Ordering; use std::hash::Hash; -use serde::{Deserialize, Serialize}; - #[cfg(feature = "future_snark")] use crate::VerificationKeyForSnark; use crate::{ @@ -13,19 +11,17 @@ use crate::{ use super::ClosedRegistrationEntry; /// Represents a signer registration entry -#[derive(PartialEq, Eq, Clone, Debug, Copy, Serialize, Deserialize)] -pub struct RegistrationEntry( +#[derive(PartialEq, Eq, Clone, Debug, Copy)] +pub(crate) struct RegistrationEntry( VerificationKeyForConcatenation, Stake, - #[cfg(feature = "future_snark")] - #[serde(skip_serializing_if = "Option::is_none")] - Option, + #[cfg(feature = "future_snark")] Option, ); impl RegistrationEntry { /// Creates a new registration entry. Verifies the proof of possession of verification key for /// concatenation and validates the schnorr verification key before creating the entry. - pub fn new( + pub(crate) fn new( bls_verification_key_proof_of_possession: VerificationKeyProofOfPossessionForConcatenation, stake: Stake, #[cfg(feature = "future_snark")] schnorr_verification_key: Option, @@ -56,18 +52,18 @@ impl RegistrationEntry { } /// Gets the verification key for concatenation. - pub fn get_verification_key_for_concatenation(&self) -> VerificationKeyForConcatenation { + pub(crate) fn get_verification_key_for_concatenation(&self) -> VerificationKeyForConcatenation { self.0 } #[cfg(feature = "future_snark")] /// Gets the verification key for snark. - pub fn get_verification_key_for_snark(&self) -> Option { + pub(crate) fn get_verification_key_for_snark(&self) -> Option { self.2 } /// Gets the stake associated with the registration entry. - pub fn get_stake(&self) -> Stake { + pub(crate) fn get_stake(&self) -> Stake { self.1 } } diff --git a/mithril-stm/src/protocol/mod.rs b/mithril-stm/src/protocol/mod.rs index be6ca245d78..22438e7b412 100644 --- a/mithril-stm/src/protocol/mod.rs +++ b/mithril-stm/src/protocol/mod.rs @@ -14,9 +14,9 @@ pub use error::RegisterError; #[cfg(feature = "future_snark")] pub use key_registration::RegistrationEntryForSnark; pub use key_registration::{ - ClosedKeyRegistration, ClosedRegistrationEntry, KeyRegistration, RegistrationEntry, - RegistrationEntryForConcatenation, + ClosedKeyRegistration, KeyRegistration, RegistrationEntryForConcatenation, }; +pub(crate) use key_registration::{ClosedRegistrationEntry, RegistrationEntry}; pub use parameters::Parameters; pub use participant::{Initializer, Signer}; pub use single_signature::{SignatureError, SingleSignature, SingleSignatureWithRegisteredParty}; diff --git a/mithril-stm/src/protocol/participant/initializer.rs b/mithril-stm/src/protocol/participant/initializer.rs index 5859001857b..a9269aee98b 100644 --- a/mithril-stm/src/protocol/participant/initializer.rs +++ b/mithril-stm/src/protocol/participant/initializer.rs @@ -95,7 +95,7 @@ impl Initializer { .get_signer_index_for_registration( &( registration_entry, - closed_key_registration.total_stake, + closed_key_registration.get_total_stake(), self.parameters.phi_f, ) .try_into()?, @@ -107,7 +107,7 @@ impl Initializer { .to_merkle_tree_batch_commitment(); let concatenation_proof_signer = ConcatenationProofSigner::new( registration_entry.get_stake(), - closed_key_registration.total_stake, + closed_key_registration.get_total_stake(), self.parameters, self.bls_signing_key, self.bls_verification_key_proof_of_possession.vk, @@ -123,7 +123,7 @@ impl Initializer { .to_merkle_tree_commitment(); let lottery_target_value = ClosedRegistrationEntry::try_from(( registration_entry, - closed_key_registration.total_stake, + closed_key_registration.get_total_stake(), self.parameters.phi_f, ))? .get_lottery_target_value() diff --git a/mithril-stm/src/protocol/single_signature/signature.rs b/mithril-stm/src/protocol/single_signature/signature.rs index 88a97f0d7fa..de349b9ca50 100644 --- a/mithril-stm/src/protocol/single_signature/signature.rs +++ b/mithril-stm/src/protocol/single_signature/signature.rs @@ -96,55 +96,70 @@ impl SingleSignature { let nr_indexes = u64::from_be_bytes(u64_bytes) as usize; let mut indexes = Vec::new(); - for i in 0..nr_indexes { + let mut offset = 8usize; + for _ in 0..nr_indexes { + let index_end = + offset.checked_add(8).ok_or(SignatureError::SerializationError)?; u64_bytes.copy_from_slice( bytes - .get(8 + i * 8..16 + i * 8) + .get(offset..index_end) .ok_or(SignatureError::SerializationError)?, ); indexes.push(u64::from_be_bytes(u64_bytes)); + offset = index_end; } - let offset = 8 + nr_indexes * 8; + let sigma_end = offset.checked_add(48).ok_or(SignatureError::SerializationError)?; let sigma = BlsSignature::from_bytes( bytes - .get(offset..offset + 48) + .get(offset..sigma_end) .ok_or(SignatureError::SerializationError)?, )?; + let signer_index_end = + sigma_end.checked_add(8).ok_or(SignatureError::SerializationError)?; u64_bytes.copy_from_slice( bytes - .get(offset + 48..offset + 56) + .get(sigma_end..signer_index_end) .ok_or(SignatureError::SerializationError)?, ); let signer_index = u64::from_be_bytes(u64_bytes); #[cfg(feature = "future_snark")] let snark_signature = { - let snark_offset = offset + 56; + let snark_offset = signer_index_end; if snark_offset < bytes.len() { + let schnorr_signature_end = snark_offset + .checked_add(96) + .ok_or(SignatureError::SerializationError)?; let schnorr_signature = crate::UniqueSchnorrSignature::from_bytes( bytes - .get(snark_offset..snark_offset + 96) + .get(snark_offset..schnorr_signature_end) .ok_or(SignatureError::SerializationError)?, )?; - let mut snark_idx_offset = snark_offset + 96; + let nr_snark_indices_end = schnorr_signature_end + .checked_add(8) + .ok_or(SignatureError::SerializationError)?; u64_bytes.copy_from_slice( bytes - .get(snark_idx_offset..snark_idx_offset + 8) + .get(schnorr_signature_end..nr_snark_indices_end) .ok_or(SignatureError::SerializationError)?, ); let nr_snark_indices = u64::from_be_bytes(u64_bytes) as usize; - snark_idx_offset += 8; - let mut snark_indices = Vec::with_capacity(nr_snark_indices); - for i in 0..nr_snark_indices { + let mut snark_indices = Vec::new(); + let mut snark_idx_offset = nr_snark_indices_end; + for _ in 0..nr_snark_indices { + let snark_index_end = snark_idx_offset + .checked_add(8) + .ok_or(SignatureError::SerializationError)?; u64_bytes.copy_from_slice( bytes - .get(snark_idx_offset + i * 8..snark_idx_offset + (i + 1) * 8) + .get(snark_idx_offset..snark_index_end) .ok_or(SignatureError::SerializationError)?, ); snark_indices.push(u64::from_be_bytes(u64_bytes)); + snark_idx_offset = snark_index_end; } Some(SingleSignatureForSnark::new( @@ -337,7 +352,7 @@ mod tests { ).to_merkle_tree_commitment(); let closed_registration_entry = ClosedRegistrationEntry::try_from(( entry1, - closed_key_registration.total_stake, + closed_key_registration.get_total_stake(), params.phi_f, )) .unwrap(); @@ -550,7 +565,7 @@ mod tests { ).to_merkle_tree_commitment(); let closed_registration_entry = ClosedRegistrationEntry::try_from(( entry1, - closed_key_registration.total_stake, + closed_key_registration.get_total_stake(), params.phi_f, )) .unwrap(); 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 68d5998e090..a5581bbaf93 100644 --- a/mithril-stm/src/protocol/single_signature/signature_registered_party.rs +++ b/mithril-stm/src/protocol/single_signature/signature_registered_party.rs @@ -17,6 +17,7 @@ struct SingleSignatureWithRegisteredPartyCborEnvelope { } /// Signature with its registered party. +#[allow(private_interfaces)] #[derive(Debug, Clone, Hash, Deserialize, Eq, PartialEq, Ord, PartialOrd)] pub struct SingleSignatureWithRegisteredParty { /// Stm signature @@ -251,7 +252,7 @@ mod tests { key_reg.register_by_entry(&entry2).unwrap(); let closed_key_reg: ClosedKeyRegistration = key_reg.close_registration(¶ms).unwrap(); - let total_stake = closed_key_reg.total_stake; + let total_stake = closed_key_reg.get_total_stake(); let concatenation_proof_signer: ConcatenationProofSigner = ConcatenationProofSigner::new( @@ -272,7 +273,7 @@ mod tests { ).to_merkle_tree_commitment(); let closed_registration_entry = ClosedRegistrationEntry::try_from(( entry1, - closed_key_reg.total_stake, + closed_key_reg.get_total_stake(), params.phi_f, )) .unwrap(); diff --git a/mithril-stm/tests/test_extensions/protocol_phase.rs b/mithril-stm/tests/test_extensions/protocol_phase.rs index a76a4c22bb0..ecaf5772b0a 100644 --- a/mithril-stm/tests/test_extensions/protocol_phase.rs +++ b/mithril-stm/tests/test_extensions/protocol_phase.rs @@ -41,11 +41,16 @@ pub fn initialization_phase( for stake in parties { let p = Initializer::new(params, stake, &mut rng); - 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, - )); + let vk_pop = p.get_verification_key_proof_of_possession_for_concatenation(); + key_reg + .register( + stake, + &vk_pop, + #[cfg(feature = "future_snark")] + p.schnorr_verification_key, + ) + .unwrap(); + reg_parties.push((vk_pop.vk, stake)); initializers.push(p); }