Skip to content

Commit 352c15d

Browse files
starknet_os_flow_tests: state diff comparison (#8497)
1 parent 6f89c52 commit 352c15d

File tree

6 files changed

+84
-24
lines changed

6 files changed

+84
-24
lines changed

crates/blockifier/src/state/cached_state.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,16 @@ impl StateMaps {
368368
compiled_class_hash_keys: self.compiled_class_hashes.keys().cloned().collect(),
369369
}
370370
}
371+
372+
/// Returns the set of keys that aliases were potentially allocated for.
373+
#[cfg(any(test, feature = "testing"))]
374+
pub fn alias_keys(&self) -> HashSet<crate::state::stateful_compression::AliasKey> {
375+
let mut keys = HashSet::from_iter(
376+
self.get_contract_addresses().into_iter().map(|address| StorageKey(address.0)),
377+
);
378+
keys.extend(self.storage.keys().map(|(_address, storage_key)| *storage_key));
379+
keys
380+
}
371381
}
372382

373383
/// Caches read and write requests.

crates/starknet_os/src/io.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
pub mod os_input;
22
pub mod os_output;
33
pub mod os_output_types;
4-
#[cfg(test)]
4+
#[cfg(any(feature = "testing", test))]
55
pub mod test_utils;

crates/starknet_os/src/io/test_utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,13 @@ fn to_state_maps<CO: ContractChangesGetter, CL: UpdateGetter<ClassHash, Compiled
132132
}
133133

