diff --git a/Cargo.lock b/Cargo.lock index 9191642a757..015bfbd0f32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4816,7 +4816,7 @@ dependencies = [ [[package]] name = "mithril-stm" -version = "0.11.4" +version = "0.12.0" dependencies = [ "anyhow", "blake2 0.10.6", diff --git a/mithril-common/Cargo.toml b/mithril-common/Cargo.toml index 33971c5708b..2b3643180ef 100644 --- a/mithril-common/Cargo.toml +++ b/mithril-common/Cargo.toml @@ -46,7 +46,7 @@ fixed = "1.31.0" hex = { workspace = true } kes-summed-ed25519 = { version = "0.2.1", features = ["serde_enabled", "sk_clone_enabled"] } mithril-merkle-tree = { path = "../internal/mithril-merkle-tree", version = "0.1.4" } -mithril-stm = { path = "../mithril-stm", version = "0.11.4", default-features = false } +mithril-stm = { path = "../mithril-stm", version = "0.12.0", default-features = false } nom = "8.0.0" rand_chacha = { workspace = true } rand_core = { workspace = true } diff --git a/mithril-common/src/protocol/signer_builder.rs b/mithril-common/src/protocol/signer_builder.rs index 539bc38a52d..69b1c9e99de 100644 --- a/mithril-common/src/protocol/signer_builder.rs +++ b/mithril-common/src/protocol/signer_builder.rs @@ -230,7 +230,7 @@ mod test { ); match error.downcast_ref::() { - Some(RegisterError::EntryAlreadyRegistered { .. }) => (), + Some(RegisterError::EntryAlreadyRegistered) => (), _ => panic!("Expected an CoreRegister error, got: {error:?}"), } } diff --git a/mithril-stm/CHANGELOG.md b/mithril-stm/CHANGELOG.md index 837c4173eef..2b9e5d55203 100644 --- a/mithril-stm/CHANGELOG.md +++ b/mithril-stm/CHANGELOG.md @@ -5,6 +5,25 @@ 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.12.0 (07-29-2026) + +### Added + +- Added a `ProtocolError` enum with a `PhiFValueOutOfRange` variant for invalid `phi_f` values + +### Changed + +- Updated the `from_bytes_legacy` functions for `MerkleTreeBatchCommitment`, `MerkleTree`, `ConcatenationProof` and `SingleSignature` +- Updated the validation of the `phi_f` value in the lottery/eligibility computations +- Updated the visibility of `ClosedKeyRegistration`, `RegistrationEntry` and `ClosedRegistrationEntry` to ensure proper verification of the proof of possession +- Updated `RegisterError::EntryAlreadyRegistered` to no longer carry the conflicting `RegistrationEntry`, since the type is now crate-private +- Updated `ClosedKeyRegistration::number_of_registered_parties` to be available only when the `future_snark` feature is enabled +- Updated `ConcatenationProofSigner::check_lottery` to return `StmResult>` instead of `Vec` + +### Removed + +- Removed the unused `to_bytes` and `from_bytes` methods from `MerkleTree` + ## 0.11.4 (07-27-2026) ### Added diff --git a/mithril-stm/Cargo.toml b/mithril-stm/Cargo.toml index e0b14255715..26d14621ff1 100644 --- a/mithril-stm/Cargo.toml +++ b/mithril-stm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-stm" -version = "0.11.4" +version = "0.12.0" edition = { workspace = true } authors = { workspace = true } homepage = { workspace = true } diff --git a/mithril-stm/README.md b/mithril-stm/README.md index 72a22c3fd71..43aa3744d3a 100644 --- a/mithril-stm/README.md +++ b/mithril-stm/README.md @@ -69,7 +69,7 @@ use rayon::prelude::*; use mithril_stm::{ AggregateSignatureType, AggregationError, AncillaryGenesisData, AncillaryProofInput, Clerk, - Initializer, KeyRegistration, Parameters, RegistrationEntry, Signer, SingleSignature, + Initializer, KeyRegistration, Parameters, Signer, SingleSignature, MithrilMembershipDigest, AggregateVerificationKey, }; @@ -100,13 +100,12 @@ let mut key_reg = KeyRegistration::initialize(); let mut ps: Vec = Vec::with_capacity(nparties as usize); for stake in parties { let p = Initializer::new(params, stake, &mut rng); - 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); } 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..163b5f11ca8 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); //! } //! @@ -148,14 +147,15 @@ mod protocol; mod signature_scheme; pub use proof_system::AggregateVerificationKeyForConcatenation; +pub(crate) use protocol::RegistrationEntry; 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, + Parameters, RegisterError, RegistrationEntryForConcatenation, SignatureError, Signer, + SingleSignature, SingleSignatureWithRegisteredParty, VerificationKeyForConcatenation, + VerificationKeyProofOfPossessionForConcatenation, }; pub use signature_scheme::BlsSignatureError; diff --git a/mithril-stm/src/membership_commitment/merkle_tree/commitment.rs b/mithril-stm/src/membership_commitment/merkle_tree/commitment.rs index d24b9ff0a74..02379a39658 100644 --- a/mithril-stm/src/membership_commitment/merkle_tree/commitment.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/commitment.rs @@ -176,14 +176,31 @@ impl MerkleTreeBatchCommitment ))); } - let nr_nodes = self.nr_leaves + self.nr_leaves.next_power_of_two() - 1; + let nr_leaves_next_pow_2 = self + .nr_leaves + .checked_next_power_of_two() + .ok_or(MerkleTreeError::SerializationError)?; + + let nr_nodes = nr_leaves_next_pow_2 + .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(); - - let mut idx = ordered_indices[0]; + .map(|i| { + nr_leaves_next_pow_2 + .checked_add(i) + .and_then(|idx| idx.checked_sub(1)) + .ok_or(MerkleTreeError::SerializationError) + }) + .collect::, _>>()?; + + let mut idx = ordered_indices + .first() + .copied() + .ok_or(MerkleTreeError::SerializationError) + .with_context(|| "The ordered indices list is empty.")?; // First we need to hash the leave values let mut leaves: Vec> = batch_val .iter() diff --git a/mithril-stm/src/membership_commitment/merkle_tree/tree.rs b/mithril-stm/src/membership_commitment/merkle_tree/tree.rs index b71fa0cff92..483ea126251 100644 --- a/mithril-stm/src/membership_commitment/merkle_tree/tree.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/tree.rs @@ -3,16 +3,11 @@ use std::marker::PhantomData; use digest::{Digest, FixedOutput}; use serde::{Deserialize, Serialize}; -use crate::StmResult; -use crate::codec; - 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 -#[allow(dead_code)] use super::{MerklePath, MerkleTreeCommitment}; /// Tree of hashes, providing a commitment of data and its ordering. @@ -146,60 +141,13 @@ impl MerkleTree { MerkleBatchPath::new(proof, indices) } - /// Convert a `MerkleTree` into a byte string. - pub fn to_bytes(&self) -> StmResult> { - codec::to_cbor_bytes(self) - } - - /// Try to convert a byte string into a `MerkleTree`. - /// # Error - /// It returns error if conversion fails. - pub fn from_bytes(bytes: &[u8]) -> StmResult { - codec::from_versioned_bytes(bytes, Self::from_bytes_legacy) - } - - /// Try to convert a byte string into a `MerkleTree` using the legacy format. - /// # Layout - /// * Number of leaves committed in the Merkle Tree (as u64) - /// * All nodes of the merkle tree (starting with the root) - 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); - for i in 0..num_nodes { - nodes.push( - bytes - .get( - 8 + i * ::output_size() - ..8 + (i + 1) * ::output_size(), - ) - .ok_or(MerkleTreeError::SerializationError)? - .to_vec(), - ); - } - Ok(Self { - nodes, - leaf_off: num_nodes - n, - n, - hasher: PhantomData, - leaves: PhantomData, - }) - } - #[cfg(feature = "future_snark")] - // TODO: remove this allow dead_code directive when function is called or future_snark is activated - #[allow(dead_code)] /// Convert merkle tree to a commitment. This function simply returns the root. pub(crate) fn to_merkle_tree_commitment(&self) -> MerkleTreeCommitment { MerkleTreeCommitment::new(self.nodes[0].clone()) // Use private constructor } #[cfg(feature = "future_snark")] - // TODO: remove this allow dead_code directive when function is called or future_snark is activated - #[allow(dead_code)] /// Get a path (hashes of siblings of the path to the root node) /// for the `i`th value stored in the tree. /// Requires `i < self.n` @@ -313,14 +261,6 @@ mod tests { assert_eq!(tree_commitment.root, decoded.root); } - #[test] - fn test_bytes_tree((t, values) in arb_tree(5)) { - let bytes = t.to_bytes().expect("MerkleTree serialization should not fail"); - let deserialised = MerkleTree::::from_bytes(&bytes).unwrap(); - let tree = MerkleTree::::new(&values); - assert_eq!(tree.nodes, deserialised.nodes); - } - #[cfg(feature = "future_snark")] #[test] fn test_bytes_tree_commitment_batch_compat((t, values) in arb_tree(5)) { @@ -507,70 +447,6 @@ mod tests { assert_eq!(golden_serialized, serialized); } } - - mod golden_cbor { - use super::*; - - fn golden_value() -> MerkleTree { - let number_of_leaves = 4; - let pks = vec![VerificationKeyForConcatenation::default(); number_of_leaves]; - let stakes: Vec = (0..number_of_leaves).map(|i| i as u64).collect(); - let leaves = pks - .into_iter() - .zip(stakes) - .map(|(key, stake)| MerkleTreeConcatenationLeaf(key, stake)) - .collect::>(); - - MerkleTree::::new(&leaves) - } - - const GOLDEN_CBOR_BYTES: &[u8; 485] = &[ - 1, 165, 101, 110, 111, 100, 101, 115, 135, 152, 32, 24, 178, 24, 30, 24, 231, 24, - 127, 24, 65, 24, 247, 24, 162, 24, 149, 24, 33, 24, 29, 24, 147, 24, 148, 24, 224, - 24, 156, 24, 96, 24, 113, 24, 140, 24, 42, 24, 98, 24, 166, 24, 137, 14, 24, 69, - 24, 29, 24, 28, 24, 244, 24, 161, 24, 145, 24, 207, 24, 146, 24, 236, 24, 249, 152, - 32, 24, 135, 24, 51, 24, 101, 24, 36, 24, 82, 24, 28, 24, 47, 24, 190, 24, 104, 24, - 152, 24, 106, 24, 167, 24, 128, 24, 167, 22, 24, 36, 24, 125, 24, 91, 24, 137, 24, - 208, 24, 224, 14, 24, 82, 24, 41, 24, 130, 24, 214, 24, 108, 24, 254, 24, 77, 24, - 44, 24, 160, 24, 117, 152, 32, 24, 75, 24, 152, 24, 193, 24, 39, 24, 81, 24, 31, - 24, 79, 24, 34, 24, 232, 24, 192, 24, 38, 24, 94, 24, 109, 24, 160, 24, 171, 24, - 148, 24, 173, 24, 203, 24, 85, 24, 33, 24, 116, 20, 24, 62, 24, 51, 1, 24, 112, 24, - 227, 4, 24, 226, 24, 56, 24, 30, 24, 27, 152, 32, 24, 133, 24, 163, 24, 160, 24, - 181, 24, 171, 24, 82, 24, 229, 24, 168, 24, 80, 24, 193, 24, 182, 24, 163, 24, 172, - 24, 80, 24, 114, 21, 24, 169, 24, 159, 24, 100, 24, 68, 24, 217, 24, 39, 24, 34, - 24, 37, 24, 155, 24, 218, 24, 120, 24, 101, 24, 51, 24, 25, 24, 43, 24, 84, 152, - 32, 24, 57, 20, 24, 139, 24, 83, 24, 194, 24, 218, 24, 202, 24, 66, 24, 139, 24, - 49, 24, 144, 24, 148, 24, 132, 6, 2, 24, 97, 24, 112, 24, 38, 24, 29, 24, 60, 24, - 139, 24, 233, 24, 189, 24, 95, 24, 168, 24, 204, 24, 84, 24, 33, 24, 201, 24, 140, - 23, 22, 152, 32, 24, 102, 24, 192, 24, 198, 24, 230, 0, 24, 203, 24, 217, 24, 240, - 24, 214, 24, 244, 24, 135, 24, 236, 24, 49, 24, 219, 24, 229, 14, 24, 209, 24, 46, - 24, 61, 24, 237, 24, 28, 24, 195, 24, 231, 24, 61, 24, 214, 24, 51, 24, 59, 24, 39, - 24, 186, 24, 114, 24, 64, 24, 107, 152, 32, 24, 128, 24, 78, 24, 52, 24, 132, 24, - 35, 24, 118, 24, 64, 24, 83, 24, 60, 24, 38, 24, 181, 24, 197, 24, 144, 24, 188, - 24, 235, 24, 225, 2, 24, 223, 24, 135, 24, 104, 24, 193, 24, 226, 24, 148, 24, 170, - 24, 114, 24, 26, 24, 173, 24, 161, 24, 34, 24, 70, 24, 241, 24, 90, 104, 108, 101, - 97, 102, 95, 111, 102, 102, 3, 97, 110, 4, 102, 104, 97, 115, 104, 101, 114, 246, - 102, 108, 101, 97, 118, 101, 115, 246, - ]; - - #[test] - fn cbor_golden_bytes_can_be_decoded() { - let decoded = - MerkleTree::::from_bytes( - GOLDEN_CBOR_BYTES, - ) - .expect("CBOR golden bytes deserialization should not fail"); - assert_eq!(golden_value().nodes, decoded.nodes); - assert_eq!(golden_value().n, decoded.n); - assert_eq!(golden_value().leaf_off, decoded.leaf_off); - } - - #[test] - fn cbor_encoding_is_stable() { - let bytes = golden_value().to_bytes().expect("CBOR serialization should not fail"); - assert_eq!(GOLDEN_CBOR_BYTES.as_slice(), bytes.as_slice()); - } - } } #[cfg(feature = "future_snark")] @@ -683,15 +559,6 @@ mod tests { let tree_commitment = MerkleTree::::new(&values).to_merkle_tree_commitment(); assert_eq!(tree_commitment.root, decoded.root); } - - #[cfg(feature = "future_snark")] - #[test] - fn test_bytes_tree((t, values) in arb_tree_poseidon(5)) { - let bytes = t.to_bytes().expect("MerkleTree serialization should not fail"); - let deserialised = MerkleTree::::from_bytes(&bytes).unwrap(); - let tree = MerkleTree::::new(&values); - assert_eq!(tree.nodes, deserialised.nodes); - } } prop_compose! { 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..becc0432f75 100644 --- a/mithril-stm/src/proof_system/concatenation/eligibility.rs +++ b/mithril-stm/src/proof_system/concatenation/eligibility.rs @@ -1,4 +1,4 @@ -use crate::{PhiFValue, Stake}; +use crate::{PhiFValue, RegisterError, Stake, StmResult, protocol::ProtocolError}; cfg_num_integer! { use num_bigint::{BigInt, Sign}; @@ -29,23 +29,33 @@ 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 { + #[allow(clippy::unnecessary_lazy_evaluations)] + pub(crate) fn is_lottery_won(phi_f: PhiFValue, ev: [u8; 64], stake: Stake, total_stake: Stake) -> StmResult { + if total_stake == 0 { + return Err(RegisterError::ZeroTotalStake.into()); + } + // 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(ProtocolError::PhiFValueOutOfRange(phi_f).into()); } let ev_max = BigInt::from(2u8).pow(512); let ev = BigInt::from_bytes_le(Sign::Plus, &ev); + if ev == ev_max { + return Ok(false); + } 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_else(|| ProtocolError::PhiFValueOutOfRange(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 +102,16 @@ 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 total_stake == 0 { + return Err(RegisterError::ZeroTotalStake.into()); + } + // 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(ProtocolError::PhiFValueOutOfRange(phi_f).into()); } let ev = rug::Integer::from_digits(&ev, Order::LsfLe); let ev_max: Float = Float::with_val(117, 2).pow(512); @@ -104,7 +120,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 +158,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 +175,53 @@ 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] { + let result = is_lottery_won(invalid_phi_f, ev, stake, total_stake) + .expect_err("The lottery check should fail."); + assert!( + matches!( + result.downcast_ref::(), + Some(ProtocolError::PhiFValueOutOfRange(..)) + ), + "Unexpected error: {result:?}" + ); + } + } + + #[test] + fn is_lottery_won_rejects_zero_total_stake() { + let ev = [0u8; 64]; + let stake = 100; + let total_stake = 0; + let phi_f = 0.8f64; + + let result = is_lottery_won(phi_f, ev, stake, total_stake) + .expect_err("The lottery check should fail."); + assert!( + matches!( + result.downcast_ref::(), + Some(RegisterError::ZeroTotalStake) + ), + "Unexpected error: {result:?}" + ); + } + + #[test] + fn is_lottery_won_rejects_max_ev_value() { + let ev = [255u8; 64]; + let stake = 100; + let total_stake = 1000; + let phi_f = 0.8f64; + + let result = is_lottery_won(phi_f, ev, stake, total_stake).unwrap(); + + assert!(!result); + } } 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..24a99253874 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,13 @@ 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 { + /// # Error + /// Returns an error if protocol parameters are invalid (e.g. `phi_f` outside `(0, 1]`). + 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 +85,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..c9ccadacdb1 100644 --- a/mithril-stm/src/proof_system/halo2_snark/eligibility.rs +++ b/mithril-stm/src/proof_system/halo2_snark/eligibility.rs @@ -1,6 +1,6 @@ use anyhow::anyhow; -use crate::{PhiFValue, RegisterError}; +use crate::{PhiFValue, RegisterError, protocol::ProtocolError}; #[cfg(feature = "future_snark")] use crate::{ @@ -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,8 +54,12 @@ cfg_num_integer! { return Ok(&LotteryTargetValue::default() - &LotteryTargetValue::get_one()); } + if !(phi_f > 0.0 && phi_f <= 1.0) { + return Err(ProtocolError::PhiFValueOutOfRange(phi_f).into()); + } + 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."))?; + Ratio::approximate_float(phi_f).ok_or_else(|| anyhow!("Approximation of float as a Ratio failed because it 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()), @@ -274,7 +281,10 @@ mod tests { use proptest::prelude::*; use rand_core::OsRng; - use crate::{LotteryTargetValue, SchnorrSigningKey, signature_scheme::BaseFieldElement}; + use crate::{ + LotteryTargetValue, SchnorrSigningKey, protocol::ProtocolError, + signature_scheme::BaseFieldElement, + }; use super::{ TAYLOR_EXPANSION_ITERATIONS, check_lottery_for_index, compute_exponential_taylor_expansion, @@ -333,6 +343,24 @@ 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] { + let result = compute_target_value_for_snark_lottery(invalid_phi_f, stake, total_stake) + .expect_err("The lottery check should fail."); + assert!( + matches!( + result.downcast_ref::(), + Some(ProtocolError::PhiFValueOutOfRange(..)) + ), + "Unexpected error: {result:?}" + ); + } + } + 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..bae44586b42 100644 --- a/mithril-stm/src/protocol/error.rs +++ b/mithril-stm/src/protocol/error.rs @@ -1,4 +1,4 @@ -use crate::{RegistrationEntry, VerificationKeyForConcatenation}; +use crate::VerificationKeyForConcatenation; #[cfg(feature = "future_snark")] use crate::VerificationKeyForSnark; @@ -8,7 +8,7 @@ use crate::VerificationKeyForSnark; pub enum RegisterError { /// This key has already been registered by a participant #[error("This key has already been registered.")] - EntryAlreadyRegistered(Box), + EntryAlreadyRegistered, /// Cannot register if the registration is closed. #[error("Cannot register if the registration is closed.")] @@ -52,3 +52,11 @@ pub enum RegisterError { #[error("Cannot run the protocol if total stake is zero.")] ZeroTotalStake, } + +/// Errors which are due to an error in the protocol. +#[derive(Debug, Clone, thiserror::Error, PartialEq)] +pub enum ProtocolError { + /// Value of phi_f is out of the (0,1] range + #[error("phi_f must be in the range (0, 1], got {0}")] + PhiFValueOutOfRange(f64), +} 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..b25eb7af866 100644 --- a/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs +++ b/mithril-stm/src/protocol/key_registration/closed_registration_entry.rs @@ -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< diff --git a/mithril-stm/src/protocol/key_registration/mod.rs b/mithril-stm/src/protocol/key_registration/mod.rs index 730522fc977..26c67511d92 100644 --- a/mithril-stm/src/protocol/key_registration/mod.rs +++ b/mithril-stm/src/protocol/key_registration/mod.rs @@ -9,6 +9,6 @@ mod snark_registration_entry; pub 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..4daa55e811e 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); @@ -50,7 +57,7 @@ impl KeyRegistration { .is_some_and(|vk_snark| self.registered_keys_for_snark.contains(&vk_snark)); if is_already_registered { - return Err(RegisterError::EntryAlreadyRegistered(Box::new(*entry)).into()); + return Err(RegisterError::EntryAlreadyRegistered.into()); } self.registered_keys_for_concatenation.insert(vk_concatenation); @@ -104,10 +111,10 @@ 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, - }) + )) } } @@ -115,15 +122,31 @@ impl KeyRegistration { #[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct ClosedKeyRegistration { /// The closed key registration entries - pub closed_registration_entries: BTreeSet, - + closed_registration_entries: BTreeSet, /// The total stake registered - pub total_stake: Stake, + 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 +160,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,14 +172,15 @@ 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 { + #[cfg(feature = "future_snark")] + pub(crate) fn number_of_registered_parties(&self) -> usize { self.closed_registration_entries.len() } @@ -361,7 +385,7 @@ mod tests { assert!(matches!( result.unwrap_err().downcast_ref::(), - Some(RegisterError::EntryAlreadyRegistered(_)) + Some(RegisterError::EntryAlreadyRegistered) )); } @@ -388,7 +412,7 @@ mod tests { assert!(matches!( result.unwrap_err().downcast_ref::(), - Some(RegisterError::EntryAlreadyRegistered(_)) + Some(RegisterError::EntryAlreadyRegistered) )); } @@ -444,8 +468,7 @@ mod tests { assert!(registered_entries.insert(entry)); }, Err(error) => match error.downcast_ref::(){ - Some(RegisterError::EntryAlreadyRegistered(e1)) => { - assert!(e1.as_ref() == &entry); + Some(RegisterError::EntryAlreadyRegistered) => { assert!(keys.contains(&vk)); }, _ => {panic!("Unexpected error: {error}")} 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..23bc593a291 100644 --- a/mithril-stm/src/protocol/mod.rs +++ b/mithril-stm/src/protocol/mod.rs @@ -10,11 +10,12 @@ pub use aggregate_signature::{ AggregationError, AncillaryGenesisData, AncillaryProofInput, AncillaryProofOutput, AncillaryProverData, AncillaryVerifierData, Clerk, GenesisVerificationKeyBundle, }; -pub use error::RegisterError; +pub use error::{ProtocolError, RegisterError}; +pub(crate) use key_registration::RegistrationEntry; #[cfg(feature = "future_snark")] pub use key_registration::RegistrationEntryForSnark; pub use key_registration::{ - ClosedKeyRegistration, ClosedRegistrationEntry, KeyRegistration, RegistrationEntry, + ClosedKeyRegistration, ClosedRegistrationEntry, KeyRegistration, RegistrationEntryForConcatenation, }; pub use parameters::Parameters; 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..45b348f29b2 100644 --- a/mithril-stm/src/protocol/single_signature/signature.rs +++ b/mithril-stm/src/protocol/single_signature/signature.rs @@ -96,55 +96,69 @@ 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 +351,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 +564,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..694f3dde2fe 100644 --- a/mithril-stm/src/protocol/single_signature/signature_registered_party.rs +++ b/mithril-stm/src/protocol/single_signature/signature_registered_party.rs @@ -22,7 +22,7 @@ pub struct SingleSignatureWithRegisteredParty { /// Stm signature pub sig: SingleSignature, /// Registered party - pub reg_party: ClosedRegistrationEntry, + pub(crate) reg_party: ClosedRegistrationEntry, } impl SingleSignatureWithRegisteredParty { @@ -251,7 +251,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 +272,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); }