From 511537c6f3477b63a9c32fe41b00b16fdbc3a1be Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 11:25:38 +0000 Subject: [PATCH] starknet_os: add the privacy proof-fact leaf digest The digest of one privacy transaction's proof facts, exactly as the proving side computes the corresponding leaf proof's public output: blake2s(encode_felt252s_to_u32s(proof_facts[2..])) - dropping the proof version and variant markers, matching the leaf simple bootloader's own output hashing. Pinned against the proving side's leaf digest golden. The recursive tree fold over these digests lands on top of this module. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01XmPJM3Wph4QLmFmhcxVsh4 --- crates/starknet_os/src/lib.rs | 1 + crates/starknet_os/src/proof_fact_fold.rs | 31 +++++++++++++++++++ .../starknet_os/src/proof_fact_fold_test.rs | 23 ++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 crates/starknet_os/src/proof_fact_fold.rs create mode 100644 crates/starknet_os/src/proof_fact_fold_test.rs diff --git a/crates/starknet_os/src/lib.rs b/crates/starknet_os/src/lib.rs index 0faa8ee0d3c..7e5c0609c62 100644 --- a/crates/starknet_os/src/lib.rs +++ b/crates/starknet_os/src/lib.rs @@ -6,6 +6,7 @@ pub mod hint_processor; pub mod hints; pub mod io; pub mod metrics; +pub mod proof_fact_fold; pub mod runner; pub mod syscall_handler_utils; #[cfg(any(test, feature = "testing"))] diff --git a/crates/starknet_os/src/proof_fact_fold.rs b/crates/starknet_os/src/proof_fact_fold.rs new file mode 100644 index 00000000000..8bd09d872e6 --- /dev/null +++ b/crates/starknet_os/src/proof_fact_fold.rs @@ -0,0 +1,31 @@ +//! Reproduces the proving side's recursive proof-tree digests over privacy transactions' +//! proof facts. Matches the goldens from the `proving` crate `stwo_run_and_prove_recursive_tree`. + +use blake2::{Blake2s256, Digest}; +use starknet_types_core::felt::Felt; +use starknet_types_core::hash::Blake2Felt252; + +#[cfg(test)] +#[path = "proof_fact_fold_test.rs"] +mod proof_fact_fold_test; + +pub const BLAKE2S_DIGEST_N_WORDS: usize = 8; + +/// A Blake2s-256 digest as little-endian u32 words. +pub type Blake2sDigestWords = [u32; BLAKE2S_DIGEST_N_WORDS]; + +/// Computes one transaction's leaf output digest: +/// blake2s(encode_felt252s_to_u32s(proof_facts[2..])). The preimage drops the two +/// version markers, keeping [program_hash, ...virtual OS output]. +pub fn compute_leaf_output_digest(proof_facts: &[Felt]) -> Blake2sDigestWords { + assert!(proof_facts.len() >= 3, "proof facts must contain at least 3 felts"); + blake2s_over_u32_words(&Blake2Felt252::encode_felts_to_u32s(&proof_facts[2..])) +} + +pub fn blake2s_over_u32_words(words: &[u32]) -> Blake2sDigestWords { + let bytes: Vec = words.iter().flat_map(|word| word.to_le_bytes()).collect(); + let digest_bytes: [u8; 32] = Blake2s256::digest(&bytes).into(); + std::array::from_fn(|word_index| { + u32::from_le_bytes(digest_bytes[word_index * 4..(word_index + 1) * 4].try_into().unwrap()) + }) +} diff --git a/crates/starknet_os/src/proof_fact_fold_test.rs b/crates/starknet_os/src/proof_fact_fold_test.rs new file mode 100644 index 00000000000..0dc7dcd22a9 --- /dev/null +++ b/crates/starknet_os/src/proof_fact_fold_test.rs @@ -0,0 +1,23 @@ +use starknet_types_core::felt::Felt; + +use super::{compute_leaf_output_digest, Blake2sDigestWords}; + +#[test] +fn test_leaf_output_digest_matches_proving_side_golden() { + let proving_side_preimage = [ + Felt::from_dec_str( + "1433852663250257978909904594223798547176815246431631498282706690602142197827", + ) + .unwrap(), + Felt::from(11), + Felt::from(13), + Felt::from(17), + ]; + let proof_facts: Vec = + [Felt::ZERO, Felt::ZERO].into_iter().chain(proving_side_preimage).collect(); + let expected_digest_words: Blake2sDigestWords = [ + 1603116091, 3258597502, 2711032228, 4175407283, 343882323, 1898618121, 1344732087, + 1064799167, + ]; + assert_eq!(compute_leaf_output_digest(&proof_facts), expected_digest_words); +}