From 776800678ea8df131a6ec0f6ee0a35209c9978f2 Mon Sep 17 00:00:00 2001 From: Ariel Elperin Date: Thu, 7 May 2026 12:01:36 +0300 Subject: [PATCH] starknet_committer: compute and store accessed keys digest --- crates/starknet_committer/Cargo.toml | 4 ++ .../src/db/serde_db_utils.rs | 55 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/crates/starknet_committer/Cargo.toml b/crates/starknet_committer/Cargo.toml index 906ede3d156..9e11750a48e 100644 --- a/crates/starknet_committer/Cargo.toml +++ b/crates/starknet_committer/Cargo.toml @@ -7,9 +7,13 @@ license.workspace = true description = "Computes and manages Starknet state." [features] +os_input = ["dep:bincode", "dep:blake2", "dep:digest"] testing = ["starknet_patricia/testing"] [dependencies] +bincode = { workspace = true, optional = true } +blake2 = { workspace = true, optional = true } +digest = { workspace = true, optional = true } apollo_config.workspace = true async-trait.workspace = true derive_more = { workspace = true, features = ["as_ref", "from", "into"] } diff --git a/crates/starknet_committer/src/db/serde_db_utils.rs b/crates/starknet_committer/src/db/serde_db_utils.rs index 67babb06e9d..6ff11c04a8f 100644 --- a/crates/starknet_committer/src/db/serde_db_utils.rs +++ b/crates/starknet_committer/src/db/serde_db_utils.rs @@ -1,8 +1,22 @@ +#[cfg(feature = "os_input")] +use std::collections::HashMap; + +#[cfg(feature = "os_input")] +use blake2::Blake2s256; +#[cfg(feature = "os_input")] +use digest::Digest; use serde::{Deserialize, Serialize}; use starknet_api::block::BlockNumber; +#[cfg(feature = "os_input")] +use starknet_api::core::{ClassHash, ContractAddress}; +#[cfg(feature = "os_input")] +use starknet_api::state::StorageKey; use starknet_patricia_storage::storage_trait::DbValue; use starknet_types_core::felt::Felt; +#[cfg(feature = "os_input")] +use crate::block_committer::input::StarknetStorageKey; + #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Hash, Serialize)] pub struct DbBlockNumber(pub BlockNumber); @@ -23,3 +37,44 @@ pub fn serialize_felt_no_packing(felt: Felt) -> DbValue { pub fn deserialize_felt_no_packing(value: &DbValue) -> Felt { Felt::from_bytes_be_slice(&value.0) } + +/// Wire layout for [`accessed_keys_digest`]. Field order and sorting are part of the digest. +#[cfg(feature = "os_input")] +#[derive(Serialize, Deserialize)] +struct AccessedKeysCanonical { + class_hashes: Vec, + /// Contract trie reads (e.g. class / nonce), sorted. + contract_addresses: Vec, + /// Storage-slot reads: sorted by contract address; each key list sorted by underlying felt. + contract_storage: Vec<(ContractAddress, Vec)>, +} + +/// BLAKE2s-256 digest of canonical serialized accessed read keys (replay fingerprint). +#[cfg(feature = "os_input")] +pub fn accessed_keys_digest( + class_hashes: &[ClassHash], + contract_addresses: &[ContractAddress], + contract_storage_keys: &HashMap>, +) -> [u8; 32] { + let mut class_hashes: Vec = class_hashes.iter().copied().collect(); + class_hashes.sort(); + + let mut contract_addresses: Vec = contract_addresses.iter().copied().collect(); + contract_addresses.sort(); + + let mut contract_storage: Vec<(ContractAddress, Vec)> = contract_storage_keys + .iter() + .map(|(addr, keys)| { + let mut keys: Vec = keys.iter().map(|k| k.0).collect(); + keys.sort_by_key(|k| Felt::from(*k)); + (*addr, keys) + }) + .collect(); + contract_storage.sort_by_key(|(addr, _)| *addr); + + let canonical = AccessedKeysCanonical { class_hashes, contract_addresses, contract_storage }; + let bytes = bincode::serialize(&canonical).expect("accessed keys bincode serialization"); + + let hash = Blake2s256::digest(&bytes); + hash.into() +}