Skip to content

Commit 923885c

Browse files
starknet_os_flow_tests: add control for expected reverting txs
1 parent fc8c397 commit 923885c

File tree

2 files changed

+110
-37
lines changed

2 files changed

+110
-37
lines changed

crates/starknet_os_flow_tests/src/test_manager.rs

Lines changed: 80 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use blockifier::context::{BlockContext, ChainInfo, FeeTokenAddresses};
99
use blockifier::state::cached_state::{CommitmentStateDiff, StateMaps};
1010
use blockifier::state::stateful_compression_test_utils::decompress;
1111
use blockifier::test_utils::ALIAS_CONTRACT_ADDRESS;
12+
use blockifier::transaction::objects::TransactionExecutionInfo;
1213
use blockifier::transaction::transaction_execution::Transaction as BlockifierTransaction;
1314
use blockifier_test_utils::contracts::FeatureContract;
1415
use itertools::Itertools;
@@ -86,12 +87,17 @@ pub(crate) struct TestParameters {
8687
pub(crate) messages_to_l2: Vec<MessageToL2>,
8788
}
8889

90+
pub(crate) struct FlowTestTx {
91+
tx: BlockifierTransaction,
92+
revert_reason: Option<String>,
93+
}
94+
8995
/// Manages the execution of flow tests by maintaining the initial state and transactions.
9096
pub(crate) struct TestManager<S: FlowTestState> {
9197
pub(crate) initial_state: InitialState<S>,
9298
pub(crate) execution_contracts: OsExecutionContracts,
9399

94-
per_block_transactions: Vec<Vec<BlockifierTransaction>>,
100+
per_block_transactions: Vec<Vec<FlowTestTx>>,
95101
}
96102