134134
impl FullOsStateDiff {
135-
pub(crate) fn as_state_maps(&self) -> StateMaps {
135+
pub fn as_state_maps(&self) -> StateMaps {
136136
to_state_maps(&self.contracts, &self.classes)
137137
}
138138
}
139139

140140
impl PartialOsStateDiff {
141-
pub(crate) fn _as_state_maps(&self) -> StateMaps {
141+
pub fn as_state_maps(&self) -> StateMaps {
142142
to_state_maps(&self.contracts, &self.classes)
143143
}
144144
}

crates/starknet_os_flow_tests/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ rstest.workspace = true
1414
starknet-types-core.workspace = true
1515
starknet_api.workspace = true
1616
starknet_committer.workspace = true
17-
starknet_os = { workspace = true, features = ["testing"] }
17+
starknet_os = { workspace = true, features = ["include_program_output", "testing"] }
1818
starknet_patricia = { workspace = true, features = ["testing"] }
1919
starknet_patricia_storage = { workspace = true, features = ["testing"] }
2020
tokio.workspace = true

crates/starknet_os_flow_tests/src/test_manager.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
#![allow(dead_code)]
2-
use std::collections::HashMap;
2+
use std::collections::{HashMap, HashSet};
33
use std::sync::LazyLock;
44

55
use blockifier::abi::constants::STORED_BLOCK_HASH_BUFFER;
66
use blockifier::blockifier_versioned_constants::VersionedConstants;
77
use blockifier::bouncer::BouncerConfig;
88
use blockifier::context::{BlockContext, ChainInfo, FeeTokenAddresses};
9+
use blockifier::state::cached_state::StateMaps;
10+
use blockifier::state::stateful_compression_test_utils::decompress;
11+
use blockifier::test_utils::ALIAS_CONTRACT_ADDRESS;
912
use blockifier::transaction::transaction_execution::Transaction as BlockifierTransaction;
1013
use starknet_api::block::{BlockHash, BlockInfo, BlockNumber};
1114
use starknet_api::contract_class::ContractClass;
@@ -26,7 +29,7 @@ use starknet_os::io::os_input::{
2629
OsHintsConfig,
2730
StarknetOsInput,
2831
};
29-
use starknet_os::io::os_output::StarknetOsRunnerOutput;
32+
use starknet_os::io::os_output::{OsStateDiff, StarknetOsRunnerOutput};
3033
use starknet_os::runner::{run_os_stateless, DEFAULT_OS_LAYOUT};
3134
use starknet_patricia_storage::map_storage::BorrowedMapStorage;
3235
use starknet_types_core::felt::Felt;
@@ -68,6 +71,11 @@ pub(crate) struct TestManager<S: FlowTestState> {
6871
per_block_transactions: Vec<Vec<BlockifierTransaction>>,
6972
}
7073

74+
pub(crate) struct OsTestOutput {
75+
pub(crate) os_output: StarknetOsRunnerOutput,
76+
pub(crate) decompressed_state_diff: StateMaps,
77+
}
78+
7179
impl<S: FlowTestState> TestManager<S> {
7280
/// Creates a new `TestManager` with the provided initial state data.
7381
pub(crate) fn new_with_initial_state_data(initial_state_data: InitialStateData<S>) -> Self {
@@ -154,7 +162,7 @@ impl<S: FlowTestState> TestManager<S> {
154162
pub(crate) async fn execute_test_with_default_block_contexts(
155163
self,
156164
initial_block_number: u64,
157-
) -> StarknetOsRunnerOutput {
165+
) -> OsTestOutput {
158166
let n_blocks = self.per_block_transactions.len();
159167
let block_contexts: Vec<BlockContext> = (0..n_blocks)
160168
.map(|i| {
@@ -171,7 +179,7 @@ impl<S: FlowTestState> TestManager<S> {
171179
pub(crate) async fn execute_test_with_block_contexts(
172180
self,
173181
block_contexts: Vec<BlockContext>,
174-
) -> StarknetOsRunnerOutput {
182+
) -> OsTestOutput {
175183
assert_eq!(
176184
block_contexts.len(),
177185
self.per_block_transactions.len(),
@@ -193,10 +201,7 @@ impl<S: FlowTestState> TestManager<S> {
193201
}
194202

195203
// Private method which executes the flow test.
196-
async fn execute_flow_test(
197-
mut self,
198-
block_contexts: Vec<BlockContext>,
199-
) -> StarknetOsRunnerOutput {
204+
async fn execute_flow_test(mut self, block_contexts: Vec<BlockContext>) -> OsTestOutput {
200205
let per_block_txs = self.per_block_transactions;
201206
let mut os_block_inputs = vec![];
202207
let mut cached_state_inputs = vec![];
@@ -209,6 +214,7 @@ impl<S: FlowTestState> TestManager<S> {
209214
contracts_trie_root_hash: self.initial_state.contracts_trie_root_hash,
210215
classes_trie_root_hash: self.initial_state.classes_trie_root_hash,
211216
};
217+
let mut alias_keys = HashSet::new();
212218
for (block_txs, block_context) in per_block_txs.into_iter().zip(block_contexts.into_iter())
213219
{
214220
// Clone the block info for later use.
@@ -220,6 +226,7 @@ impl<S: FlowTestState> TestManager<S> {
220226
// Update the wrapped state.
221227
let state_diff = final_state.to_state_diff().unwrap();
222228
state = final_state.state;
229+
alias_keys.extend(state_diff.state_maps.alias_keys());
223230
state.apply_writes(&state_diff.state_maps, &final_state.class_hash_to_class.borrow());
224231
// Commit the state diff.
225232
let committer_state_diff = create_committer_state_diff(block_summary.state_diff);
@@ -290,10 +297,18 @@ impl<S: FlowTestState> TestManager<S> {
290297
chain_id: CHAIN_ID_FOR_TESTS.clone(),
291298
strk_fee_token_address: *STRK_FEE_TOKEN_ADDRESS,
292299
};
293-
let os_hints_config = OsHintsConfig { full_output: true, chain_info, ..Default::default() };
300+
let os_hints_config = OsHintsConfig { chain_info, ..Default::default() };
294301
let os_hints = OsHints { os_input: starknet_os_input, os_hints_config };
295302
let layout = DEFAULT_OS_LAYOUT;
296-
run_os_stateless(layout, os_hints).unwrap()
303+
let os_output = run_os_stateless(layout, os_hints).unwrap();
304+
let OsStateDiff::Partial(ref partial_os_state_diff) = os_output.os_output.state_diff else {
305+
panic!("Expected a partial state diff in the output because of the OS config.");
306+
};
307+
let compressed_state_diff = partial_os_state_diff.as_state_maps();
308+
let decompressed_state_diff =
309+
decompress(&compressed_state_diff, &state, *ALIAS_CONTRACT_ADDRESS, alias_keys);
310+
311+
OsTestOutput { os_output, decompressed_state_diff }
297312
}
298313
}
299314

crates/starknet_os_flow_tests/src/tests.rs

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use blockifier_test_utils::cairo_versions::{CairoVersion, RunnableCairo1};
66
use blockifier_test_utils::calldata::create_calldata;
77
use blockifier_test_utils::contracts::FeatureContract;
88
use rstest::rstest;
9+
use starknet_api::abi::abi_utils::get_storage_var_address;
10+
use starknet_api::core::calculate_contract_address;
911
use starknet_api::executable_transaction::{DeclareTransaction, InvokeTransaction};
1012
use starknet_api::execution_resources::GasAmount;
1113
use starknet_api::test_utils::declare::declare_tx;
@@ -18,12 +20,18 @@ use starknet_api::test_utils::{
1820
DEFAULT_STRK_L2_GAS_PRICE,
1921
};
2022
use starknet_api::transaction::constants::DEPLOY_CONTRACT_FUNCTION_ENTRY_POINT_NAME;
21-
use starknet_api::transaction::fields::{AllResourceBounds, ResourceBounds, ValidResourceBounds};
23+
use starknet_api::transaction::fields::{
24+
AllResourceBounds,
25+
Calldata,
26+
ContractAddressSalt,
27+
ResourceBounds,
28+
ValidResourceBounds,
29+
};
2230
use starknet_api::{declare_tx_args, invoke_tx_args};
2331
use starknet_types_core::felt::Felt;
2432

2533
use crate::initial_state::create_default_initial_state_data;
26-
use crate::test_manager::{TestManager, FUNDED_ACCOUNT_ADDRESS};
34+
use crate::test_manager::{OsTestOutput, TestManager, FUNDED_ACCOUNT_ADDRESS};
2735
use crate::utils::{divide_vec_into_n_parts, get_class_info_of_cairo_1_feature_contract};
2836

2937
pub(crate) static NON_TRIVIAL_RESOURCE_BOUNDS: LazyLock<ValidResourceBounds> =
@@ -92,16 +100,17 @@ async fn declare_deploy_scenario(#[values(1, 2)] n_blocks: usize) {
92100
DeclareTransaction::create(account_declare_tx, class_info, &CHAIN_ID_FOR_TESTS).unwrap();
93101
// Add the transaction to the test manager.
94102
test_manager.add_cairo1_declare_tx(tx, &test_contract_sierra);
95-
103+
let arg1 = Felt::from(7);
104+
let arg2 = Felt::from(90);
96105
// Deploy the test contract using the deploy contract syscall.
97106
let constructor_calldata = [
98-
2.into(), // constructor length
99-
7.into(), // arg1
100-
90.into(), // arg2
107+
2.into(), // constructor length
108+
arg1,
109+
arg2,
101110
];
102-
let contract_address_salt = Felt::ONE;
111+
let contract_address_salt = ContractAddressSalt(Felt::ONE);
103112
let calldata: Vec<_> =
104-
[class_hash.0, contract_address_salt].into_iter().chain(constructor_calldata).collect();
113+
[class_hash.0, contract_address_salt.0].into_iter().chain(constructor_calldata).collect();
105114
let deploy_contract_calldata = create_calldata(
106115
*FUNDED_ACCOUNT_ADDRESS,
107116
DEPLOY_CONTRACT_FUNCTION_ENTRY_POINT_NAME,
@@ -113,14 +122,40 @@ async fn declare_deploy_scenario(#[values(1, 2)] n_blocks: usize) {
113122
calldata: deploy_contract_calldata,
114123
resource_bounds: *NON_TRIVIAL_RESOURCE_BOUNDS,
115124
};
125+
let expected_contract_address = calculate_contract_address(
126+
contract_address_salt,
127+
class_hash,
128+
&Calldata(constructor_calldata[1..].to_vec().into()),
129+
*FUNDED_ACCOUNT_ADDRESS,
130+
)
131+
.unwrap();
116132
let deploy_contract_tx = invoke_tx(invoke_tx_args);
117133
let deploy_contract_tx =
118134
InvokeTransaction::create(deploy_contract_tx, &CHAIN_ID_FOR_TESTS).unwrap();
119135
test_manager.add_invoke_tx(deploy_contract_tx);
120136
test_manager.divide_transactions_into_n_blocks(n_blocks);
121137
let initial_block_number = CURRENT_BLOCK_NUMBER + 1;
122-
let _os_runner_output =
138+
let OsTestOutput { decompressed_state_diff, .. } =
123139
test_manager.execute_test_with_default_block_contexts(initial_block_number).await;
124140

125-
// TODO(Nimrod): Validate the OS output.
141+
// Verify that the OS output contains the entry in the classes changes from the declare tx.
142+
assert!(decompressed_state_diff.compiled_class_hashes.get(&class_hash).is_some_and(
143+
|actual_compiled_class_hash| *actual_compiled_class_hash == compiled_class_hash
144+
));
145+
146+
// Verify the deployed contract.
147+
assert!(
148+
decompressed_state_diff
149+
.class_hashes
150+
.get(&expected_contract_address)
151+
.is_some_and(|actual_class_hash| *actual_class_hash == class_hash)
152+
);
153+
154+
// Verify the storage update from the contract's constructor.
155+
assert!(
156+
decompressed_state_diff
157+
.storage
158+
.get(&(expected_contract_address, get_storage_var_address("my_storage_var", &[])))
159+
.is_some_and(|actual_value| *actual_value == arg1 + arg2)
160+
);
126161
}

0 commit comments

Comments
 (0)