Skip to content

Commit 40e064c

Browse files
starknet_os_flow_tests: initial state with extra contracts (#9422)
1 parent 76a185b commit 40e064c

File tree

4 files changed

+89
-88
lines changed

4 files changed

+89
-88
lines changed

crates/starknet_os_flow_tests/src/initial_state.rs

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

44
use blockifier::transaction::transaction_execution::Transaction;
55
use blockifier_test_utils::cairo_versions::{CairoVersion, RunnableCairo1};
@@ -40,11 +40,14 @@ use crate::test_manager::{
4040
FUNDED_ACCOUNT_ADDRESS,
4141
STRK_FEE_TOKEN_ADDRESS,
4242
};
43+
use crate::tests::NON_TRIVIAL_RESOURCE_BOUNDS;
4344
use crate::utils::{
4445
commit_state_diff,
4546
create_cairo1_bootstrap_declare_tx,
4647
create_committer_state_diff,
48+
create_declare_tx,
4749
execute_transactions,
50+
get_class_hash_of_feature_contract,
4851
CommitmentOutput,
4952
ExecutionOutput,
5053
};
@@ -121,13 +124,18 @@ pub(crate) struct InitialState<S: FlowTestState> {
121124
/// Creates the initial state for the flow test which includes:
122125
/// Declares token and account contracts.
123126
/// Deploys both contracts and funds the account.
124-
pub(crate) async fn create_default_initial_state_data<S: FlowTestState>()
125-
-> (InitialStateData<S>, NonceManager) {
126-
let InitialTransactionsData {
127-
transactions: default_initial_state_txs,
128-
execution_contracts,
129-
nonce_manager,
130-
} = create_default_initial_state_txs_and_contracts();
127+
/// Also deploys extra contracts as requested (and declares them if they are not already declared).
128+
pub(crate) async fn create_default_initial_state_data<S: FlowTestState, const N: usize>(
129+
extra_contracts: [(FeatureContract, Calldata); N],
130+
) -> (InitialStateData<S>, NonceManager, [ContractAddress; N]) {
131+
let (
132+
InitialTransactionsData {
133+
transactions: default_initial_state_txs,
134+
execution_contracts,
135+
nonce_manager,
136+
},
137+
extra_contracts_addresses,
138+
) = create_default_initial_state_txs_and_contracts(extra_contracts);
131139
// Execute these 4 txs.
132140
let initial_state_reader = S::create_empty_state();
133141
let use_kzg_da = false;
@@ -170,7 +178,11 @@ pub(crate) async fn create_default_initial_state_data<S: FlowTestState>()
170178
classes_trie_root_hash: commitment_output.classes_trie_root_hash,
171179
};
172180

173-
(InitialStateData { initial_state, execution_contracts }, nonce_manager)
181+
(
182+
InitialStateData { initial_state, execution_contracts },
183+
nonce_manager,
184+
extra_contracts_addresses,
185+
)
174186
}
175187

176188
struct InitialTransactionsData {
@@ -179,7 +191,9 @@ struct InitialTransactionsData {
179191
pub(crate) nonce_manager: NonceManager,
180192
}
181193

182-
fn create_default_initial_state_txs_and_contracts() -> InitialTransactionsData {
194+
fn create_default_initial_state_txs_and_contracts<const N: usize>(
195+
extra_contracts: [(FeatureContract, Calldata); N],
196+
) -> (InitialTransactionsData, [ContractAddress; N]) {
183197
let mut os_execution_contracts = OsExecutionContracts::default();
184198
// Declare account and ERC20 contracts.
185199
let account_contract =
@@ -212,11 +226,38 @@ fn create_default_initial_state_txs_and_contracts() -> InitialTransactionsData {
212226
let nonce = nonce_manager.next(account_contract_address);
213227
let (deploy_contract_tx, _) = get_deploy_fee_token_tx_and_address(nonce);
214228
txs.push(deploy_contract_tx);
215-
InitialTransactionsData {
216-
transactions: txs,
217-
execution_contracts: os_execution_contracts,
218-
nonce_manager,
229+
230+
// Deploy extra contracts. Declare contracts that are not already declared.
231+
let mut declared_contracts = HashSet::from([account_contract, erc20_contract]);
232+
let mut extra_addresses = Vec::new();
233+
for (contract, calldata) in extra_contracts {
234+
if !declared_contracts.contains(&contract) {
235+
// Add a declare transaction for the contract.
236+
// No need for bootstrap mode: funded account already exists at this point.
237+
txs.push(Transaction::new_for_sequencing(StarknetAPITransaction::Account(
238+
create_declare_tx(contract, &mut nonce_manager, &mut os_execution_contracts, false),
239+
)));
240+
declared_contracts.insert(contract);
241+
}
242+
// Deploy.
243+
let (deploy_tx, address) = get_deploy_contract_tx_and_address(
244+
get_class_hash_of_feature_contract(contract),
245+
calldata,
246+
nonce_manager.next(account_contract_address),
247+
*NON_TRIVIAL_RESOURCE_BOUNDS,
248+
);
249+
txs.push(deploy_tx);
250+
extra_addresses.push(address);
219251
}
252+
253+
(
254+
InitialTransactionsData {
255+
transactions: txs,
256+
execution_contracts: os_execution_contracts,
257+
nonce_manager,
258+
},
259+
extra_addresses.try_into().unwrap(),
260+
)
220261
}
221262

222263
pub(crate) async fn commit_initial_state_diff(

crates/starknet_os_flow_tests/src/test_manager.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use blockifier::state::cached_state::{CommitmentStateDiff, StateMaps};
1010
use blockifier::state::stateful_compression_test_utils::decompress;
1111
use blockifier::test_utils::ALIAS_CONTRACT_ADDRESS;
1212
use blockifier::transaction::transaction_execution::Transaction as BlockifierTransaction;
13+
use blockifier_test_utils::contracts::FeatureContract;
1314
use itertools::Itertools;
1415
use starknet_api::block::{BlockHash, BlockInfo, BlockNumber, PreviousBlockNumber};
1516
use starknet_api::contract_class::compiled_class_hash::{HashVersion, HashableCompiledClass};
@@ -24,6 +25,7 @@ use starknet_api::executable_transaction::{
2425
};
2526
use starknet_api::state::{SierraContractClass, StorageKey};
2627
use starknet_api::test_utils::{NonceManager, CHAIN_ID_FOR_TESTS};
28+
use starknet_api::transaction::fields::Calldata;
2729
use starknet_api::transaction::MessageToL1;
2830
use starknet_committer::block_committer::input::{IsSubset, StateDiff};
2931
use starknet_os::io::os_input::{
@@ -228,10 +230,18 @@ impl<S: FlowTestState> TestManager<S> {
228230

229231
/// Creates a new `TestManager` with the default initial state.
230232
/// Returns the manager and a nonce manager to help keep track of nonces.
231-
pub(crate) async fn new_with_default_initial_state() -> (Self, NonceManager) {
232-
let (default_initial_state_data, nonce_manager) =
233-
create_default_initial_state_data::<S>().await;
234-
(Self::new_with_initial_state_data(default_initial_state_data), nonce_manager)
233+
/// Optionally provide an array of extra contracts to declare and deploy - the addresses of
234+
/// these contracts will be returned as an array of the same length.
235+
pub(crate) async fn new_with_default_initial_state<const N: usize>(
236+
extra_contracts: [(FeatureContract, Calldata); N],
237+
) -> (Self, NonceManager, [ContractAddress; N]) {
238+
let (default_initial_state_data, nonce_manager, extra_addresses) =
239+
create_default_initial_state_data::<S, N>(extra_contracts).await;
240+
(
241+
Self::new_with_initial_state_data(default_initial_state_data),
242+
nonce_manager,
243+
extra_addresses,
244+
)
235245
}
236246

237247
/// Advances the manager to the next block when adding new transactions.

crates/starknet_os_flow_tests/src/tests.rs

Lines changed: 13 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use starknet_api::transaction::fields::{
2828
ResourceBounds,
2929
ValidResourceBounds,
3030
};
31-
use starknet_api::{declare_tx_args, invoke_tx_args};
31+
use starknet_api::{calldata, declare_tx_args, invoke_tx_args};
3232
use starknet_committer::block_committer::input::{
3333
StarknetStorageKey,
3434
StarknetStorageValue,
@@ -61,7 +61,7 @@ pub(crate) static NON_TRIVIAL_RESOURCE_BOUNDS: LazyLock<ValidResourceBounds> =
6161

6262
#[tokio::test]
6363
async fn test_initial_state_creation() {
64-
let _initial_state = create_default_initial_state_data::<DictStateReader>().await;
64+
let _initial_state = create_default_initial_state_data::<DictStateReader, 0>([]).await;
6565
}
6666

6767
#[rstest]
@@ -91,8 +91,8 @@ async fn declare_deploy_scenario(
9191
// Initialize the test manager with a default initial state and get the nonce manager to help
9292
// keep track of nonces.
9393

94-
let (mut test_manager, mut nonce_manager) =
95-
TestManager::<DictStateReader>::new_with_default_initial_state().await;
94+
let (mut test_manager, mut nonce_manager, _) =
95+
TestManager::<DictStateReader>::new_with_default_initial_state([]).await;
9696

9797
// Declare a test contract.
9898
let test_contract = FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm));
@@ -184,66 +184,15 @@ async fn trivial_diff_scenario(
184184
#[values(false, true)] use_kzg_da: bool,
185185
#[values(false, true)] full_output: bool,
186186
) {
187-
// TODO(Dori): Create a util to define an initial state with the test contract declared and
188-
// deployed. The address of the test contract should be part of the initial state data.
189-
190187
// Initialize the test manager with a default initial state and get the nonce manager to help
191188
// keep track of nonces.
192189

193-
let (mut test_manager, mut nonce_manager) =
194-
TestManager::<DictStateReader>::new_with_default_initial_state().await;
195-
196-
// Declare and deploy a test contract.
197-
let test_contract = FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm));
198-
let test_contract_sierra = test_contract.get_sierra();
199-
let class_hash = test_contract_sierra.calculate_class_hash();
200-
let compiled_class_hash = test_contract.get_compiled_class_hash(&HashVersion::V2);
201-
let declare_tx_args = declare_tx_args! {
202-
sender_address: *FUNDED_ACCOUNT_ADDRESS,
203-
class_hash,
204-
compiled_class_hash,
205-
resource_bounds: *NON_TRIVIAL_RESOURCE_BOUNDS,
206-
nonce: nonce_manager.next(*FUNDED_ACCOUNT_ADDRESS),
207-
};
208-
let account_declare_tx = declare_tx(declare_tx_args);
209-
let class_info = get_class_info_of_feature_contract(test_contract);
210-
let tx =
211-
DeclareTransaction::create(account_declare_tx, class_info, &CHAIN_ID_FOR_TESTS).unwrap();
212-
// Add the transaction to the test manager.
213-
test_manager.add_cairo1_declare_tx(tx, &test_contract_sierra);
214-
let arg1 = Felt::from(7);
215-
let arg2 = Felt::from(90);
216-
// Deploy the test contract using the deploy contract syscall.
217-
let constructor_calldata = [
218-
2.into(), // constructor length
219-
arg1,
220-
arg2,
221-
];
222-
let contract_address_salt = ContractAddressSalt(Felt::ONE);
223-
let calldata: Vec<_> =
224-
[class_hash.0, contract_address_salt.0].into_iter().chain(constructor_calldata).collect();
225-
let deploy_contract_calldata = create_calldata(
226-
*FUNDED_ACCOUNT_ADDRESS,
227-
DEPLOY_CONTRACT_FUNCTION_ENTRY_POINT_NAME,
228-
&calldata,
229-
);
230-
let invoke_tx_args = invoke_tx_args! {
231-
sender_address: *FUNDED_ACCOUNT_ADDRESS,
232-
nonce: nonce_manager.next(*FUNDED_ACCOUNT_ADDRESS),
233-
calldata: deploy_contract_calldata,
234-
resource_bounds: *NON_TRIVIAL_RESOURCE_BOUNDS,
235-
};
236-
let test_contract_address = calculate_contract_address(
237-
contract_address_salt,
238-
class_hash,
239-
&Calldata(constructor_calldata[1..].to_vec().into()),
240-
*FUNDED_ACCOUNT_ADDRESS,
241-
)
242-
.unwrap();
243-
let deploy_contract_tx = invoke_tx(invoke_tx_args);
244-
let deploy_contract_tx =
245-
InvokeTransaction::create(deploy_contract_tx, &CHAIN_ID_FOR_TESTS).unwrap();
246-
test_manager.add_invoke_tx(deploy_contract_tx);
190+
let (mut test_manager, mut nonce_manager, [test_contract_address]) =
191+
TestManager::<DictStateReader>::new_with_default_initial_state([(
192+
FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm)),
193+
calldata![Felt::ONE, Felt::TWO],
194+
)])
195+
.await;
247196

248197
let key = Felt::from(10u8);
249198
let value = Felt::from(11u8);
@@ -280,15 +229,9 @@ async fn trivial_diff_scenario(
280229
)
281230
.await;
282231

283-
// Explicitly check the storage updates (not just inclusion) to make sure the key has no diff.
284-
// Only storage update expected is due to the constructor call.
285-
let expected_test_contract_storage_updates = HashMap::from([(
286-
StarknetStorageKey(get_storage_var_address("my_storage_var", &[])),
287-
StarknetStorageValue(arg1 + arg2),
288-
)]);
289-
assert_eq!(
290-
test_output.decompressed_state_diff.storage_updates.get(&test_contract_address).unwrap(),
291-
&expected_test_contract_storage_updates
232+
// Explicitly check the test contract has no storage update.
233+
assert!(
234+
!test_output.decompressed_state_diff.storage_updates.contains_key(&test_contract_address)
292235
);
293236

294237
let perform_global_validations = true;

crates/starknet_os_flow_tests/src/utils.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,3 +425,10 @@ pub(crate) fn get_class_info_of_feature_contract(feature_contract: FeatureContra
425425
}
426426
}
427427
}
428+
429+
pub(crate) fn get_class_hash_of_feature_contract(feature_contract: FeatureContract) -> ClassHash {
430+
match feature_contract.get_class() {
431+
ContractClass::V0(class) => ClassHash(compute_deprecated_class_hash(&class).unwrap()),
432+
ContractClass::V1(_) => feature_contract.get_sierra().calculate_class_hash(),
433+
}
434+
}

0 commit comments

Comments
 (0)