diff --git a/Cargo.lock b/Cargo.lock index 8b3042389e1..30b26980c93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -689,9 +689,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.16" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcdb4c7013139a150f9fc55d123186dbfaba0d912817466282c73ac49e71fb45" +checksum = "4fd49896f12ac9b6dcd7a5998466b9b58263a695a3dd1ecc1aaca2e12a90b080" dependencies = [ "cc", "glob", @@ -1571,7 +1571,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.117", ] [[package]] @@ -1715,9 +1715,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.2.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", @@ -1903,9 +1903,9 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixed" -version = "1.31.0" +version = "1.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9af2cbf772fa6d1c11358f92ef554cb6b386201210bcf0e91fb7fba8a907fb40" +checksum = "707070ccf8c4173548210893a0186e29c266901b71ed20cd9e2ca0193dfe95c3" dependencies = [ "az", "bytemuck", diff --git a/mithril-common/Cargo.toml b/mithril-common/Cargo.toml index 9b033cd5812..f0255c09013 100644 --- a/mithril-common/Cargo.toml +++ b/mithril-common/Cargo.toml @@ -42,8 +42,12 @@ chrono = { workspace = true } ciborium = "0.2.2" ckb-merkle-mountain-range = "0.6.1" digest = { workspace = true } -ed25519-dalek = { version = "2.2.0", features = ["rand_core", "serde"] } -fixed = "1.31.0" +# Exact-pinned to 2.1.1 for compatibility with the RISC0 `curve25519-dalek` +# precompile fork (tagged on the 2.1.x line). +ed25519-dalek = { version = "=2.1.1", features = ["rand_core", "serde"] } +# Exact-pinned to 1.29.0 to stay buildable on the rustc the RISC0 +# toolchain ships (fixed 1.30+ requires rustc 1.93). +fixed = "=1.29.0" hex = { workspace = true } kes-summed-ed25519 = { version = "0.2.1", features = ["serde_enabled", "sk_clone_enabled"] } mithril-stm = { path = "../mithril-stm", version = ">=0.9", default-features = false } diff --git a/mithril-stm/Cargo.toml b/mithril-stm/Cargo.toml index fca72cbf8f7..8f2859fe8f8 100644 --- a/mithril-stm/Cargo.toml +++ b/mithril-stm/Cargo.toml @@ -38,7 +38,9 @@ rug-backend = ["rug/default"] anyhow = { workspace = true } blake2 = "0.10.6" # Enforce blst portable feature for runtime detection of Intel ADX instruction set. -blst = { version = "0.3.16", features = ["portable"] } +# Exact-pinned to 0.3.15 to match the dwarf harness divergence-1 pin +# (BLS identity-rejection layer). Bump in lockstep with that pin if upgraded. +blst = { version = "=0.3.15", features = ["portable"] } ciborium = "0.2.2" digest = { workspace = true } ff = { version = "0.13.1", optional = true } diff --git a/mithril-stm/src/lib.rs b/mithril-stm/src/lib.rs index 8f57a89adc1..fb2009c9a54 100644 --- a/mithril-stm/src/lib.rs +++ b/mithril-stm/src/lib.rs @@ -123,7 +123,9 @@ mod proof_system; mod protocol; mod signature_scheme; -pub use proof_system::AggregateVerificationKeyForConcatenation; +pub use proof_system::{ + AggregateVerificationKeyForConcatenation, ConcatenationProof, SingleSignatureForConcatenation, +}; pub use protocol::{ AggregateSignature, AggregateSignatureError, AggregateSignatureType, AggregateVerificationKey, AggregationError, Clerk, ClosedKeyRegistration, ClosedRegistrationEntry, Initializer, @@ -134,6 +136,11 @@ pub use protocol::{ }; pub use signature_scheme::BlsSignatureError; +// Re-export Merkle types for custom serializer / deserializer consumers (e.g. risc0). +pub use membership_commitment::{ + MerkleBatchPath, MerkleTreeBatchCommitment, MerkleTreeConcatenationLeaf, MerkleTreeLeaf, +}; + use blake2::{Blake2b, digest::consts::U32}; use digest::{Digest, FixedOutput}; use std::fmt::Debug; @@ -152,7 +159,7 @@ pub use signature_scheme::{ #[cfg(all(feature = "future_snark", not(feature = "benchmark-internals")))] use hash::poseidon::MidnightPoseidonDigest; -#[cfg(feature = "benchmark-internals")] +#[cfg(all(feature = "benchmark-internals", feature = "future_snark"))] pub use hash::poseidon::MidnightPoseidonDigest; #[cfg(feature = "future_snark")] diff --git a/mithril-stm/src/membership_commitment/merkle_tree/commitment.rs b/mithril-stm/src/membership_commitment/merkle_tree/commitment.rs index d24b9ff0a74..a29eb887580 100644 --- a/mithril-stm/src/membership_commitment/merkle_tree/commitment.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/commitment.rs @@ -118,7 +118,9 @@ pub struct MerkleTreeBatchCommitment { } impl MerkleTreeBatchCommitment { - pub(crate) fn new(root: Vec, nr_leaves: usize) -> Self { + // Made public for AVK reconstruction from byte form in custom serializer + // consumers (e.g. risc0). + pub fn new(root: Vec, nr_leaves: usize) -> Self { Self { root, nr_leaves, @@ -127,6 +129,11 @@ impl MerkleTreeBatchCommitment } } + /// Number of leaves in the committed Merkle tree. + pub fn nr_leaves(&self) -> usize { + self.nr_leaves + } + #[cfg(feature = "future_snark")] // TODO: remove this allow dead_code directive when function is called or future_snark is activated #[allow(dead_code)] diff --git a/mithril-stm/src/membership_commitment/merkle_tree/path.rs b/mithril-stm/src/membership_commitment/merkle_tree/path.rs index 1cce05985f1..2273cc2984f 100644 --- a/mithril-stm/src/membership_commitment/merkle_tree/path.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/path.rs @@ -103,6 +103,18 @@ impl MerkleBatchPath { codec::to_cbor_bytes(self) } + /// Borrow the path's value list. Exposed for custom serializer / deserializer + /// consumers (e.g. risc0) that need the legacy raw byte layout. + pub fn values(&self) -> &[Vec] { + &self.values + } + + /// Borrow the path's index list. Exposed for custom serializer / deserializer + /// consumers (e.g. risc0) that need the legacy raw byte layout. + pub fn indices(&self) -> &[usize] { + &self.indices + } + /// Try to convert a byte string into a `BatchPath`. pub fn from_bytes(bytes: &[u8]) -> StmResult { codec::from_versioned_bytes(bytes, Self::from_bytes_legacy) diff --git a/mithril-stm/src/proof_system/concatenation/aggregate_key.rs b/mithril-stm/src/proof_system/concatenation/aggregate_key.rs index 98d87e6970c..d36be21f236 100644 --- a/mithril-stm/src/proof_system/concatenation/aggregate_key.rs +++ b/mithril-stm/src/proof_system/concatenation/aggregate_key.rs @@ -20,6 +20,18 @@ pub struct AggregateVerificationKeyForConcatenation { } impl AggregateVerificationKeyForConcatenation { + /// Construct from already-built components. Exposed for custom + /// serializer / deserializer consumers (e.g. risc0). + pub fn new( + mt_commitment: MerkleTreeBatchCommitment, + total_stake: Stake, + ) -> Self { + Self { + mt_commitment, + total_stake, + } + } + /// Get the Merkle tree batch commitment. pub(crate) fn get_merkle_tree_batch_commitment( &self, @@ -27,6 +39,14 @@ impl AggregateVerificationKeyForConcatenation { self.mt_commitment.clone() } + /// Borrow the Merkle tree batch commitment. Exposed for custom + /// serializer consumers that need byte-level access to root + nr_leaves. + pub fn get_mt_commitment( + &self, + ) -> &MerkleTreeBatchCommitment { + &self.mt_commitment + } + /// Get the total stake. pub fn get_total_stake(&self) -> Stake { self.total_stake @@ -48,11 +68,16 @@ impl AggregateVerificationKeyForConcatenation { let mut u64_bytes = [0u8; 8]; let size = bytes.len(); + // Guard against short inputs: a legacy AVK ends with an 8-byte + // stake suffix. Without this check, `&bytes[size - 8..]` panics + // with an out-of-range slice index on inputs shorter than 8 + // bytes instead of returning a proper serialization error. + let mt_commitment_slice = bytes + .get(..size.checked_sub(8).ok_or(MerkleTreeError::SerializationError)?) + .ok_or(MerkleTreeError::SerializationError)?; u64_bytes.copy_from_slice(&bytes[size - 8..]); let stake = u64::from_be_bytes(u64_bytes); - let mt_commitment = MerkleTreeBatchCommitment::from_bytes( - bytes.get(..size - 8).ok_or(MerkleTreeError::SerializationError)?, - )?; + let mt_commitment = MerkleTreeBatchCommitment::from_bytes(mt_commitment_slice)?; Ok(Self { mt_commitment, total_stake: stake, diff --git a/mithril-stm/src/proof_system/concatenation/mod.rs b/mithril-stm/src/proof_system/concatenation/mod.rs index fc0ecfb5b62..435dee0c980 100644 --- a/mithril-stm/src/proof_system/concatenation/mod.rs +++ b/mithril-stm/src/proof_system/concatenation/mod.rs @@ -10,4 +10,4 @@ pub use clerk::ConcatenationClerk; pub(super) use eligibility::is_lottery_won; pub use proof::ConcatenationProof; pub(crate) use signer::ConcatenationProofSigner; -pub(crate) use single_signature::SingleSignatureForConcatenation; +pub use single_signature::SingleSignatureForConcatenation; diff --git a/mithril-stm/src/proof_system/concatenation/proof.rs b/mithril-stm/src/proof_system/concatenation/proof.rs index f0050278e55..028becac4be 100644 --- a/mithril-stm/src/proof_system/concatenation/proof.rs +++ b/mithril-stm/src/proof_system/concatenation/proof.rs @@ -38,6 +38,23 @@ pub struct ConcatenationProof { } impl ConcatenationProof { + /// Construct a `ConcatenationProof` from already-aggregated components. + /// Exposed for custom serializer / deserializer consumers (e.g. risc0). + pub fn new( + signatures: Vec, + batch_proof: MerkleBatchPath, + ) -> Self { + Self { + signatures, + batch_proof, + } + } + + /// Borrow the underlying signatures. Exposed for custom serializer consumers. + pub fn signatures(&self) -> &[SingleSignatureWithRegisteredParty] { + &self.signatures + } + /// Aggregate a set of signatures for their corresponding indices. /// /// This function first deduplicates the repeated signatures, and if there are enough signatures, it collects the merkle tree indexes of unique signatures. diff --git a/mithril-stm/src/proof_system/concatenation/single_signature.rs b/mithril-stm/src/proof_system/concatenation/single_signature.rs index 065811cc24e..64ff4d7c9ef 100644 --- a/mithril-stm/src/proof_system/concatenation/single_signature.rs +++ b/mithril-stm/src/proof_system/concatenation/single_signature.rs @@ -13,7 +13,7 @@ use super::is_lottery_won; /// Single signature for the concatenation proof system. #[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct SingleSignatureForConcatenation { +pub struct SingleSignatureForConcatenation { /// The underlying BLS signature sigma: BlsSignature, /// The index(es) for which the signature is valid @@ -22,11 +22,21 @@ pub(crate) struct SingleSignatureForConcatenation { impl SingleSignatureForConcatenation { /// Create and return a new instance of `SingleSignatureForConcatenation` for given `sigma` and - /// `indexes`. - pub(crate) fn new(sigma: BlsSignature, indexes: Vec) -> Self { + /// `indexes`. Exposed for custom serializer / deserializer consumers (e.g. risc0). + pub fn new(sigma: BlsSignature, indexes: Vec) -> Self { Self { sigma, indexes } } + /// Borrow the underlying BLS signature. Exposed for custom serializer consumers. + pub fn sigma(&self) -> &BlsSignature { + &self.sigma + } + + /// Borrow the lottery indices the signature is valid for. Exposed for custom serializer consumers. + pub fn indexes(&self) -> &[LotteryIndex] { + &self.indexes + } + /// Verify a `SingleSignatureForConcatenation` by validating the underlying BLS signature and checking /// that the lottery was won for all indexes. pub(crate) fn verify( diff --git a/mithril-stm/src/proof_system/mod.rs b/mithril-stm/src/proof_system/mod.rs index 8cebdbb1fa9..cf1f72f3d72 100644 --- a/mithril-stm/src/proof_system/mod.rs +++ b/mithril-stm/src/proof_system/mod.rs @@ -22,8 +22,9 @@ mod halo2_snark; pub use concatenation::{ AggregateVerificationKeyForConcatenation, ConcatenationClerk, ConcatenationProof, + SingleSignatureForConcatenation, }; -pub(crate) use concatenation::{ConcatenationProofSigner, SingleSignatureForConcatenation}; +pub(crate) use concatenation::ConcatenationProofSigner; #[cfg(feature = "future_snark")] pub use halo2_snark::AggregateVerificationKeyForSnark; diff --git a/mithril-stm/src/protocol/single_signature/signature.rs b/mithril-stm/src/protocol/single_signature/signature.rs index 88a97f0d7fa..fc80dd609ba 100644 --- a/mithril-stm/src/protocol/single_signature/signature.rs +++ b/mithril-stm/src/protocol/single_signature/signature.rs @@ -30,6 +30,26 @@ pub struct SingleSignature { } impl SingleSignature { + /// Construct a `SingleSignature` from already-built components. + /// Exposed for custom serializer / deserializer consumers (e.g. risc0). + pub fn new( + concatenation_signature: SingleSignatureForConcatenation, + signer_index: SignerIndex, + #[cfg(feature = "future_snark")] snark_signature: Option, + ) -> Self { + Self { + concatenation_signature, + signer_index, + #[cfg(feature = "future_snark")] + snark_signature, + } + } + + /// Borrow the concatenation-proof-system signature. Exposed for custom serializer consumers. + pub fn concatenation_signature(&self) -> &SingleSignatureForConcatenation { + &self.concatenation_signature + } + /// Verify a `SingleSignature` by validating the underlying single signature for proof system. pub fn verify( &self,