97103
pub(crate) struct OsTestExpectedValues {
@@ -255,7 +261,7 @@ impl<S: FlowTestState> TestManager<S> {
255261
self.per_block_transactions.push(vec![]);
256262
}
257263

258-
fn last_block_txs_mut(&mut self) -> &mut Vec<BlockifierTransaction> {
264+
fn last_block_txs_mut(&mut self) -> &mut Vec<FlowTestTx> {
259265
self.per_block_transactions
260266
.last_mut()
261267
.expect("Always initialized with at least one tx list (at least one block).")
@@ -271,9 +277,12 @@ impl<S: FlowTestState> TestManager<S> {
271277
else {
272278
panic!("Expected a V1 contract class");
273279
};
274-
self.last_block_txs_mut().push(BlockifierTransaction::new_for_sequencing(
275-
StarknetApiTransaction::Account(AccountTransaction::Declare(tx)),
276-
));
280+
self.last_block_txs_mut().push(FlowTestTx {
281+
tx: BlockifierTransaction::new_for_sequencing(StarknetApiTransaction::Account(
282+
AccountTransaction::Declare(tx),
283+
)),
284+
revert_reason: None,
285+
});
277286

278287
self.execution_contracts
279288
.declared_class_hash_to_component_hashes
@@ -285,14 +294,25 @@ impl<S: FlowTestState> TestManager<S> {
285294
.insert(compiled_class_hash, casm.clone());
286295
}
287296

288-
pub(crate) fn add_invoke_tx(&mut self, tx: InvokeTransaction) {
289-
self.last_block_txs_mut().push(BlockifierTransaction::new_for_sequencing(
290-
StarknetApiTransaction::Account(AccountTransaction::Invoke(tx)),
291-
));
297+
pub(crate) fn add_invoke_tx(&mut self, tx: InvokeTransaction, revert_reason: Option<String>) {
298+
self.last_block_txs_mut().push(FlowTestTx {
299+
tx: BlockifierTransaction::new_for_sequencing(StarknetApiTransaction::Account(
300+
AccountTransaction::Invoke(tx),
301+
)),
302+
revert_reason,
303+
});
292304
}
293305

294-
pub(crate) fn add_invoke_tx_from_args(&mut self, args: InvokeTxArgs, chain_id: &ChainId) {
295-
self.add_invoke_tx(InvokeTransaction::create(invoke_tx(args), chain_id).unwrap());
306+
pub(crate) fn add_invoke_tx_from_args(
307+
&mut self,
308+
args: InvokeTxArgs,
309+
chain_id: &ChainId,
310+
revert_reason: Option<String>,
311+
) {
312+
self.add_invoke_tx(
313+
InvokeTransaction::create(invoke_tx(args), chain_id).unwrap(),
314+
revert_reason,
315+
);
296316
}
297317

298318
pub(crate) fn add_cairo0_declare_tx(
@@ -303,24 +323,36 @@ impl<S: FlowTestState> TestManager<S> {
303323
let ContractClass::V0(class) = tx.class_info.contract_class.clone() else {
304324
panic!("Expected a V0 contract class");
305325
};
306-
self.last_block_txs_mut().push(BlockifierTransaction::new_for_sequencing(
307-
StarknetApiTransaction::Account(AccountTransaction::Declare(tx)),
308-
));
326+
self.last_block_txs_mut().push(FlowTestTx {
327+
tx: BlockifierTransaction::new_for_sequencing(StarknetApiTransaction::Account(
328+
AccountTransaction::Declare(tx),
329+
)),
330+
revert_reason: None,
331+
});
309332
self.execution_contracts
310333
.executed_contracts
311334
.deprecated_contracts
312335
.insert(compiled_class_hash, class);
313336
}
314337

315338
pub(crate) fn add_deploy_account_tx(&mut self, tx: DeployAccountTransaction) {
316-
self.last_block_txs_mut().push(BlockifierTransaction::new_for_sequencing(
317-
StarknetApiTransaction::Account(AccountTransaction::DeployAccount(tx)),
318-
));
339+
self.last_block_txs_mut().push(FlowTestTx {
340+
tx: BlockifierTransaction::new_for_sequencing(StarknetApiTransaction::Account(
341+
AccountTransaction::DeployAccount(tx),
342+
)),
343+
revert_reason: None,
344+
});
319345
}
320346

321-
pub(crate) fn add_l1_handler_tx(&mut self, tx: L1HandlerTransaction) {
322-
self.last_block_txs_mut()
323-
.push(BlockifierTransaction::new_for_sequencing(StarknetApiTransaction::L1Handler(tx)));
347+
pub(crate) fn add_l1_handler_tx(
348+
&mut self,
349+
tx: L1HandlerTransaction,
350+
revert_reason: Option<String>,
351+
) {
352+
self.last_block_txs_mut().push(FlowTestTx {
353+
tx: BlockifierTransaction::new_for_sequencing(StarknetApiTransaction::L1Handler(tx)),
354+
revert_reason,
355+
});
324356
}
325357

326358
/// Executes the test using default block contexts, starting from the given block number.
@@ -395,6 +427,27 @@ impl<S: FlowTestState> TestManager<S> {
395427
first_use_kzg_da
396428
}
397429

430+
/// Verifies all the execution outputs are as expected w.r.t. revert reasons.
431+
fn verify_execution_outputs(
432+
revert_reasons: &[Option<String>],
433+
execution_outputs: &[(TransactionExecutionInfo, StateMaps)],
434+
) {
435+
for (revert_reason, (execution_info, _)) in
436+
revert_reasons.iter().zip(execution_outputs.iter())
437+
{
438+
if let Some(revert_reason) = revert_reason {
439+
let actual_revert_reason =
440+
execution_info.revert_error.as_ref().unwrap().to_string();
441+
assert!(
442+
actual_revert_reason.contains(revert_reason),
443+
"Expected '{revert_reason}' to be in revert string:\n'{actual_revert_reason}'"
444+
);
445+
} else {
446+
assert!(execution_info.revert_error.is_none());
447+
}
448+
}
449+
}
450+
398451
/// Decompresses the state diff from the OS output using the given OS output, state and alias
399452
/// keys.
400453
fn get_decompressed_state_diff(
@@ -454,13 +507,19 @@ impl<S: FlowTestState> TestManager<S> {
454507
"use_kzg_da flag in block contexts must match the test parameter."
455508
);
456509
let mut alias_keys = HashSet::new();
457-
for (block_txs, block_context) in per_block_txs.into_iter().zip(block_contexts.into_iter())
510+
for (block_txs_with_reason, block_context) in
511+
per_block_txs.into_iter().zip(block_contexts.into_iter())
458512
{
459513
// Clone the block info for later use.
514+
let (block_txs, revert_reasons): (Vec<_>, Vec<_>) = block_txs_with_reason
515+
.into_iter()
516+
.map(|flow_test_tx| (flow_test_tx.tx, flow_test_tx.revert_reason))
517+
.unzip();
460518
let block_info = block_context.block_info().clone();
461519
// Execute the transactions.
462520
let ExecutionOutput { execution_outputs, block_summary, mut final_state } =
463521
execute_transactions(state, &block_txs, block_context);
522+
Self::verify_execution_outputs(&revert_reasons, &execution_outputs);
464523
let extended_state_diff = final_state.cache.borrow().extended_state_diff();
465524
// Update the wrapped state.
466525
let state_diff = final_state.to_state_diff().unwrap();

crates/starknet_os_flow_tests/src/tests.rs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ async fn declare_deploy_scenario(
149149
*FUNDED_ACCOUNT_ADDRESS,
150150
)
151151
.unwrap();
152-
test_manager.add_invoke_tx_from_args(invoke_tx_args, &CHAIN_ID_FOR_TESTS);
152+
test_manager.add_invoke_tx_from_args(invoke_tx_args, &CHAIN_ID_FOR_TESTS, None);
153153
test_manager.divide_transactions_into_n_blocks(n_blocks);
154154
let test_output = test_manager
155155
.execute_test_with_default_block_contexts(&TestParameters {
@@ -214,7 +214,7 @@ async fn trivial_diff_scenario(
214214
calldata: create_calldata(test_contract_address, function_name, &[key, value]),
215215
resource_bounds: *NON_TRIVIAL_RESOURCE_BOUNDS,
216216
};
217-
test_manager.add_invoke_tx_from_args(invoke_tx_args, &CHAIN_ID_FOR_TESTS);
217+
test_manager.add_invoke_tx_from_args(invoke_tx_args, &CHAIN_ID_FOR_TESTS, None);
218218

219219
// Move to next block, and add an invoke that reverts the previous change.
220220
test_manager.move_to_next_block();
@@ -224,7 +224,7 @@ async fn trivial_diff_scenario(
224224
calldata: create_calldata(test_contract_address, function_name, &[key, Felt::ZERO]),
225225
resource_bounds: *NON_TRIVIAL_RESOURCE_BOUNDS,
226226
};
227-
test_manager.add_invoke_tx_from_args(invoke_tx_args, &CHAIN_ID_FOR_TESTS);
227+
test_manager.add_invoke_tx_from_args(invoke_tx_args, &CHAIN_ID_FOR_TESTS, None);
228228

229229
// Execute the test.
230230
let test_output = test_manager
@@ -244,13 +244,18 @@ async fn trivial_diff_scenario(
244244
}
245245

246246
#[rstest]
247+
#[case::cairo0(
248+
FeatureContract::TestContract(CairoVersion::Cairo0),
249+
"ASSERT_EQ instruction failed: 1 != 0"
250+
)]
251+
#[case::cairo1(
252+
FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm)),
253+
"Panic for revert"
254+
)]
247255
#[tokio::test]
248256
async fn test_reverted_invoke_tx(
249-
#[values(
250-
FeatureContract::TestContract(CairoVersion::Cairo0),
251-
FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm))
252-
)]
253-
test_contract: FeatureContract,
257+
#[case] test_contract: FeatureContract,
258+
#[case] revert_reason: &str,
254259
) {
255260
let (use_kzg_da, full_output) = (true, false);
256261

@@ -265,10 +270,14 @@ async fn test_reverted_invoke_tx(
265270
let invoke_tx_args = invoke_tx_args! {
266271
sender_address: *FUNDED_ACCOUNT_ADDRESS,
267272
nonce: nonce_manager.next(*FUNDED_ACCOUNT_ADDRESS),
268-
calldata: create_calldata(test_contract_address, "write_and_revert", &[]),
273+
calldata: create_calldata(test_contract_address, "write_and_revert", &[Felt::ONE, Felt::TWO]),
269274
resource_bounds: *NON_TRIVIAL_RESOURCE_BOUNDS,
270275
};
271-
test_manager.add_invoke_tx_from_args(invoke_tx_args, &CHAIN_ID_FOR_TESTS);
276+
test_manager.add_invoke_tx_from_args(
277+
invoke_tx_args,
278+
&CHAIN_ID_FOR_TESTS,
279+
Some(revert_reason.to_string()),
280+
);
272281

273282
// Execute the test.
274283
let test_output = test_manager
@@ -292,13 +301,18 @@ async fn test_reverted_invoke_tx(
292301
}
293302

294303
#[rstest]
304+
#[case::cairo0(
305+
FeatureContract::TestContract(CairoVersion::Cairo0),
306+
"ASSERT_EQ instruction failed: 1 != 0."
307+
)]
308+
#[case::cairo1(
309+
FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm)),
310+
"revert in l1 handler"
311+
)]
295312
#[tokio::test]
296313
async fn test_reverted_l1_handler_tx(
297-
#[values(
298-
FeatureContract::TestContract(CairoVersion::Cairo0),
299-
FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm))
300-
)]
301-
test_contract: FeatureContract,
314+
#[case] test_contract: FeatureContract,
315+
#[case] revert_reason: &str,
302316
) {
303317
let (mut test_manager, _, [test_contract_address]) =
304318
TestManager::<DictStateReader>::new_with_default_initial_state([(
@@ -320,7 +334,7 @@ async fn test_reverted_l1_handler_tx(
320334
Fee(1_000_000),
321335
)
322336
.unwrap();
323-
test_manager.add_l1_handler_tx(tx);
337+
test_manager.add_l1_handler_tx(tx, Some(revert_reason.to_string()));
324338

325339
let test_output =
326340
test_manager.execute_test_with_default_block_contexts(&TestParameters::default()).await;

0 commit comments

Comments
 (0)