Skip to content

Commit c1bdb25

Browse files
starknet_os_runner: storage proof provider trait
1 parent 6f594cb commit c1bdb25

File tree

5 files changed

+170
-0
lines changed

5 files changed

+170
-0
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/starknet_os_runner/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ starknet_api.workspace = true
1717
starknet_os.workspace = true
1818
starknet_patricia.workspace = true
1919
thiserror.workspace = true
20+
tokio.workspace = true
2021
url.workspace = true
2122

23+
[dev-dependencies]
24+
blockifier = { workspace = true, features = ["testing"] }
25+
rstest.workspace = true
26+
2227
[lints]
2328
workspace = true

crates/starknet_os_runner/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@ pub mod errors;
22
pub mod storage_proofs;
33
pub mod virtual_block_executor;
44

5+
#[cfg(test)]
6+
mod storage_proofs_test;
57
#[cfg(test)]
68
mod virtual_block_executor_test;

crates/starknet_os_runner/src/storage_proofs.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,23 @@ use starknet_rust_core::types::{
2323
use crate::errors::ProofProviderError;
2424
use crate::virtual_block_executor::VirtualBlockExecutionData;
2525

26+
/// Provides Patricia Merkle proofs for the initial state used in transaction execution.
27+
///
28+
/// This trait abstracts the retrieval of storage proofs, which are essential for OS input
29+
/// generation. The proofs allow the OS to verify that the initial state values (read during
30+
/// execution) are consistent with the global state commitment (Patricia root).
31+
///
32+
/// The returned `StorageProofs` contains:
33+
/// - `proof_state`: The ambient state values (nonces, class hashes) discovered in the proof.
34+
/// - `commitment_infos`: The Patricia Merkle proof nodes for contracts, classes, and storage tries.
35+
pub trait StorageProofProvider {
36+
fn get_storage_proofs(
37+
&self,
38+
block_number: BlockNumber,
39+
execution_data: &VirtualBlockExecutionData,
40+
) -> Result<StorageProofs, ProofProviderError>;
41+
}
42+
2643
/// Query parameters for fetching storage proofs from RPC.
2744
pub struct RpcStorageProofsQuery {
2845
pub class_hashes: Vec<Felt>,
@@ -270,3 +287,19 @@ impl RpcStorageProofsProvider {
270287
.collect()
271288
}
272289
}
290+
291+
impl StorageProofProvider for RpcStorageProofsProvider {
292+
fn get_storage_proofs(
293+
&self,
294+
block_number: BlockNumber,
295+
execution_data: &VirtualBlockExecutionData,
296+
) -> Result<StorageProofs, ProofProviderError> {
297+
let query = Self::prepare_query(execution_data);
298+
let contract_addresses = query.contract_addresses.clone();
299+
300+
let runtime = tokio::runtime::Runtime::new().expect("Failed to create tokio runtime");
301+
let rpc_proof = runtime.block_on(self.fetch_proofs(block_number, &query))?;
302+
303+
Ok(Self::to_storage_proofs(&rpc_proof, &query)?)
304+
}
305+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
use std::collections::HashSet;
2+
use std::env;
3+
4+
use blockifier::context::BlockContext;
5+
use blockifier::state::cached_state::StateMaps;
6+
use rstest::{fixture, rstest};
7+
use starknet_api::block::BlockNumber;
8+
use starknet_api::core::ContractAddress;
9+
use starknet_api::state::StorageKey;
10+
use starknet_rust::providers::Provider;
11+
use starknet_types_core::felt::Felt;
12+
use url::Url;
13+
14+
use crate::storage_proofs::{RpcStorageProofsProvider, StorageProofProvider};
15+
use crate::virtual_block_executor::VirtualBlockExecutionData;
16+
17+
/// Mainnet STRK token contract address.
18+
const STRK_CONTRACT_ADDRESS: Felt =
19+
Felt::from_hex_unchecked("0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d");
20+
21+
/// Fixture: Creates initial reads with the STRK contract and storage slot 0.
22+
#[fixture]
23+
fn initial_reads() -> (StateMaps, ContractAddress, StorageKey) {
24+
let mut state_maps = StateMaps::default();
25+
let contract_address = ContractAddress::try_from(STRK_CONTRACT_ADDRESS).unwrap();
26+
27+
// Add a storage read for slot 0 (commonly used for total_supply or similar).
28+
let storage_key = StorageKey::from(0u32);
29+
state_maps.storage.insert((contract_address, storage_key), Felt::ZERO);
30+
31+
(state_maps, contract_address, storage_key)
32+
}
33+
34+
/// Fixture: Creates an RPC provider from the RPC_URL environment variable.
35+
#[fixture]
36+
fn rpc_provider() -> RpcStorageProofsProvider {
37+
let rpc_url_str = env::var("NODE_URL").expect("NODE_URL environment variable must be set");
38+
let rpc_url = Url::parse(&rpc_url_str).expect("Invalid RPC URL");
39+
RpcStorageProofsProvider::new(rpc_url)
40+
}
41+
42+
/// Sanity test that verifies storage proof fetching works with a real RPC endpoint.
43+
///
44+
/// This test is ignored by default because it requires a running RPC node.
45+
/// Run with: `RPC_URL=<your_rpc_url> cargo test -p starknet_os_runner -- --ignored`
46+
#[rstest]
47+
#[ignore]
48+
fn test_get_storage_proofs_from_rpc(
49+
rpc_provider: RpcStorageProofsProvider,
50+
initial_reads: (StateMaps, ContractAddress, StorageKey),
51+
) {
52+
let (state_maps, contract_address, storage_key) = initial_reads;
53+
54+
// Fetch latest block number.
55+
let runtime = tokio::runtime::Runtime::new().expect("Failed to create tokio runtime");
56+
let block_number = runtime.block_on(async { rpc_provider.0.block_number().await }).unwrap();
57+
58+
let execution_data = VirtualBlockExecutionData {
59+
execution_outputs: vec![],
60+
block_context: BlockContext::create_for_account_testing(),
61+
initial_reads: state_maps,
62+
executed_class_hashes: HashSet::new(),
63+
};
64+
65+
let result = rpc_provider.get_storage_proofs(BlockNumber(block_number), &execution_data);
66+
assert!(result.is_ok(), "Failed to get storage proofs: {:?}", result.err());
67+
68+
let storage_proofs = result.unwrap();
69+
70+
// Verify contracts tree root is non-zero.
71+
assert!(
72+
storage_proofs.commitment_infos.contracts_trie_commitment_info.previous_root.0
73+
!= Felt::ZERO,
74+
"Expected non-zero contracts tree root"
75+
);
76+
77+
// Verify contracts tree commitment facts are not empty.
78+
assert!(
79+
!storage_proofs.commitment_infos.contracts_trie_commitment_info.commitment_facts.is_empty(),
80+
"Expected non-empty contracts tree commitment facts"
81+
);
82+
83+
// Verify the queried contract is in proof_state.
84+
assert!(
85+
storage_proofs.proof_state.class_hashes.contains_key(&contract_address),
86+
"Expected contract address {:?} in class_hashes",
87+
contract_address
88+
);
89+
assert!(
90+
storage_proofs.proof_state.nonces.contains_key(&contract_address),
91+
"Expected contract address {:?} in nonces",
92+
contract_address
93+
);
94+
95+
// Verify the queried storage is in the original execution_data (not proof_state, which only has
96+
// nonces/hashes)
97+
assert!(
98+
execution_data.initial_reads.storage.contains_key(&(contract_address, storage_key)),
99+
"Expected storage key {:?} in contract's storage",
100+
storage_key
101+
);
102+
103+
// Verify the contract has a storage trie commitment info.
104+
assert!(
105+
storage_proofs
106+
.commitment_infos
107+
.storage_tries_commitment_infos
108+
.contains_key(&contract_address),
109+
"Expected contract address {:?} in storage_tries_commitment_infos",
110+
contract_address
111+
);
112+
113+
// Verify the storage trie commitment facts are not empty.
114+
let storage_commitment =
115+
&storage_proofs.commitment_infos.storage_tries_commitment_infos[&contract_address];
116+
assert!(
117+
!storage_commitment.commitment_facts.is_empty(),
118+
"Expected non-empty storage trie commitment facts for contract {:?}",
119+
contract_address
120+
);
121+
122+
// Verify the storage root is non-zero.
123+
assert!(
124+
storage_commitment.previous_root.0 != Felt::ZERO,
125+
"Expected non-zero storage root for contract {:?}",
126+
contract_address
127+
);
128+
}

0 commit comments

Comments
 (0)