Skip to content

Commit df91aa4

Browse files
starknet_os_runner: storage proof provider trait
1 parent 603639d commit df91aa4

File tree

5 files changed

+167
-3
lines changed

5 files changed

+167
-3
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: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,18 @@ description = "Runs transactions through the Starknet OS and returns Cairo PIE a
1010
blockifier.workspace = true
1111
blockifier_reexecution.workspace = true
1212
indexmap.workspace = true
13-
starknet_api.workspace = true
14-
starknet_os.workspace = true
15-
starknet_patricia.workspace = true
1613
starknet-rust.workspace = true
1714
starknet-rust-core.workspace = true
1815
starknet-types-core.workspace = true
16+
starknet_api.workspace = true
17+
starknet_os.workspace = true
18+
starknet_patricia.workspace = true
1919
thiserror.workspace = true
20+
tokio.workspace = true
2021
url.workspace = true
2122

23+
[dev-dependencies]
24+
rstest.workspace = true
25+
2226
[lints]
2327
workspace = true
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
pub mod errors;
22
pub mod execution_data;
33
pub mod storage_proofs;
4+
5+
#[cfg(test)]
6+
mod storage_proofs_test;

crates/starknet_os_runner/src/storage_proofs.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,23 @@ use starknet_types_core::felt::Felt as TypesFelt;
2424

2525
use crate::errors::ProofProviderError;
2626

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

0 commit comments

Comments
 (0)