diff --git a/Cargo.lock b/Cargo.lock index 1220d5c3a9f..9f66c7cfacf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12222,7 +12222,6 @@ dependencies = [ "starknet_patricia", "starknet_patricia_storage", "strum 0.25.0", - "strum_macros 0.25.3", "thiserror 1.0.69", "tokio", "tracing", diff --git a/crates/starknet_committer/Cargo.toml b/crates/starknet_committer/Cargo.toml index 6b2cda6fdb1..cee16317727 100644 --- a/crates/starknet_committer/Cargo.toml +++ b/crates/starknet_committer/Cargo.toml @@ -6,6 +6,9 @@ repository.workspace = true license.workspace = true description = "Computes and manages Starknet state." +[features] +testing = ["starknet_patricia/testing"] + [dependencies] ethnum.workspace = true hex.workspace = true @@ -19,7 +22,6 @@ starknet_api.workspace = true starknet_patricia.workspace = true starknet_patricia_storage.workspace = true strum.workspace = true -strum_macros.workspace = true thiserror.workspace = true tokio = { workspace = true, features = ["rt"] } tracing.workspace = true diff --git a/crates/starknet_committer/src/block_committer.rs b/crates/starknet_committer/src/block_committer.rs index 431c7096e3d..ed46649dab1 100644 --- a/crates/starknet_committer/src/block_committer.rs +++ b/crates/starknet_committer/src/block_committer.rs @@ -1,4 +1,7 @@ pub mod commit; pub mod errors; pub mod input; +#[cfg(any(feature = "testing", test))] pub mod random_structs; +#[cfg(any(feature = "testing", test))] +pub mod state_diff_generator; diff --git a/crates/starknet_committer/src/block_committer/random_structs.rs b/crates/starknet_committer/src/block_committer/random_structs.rs index f9fae53c9a4..0be546401d8 100644 --- a/crates/starknet_committer/src/block_committer/random_structs.rs +++ b/crates/starknet_committer/src/block_committer/random_structs.rs @@ -6,7 +6,13 @@ use rand::prelude::IteratorRandom; use rand::Rng; use rand_distr::num_traits::ToPrimitive; use rand_distr::{Distribution, Geometric}; -use starknet_api::core::{ClassHash, ContractAddress, Nonce, PATRICIA_KEY_UPPER_BOUND}; +use starknet_api::core::{ + ClassHash, + ContractAddress, + Nonce, + PatriciaKey, + PATRICIA_KEY_UPPER_BOUND, +}; use starknet_patricia::felt::u256_from_felt; use starknet_patricia::hash::hash_trait::HashOutput; use starknet_patricia::patricia_merkle_tree::external_test_utils::{ @@ -181,14 +187,18 @@ random_filled_node!(StarknetStorageValue); random_filled_node!(CompiledClassHash); random_filled_node!(ContractState); +impl RandomValue for PatriciaKey { + fn random(rng: &mut R, max: Option) -> Self { + let upper_bound = u256_from_felt(&Felt::from_hex_unchecked(PATRICIA_KEY_UPPER_BOUND)); + let max_patricia_key = min(upper_bound, max.unwrap_or(upper_bound)); + + Self::try_from(Felt::random(rng, Some(max_patricia_key))).unwrap() + } +} + impl RandomValue for ContractAddress { fn random(rng: &mut R, max: Option) -> Self { - let address_max = u256_from_felt(&Felt::from_hex_unchecked(PATRICIA_KEY_UPPER_BOUND)); - let max = match max { - None => address_max, - Some(caller_max) => min(address_max, caller_max), - }; - ContractAddress::try_from(Felt::random(rng, Some(max))).unwrap() + ContractAddress(PatriciaKey::random(rng, max)) } } diff --git a/crates/starknet_committer/src/block_committer/state_diff_generator.rs b/crates/starknet_committer/src/block_committer/state_diff_generator.rs new file mode 100644 index 00000000000..fcb8facb362 --- /dev/null +++ b/crates/starknet_committer/src/block_committer/state_diff_generator.rs @@ -0,0 +1,36 @@ +use std::collections::HashMap; + +use rand::Rng; +use starknet_api::core::{ContractAddress, PatriciaKey}; +use starknet_api::state::StorageKey; + +use crate::block_committer::input::{StarknetStorageKey, StarknetStorageValue, StateDiff}; +use crate::block_committer::random_structs::RandomValue; + +#[cfg(test)] +#[path = "state_diff_generator_test.rs"] +pub mod state_diff_generator_test; + +pub(crate) const RANDOM_STATE_DIFF_CONTRACT_ADDRESS: u32 = 500_u32; +pub(crate) const N_STORAGE_UPDATES: usize = 1000_usize; + +pub fn generate_random_state_diff(rng: &mut R) -> StateDiff { + let mut storage_updates = HashMap::new(); + let mut contract_updates = HashMap::with_capacity(N_STORAGE_UPDATES); + for _ in 0..N_STORAGE_UPDATES { + let storage_entry = generate_random_storage_entry(rng); + contract_updates.insert(storage_entry.0, storage_entry.1); + } + + storage_updates + .insert(ContractAddress::from(RANDOM_STATE_DIFF_CONTRACT_ADDRESS), contract_updates); + StateDiff { storage_updates, ..Default::default() } +} + +fn generate_random_storage_entry( + rng: &mut R, +) -> (StarknetStorageKey, StarknetStorageValue) { + let key = StarknetStorageKey(StorageKey(PatriciaKey::random(rng, None))); + let value = StarknetStorageValue::random(rng, None); + (key, value) +} diff --git a/crates/starknet_committer/src/block_committer/state_diff_generator_test.rs b/crates/starknet_committer/src/block_committer/state_diff_generator_test.rs new file mode 100644 index 00000000000..bf622d9447f --- /dev/null +++ b/crates/starknet_committer/src/block_committer/state_diff_generator_test.rs @@ -0,0 +1,40 @@ +use std::collections::HashMap; + +use rand::rngs::SmallRng; +use rand::{Rng, SeedableRng}; +use rstest::{fixture, rstest}; +use starknet_api::core::ContractAddress; + +use crate::block_committer::state_diff_generator::{ + generate_random_state_diff, + generate_random_storage_entry, + N_STORAGE_UPDATES, + RANDOM_STATE_DIFF_CONTRACT_ADDRESS, +}; + +#[fixture] +fn rng() -> SmallRng { + let seed = 42_u64; // Constant seed for reproducibility. + SmallRng::seed_from_u64(seed) +} + +#[rstest] +fn generate_random_state_diff_test(mut rng: impl Rng) { + let state_diff = generate_random_state_diff(&mut rng); + let contract = state_diff + .storage_updates + .get(&ContractAddress::from(RANDOM_STATE_DIFF_CONTRACT_ADDRESS)) + .unwrap(); + assert_eq!(contract.len(), N_STORAGE_UPDATES); +} + +#[rstest] +fn key_distribution_test(mut rng: impl Rng) { + let n_iterations = N_STORAGE_UPDATES * 100; + let mut storage_updates = HashMap::with_capacity(n_iterations); + for _ in 0..n_iterations { + let (key, value) = generate_random_storage_entry(&mut rng); + storage_updates.insert(key, value); + } + assert!(storage_updates.len() >= (n_iterations * 99 / 100), "Key distribution is limited"); +} diff --git a/crates/starknet_committer_and_os_cli/Cargo.toml b/crates/starknet_committer_and_os_cli/Cargo.toml index 73478db7380..d875b8326cd 100644 --- a/crates/starknet_committer_and_os_cli/Cargo.toml +++ b/crates/starknet_committer_and_os_cli/Cargo.toml @@ -38,7 +38,8 @@ serde_json.workspace = true serde_repr.workspace = true starknet-types-core.workspace = true starknet_api.workspace = true -starknet_committer.workspace = true +# The testing feature is needed for the python tests. +starknet_committer = { workspace = true, features = ["testing"] } # The 'testing' feature of starknet_os should be moved under this crate's `testing` feature, when it # exists. starknet_os = { workspace = true, features = ["deserialize", "testing"] }