From b55a95077ed13d0f1e92d4947dd232247fa7e163 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Thu, 23 Oct 2025 16:28:17 +0200 Subject: [PATCH 01/33] Add `get_current_step` --- CHANGELOG.md | 4 ++ .../forge_config_extension.rs | 2 + .../forge_runtime_extension/mod.rs | 9 ++++ crates/runtime/src/lib.rs | 2 + .../src/starknet_commands/script/run.rs | 1 + docs/listings/testing_reference/Scarb.toml | 12 +++++ docs/listings/testing_reference/src/lib.cairo | 33 ++++++++++++ .../testing_reference/tests/tests.cairo | 30 +++++++++++ docs/src/SUMMARY.md | 2 + docs/src/appendix/snforge-library/testing.md | 7 +++ .../testing/get_current_step.md | 51 +++++++++++++++++++ snforge_std/src/lib.cairo | 2 + snforge_std/src/testing.cairo | 6 +++ 13 files changed, 161 insertions(+) create mode 100644 docs/listings/testing_reference/Scarb.toml create mode 100644 docs/listings/testing_reference/src/lib.cairo create mode 100644 docs/listings/testing_reference/tests/tests.cairo create mode 100644 docs/src/appendix/snforge-library/testing.md create mode 100644 docs/src/appendix/snforge-library/testing/get_current_step.md create mode 100644 snforge_std/src/testing.cairo diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b3d370ef7..8054d5ae15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Forge +#### Added + +- `get_current_step` function to get the current step from Cairo VM during test execution. For more see [docs]() + #### Changed - Gas values in fuzzing test output are now displayed as whole numbers without fractional parts diff --git a/crates/cheatnet/src/runtime_extensions/forge_config_extension.rs b/crates/cheatnet/src/runtime_extensions/forge_config_extension.rs index 82b139887e..92e29c8090 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_config_extension.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_config_extension.rs @@ -1,3 +1,4 @@ +use cairo_vm::vm::vm_core::VirtualMachine; use config::RawForgeConfig; use conversions::serde::deserialize::BufferReader; use runtime::{CheatcodeHandlingResult, EnhancedHintError, ExtensionLogic, StarknetRuntime}; @@ -17,6 +18,7 @@ impl<'a> ExtensionLogic for ForgeConfigExtension<'a> { selector: &str, mut input_reader: BufferReader<'_>, _extended_runtime: &mut Self::Runtime, + _vm: &mut VirtualMachine, ) -> Result { macro_rules! config_cheatcode { ( $prop:ident) => {{ diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs index 8d854afdb2..03725bd8fb 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -81,6 +81,7 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { selector: &str, mut input_reader: BufferReader<'_>, extended_runtime: &mut Self::Runtime, + vm: &mut VirtualMachine, ) -> Result { if let Some(oracle_selector) = self .oracle_hint_service @@ -552,6 +553,14 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { .cheat_block_hash(block_number, operation); Ok(CheatcodeHandlingResult::from_serializable(())) } + "get_current_step" => { + let current_step: u32 = vm + .get_current_step() + .try_into() + .context("Current step value exceeds u32")?; + + Ok(CheatcodeHandlingResult::from_serializable(current_step)) + } _ => Ok(CheatcodeHandlingResult::Forwarded), } } diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 4cd1882906..e33f3e5f33 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -279,6 +279,7 @@ impl ExtendedRuntime { &selector, BufferReader::new(&inputs), &mut self.extended_runtime, + vm, ); let res = match result { @@ -423,6 +424,7 @@ pub trait ExtensionLogic { _selector: &str, _input_reader: BufferReader, _extended_runtime: &mut Self::Runtime, + _vm: &mut VirtualMachine, ) -> Result { Ok(CheatcodeHandlingResult::Forwarded) } diff --git a/crates/sncast/src/starknet_commands/script/run.rs b/crates/sncast/src/starknet_commands/script/run.rs index ea0b398ee0..a7ef749552 100644 --- a/crates/sncast/src/starknet_commands/script/run.rs +++ b/crates/sncast/src/starknet_commands/script/run.rs @@ -104,6 +104,7 @@ impl<'a> ExtensionLogic for CastScriptExtension<'a> { selector: &str, mut input_reader: BufferReader, _extended_runtime: &mut Self::Runtime, + _vm: &mut VirtualMachine, ) -> Result { match selector { "call" => { diff --git a/docs/listings/testing_reference/Scarb.toml b/docs/listings/testing_reference/Scarb.toml new file mode 100644 index 0000000000..5e79b4acf9 --- /dev/null +++ b/docs/listings/testing_reference/Scarb.toml @@ -0,0 +1,12 @@ +[package] +name = "testing_reference" +version = "0.1.0" +edition = "2024_07" + +[dependencies] +starknet = "2.12.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } + +[[target.starknet-contract]] diff --git a/docs/listings/testing_reference/src/lib.cairo b/docs/listings/testing_reference/src/lib.cairo new file mode 100644 index 0000000000..3c1a54aaaa --- /dev/null +++ b/docs/listings/testing_reference/src/lib.cairo @@ -0,0 +1,33 @@ +#[starknet::interface] +pub trait ICounter { + fn increment(ref self: TContractState); + fn decrement(ref self: TContractState); +} + +#[starknet::contract] +pub mod Counter { + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + + #[storage] + struct Storage { + i: felt252, + } + + #[constructor] + fn constructor(ref self: ContractState) { + self.i.write(0); + } + + #[abi(embed_v0)] + impl CounterImpl of super::ICounter { + fn increment(ref self: ContractState) { + let current_value = self.i.read(); + self.i.write(current_value + 1); + } + + fn decrement(ref self: ContractState) { + let current_value = self.i.read(); + self.i.write(current_value - 1); + } + } +} diff --git a/docs/listings/testing_reference/tests/tests.cairo b/docs/listings/testing_reference/tests/tests.cairo new file mode 100644 index 0000000000..9416e3a1ef --- /dev/null +++ b/docs/listings/testing_reference/tests/tests.cairo @@ -0,0 +1,30 @@ +use snforge_std::testing::get_current_step; +use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; +use testing_reference::{ICounterSafeDispatcher, ICounterSafeDispatcherTrait}; + +#[feature("safe_dispatcher")] +fn setup() { + // Deploy contract + let (contract_address, _) = declare("Counter") + .unwrap() + .contract_class() + .deploy(@array![]) + .unwrap(); + + let dispatcher = ICounterSafeDispatcher { contract_address }; + + // Increment counter a few times + dispatcher.increment(); + dispatcher.increment(); + dispatcher.increment(); +} + +#[test] +fn test_setup_steps() { + let steps_start = get_current_step(); + setup(); + let steps_end = get_current_step(); + + // Assert that setup used no more than 100 steps + assert!(steps_end - steps_start <= 100); +} diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 111a8991b7..b103b57dcf 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -129,6 +129,8 @@ * [env](appendix/snforge-library/env.md) * [signature](appendix/snforge-library/signature.md) * [fuzzable](appendix/snforge-library/fuzzable.md) + * [testing](appendix/snforge-library/testing.md) + * [get_current_step](appendix/snforge-library/testing/get_current_step.md) * [`sncast` Commands](appendix/sncast.md) * [common flags](appendix/sncast/common.md) * [account](appendix/sncast/account/account.md) diff --git a/docs/src/appendix/snforge-library/testing.md b/docs/src/appendix/snforge-library/testing.md new file mode 100644 index 0000000000..9481e9e068 --- /dev/null +++ b/docs/src/appendix/snforge-library/testing.md @@ -0,0 +1,7 @@ +# `testing` Module + +Module containing functions usefule for testing. + +## Functions + +* [`get_current_step`](./testing/get_current_step.md) diff --git a/docs/src/appendix/snforge-library/testing/get_current_step.md b/docs/src/appendix/snforge-library/testing/get_current_step.md new file mode 100644 index 0000000000..ade4c9abb2 --- /dev/null +++ b/docs/src/appendix/snforge-library/testing/get_current_step.md @@ -0,0 +1,51 @@ +# `get_current_step` + +Function for getting the current step from Cairo VM during test execution. + +```rust +fn get_current_step() -> u32; +``` + +## Example + +Let's consider a simple counter contract that increments a value stored in its storage. + +```rust +{{#include ../../../../listings/testing_reference/src/lib.cairo}} +``` + +Now, let's say that we have some kind of setup function which deploys this contract and increments the counter a few times. + +```rust +{{#include ../../../../listings/testing_reference/src/tests/tests.cairo}} +``` + +With `get_current_step`, we can assert that `setup` function does not exceed a certain number of steps during its execution. This is particularly useful for performance testing and ensuring that our setup logic remains efficient. + + +Let's run the test: +```shell +$ snforge test test_setup_steps +``` + +
+Output: + +```shell +Collected 1 test(s) from testing_reference package +Running 0 test(s) from src/ +Running 1 test(s) from tests/ +[FAIL] testing_reference_integrationtest::tests::test_setup_steps + +Failure data: + "assertion failed: `steps_end - steps_start <= 100`." + +Tests: 0 passed, 1 failed, 0 ignored, 0 filtered out + +Failures: + testing_reference_integrationtest::tests::test_setup_steps +``` +
+
+ +As we can see, the test fails because the `setup` function exceeded the allowed number of steps (100 in this case). diff --git a/snforge_std/src/lib.cairo b/snforge_std/src/lib.cairo index f819af4ebb..cf10bb4d02 100644 --- a/snforge_std/src/lib.cairo +++ b/snforge_std/src/lib.cairo @@ -128,6 +128,8 @@ pub mod signature; pub mod trace; +pub mod testing; + #[doc(hidden)] pub mod _internals { pub use cheatcode::{is_config_run, save_fuzzer_arg}; diff --git a/snforge_std/src/testing.cairo b/snforge_std/src/testing.cairo new file mode 100644 index 0000000000..27b41449c6 --- /dev/null +++ b/snforge_std/src/testing.cairo @@ -0,0 +1,6 @@ +use crate::cheatcode::execute_cheatcode_and_deserialize; + +/// Gets the current step from Cairo VM during test execution +pub fn get_current_step() -> u32 { + execute_cheatcode_and_deserialize::<'get_current_step', u32>(array![].span()) +} From 8a8a80f297ab0dae426ec0046509395251386081 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Thu, 23 Oct 2025 16:39:56 +0200 Subject: [PATCH 02/33] Minor refactor --- docs/listings/testing_reference/src/lib.cairo | 6 ------ docs/src/appendix/snforge-library/testing.md | 2 +- .../appendix/snforge-library/testing/get_current_step.md | 8 +++----- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/docs/listings/testing_reference/src/lib.cairo b/docs/listings/testing_reference/src/lib.cairo index 3c1a54aaaa..7dee15a673 100644 --- a/docs/listings/testing_reference/src/lib.cairo +++ b/docs/listings/testing_reference/src/lib.cairo @@ -1,7 +1,6 @@ #[starknet::interface] pub trait ICounter { fn increment(ref self: TContractState); - fn decrement(ref self: TContractState); } #[starknet::contract] @@ -24,10 +23,5 @@ pub mod Counter { let current_value = self.i.read(); self.i.write(current_value + 1); } - - fn decrement(ref self: ContractState) { - let current_value = self.i.read(); - self.i.write(current_value - 1); - } } } diff --git a/docs/src/appendix/snforge-library/testing.md b/docs/src/appendix/snforge-library/testing.md index 9481e9e068..11ccd1c804 100644 --- a/docs/src/appendix/snforge-library/testing.md +++ b/docs/src/appendix/snforge-library/testing.md @@ -1,6 +1,6 @@ # `testing` Module -Module containing functions usefule for testing. +Module containing functions useful for testing. ## Functions diff --git a/docs/src/appendix/snforge-library/testing/get_current_step.md b/docs/src/appendix/snforge-library/testing/get_current_step.md index ade4c9abb2..ac055fcd85 100644 --- a/docs/src/appendix/snforge-library/testing/get_current_step.md +++ b/docs/src/appendix/snforge-library/testing/get_current_step.md @@ -1,6 +1,6 @@ # `get_current_step` -Function for getting the current step from Cairo VM during test execution. +Gets the current step from Cairo VM during test execution. ```rust fn get_current_step() -> u32; @@ -14,14 +14,12 @@ Let's consider a simple counter contract that increments a value stored in its s {{#include ../../../../listings/testing_reference/src/lib.cairo}} ``` -Now, let's say that we have some kind of setup function which deploys this contract and increments the counter a few times. +Now, let's define `setup` function which deploys this contract and increments the counter a few times and assert that `setup` function does not exceed a certain number of steps during its execution. This is particularly useful for performance testing and ensuring that our setup logic remains efficient. ```rust -{{#include ../../../../listings/testing_reference/src/tests/tests.cairo}} +{{#include ../../../../listings/testing_reference/tests/tests.cairo}} ``` -With `get_current_step`, we can assert that `setup` function does not exceed a certain number of steps during its execution. This is particularly useful for performance testing and ensuring that our setup logic remains efficient. - Let's run the test: ```shell From 073c95fe32ae24762ede93699674b46439f5f779 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Thu, 23 Oct 2025 16:45:09 +0200 Subject: [PATCH 03/33] Fix scarb formatting --- snforge_std/src/lib.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/snforge_std/src/lib.cairo b/snforge_std/src/lib.cairo index cf10bb4d02..7c1ff760ab 100644 --- a/snforge_std/src/lib.cairo +++ b/snforge_std/src/lib.cairo @@ -126,10 +126,10 @@ pub mod fuzzable; pub mod signature; -pub mod trace; - pub mod testing; +pub mod trace; + #[doc(hidden)] pub mod _internals { pub use cheatcode::{is_config_run, save_fuzzer_arg}; From 77dd8b9aa5291123cdaaa01d2aa90e1f0069ae88 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Thu, 23 Oct 2025 18:45:44 +0200 Subject: [PATCH 04/33] Add test --- .../tests/integration/get_current_step.rs | 39 +++++++++++++++++++ crates/forge/tests/integration/mod.rs | 1 + 2 files changed, 40 insertions(+) create mode 100644 crates/forge/tests/integration/get_current_step.rs diff --git a/crates/forge/tests/integration/get_current_step.rs b/crates/forge/tests/integration/get_current_step.rs new file mode 100644 index 0000000000..420a1099c1 --- /dev/null +++ b/crates/forge/tests/integration/get_current_step.rs @@ -0,0 +1,39 @@ +use forge_runner::forge_config::ForgeTrackedResource; +use indoc::indoc; +use std::path::Path; +use test_utils::runner::{Contract, assert_passed}; +use test_utils::running_tests::run_test_case; +use test_utils::test_case; + +#[test] +fn test_get_current_step() { + let test = test_case!( + indoc!( + r#" + use snforge_std::testing::get_current_step; + use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; + + #[test] + fn check_current_step() { + let step_start = get_current_step(); + + let contract = declare("HelloStarknet").unwrap().contract_class().clone(); + let _ = contract.deploy(@ArrayTrait::new()).unwrap(); + + let step_end = get_current_step(); + + assert!(step_end > step_start); + } + "# + ), + Contract::from_code_path( + "HelloStarknet".to_string(), + Path::new("tests/data/contracts/hello_starknet.cairo"), + ) + .unwrap() + ); + + let result = run_test_case(&test, ForgeTrackedResource::SierraGas); + + assert_passed(&result); +} diff --git a/crates/forge/tests/integration/mod.rs b/crates/forge/tests/integration/mod.rs index f86eae4af6..afcda225b2 100644 --- a/crates/forge/tests/integration/mod.rs +++ b/crates/forge/tests/integration/mod.rs @@ -18,6 +18,7 @@ mod gas; mod generate_random_felt; mod get_available_gas; mod get_class_hash; +mod get_current_step; mod interact_with_state; mod l1_handler_executor; mod message_to_l1; From 5672903e0d3af2e26fecaee351500b2c1f66e02f Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Thu, 23 Oct 2025 18:57:05 +0200 Subject: [PATCH 05/33] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8054d5ae15..03d7acac23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Added -- `get_current_step` function to get the current step from Cairo VM during test execution. For more see [docs]() +- `get_current_step` function to get the current step from Cairo VM during test execution. For more see [docs](https://foundry-rs.github.io/starknet-foundry/snforge-library/testing/get_current_step.html) #### Changed From 53494dab8d7fa579cbcd17efe1e01cbd267b8b30 Mon Sep 17 00:00:00 2001 From: Maksymilian Kowalski Date: Fri, 24 Oct 2025 16:10:20 +0200 Subject: [PATCH 06/33] Ignore cheats test of `meta_tx_v0` on `cairo-native` --- crates/forge/tests/integration/meta_tx_v0.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/forge/tests/integration/meta_tx_v0.rs b/crates/forge/tests/integration/meta_tx_v0.rs index aaa46e0782..eda42965f5 100644 --- a/crates/forge/tests/integration/meta_tx_v0.rs +++ b/crates/forge/tests/integration/meta_tx_v0.rs @@ -95,6 +95,10 @@ fn meta_tx_v0_with_cheat_caller_address() { assert_passed(&result); } +#[cfg_attr( + feature = "cairo-native", + ignore = "Cheats in `meta_tx_v0` are not supported on `cairo-native`" +)] #[test] fn meta_tx_v0_with_cheat_block_hash() { // TODO(#3704) Remove scarb version check From 5dd5177eed36554ac33a51c3882b6512c29de9ca Mon Sep 17 00:00:00 2001 From: Maksymilian Kowalski Date: Fri, 24 Oct 2025 16:10:41 +0200 Subject: [PATCH 07/33] Additional check native test --- Cargo.lock | 16 ++--- .../data/contracts/meta_tx_v0_checkers.cairo | 22 +++++++ crates/forge/tests/integration/meta_tx_v0.rs | 62 +++++++++++++++++++ 3 files changed, 92 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1dcfb1824..26f92ad58f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,7 +224,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -709,7 +709,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -720,7 +720,7 @@ dependencies = [ "regex", "rustc-hash 2.1.1", "shlex", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -2844,7 +2844,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -4396,7 +4396,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -4544,7 +4544,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.106", + "syn 2.0.107", "tblgen", "unindent", ] @@ -4670,7 +4670,7 @@ checksum = "b52c1b33ff98142aecea13138bd399b68aa7ab5d9546c300988c345004001eea" [[package]] name = "native-api" -version = "0.50.0" +version = "0.51.1" dependencies = [ "cairo-lang-starknet-classes", "cairo-native", @@ -5381,7 +5381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] diff --git a/crates/forge/tests/data/contracts/meta_tx_v0_checkers.cairo b/crates/forge/tests/data/contracts/meta_tx_v0_checkers.cairo index b45f83866f..244c3266b1 100644 --- a/crates/forge/tests/data/contracts/meta_tx_v0_checkers.cairo +++ b/crates/forge/tests/data/contracts/meta_tx_v0_checkers.cairo @@ -55,3 +55,25 @@ mod CheatBlockHashCheckerMetaTxV0 { } } } + +#[starknet::interface] +trait ISimpleCheckerMetaTxV0 { + fn __execute__(ref self: TContractState) -> felt252; +} + +#[starknet::contract(account)] +mod SimpleCheckerMetaTxV0 { + use starknet::SyscallResultTrait; + use starknet::syscalls::get_block_hash_syscall; + + #[storage] + struct Storage {} + + #[abi(embed_v0)] + impl ISimpleCheckerMetaTxV0 of super::ISimpleCheckerMetaTxV0 { + fn __execute__(ref self: ContractState) -> felt252 { + 1234567890.into() + } + } +} + diff --git a/crates/forge/tests/integration/meta_tx_v0.rs b/crates/forge/tests/integration/meta_tx_v0.rs index eda42965f5..6c3bf49d42 100644 --- a/crates/forge/tests/integration/meta_tx_v0.rs +++ b/crates/forge/tests/integration/meta_tx_v0.rs @@ -21,6 +21,68 @@ fn skip_scarb_lt_2_11_0() -> bool { } } +#[cfg(feature = "cairo-native")] +#[test] +fn check_meta_tx_v0_syscall_work_on_native() { + // TODO(#3704) Remove scarb version check + if skip_scarb_lt_2_11_0() { + return; + } + + let test = test_case!( + indoc!( + r#" + use result::ResultTrait; + use array::ArrayTrait; + use option::OptionTrait; + use traits::TryInto; + use starknet::ContractAddress; + use snforge_std::{ + declare, ContractClassTrait, DeclareResultTrait + }; + + #[starknet::interface] + trait IMetaTxV0Test { + fn execute_meta_tx_v0( + ref self: TContractState, + target: starknet::ContractAddress, + signature: Span, + ) -> felt252; + } + + #[test] + fn test_meta_tx_v0_verify_tx_context_modification() { + let checker_contract = declare("SimpleCheckerMetaTxV0").unwrap().contract_class(); + let (checker_address, _) = checker_contract.deploy(@ArrayTrait::new()).unwrap(); + + let meta_contract = declare("MetaTxV0Test").unwrap().contract_class(); + let (meta_address, _) = meta_contract.deploy(@ArrayTrait::new()).unwrap(); + let meta_dispatcher = IMetaTxV0TestDispatcher { contract_address: meta_address }; + + let mut signature = ArrayTrait::new(); + + let result = meta_dispatcher.execute_meta_tx_v0(checker_address, signature.span()); + + assert(result == 1234567890, 'Result should be 1234567890'); + } + "# + ), + Contract::from_code_path( + "SimpleCheckerMetaTxV0".to_string(), + Path::new("tests/data/contracts/meta_tx_v0_checkers.cairo"), + ) + .unwrap(), + Contract::from_code_path( + "MetaTxV0Test".to_string(), + Path::new("tests/data/contracts/meta_tx_v0_test.cairo"), + ) + .unwrap() + ); + + let result = run_test_case(&test, ForgeTrackedResource::SierraGas); + assert_passed(&result); +} + #[test] fn meta_tx_v0_with_cheat_caller_address() { // TODO(#3704) Remove scarb version check From ab287a44af84b29336f765b61a8ca7150c915b99 Mon Sep 17 00:00:00 2001 From: Maksymilian Kowalski Date: Fri, 24 Oct 2025 16:45:39 +0200 Subject: [PATCH 08/33] Enable test on vm --- crates/forge/tests/integration/meta_tx_v0.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/forge/tests/integration/meta_tx_v0.rs b/crates/forge/tests/integration/meta_tx_v0.rs index 6c3bf49d42..b1469d5721 100644 --- a/crates/forge/tests/integration/meta_tx_v0.rs +++ b/crates/forge/tests/integration/meta_tx_v0.rs @@ -21,9 +21,8 @@ fn skip_scarb_lt_2_11_0() -> bool { } } -#[cfg(feature = "cairo-native")] #[test] -fn check_meta_tx_v0_syscall_work_on_native() { +fn check_meta_tx_v0_syscall_work_without_cheats() { // TODO(#3704) Remove scarb version check if skip_scarb_lt_2_11_0() { return; From feed6d5317fd18375b6068c5937d8b6b87fbc1ac Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 28 Oct 2025 08:57:20 +0100 Subject: [PATCH 09/33] Apply code review suggestions --- .../runtime_extensions/forge_config_extension.rs | 2 +- .../forge_runtime_extension/mod.rs | 13 ++++--------- crates/runtime/src/lib.rs | 2 +- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/forge_config_extension.rs b/crates/cheatnet/src/runtime_extensions/forge_config_extension.rs index 92e29c8090..2a7b0890b6 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_config_extension.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_config_extension.rs @@ -18,7 +18,7 @@ impl<'a> ExtensionLogic for ForgeConfigExtension<'a> { selector: &str, mut input_reader: BufferReader<'_>, _extended_runtime: &mut Self::Runtime, - _vm: &mut VirtualMachine, + _vm: &VirtualMachine, ) -> Result { macro_rules! config_cheatcode { ( $prop:ident) => {{ diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs index 03725bd8fb..d2577acf39 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -81,7 +81,7 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { selector: &str, mut input_reader: BufferReader<'_>, extended_runtime: &mut Self::Runtime, - vm: &mut VirtualMachine, + vm: &VirtualMachine, ) -> Result { if let Some(oracle_selector) = self .oracle_hint_service @@ -553,14 +553,9 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { .cheat_block_hash(block_number, operation); Ok(CheatcodeHandlingResult::from_serializable(())) } - "get_current_step" => { - let current_step: u32 = vm - .get_current_step() - .try_into() - .context("Current step value exceeds u32")?; - - Ok(CheatcodeHandlingResult::from_serializable(current_step)) - } + "get_current_step" => Ok(CheatcodeHandlingResult::from_serializable( + vm.get_current_step(), + )), _ => Ok(CheatcodeHandlingResult::Forwarded), } } diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index e33f3e5f33..fb89e2343e 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -424,7 +424,7 @@ pub trait ExtensionLogic { _selector: &str, _input_reader: BufferReader, _extended_runtime: &mut Self::Runtime, - _vm: &mut VirtualMachine, + _vm: &VirtualMachine, ) -> Result { Ok(CheatcodeHandlingResult::Forwarded) } From 2afa790067f1a658b404a09fc4432d09d5d47af3 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 28 Oct 2025 08:58:06 +0100 Subject: [PATCH 10/33] Fix linting --- crates/sncast/src/starknet_commands/script/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sncast/src/starknet_commands/script/run.rs b/crates/sncast/src/starknet_commands/script/run.rs index a7ef749552..b9baeda866 100644 --- a/crates/sncast/src/starknet_commands/script/run.rs +++ b/crates/sncast/src/starknet_commands/script/run.rs @@ -104,7 +104,7 @@ impl<'a> ExtensionLogic for CastScriptExtension<'a> { selector: &str, mut input_reader: BufferReader, _extended_runtime: &mut Self::Runtime, - _vm: &mut VirtualMachine, + _vm: &VirtualMachine, ) -> Result { match selector { "call" => { From 30d1404303ccd4a0ed22dbc3611d61292cbb1a06 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 28 Oct 2025 09:00:25 +0100 Subject: [PATCH 11/33] Fix lock --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72df01fdf6..ffb81d2069 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,7 +224,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -720,7 +720,7 @@ dependencies = [ "regex", "rustc-hash 2.1.1", "shlex", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -2844,7 +2844,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -4544,7 +4544,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.107", + "syn 2.0.108", "tblgen", "unindent", ] @@ -5381,7 +5381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] From b5c1de025c9dedf926f9b5df058f15ecc43b6c84 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Mon, 3 Nov 2025 17:13:26 +0100 Subject: [PATCH 12/33] WIP --- .../execution/entry_point.rs | 11 ++++++++ .../mod.rs | 10 ++++++- .../cheatable_starknet_runtime_extension.rs | 10 ++++++- .../forge_runtime_extension/mod.rs | 26 ++++++++++++++++--- crates/cheatnet/src/state.rs | 21 +++++++++++++++ crates/forge-runner/src/running.rs | 6 ++++- 6 files changed, 78 insertions(+), 6 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs index 55ba5f6749..fa4f21382b 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs @@ -225,6 +225,17 @@ pub fn execute_call_entry_point( cheatnet_state.trace_data.exit_nested_call(); } + let resources_from_inner_calls = &res + .call_info + .inner_calls + .iter() + .fold(ExecutionResources::default(), |acc, call_info| { + &acc + &call_info.resources + }); + let used_resources = &res.call_info.resources - resources_from_inner_calls; + + cheatnet_state.add_already_used_resources(&used_resources); + Ok(res.call_info) } Err(EntryPointExecutionError::PreExecutionError(err)) diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs index ae56e0172f..e1d6d5ba3b 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs @@ -6,7 +6,8 @@ use blockifier::execution::syscalls::hint_processor::{OUT_OF_GAS_ERROR, SyscallH use blockifier::execution::syscalls::syscall_executor::SyscallExecutor; use blockifier::execution::syscalls::vm_syscall_utils::{ CallContractRequest, LibraryCallRequest, RevertData, SingleSegmentResponse, - SyscallExecutorBaseError, SyscallRequestWrapper, SyscallSelector, + SyscallExecutorBaseError, SyscallRequestWrapper, SyscallSelector, SyscallUsage, + SyscallUsageMap, }; use blockifier::execution::{ execution_utils::ReadOnlySegment, @@ -194,6 +195,13 @@ fn execute_syscall( syscall_handler.increment_syscall_count_by(&selector, 1); // endregion + let mut syscall_usage_map = SyscallUsageMap::new(); + let mut syscall_usage = SyscallUsage::default(); + syscall_usage.increment_call_count(); + syscall_usage_map.insert(selector, syscall_usage); + + cheatnet_state.add_already_used_syscalls(&syscall_usage_map); + let syscall_gas_cost = syscall_handler .get_gas_cost_from_selector(&selector) .map_err(|error| SyscallExecutorBaseError::GasCost { error, selector })?; diff --git a/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs b/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs index 252b66bc66..47c55b7956 100644 --- a/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs +++ b/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs @@ -8,7 +8,7 @@ use blockifier::execution::syscalls::syscall_base::SyscallResult; use blockifier::execution::syscalls::syscall_executor::SyscallExecutor; use blockifier::execution::syscalls::vm_syscall_utils::{ RevertData, SyscallExecutorBaseError, SyscallRequest, SyscallRequestWrapper, SyscallResponse, - SyscallResponseWrapper, SyscallSelector, + SyscallResponseWrapper, SyscallSelector, SyscallUsage, SyscallUsageMap, }; use blockifier::execution::{ common_hints::HintExecutionResult, @@ -195,6 +195,14 @@ impl CheatableStarknetRuntimeExtension<'_> { syscall_handler.syscall_ptr += 1; syscall_handler.increment_syscall_count_by(&selector, 1); + let mut syscall_usage_map = SyscallUsageMap::new(); + let mut syscall_usage = SyscallUsage::default(); + syscall_usage.increment_call_count(); + syscall_usage_map.insert(selector, syscall_usage); + + self.cheatnet_state + .add_already_used_syscalls(&syscall_usage_map); + let syscall_gas_cost = syscall_handler .get_gas_cost_from_selector(&selector) .map_err(|error| SyscallExecutorBaseError::GasCost { error, selector })?; diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs index d2577acf39..4673712d2d 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -19,6 +19,7 @@ use crate::runtime_extensions::{ }; use crate::trace_data::{CallTrace, CallTraceNode, GasReportData}; use anyhow::{Context, Result, anyhow}; +use blockifier::blockifier_versioned_constants::VersionedConstants; use blockifier::bouncer::vm_resources_to_sierra_gas; use blockifier::context::TransactionContext; use blockifier::execution::call_info::{ @@ -553,9 +554,28 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { .cheat_block_hash(block_number, operation); Ok(CheatcodeHandlingResult::from_serializable(())) } - "get_current_step" => Ok(CheatcodeHandlingResult::from_serializable( - vm.get_current_step(), - )), + "get_current_step" => { + let execution_resources_from_used_resources = &extended_runtime + .extended_runtime + .extension + .cheatnet_state + .already_used_resources; + + let execution_resources_from_used_syscalls = + &VersionedConstants::latest_constants().get_additional_os_syscall_resources( + &extended_runtime + .extended_runtime + .extension + .cheatnet_state + .already_used_syscalls, + ); + + let resources_from_calls = execution_resources_from_used_resources + + execution_resources_from_used_syscalls; + + let vm_steps_total = resources_from_calls.n_steps + vm.get_current_step(); + Ok(CheatcodeHandlingResult::from_serializable(vm_steps_total)) + } _ => Ok(CheatcodeHandlingResult::Forwarded), } } diff --git a/crates/cheatnet/src/state.rs b/crates/cheatnet/src/state.rs index 7b02489375..65bfdadfd3 100644 --- a/crates/cheatnet/src/state.rs +++ b/crates/cheatnet/src/state.rs @@ -10,9 +10,11 @@ use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_events:: use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_messages_to_l1::MessageToL1; use crate::trace_data::{CallTrace, NotEmptyCallStack, TraceData}; use blockifier::execution::contract_class::RunnableCompiledClass; +use blockifier::execution::syscalls::vm_syscall_utils::SyscallUsageMap; use blockifier::state::errors::StateError::UndeclaredClassHash; use blockifier::state::state_api::{StateReader, StateResult}; use cairo_vm::Felt252; +use cairo_vm::vm::runners::cairo_runner::ExecutionResources; use conversions::serde::deserialize::CairoDeserialize; use conversions::string::TryFromHexStr; use indexmap::IndexMap; @@ -246,6 +248,8 @@ pub struct CheatnetState { pub fuzzer_args: Vec, pub block_hash_contracts: HashMap<(ContractAddress, u64), (CheatSpan, Felt)>, pub global_block_hash: HashMap)>, + pub already_used_resources: ExecutionResources, + pub already_used_syscalls: SyscallUsageMap, } pub type EncounteredErrors = IndexMap>; @@ -277,6 +281,8 @@ impl Default for CheatnetState { fuzzer_args: Vec::default(), block_hash_contracts: HashMap::default(), global_block_hash: HashMap::default(), + already_used_resources: ExecutionResources::default(), + already_used_syscalls: SyscallUsageMap::default(), } } } @@ -400,4 +406,19 @@ impl CheatnetState { pub fn clear_error(&mut self, class_hash: ClassHash) { self.encountered_errors.shift_remove(&class_hash); } + + pub fn add_already_used_resources(&mut self, resources: &ExecutionResources) { + self.already_used_resources += resources; + } + + pub fn add_already_used_syscalls(&mut self, syscalls: &SyscallUsageMap) { + for (selector, usage) in syscalls.iter() { + self.already_used_syscalls + .entry(*selector) + .and_modify(|existing_usage| { + existing_usage.call_count += usage.call_count; + }) + .or_insert_with(|| usage.clone()); + } + } } diff --git a/crates/forge-runner/src/running.rs b/crates/forge-runner/src/running.rs index 7d9bed3ccb..e4c3eb2ef5 100644 --- a/crates/forge-runner/src/running.rs +++ b/crates/forge-runner/src/running.rs @@ -12,6 +12,7 @@ use blockifier::execution::entry_point_execution::{ }; use blockifier::execution::errors::EntryPointExecutionError; use blockifier::state::cached_state::CachedState; +use blockifier::transaction::objects::ExecutionResourcesTraits; use cairo_vm::Felt252; use cairo_vm::types::program::Program; use cairo_vm::vm::errors::cairo_run_errors::CairoRunError; @@ -298,7 +299,10 @@ pub fn run_test_case( program_extra_data_length, tracked_resource, )?; - + println!( + "run_test_case call_info steps: {}", + call_info.resources.total_n_steps() + ); // TODO(#3744): Confirm if this is needed for the profiler let vm_resources_without_inner_calls = extract_vm_resources( &runner, From 57deb3b6db50e68e6b40e45755dedf01ada4669a Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 4 Nov 2025 14:57:31 +0100 Subject: [PATCH 13/33] Fix resources calculation --- .../mod.rs | 12 +--- .../cheatable_starknet_runtime_extension.rs | 10 +--- .../forge_runtime_extension/mod.rs | 4 +- crates/cheatnet/src/state.rs | 33 ++++++----- crates/forge/src/run_tests/filtering.rs | 59 +++++++++++++++++++ 5 files changed, 84 insertions(+), 34 deletions(-) create mode 100644 crates/forge/src/run_tests/filtering.rs diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs index e1d6d5ba3b..def0d9c431 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs @@ -6,8 +6,7 @@ use blockifier::execution::syscalls::hint_processor::{OUT_OF_GAS_ERROR, SyscallH use blockifier::execution::syscalls::syscall_executor::SyscallExecutor; use blockifier::execution::syscalls::vm_syscall_utils::{ CallContractRequest, LibraryCallRequest, RevertData, SingleSegmentResponse, - SyscallExecutorBaseError, SyscallRequestWrapper, SyscallSelector, SyscallUsage, - SyscallUsageMap, + SyscallExecutorBaseError, SyscallRequestWrapper, SyscallSelector, }; use blockifier::execution::{ execution_utils::ReadOnlySegment, @@ -193,14 +192,9 @@ fn execute_syscall( // Increment, since the selector was peeked into before syscall_handler.syscall_ptr += 1; syscall_handler.increment_syscall_count_by(&selector, 1); - // endregion - - let mut syscall_usage_map = SyscallUsageMap::new(); - let mut syscall_usage = SyscallUsage::default(); - syscall_usage.increment_call_count(); - syscall_usage_map.insert(selector, syscall_usage); - cheatnet_state.add_already_used_syscalls(&syscall_usage_map); + cheatnet_state.add_used_syscall(&selector); + // endregion let syscall_gas_cost = syscall_handler .get_gas_cost_from_selector(&selector) diff --git a/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs b/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs index 47c55b7956..614f725c0d 100644 --- a/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs +++ b/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs @@ -8,7 +8,7 @@ use blockifier::execution::syscalls::syscall_base::SyscallResult; use blockifier::execution::syscalls::syscall_executor::SyscallExecutor; use blockifier::execution::syscalls::vm_syscall_utils::{ RevertData, SyscallExecutorBaseError, SyscallRequest, SyscallRequestWrapper, SyscallResponse, - SyscallResponseWrapper, SyscallSelector, SyscallUsage, SyscallUsageMap, + SyscallResponseWrapper, SyscallSelector, }; use blockifier::execution::{ common_hints::HintExecutionResult, @@ -195,13 +195,7 @@ impl CheatableStarknetRuntimeExtension<'_> { syscall_handler.syscall_ptr += 1; syscall_handler.increment_syscall_count_by(&selector, 1); - let mut syscall_usage_map = SyscallUsageMap::new(); - let mut syscall_usage = SyscallUsage::default(); - syscall_usage.increment_call_count(); - syscall_usage_map.insert(selector, syscall_usage); - - self.cheatnet_state - .add_already_used_syscalls(&syscall_usage_map); + self.cheatnet_state.add_used_syscall(&selector); let syscall_gas_cost = syscall_handler .get_gas_cost_from_selector(&selector) diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs index 4673712d2d..ebc9ec293e 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -559,7 +559,7 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { .extended_runtime .extension .cheatnet_state - .already_used_resources; + .used_resources; let execution_resources_from_used_syscalls = &VersionedConstants::latest_constants().get_additional_os_syscall_resources( @@ -567,7 +567,7 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { .extended_runtime .extension .cheatnet_state - .already_used_syscalls, + .used_syscalls, ); let resources_from_calls = execution_resources_from_used_resources diff --git a/crates/cheatnet/src/state.rs b/crates/cheatnet/src/state.rs index 65bfdadfd3..ef68b8589c 100644 --- a/crates/cheatnet/src/state.rs +++ b/crates/cheatnet/src/state.rs @@ -10,7 +10,9 @@ use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_events:: use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_messages_to_l1::MessageToL1; use crate::trace_data::{CallTrace, NotEmptyCallStack, TraceData}; use blockifier::execution::contract_class::RunnableCompiledClass; -use blockifier::execution::syscalls::vm_syscall_utils::SyscallUsageMap; +use blockifier::execution::syscalls::vm_syscall_utils::{ + SyscallSelector, SyscallUsage, SyscallUsageMap, +}; use blockifier::state::errors::StateError::UndeclaredClassHash; use blockifier::state::state_api::{StateReader, StateResult}; use cairo_vm::Felt252; @@ -248,8 +250,8 @@ pub struct CheatnetState { pub fuzzer_args: Vec, pub block_hash_contracts: HashMap<(ContractAddress, u64), (CheatSpan, Felt)>, pub global_block_hash: HashMap)>, - pub already_used_resources: ExecutionResources, - pub already_used_syscalls: SyscallUsageMap, + pub used_resources: ExecutionResources, + pub used_syscalls: SyscallUsageMap, } pub type EncounteredErrors = IndexMap>; @@ -281,8 +283,8 @@ impl Default for CheatnetState { fuzzer_args: Vec::default(), block_hash_contracts: HashMap::default(), global_block_hash: HashMap::default(), - already_used_resources: ExecutionResources::default(), - already_used_syscalls: SyscallUsageMap::default(), + used_resources: ExecutionResources::default(), + used_syscalls: SyscallUsageMap::default(), } } } @@ -408,17 +410,18 @@ impl CheatnetState { } pub fn add_already_used_resources(&mut self, resources: &ExecutionResources) { - self.already_used_resources += resources; + self.used_resources += resources; } - pub fn add_already_used_syscalls(&mut self, syscalls: &SyscallUsageMap) { - for (selector, usage) in syscalls.iter() { - self.already_used_syscalls - .entry(*selector) - .and_modify(|existing_usage| { - existing_usage.call_count += usage.call_count; - }) - .or_insert_with(|| usage.clone()); - } + pub fn add_used_syscall(&mut self, syscall: &SyscallSelector) { + self.used_syscalls + .entry(*syscall) + .and_modify(|existing_usage| { + existing_usage.call_count += 1; + }) + .or_insert(SyscallUsage { + call_count: 1, + linear_factor: 0, + }); } } diff --git a/crates/forge/src/run_tests/filtering.rs b/crates/forge/src/run_tests/filtering.rs new file mode 100644 index 0000000000..d52c9d6631 --- /dev/null +++ b/crates/forge/src/run_tests/filtering.rs @@ -0,0 +1,59 @@ +use crate::{ + run_tests::workspace::PackagesWithTestTargets, + test_filter::{NameFilter, TestsFilter}, +}; +use forge_runner::running::with_config::{TestCandidate, TestTarget}; +use std::collections::HashMap; + +pub enum FilterResult { + Included(TestCandidate), + Excluded, +} + +pub fn apply_name_filter( + packages_with_test_targets: PackagesWithTestTargets, + tests_filter: &TestsFilter, +) -> PackagesWithTestTargets { + let mut result = PackagesWithTestTargets(HashMap::new()); + + for (package_id, test_targets) in packages_with_test_targets.0 { + let filtered_test_targets = test_targets + .into_iter() + .map(|test_target| { + let tests = test_target + .tests + .into_iter() + .map(|test_candidate| match tests_filter.name_filter { + NameFilter::All => FilterResult::Included(test_candidate), + NameFilter::Match(ref name) => { + if test_candidate.name.contains(name) { + FilterResult::Included(test_candidate) + } else { + FilterResult::Excluded + } + } + NameFilter::ExactMatch(ref name) => { + if test_candidate.name == *name { + FilterResult::Included(test_candidate) + } else { + FilterResult::Excluded + } + } + }) + .collect::>(); + + TestTarget { + tests_location: test_target.tests_location, + sierra_program: test_target.sierra_program, + sierra_program_path: test_target.sierra_program_path, + casm_program: test_target.casm_program, + tests, + } + }) + .collect::>(); + + result.0.insert(package_id, filtered_test_targets); + } + + result +} From a674c9d4779195c0541156d266754f745704d8d6 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 4 Nov 2025 16:21:26 +0100 Subject: [PATCH 14/33] Remove print --- crates/forge-runner/src/running.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/forge-runner/src/running.rs b/crates/forge-runner/src/running.rs index e4c3eb2ef5..7d9bed3ccb 100644 --- a/crates/forge-runner/src/running.rs +++ b/crates/forge-runner/src/running.rs @@ -12,7 +12,6 @@ use blockifier::execution::entry_point_execution::{ }; use blockifier::execution::errors::EntryPointExecutionError; use blockifier::state::cached_state::CachedState; -use blockifier::transaction::objects::ExecutionResourcesTraits; use cairo_vm::Felt252; use cairo_vm::types::program::Program; use cairo_vm::vm::errors::cairo_run_errors::CairoRunError; @@ -299,10 +298,7 @@ pub fn run_test_case( program_extra_data_length, tracked_resource, )?; - println!( - "run_test_case call_info steps: {}", - call_info.resources.total_n_steps() - ); + // TODO(#3744): Confirm if this is needed for the profiler let vm_resources_without_inner_calls = extract_vm_resources( &runner, From 8b22af2b67478172a5e20984d4f7fce57daa5362 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 4 Nov 2025 16:59:51 +0100 Subject: [PATCH 15/33] Refactor --- .../execution/entry_point.rs | 5 ++--- crates/cheatnet/src/state.rs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs index fa4f21382b..494416f7f4 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs @@ -232,9 +232,8 @@ pub fn execute_call_entry_point( .fold(ExecutionResources::default(), |acc, call_info| { &acc + &call_info.resources }); - let used_resources = &res.call_info.resources - resources_from_inner_calls; - - cheatnet_state.add_already_used_resources(&used_resources); + let resources_from_current_call = &res.call_info.resources - resources_from_inner_calls; + cheatnet_state.add_used_resources(&resources_from_current_call); Ok(res.call_info) } diff --git a/crates/cheatnet/src/state.rs b/crates/cheatnet/src/state.rs index ef68b8589c..f83e3feb4b 100644 --- a/crates/cheatnet/src/state.rs +++ b/crates/cheatnet/src/state.rs @@ -409,7 +409,7 @@ impl CheatnetState { self.encountered_errors.shift_remove(&class_hash); } - pub fn add_already_used_resources(&mut self, resources: &ExecutionResources) { + pub fn add_used_resources(&mut self, resources: &ExecutionResources) { self.used_resources += resources; } From 725479935b34d86125a4b09e06a226c197326604 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 4 Nov 2025 17:01:23 +0100 Subject: [PATCH 16/33] Little refactor --- .../runtime_extensions/forge_runtime_extension/mod.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs index ebc9ec293e..e010879d4e 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -555,12 +555,11 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { Ok(CheatcodeHandlingResult::from_serializable(())) } "get_current_step" => { - let execution_resources_from_used_resources = &extended_runtime + let used_resources = &extended_runtime .extended_runtime .extension .cheatnet_state .used_resources; - let execution_resources_from_used_syscalls = &VersionedConstants::latest_constants().get_additional_os_syscall_resources( &extended_runtime @@ -569,11 +568,9 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { .cheatnet_state .used_syscalls, ); - - let resources_from_calls = execution_resources_from_used_resources - + execution_resources_from_used_syscalls; - + let resources_from_calls = used_resources + execution_resources_from_used_syscalls; let vm_steps_total = resources_from_calls.n_steps + vm.get_current_step(); + Ok(CheatcodeHandlingResult::from_serializable(vm_steps_total)) } _ => Ok(CheatcodeHandlingResult::Forwarded), From 86b73b0fb8f2eba5d9a80b03ec074922606bbbd0 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 4 Nov 2025 17:16:08 +0100 Subject: [PATCH 17/33] Remove unused file --- crates/forge/src/run_tests/filtering.rs | 59 ------------------------- 1 file changed, 59 deletions(-) delete mode 100644 crates/forge/src/run_tests/filtering.rs diff --git a/crates/forge/src/run_tests/filtering.rs b/crates/forge/src/run_tests/filtering.rs deleted file mode 100644 index d52c9d6631..0000000000 --- a/crates/forge/src/run_tests/filtering.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::{ - run_tests::workspace::PackagesWithTestTargets, - test_filter::{NameFilter, TestsFilter}, -}; -use forge_runner::running::with_config::{TestCandidate, TestTarget}; -use std::collections::HashMap; - -pub enum FilterResult { - Included(TestCandidate), - Excluded, -} - -pub fn apply_name_filter( - packages_with_test_targets: PackagesWithTestTargets, - tests_filter: &TestsFilter, -) -> PackagesWithTestTargets { - let mut result = PackagesWithTestTargets(HashMap::new()); - - for (package_id, test_targets) in packages_with_test_targets.0 { - let filtered_test_targets = test_targets - .into_iter() - .map(|test_target| { - let tests = test_target - .tests - .into_iter() - .map(|test_candidate| match tests_filter.name_filter { - NameFilter::All => FilterResult::Included(test_candidate), - NameFilter::Match(ref name) => { - if test_candidate.name.contains(name) { - FilterResult::Included(test_candidate) - } else { - FilterResult::Excluded - } - } - NameFilter::ExactMatch(ref name) => { - if test_candidate.name == *name { - FilterResult::Included(test_candidate) - } else { - FilterResult::Excluded - } - } - }) - .collect::>(); - - TestTarget { - tests_location: test_target.tests_location, - sierra_program: test_target.sierra_program, - sierra_program_path: test_target.sierra_program_path, - casm_program: test_target.casm_program, - tests, - } - }) - .collect::>(); - - result.0.insert(package_id, filtered_test_targets); - } - - result -} From 660ec59a788b0638b29b8d1974fb5068084763ca Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 4 Nov 2025 17:23:02 +0100 Subject: [PATCH 18/33] Refactor names --- .../call_to_blockifier_runtime_extension/mod.rs | 2 +- .../cheatable_starknet_runtime_extension.rs | 2 +- crates/cheatnet/src/state.rs | 17 ++++------------- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs index def0d9c431..943e3b7fc6 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs @@ -193,7 +193,7 @@ fn execute_syscall( syscall_handler.syscall_ptr += 1; syscall_handler.increment_syscall_count_by(&selector, 1); - cheatnet_state.add_used_syscall(&selector); + cheatnet_state.add_used_syscall(&selector, 1); // endregion let syscall_gas_cost = syscall_handler diff --git a/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs b/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs index 614f725c0d..8b69bd22eb 100644 --- a/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs +++ b/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs @@ -195,7 +195,7 @@ impl CheatableStarknetRuntimeExtension<'_> { syscall_handler.syscall_ptr += 1; syscall_handler.increment_syscall_count_by(&selector, 1); - self.cheatnet_state.add_used_syscall(&selector); + self.cheatnet_state.add_used_syscall(&selector, 1); let syscall_gas_cost = syscall_handler .get_gas_cost_from_selector(&selector) diff --git a/crates/cheatnet/src/state.rs b/crates/cheatnet/src/state.rs index f83e3feb4b..716ea06cd7 100644 --- a/crates/cheatnet/src/state.rs +++ b/crates/cheatnet/src/state.rs @@ -10,9 +10,7 @@ use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_events:: use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_messages_to_l1::MessageToL1; use crate::trace_data::{CallTrace, NotEmptyCallStack, TraceData}; use blockifier::execution::contract_class::RunnableCompiledClass; -use blockifier::execution::syscalls::vm_syscall_utils::{ - SyscallSelector, SyscallUsage, SyscallUsageMap, -}; +use blockifier::execution::syscalls::vm_syscall_utils::{SyscallSelector, SyscallUsageMap}; use blockifier::state::errors::StateError::UndeclaredClassHash; use blockifier::state::state_api::{StateReader, StateResult}; use cairo_vm::Felt252; @@ -413,15 +411,8 @@ impl CheatnetState { self.used_resources += resources; } - pub fn add_used_syscall(&mut self, syscall: &SyscallSelector) { - self.used_syscalls - .entry(*syscall) - .and_modify(|existing_usage| { - existing_usage.call_count += 1; - }) - .or_insert(SyscallUsage { - call_count: 1, - linear_factor: 0, - }); + pub fn add_used_syscall(&mut self, syscall: &SyscallSelector, count: usize) { + let usage = self.used_syscalls.entry(*syscall).or_default(); + usage.call_count += count; } } From 65c6297b466b4779d8bd76c655a1d064cbdc211f Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 4 Nov 2025 17:25:39 +0100 Subject: [PATCH 19/33] Rename `vm_steps_total` -> `total_steps` --- .../src/runtime_extensions/forge_runtime_extension/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs index e010879d4e..8b615fa0c0 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -569,9 +569,9 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { .used_syscalls, ); let resources_from_calls = used_resources + execution_resources_from_used_syscalls; - let vm_steps_total = resources_from_calls.n_steps + vm.get_current_step(); + let total_steps = resources_from_calls.n_steps + vm.get_current_step(); - Ok(CheatcodeHandlingResult::from_serializable(vm_steps_total)) + Ok(CheatcodeHandlingResult::from_serializable(total_steps)) } _ => Ok(CheatcodeHandlingResult::Forwarded), } From c691cb1d91d2deca250febd7e4ae19a96b0e2064 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Wed, 5 Nov 2025 14:48:03 +0100 Subject: [PATCH 20/33] Apply code review suggestions --- .../execution/entry_point.rs | 10 ---- .../mod.rs | 2 - .../cheatable_starknet_runtime_extension.rs | 2 - .../forge_runtime_extension/cheatcodes/mod.rs | 1 + .../cheatcodes/testing.rs | 52 +++++++++++++++++++ .../forge_runtime_extension/mod.rs | 28 +++++----- crates/cheatnet/src/state.rs | 15 ------ 7 files changed, 68 insertions(+), 42 deletions(-) create mode 100644 crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/testing.rs diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs index 494416f7f4..55ba5f6749 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs @@ -225,16 +225,6 @@ pub fn execute_call_entry_point( cheatnet_state.trace_data.exit_nested_call(); } - let resources_from_inner_calls = &res - .call_info - .inner_calls - .iter() - .fold(ExecutionResources::default(), |acc, call_info| { - &acc + &call_info.resources - }); - let resources_from_current_call = &res.call_info.resources - resources_from_inner_calls; - cheatnet_state.add_used_resources(&resources_from_current_call); - Ok(res.call_info) } Err(EntryPointExecutionError::PreExecutionError(err)) diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs index 943e3b7fc6..ae56e0172f 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/mod.rs @@ -192,8 +192,6 @@ fn execute_syscall( // Increment, since the selector was peeked into before syscall_handler.syscall_ptr += 1; syscall_handler.increment_syscall_count_by(&selector, 1); - - cheatnet_state.add_used_syscall(&selector, 1); // endregion let syscall_gas_cost = syscall_handler diff --git a/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs b/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs index 8b69bd22eb..252b66bc66 100644 --- a/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs +++ b/crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs @@ -195,8 +195,6 @@ impl CheatableStarknetRuntimeExtension<'_> { syscall_handler.syscall_ptr += 1; syscall_handler.increment_syscall_count_by(&selector, 1); - self.cheatnet_state.add_used_syscall(&selector, 1); - let syscall_gas_cost = syscall_handler .get_gas_cost_from_selector(&selector) .map_err(|error| SyscallExecutorBaseError::GasCost { error, selector })?; diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/mod.rs index 36e77bdddf..bc311dd055 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/mod.rs @@ -20,6 +20,7 @@ pub mod replace_bytecode; pub mod spy_events; pub mod spy_messages_to_l1; pub mod storage; +pub mod testing; /// A structure used for returning cheatcode errors in tests #[derive(Debug)] diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/testing.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/testing.rs new file mode 100644 index 0000000000..96fc8b94b4 --- /dev/null +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/testing.rs @@ -0,0 +1,52 @@ +use std::{cell::RefCell, rc::Rc}; + +use blockifier::{ + blockifier_versioned_constants::VersionedConstants, + execution::syscalls::vm_syscall_utils::SyscallUsageMap, +}; +use cairo_vm::vm::runners::cairo_runner::ExecutionResources; + +use crate::{ + runtime_extensions::common::sum_syscall_usage, + trace_data::{CallTrace, CallTraceNode}, +}; + +pub fn calculate_steps_from_calls( + top_call: &Rc>, + top_call_syscalls: &SyscallUsageMap, +) -> usize { + let used_resources = + &top_call + .borrow() + .nested_calls + .iter() + .fold(ExecutionResources::default(), |acc, node| match node { + CallTraceNode::EntryPointCall(call_trace) => { + &acc + &call_trace.borrow().used_execution_resources + } + CallTraceNode::DeployWithoutConstructor => acc, + }); + + let inner_calls_syscalls = &top_call.borrow().nested_calls.iter().fold( + SyscallUsageMap::new(), + |acc, node| match node { + CallTraceNode::EntryPointCall(call_trace) => sum_syscall_usage( + acc, + &sum_syscall_usage( + call_trace.borrow().used_syscalls_sierra_gas.clone(), + &call_trace.borrow().used_syscalls_vm_resources, + ), + ), + CallTraceNode::DeployWithoutConstructor => acc, + }, + ); + + let total_syscalls = sum_syscall_usage(inner_calls_syscalls.clone(), top_call_syscalls); + + let total_syscalls_exeucution_resources = &VersionedConstants::latest_constants() + .get_additional_os_syscall_resources(&total_syscalls); + + let resources_from_calls = used_resources + total_syscalls_exeucution_resources; + + resources_from_calls.n_steps +} diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs index 8b615fa0c0..c45749a999 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -2,6 +2,7 @@ use self::contracts_data::ContractsData; use crate::runtime_extensions::call_to_blockifier_runtime_extension::rpc::UsedResources; use crate::runtime_extensions::common::sum_syscall_usage; use crate::runtime_extensions::forge_runtime_extension::cheatcodes::replace_bytecode::ReplaceBytecodeError; +use crate::runtime_extensions::forge_runtime_extension::cheatcodes::testing::calculate_steps_from_calls; use crate::runtime_extensions::{ call_to_blockifier_runtime_extension::{ CallToBlockifierRuntime, @@ -19,7 +20,6 @@ use crate::runtime_extensions::{ }; use crate::trace_data::{CallTrace, CallTraceNode, GasReportData}; use anyhow::{Context, Result, anyhow}; -use blockifier::blockifier_versioned_constants::VersionedConstants; use blockifier::bouncer::vm_resources_to_sierra_gas; use blockifier::context::TransactionContext; use blockifier::execution::call_info::{ @@ -555,21 +555,23 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { Ok(CheatcodeHandlingResult::from_serializable(())) } "get_current_step" => { - let used_resources = &extended_runtime + let top_call = extended_runtime .extended_runtime .extension .cheatnet_state - .used_resources; - let execution_resources_from_used_syscalls = - &VersionedConstants::latest_constants().get_additional_os_syscall_resources( - &extended_runtime - .extended_runtime - .extension - .cheatnet_state - .used_syscalls, - ); - let resources_from_calls = used_resources + execution_resources_from_used_syscalls; - let total_steps = resources_from_calls.n_steps + vm.get_current_step(); + .trace_data + .current_call_stack + .top(); + + let top_call_syscalls = &extended_runtime + .extended_runtime + .extended_runtime + .hint_handler + .base + .syscalls_usage; + + let steps_from_calls = calculate_steps_from_calls(&top_call, top_call_syscalls); + let total_steps = steps_from_calls + vm.get_current_step(); Ok(CheatcodeHandlingResult::from_serializable(total_steps)) } diff --git a/crates/cheatnet/src/state.rs b/crates/cheatnet/src/state.rs index 716ea06cd7..7b02489375 100644 --- a/crates/cheatnet/src/state.rs +++ b/crates/cheatnet/src/state.rs @@ -10,11 +10,9 @@ use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_events:: use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_messages_to_l1::MessageToL1; use crate::trace_data::{CallTrace, NotEmptyCallStack, TraceData}; use blockifier::execution::contract_class::RunnableCompiledClass; -use blockifier::execution::syscalls::vm_syscall_utils::{SyscallSelector, SyscallUsageMap}; use blockifier::state::errors::StateError::UndeclaredClassHash; use blockifier::state::state_api::{StateReader, StateResult}; use cairo_vm::Felt252; -use cairo_vm::vm::runners::cairo_runner::ExecutionResources; use conversions::serde::deserialize::CairoDeserialize; use conversions::string::TryFromHexStr; use indexmap::IndexMap; @@ -248,8 +246,6 @@ pub struct CheatnetState { pub fuzzer_args: Vec, pub block_hash_contracts: HashMap<(ContractAddress, u64), (CheatSpan, Felt)>, pub global_block_hash: HashMap)>, - pub used_resources: ExecutionResources, - pub used_syscalls: SyscallUsageMap, } pub type EncounteredErrors = IndexMap>; @@ -281,8 +277,6 @@ impl Default for CheatnetState { fuzzer_args: Vec::default(), block_hash_contracts: HashMap::default(), global_block_hash: HashMap::default(), - used_resources: ExecutionResources::default(), - used_syscalls: SyscallUsageMap::default(), } } } @@ -406,13 +400,4 @@ impl CheatnetState { pub fn clear_error(&mut self, class_hash: ClassHash) { self.encountered_errors.shift_remove(&class_hash); } - - pub fn add_used_resources(&mut self, resources: &ExecutionResources) { - self.used_resources += resources; - } - - pub fn add_used_syscall(&mut self, syscall: &SyscallSelector, count: usize) { - let usage = self.used_syscalls.entry(*syscall).or_default(); - usage.call_count += count; - } } From 113bc5f03c2ab043766ab1a145228a2cf26505c1 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Wed, 5 Nov 2025 16:16:44 +0100 Subject: [PATCH 21/33] Apply code review suggestions --- .../forge_runtime_extension/cheatcodes/mod.rs | 1 - .../cheatcodes/testing.rs | 52 ------------------- .../forge_runtime_extension/mod.rs | 25 ++++++++- 3 files changed, 24 insertions(+), 54 deletions(-) delete mode 100644 crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/testing.rs diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/mod.rs index bc311dd055..36e77bdddf 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/mod.rs @@ -20,7 +20,6 @@ pub mod replace_bytecode; pub mod spy_events; pub mod spy_messages_to_l1; pub mod storage; -pub mod testing; /// A structure used for returning cheatcode errors in tests #[derive(Debug)] diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/testing.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/testing.rs deleted file mode 100644 index 96fc8b94b4..0000000000 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/testing.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::{cell::RefCell, rc::Rc}; - -use blockifier::{ - blockifier_versioned_constants::VersionedConstants, - execution::syscalls::vm_syscall_utils::SyscallUsageMap, -}; -use cairo_vm::vm::runners::cairo_runner::ExecutionResources; - -use crate::{ - runtime_extensions::common::sum_syscall_usage, - trace_data::{CallTrace, CallTraceNode}, -}; - -pub fn calculate_steps_from_calls( - top_call: &Rc>, - top_call_syscalls: &SyscallUsageMap, -) -> usize { - let used_resources = - &top_call - .borrow() - .nested_calls - .iter() - .fold(ExecutionResources::default(), |acc, node| match node { - CallTraceNode::EntryPointCall(call_trace) => { - &acc + &call_trace.borrow().used_execution_resources - } - CallTraceNode::DeployWithoutConstructor => acc, - }); - - let inner_calls_syscalls = &top_call.borrow().nested_calls.iter().fold( - SyscallUsageMap::new(), - |acc, node| match node { - CallTraceNode::EntryPointCall(call_trace) => sum_syscall_usage( - acc, - &sum_syscall_usage( - call_trace.borrow().used_syscalls_sierra_gas.clone(), - &call_trace.borrow().used_syscalls_vm_resources, - ), - ), - CallTraceNode::DeployWithoutConstructor => acc, - }, - ); - - let total_syscalls = sum_syscall_usage(inner_calls_syscalls.clone(), top_call_syscalls); - - let total_syscalls_exeucution_resources = &VersionedConstants::latest_constants() - .get_additional_os_syscall_resources(&total_syscalls); - - let resources_from_calls = used_resources + total_syscalls_exeucution_resources; - - resources_from_calls.n_steps -} diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs index c45749a999..cb7a8443be 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -2,7 +2,6 @@ use self::contracts_data::ContractsData; use crate::runtime_extensions::call_to_blockifier_runtime_extension::rpc::UsedResources; use crate::runtime_extensions::common::sum_syscall_usage; use crate::runtime_extensions::forge_runtime_extension::cheatcodes::replace_bytecode::ReplaceBytecodeError; -use crate::runtime_extensions::forge_runtime_extension::cheatcodes::testing::calculate_steps_from_calls; use crate::runtime_extensions::{ call_to_blockifier_runtime_extension::{ CallToBlockifierRuntime, @@ -20,6 +19,7 @@ use crate::runtime_extensions::{ }; use crate::trace_data::{CallTrace, CallTraceNode, GasReportData}; use anyhow::{Context, Result, anyhow}; +use blockifier::blockifier_versioned_constants::VersionedConstants; use blockifier::bouncer::vm_resources_to_sierra_gas; use blockifier::context::TransactionContext; use blockifier::execution::call_info::{ @@ -881,3 +881,26 @@ pub fn get_all_used_resources( l1_handler_payload_lengths, } } + +fn calculate_steps_from_calls( + top_call: &Rc>, + top_call_syscalls: &SyscallUsageMap, +) -> usize { + // Resources from inner calls already include syscall resources used in them + let used_resources = + &top_call + .borrow() + .nested_calls + .iter() + .fold(ExecutionResources::default(), |acc, node| match node { + CallTraceNode::EntryPointCall(call_trace) => { + &acc + &call_trace.borrow().used_execution_resources + } + CallTraceNode::DeployWithoutConstructor => acc, + }); + let total_syscalls_exeucution_resources = &VersionedConstants::latest_constants() + .get_additional_os_syscall_resources(&top_call_syscalls); + let resources_from_calls = used_resources + total_syscalls_exeucution_resources; + + resources_from_calls.n_steps +} From cbdd37e8355e0ae69e5c1bccfdd6e5ce6e407ca6 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Thu, 6 Nov 2025 12:13:39 +0100 Subject: [PATCH 22/33] Apply code review suggestions --- CHANGELOG.md | 2 +- .../forge_runtime_extension/mod.rs | 26 ++++++++++--------- ...current_step.rs => get_current_vm_step.rs} | 8 +++--- crates/forge/tests/integration/mod.rs | 2 +- .../testing_reference/tests/tests.cairo | 6 ++--- docs/src/SUMMARY.md | 2 +- docs/src/appendix/snforge-library/testing.md | 2 +- ...current_step.md => get_current_vm_step.md} | 4 +-- snforge_std/src/testing.cairo | 4 +-- 9 files changed, 29 insertions(+), 27 deletions(-) rename crates/forge/tests/integration/{get_current_step.rs => get_current_vm_step.rs} (82%) rename docs/src/appendix/snforge-library/testing/{get_current_step.md => get_current_vm_step.md} (95%) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfa0dd10f4..f82c848789 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Added -- `get_current_step` function to get the current step from Cairo VM during test execution. For more see [docs](https://foundry-rs.github.io/starknet-foundry/snforge-library/testing/get_current_step.html) +- `get_current_vm_step` function to get the current step from Cairo VM during test execution. For more see [docs](https://foundry-rs.github.io/starknet-foundry/snforge-library/testing/get_current_vm_step.html) #### Changed diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs index cb7a8443be..435ffac760 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -554,7 +554,7 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { .cheat_block_hash(block_number, operation); Ok(CheatcodeHandlingResult::from_serializable(())) } - "get_current_step" => { + "get_current_vm_step" => { let top_call = extended_runtime .extended_runtime .extension @@ -562,18 +562,20 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { .trace_data .current_call_stack .top(); - + let vm_steps_from_calls = calculate_vm_steps_from_calls(&top_call); let top_call_syscalls = &extended_runtime .extended_runtime .extended_runtime .hint_handler .base .syscalls_usage; + let vm_steps_from_syscalls = &VersionedConstants::latest_constants() + .get_additional_os_syscall_resources(top_call_syscalls) + .n_steps; + let total_vm_steps = + vm_steps_from_calls + vm_steps_from_syscalls + vm.get_current_step(); - let steps_from_calls = calculate_steps_from_calls(&top_call, top_call_syscalls); - let total_steps = steps_from_calls + vm.get_current_step(); - - Ok(CheatcodeHandlingResult::from_serializable(total_steps)) + Ok(CheatcodeHandlingResult::from_serializable(total_vm_steps)) } _ => Ok(CheatcodeHandlingResult::Forwarded), } @@ -882,9 +884,9 @@ pub fn get_all_used_resources( } } -fn calculate_steps_from_calls( +fn calculate_vm_steps_from_calls( top_call: &Rc>, - top_call_syscalls: &SyscallUsageMap, + // top_call_syscalls: &SyscallUsageMap, ) -> usize { // Resources from inner calls already include syscall resources used in them let used_resources = @@ -898,9 +900,9 @@ fn calculate_steps_from_calls( } CallTraceNode::DeployWithoutConstructor => acc, }); - let total_syscalls_exeucution_resources = &VersionedConstants::latest_constants() - .get_additional_os_syscall_resources(&top_call_syscalls); - let resources_from_calls = used_resources + total_syscalls_exeucution_resources; + // let total_syscalls_exeucution_resources = &VersionedConstants::latest_constants() + // .get_additional_os_syscall_resources(&top_call_syscalls); + // let resources_from_calls = used_resources + total_syscalls_exeucution_resources; - resources_from_calls.n_steps + used_resources.n_steps } diff --git a/crates/forge/tests/integration/get_current_step.rs b/crates/forge/tests/integration/get_current_vm_step.rs similarity index 82% rename from crates/forge/tests/integration/get_current_step.rs rename to crates/forge/tests/integration/get_current_vm_step.rs index 420a1099c1..1742a26815 100644 --- a/crates/forge/tests/integration/get_current_step.rs +++ b/crates/forge/tests/integration/get_current_vm_step.rs @@ -6,21 +6,21 @@ use test_utils::running_tests::run_test_case; use test_utils::test_case; #[test] -fn test_get_current_step() { +fn test_get_current_vm_step() { let test = test_case!( indoc!( r#" - use snforge_std::testing::get_current_step; + use snforge_std::testing::get_current_vm_step; use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; #[test] fn check_current_step() { - let step_start = get_current_step(); + let step_start = get_current_vm_step(); let contract = declare("HelloStarknet").unwrap().contract_class().clone(); let _ = contract.deploy(@ArrayTrait::new()).unwrap(); - let step_end = get_current_step(); + let step_end = get_current_vm_step(); assert!(step_end > step_start); } diff --git a/crates/forge/tests/integration/mod.rs b/crates/forge/tests/integration/mod.rs index afcda225b2..b321e2b129 100644 --- a/crates/forge/tests/integration/mod.rs +++ b/crates/forge/tests/integration/mod.rs @@ -18,7 +18,7 @@ mod gas; mod generate_random_felt; mod get_available_gas; mod get_class_hash; -mod get_current_step; +mod get_current_vm_step; mod interact_with_state; mod l1_handler_executor; mod message_to_l1; diff --git a/docs/listings/testing_reference/tests/tests.cairo b/docs/listings/testing_reference/tests/tests.cairo index 9416e3a1ef..4ef4f11a3c 100644 --- a/docs/listings/testing_reference/tests/tests.cairo +++ b/docs/listings/testing_reference/tests/tests.cairo @@ -1,4 +1,4 @@ -use snforge_std::testing::get_current_step; +use snforge_std::testing::get_current_vm_step; use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; use testing_reference::{ICounterSafeDispatcher, ICounterSafeDispatcherTrait}; @@ -21,9 +21,9 @@ fn setup() { #[test] fn test_setup_steps() { - let steps_start = get_current_step(); + let steps_start = get_current_vm_step(); setup(); - let steps_end = get_current_step(); + let steps_end = get_current_vm_step(); // Assert that setup used no more than 100 steps assert!(steps_end - steps_start <= 100); diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index b103b57dcf..66a1847713 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -130,7 +130,7 @@ * [signature](appendix/snforge-library/signature.md) * [fuzzable](appendix/snforge-library/fuzzable.md) * [testing](appendix/snforge-library/testing.md) - * [get_current_step](appendix/snforge-library/testing/get_current_step.md) + * [get_current_vm_step](appendix/snforge-library/testing/get_current_vm_step.md) * [`sncast` Commands](appendix/sncast.md) * [common flags](appendix/sncast/common.md) * [account](appendix/sncast/account/account.md) diff --git a/docs/src/appendix/snforge-library/testing.md b/docs/src/appendix/snforge-library/testing.md index 11ccd1c804..a59fc4358e 100644 --- a/docs/src/appendix/snforge-library/testing.md +++ b/docs/src/appendix/snforge-library/testing.md @@ -4,4 +4,4 @@ Module containing functions useful for testing. ## Functions -* [`get_current_step`](./testing/get_current_step.md) +* [`get_current_vm_step`](./testing/get_current_vm_step.md) diff --git a/docs/src/appendix/snforge-library/testing/get_current_step.md b/docs/src/appendix/snforge-library/testing/get_current_vm_step.md similarity index 95% rename from docs/src/appendix/snforge-library/testing/get_current_step.md rename to docs/src/appendix/snforge-library/testing/get_current_vm_step.md index ac055fcd85..1ba5c5c3b9 100644 --- a/docs/src/appendix/snforge-library/testing/get_current_step.md +++ b/docs/src/appendix/snforge-library/testing/get_current_vm_step.md @@ -1,9 +1,9 @@ -# `get_current_step` +# `get_current_vm_step` Gets the current step from Cairo VM during test execution. ```rust -fn get_current_step() -> u32; +fn get_current_vm_step() -> u32; ``` ## Example diff --git a/snforge_std/src/testing.cairo b/snforge_std/src/testing.cairo index 27b41449c6..2b8a3801d5 100644 --- a/snforge_std/src/testing.cairo +++ b/snforge_std/src/testing.cairo @@ -1,6 +1,6 @@ use crate::cheatcode::execute_cheatcode_and_deserialize; /// Gets the current step from Cairo VM during test execution -pub fn get_current_step() -> u32 { - execute_cheatcode_and_deserialize::<'get_current_step', u32>(array![].span()) +pub fn get_current_vm_step() -> u32 { + execute_cheatcode_and_deserialize::<'get_current_vm_step', u32>(array![].span()) } From d84fe26981bbfd9ec19a102beda552deb5eb1495 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Thu, 6 Nov 2025 12:17:52 +0100 Subject: [PATCH 23/33] Rename test --- crates/forge/tests/integration/get_current_vm_step.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/tests/integration/get_current_vm_step.rs b/crates/forge/tests/integration/get_current_vm_step.rs index 1742a26815..e5f2b6f6f8 100644 --- a/crates/forge/tests/integration/get_current_vm_step.rs +++ b/crates/forge/tests/integration/get_current_vm_step.rs @@ -14,7 +14,7 @@ fn test_get_current_vm_step() { use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; #[test] - fn check_current_step() { + fn check_current_vm_step() { let step_start = get_current_vm_step(); let contract = declare("HelloStarknet").unwrap().contract_class().clone(); From b94c072ffd6dcd65fc26f288375b1286145d4185 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Thu, 6 Nov 2025 12:27:32 +0100 Subject: [PATCH 24/33] Fix imports --- crates/forge/tests/integration/get_current_vm_step.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/forge/tests/integration/get_current_vm_step.rs b/crates/forge/tests/integration/get_current_vm_step.rs index e5f2b6f6f8..07e4b4329a 100644 --- a/crates/forge/tests/integration/get_current_vm_step.rs +++ b/crates/forge/tests/integration/get_current_vm_step.rs @@ -1,9 +1,9 @@ +use crate::utils::runner::{Contract, assert_passed}; +use crate::utils::running_tests::run_test_case; +use crate::utils::test_case; use forge_runner::forge_config::ForgeTrackedResource; use indoc::indoc; use std::path::Path; -use test_utils::runner::{Contract, assert_passed}; -use test_utils::running_tests::run_test_case; -use test_utils::test_case; #[test] fn test_get_current_vm_step() { From 3a999a8d19222305b92546662674e81b46335fc0 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Thu, 6 Nov 2025 12:50:03 +0100 Subject: [PATCH 25/33] Remove comments --- .../runtime_extensions/forge_runtime_extension/mod.rs | 9 +-------- crates/forge/tests/integration/get_current_vm_step.rs | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs index 435ffac760..71274b9315 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -884,10 +884,7 @@ pub fn get_all_used_resources( } } -fn calculate_vm_steps_from_calls( - top_call: &Rc>, - // top_call_syscalls: &SyscallUsageMap, -) -> usize { +fn calculate_vm_steps_from_calls(top_call: &Rc>) -> usize { // Resources from inner calls already include syscall resources used in them let used_resources = &top_call @@ -900,9 +897,5 @@ fn calculate_vm_steps_from_calls( } CallTraceNode::DeployWithoutConstructor => acc, }); - // let total_syscalls_exeucution_resources = &VersionedConstants::latest_constants() - // .get_additional_os_syscall_resources(&top_call_syscalls); - // let resources_from_calls = used_resources + total_syscalls_exeucution_resources; - used_resources.n_steps } diff --git a/crates/forge/tests/integration/get_current_vm_step.rs b/crates/forge/tests/integration/get_current_vm_step.rs index 07e4b4329a..854c58a332 100644 --- a/crates/forge/tests/integration/get_current_vm_step.rs +++ b/crates/forge/tests/integration/get_current_vm_step.rs @@ -33,7 +33,7 @@ fn test_get_current_vm_step() { .unwrap() ); - let result = run_test_case(&test, ForgeTrackedResource::SierraGas); + let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); assert_passed(&result); } From 48c9e688eee604e03eb7fb18aa8cb302f49bb74a Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Thu, 6 Nov 2025 13:01:25 +0100 Subject: [PATCH 26/33] Add comment --- .../src/runtime_extensions/forge_runtime_extension/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs index 71274b9315..a35fac6c79 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -555,6 +555,10 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { Ok(CheatcodeHandlingResult::from_serializable(())) } "get_current_vm_step" => { + // Each contract call is executed in separate VM, hence all VM steps + // are calculated as sum of steps from calls + current VM steps. + // Since syscalls are added to VM resources after the execution, we need + // to include them manually here. let top_call = extended_runtime .extended_runtime .extension From 7f6a893aee286f4944573882fd134860687fad4c Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Fri, 7 Nov 2025 15:52:26 +0100 Subject: [PATCH 27/33] Change variable name --- .../src/runtime_extensions/forge_runtime_extension/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs index a35fac6c79..2d51e9a532 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -566,7 +566,7 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { .trace_data .current_call_stack .top(); - let vm_steps_from_calls = calculate_vm_steps_from_calls(&top_call); + let vm_steps_from_inner_calls = calculate_vm_steps_from_calls(&top_call); let top_call_syscalls = &extended_runtime .extended_runtime .extended_runtime @@ -577,7 +577,7 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> { .get_additional_os_syscall_resources(top_call_syscalls) .n_steps; let total_vm_steps = - vm_steps_from_calls + vm_steps_from_syscalls + vm.get_current_step(); + vm_steps_from_inner_calls + vm_steps_from_syscalls + vm.get_current_step(); Ok(CheatcodeHandlingResult::from_serializable(total_vm_steps)) } From 195544a5df8f588309867fa3341d7ceb914c2e41 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Mon, 10 Nov 2025 16:31:00 +0100 Subject: [PATCH 28/33] Add assertions for specific step values in `test_get_current_vm_step` --- .../tests/integration/get_current_vm_step.rs | 52 +++++++++++++++++-- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/crates/forge/tests/integration/get_current_vm_step.rs b/crates/forge/tests/integration/get_current_vm_step.rs index 854c58a332..773500cddf 100644 --- a/crates/forge/tests/integration/get_current_vm_step.rs +++ b/crates/forge/tests/integration/get_current_vm_step.rs @@ -10,19 +10,61 @@ fn test_get_current_vm_step() { let test = test_case!( indoc!( r#" + use jjj::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; use snforge_std::testing::get_current_vm_step; use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; + + const STEPS_MARGIN: u32 = 250; + + // 1173 = cost of 1 deploy syscall without calldata + const DEPLOY_SYSCALL_STEPS: u32 = 1173; + + // 903 = steps of 1 call contract syscall + const CALL_CONTRACT_SYSCALL_STEPS: u32 = 903; + + // 90 = steps of 1 call contract syscall + const STORAGE_READ_SYSCALL_STEPS: u32 = 90; + + const TOTAL_GET_BALANCE_CALL_STEPS: u32 = CALL_CONTRACT_SYSCALL_STEPS + + STORAGE_READ_SYSCALL_STEPS + + 29; // ~29 comes from VM + #[test] fn check_current_vm_step() { - let step_start = get_current_vm_step(); + let contract = declare("HelloStarknet").unwrap().contract_class(); + let step_a = get_current_vm_step(); + + let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); + + // Step after calling deploy syscall from (as top call) + let step_b = get_current_vm_step(); + + let expected_lower = DEPLOY_SYSCALL_STEPS + step_a - STEPS_MARGIN; + let expected_upper = DEPLOY_SYSCALL_STEPS + step_a + STEPS_MARGIN; + assert!( + expected_lower <= step_b && step_b <= expected_upper, + "step_b ({step_b}) not in [{expected_lower}, {expected_upper}]", + ); - let contract = declare("HelloStarknet").unwrap().contract_class().clone(); - let _ = contract.deploy(@ArrayTrait::new()).unwrap(); + let dispatcher = IHelloStarknetDispatcher { contract_address }; - let step_end = get_current_vm_step(); + // call get_balance 5 times to accumulate some steps + let _balance = dispatcher.get_balance(); + let _balance = dispatcher.get_balance(); + let _balance = dispatcher.get_balance(); + let _balance = dispatcher.get_balance(); + let _balance = dispatcher.get_balance(); - assert!(step_end > step_start); + // Step after calling call_contract syscall (as top call) and + // storage read syscall (as inner call) + let step_c = get_current_vm_step(); + let expected_lower = step_b + (TOTAL_GET_BALANCE_CALL_STEPS) * 5 - STEPS_MARGIN; + let expected_upper = step_b + (TOTAL_GET_BALANCE_CALL_STEPS) * 5 + STEPS_MARGIN; + assert!( + expected_lower <= step_c && step_c <= expected_upper, + "step_c ({step_c}) not in [{expected_lower}, {expected_upper}]", + ); } "# ), From ec48ca9203247b7890804f0e2402bf528298dfba Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Mon, 10 Nov 2025 16:57:58 +0100 Subject: [PATCH 29/33] Fix import --- crates/forge/tests/integration/get_current_vm_step.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/forge/tests/integration/get_current_vm_step.rs b/crates/forge/tests/integration/get_current_vm_step.rs index 773500cddf..7fc5a5b9ae 100644 --- a/crates/forge/tests/integration/get_current_vm_step.rs +++ b/crates/forge/tests/integration/get_current_vm_step.rs @@ -10,7 +10,6 @@ fn test_get_current_vm_step() { let test = test_case!( indoc!( r#" - use jjj::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; use snforge_std::testing::get_current_vm_step; use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; From 4aab58a96de7babd64fe4ad62c0809e47bf69916 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Mon, 10 Nov 2025 17:12:27 +0100 Subject: [PATCH 30/33] Fix test --- crates/forge/tests/integration/get_current_vm_step.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/forge/tests/integration/get_current_vm_step.rs b/crates/forge/tests/integration/get_current_vm_step.rs index 7fc5a5b9ae..a8c15a10cf 100644 --- a/crates/forge/tests/integration/get_current_vm_step.rs +++ b/crates/forge/tests/integration/get_current_vm_step.rs @@ -13,7 +13,6 @@ fn test_get_current_vm_step() { use snforge_std::testing::get_current_vm_step; use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; - const STEPS_MARGIN: u32 = 250; // 1173 = cost of 1 deploy syscall without calldata @@ -65,6 +64,11 @@ fn test_get_current_vm_step() { "step_c ({step_c}) not in [{expected_lower}, {expected_upper}]", ); } + + #[starknet::interface] + trait IHelloStarknet { + fn get_balance(self: @TContractState) -> felt252; + } "# ), Contract::from_code_path( From d17f252a80a06517cf49c4473e62a1ba0e2f13b4 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Mon, 10 Nov 2025 19:16:46 +0100 Subject: [PATCH 31/33] Improve test for `get_current_vm_step` --- .../tests/data/contracts/hello_starknet.cairo | 29 ++++++++++- .../tests/integration/get_current_vm_step.rs | 51 +++++++++---------- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/crates/forge/tests/data/contracts/hello_starknet.cairo b/crates/forge/tests/data/contracts/hello_starknet.cairo index 2ab543f589..a8e4c76254 100644 --- a/crates/forge/tests/data/contracts/hello_starknet.cairo +++ b/crates/forge/tests/data/contracts/hello_starknet.cairo @@ -2,6 +2,12 @@ trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; + fn call_other_contract( + self: @TContractState, + other_contract_address: felt252, + selector: felt252, + calldata: Option>, + ) -> Span; fn do_a_panic(self: @TContractState); fn do_a_panic_with(self: @TContractState, panic_data: Array); fn do_a_panic_with_bytearray(self: @TContractState); @@ -10,6 +16,7 @@ trait IHelloStarknet { #[starknet::contract] mod HelloStarknet { use array::ArrayTrait; + use starknet::{SyscallResultTrait, syscalls}; #[storage] struct Storage { @@ -28,6 +35,23 @@ mod HelloStarknet { self.balance.read() } + fn call_other_contract( + self: @ContractState, + other_contract_address: felt252, + selector: felt252, + calldata: Option>, + ) -> Span { + syscalls::call_contract_syscall( + other_contract_address.try_into().unwrap(), + selector, + match calldata { + Some(data) => data.span(), + None => array![].span(), + }, + ) + .unwrap_syscall() + } + // Panics fn do_a_panic(self: @ContractState) { let mut arr = ArrayTrait::new(); @@ -43,7 +67,10 @@ mod HelloStarknet { // Panics with a bytearray fn do_a_panic_with_bytearray(self: @ContractState) { - assert!(false, "This is a very long\n and multiline message that is certain to fill the buffer"); + assert!( + false, + "This is a very long\n and multiline message that is certain to fill the buffer", + ); } } } diff --git a/crates/forge/tests/integration/get_current_vm_step.rs b/crates/forge/tests/integration/get_current_vm_step.rs index a8c15a10cf..cfde2ec430 100644 --- a/crates/forge/tests/integration/get_current_vm_step.rs +++ b/crates/forge/tests/integration/get_current_vm_step.rs @@ -13,7 +13,8 @@ fn test_get_current_vm_step() { use snforge_std::testing::get_current_vm_step; use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; - const STEPS_MARGIN: u32 = 250; + + const STEPS_MARGIN: u32 = 100; // 1173 = cost of 1 deploy syscall without calldata const DEPLOY_SYSCALL_STEPS: u32 = 1173; @@ -24,51 +25,49 @@ fn test_get_current_vm_step() { // 90 = steps of 1 call contract syscall const STORAGE_READ_SYSCALL_STEPS: u32 = 90; - const TOTAL_GET_BALANCE_CALL_STEPS: u32 = CALL_CONTRACT_SYSCALL_STEPS - + STORAGE_READ_SYSCALL_STEPS - + 29; // ~29 comes from VM - #[test] fn check_current_vm_step() { let contract = declare("HelloStarknet").unwrap().contract_class(); let step_a = get_current_vm_step(); - let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); - - // Step after calling deploy syscall from (as top call) + let (contract_address_a, _) = contract.deploy(@ArrayTrait::new()).unwrap(); + let (contract_address_b, _) = contract.deploy(@ArrayTrait::new()).unwrap(); + // Sycalls between step_a and step_b: + // top call: 2 x deploy syscall + // inner call: -/- let step_b = get_current_vm_step(); - let expected_lower = DEPLOY_SYSCALL_STEPS + step_a - STEPS_MARGIN; - let expected_upper = DEPLOY_SYSCALL_STEPS + step_a + STEPS_MARGIN; + let expected_steps_taken = 2 * DEPLOY_SYSCALL_STEPS + 130; // 130 are steps from VM + let expected_lower = expected_steps_taken + step_a - STEPS_MARGIN; + let expected_upper = expected_steps_taken + step_a + STEPS_MARGIN; assert!( expected_lower <= step_b && step_b <= expected_upper, "step_b ({step_b}) not in [{expected_lower}, {expected_upper}]", ); - let dispatcher = IHelloStarknetDispatcher { contract_address }; + let dispatcher_a = IHelloStarknetDispatcher { contract_address: contract_address_a }; - // call get_balance 5 times to accumulate some steps - let _balance = dispatcher.get_balance(); - let _balance = dispatcher.get_balance(); - let _balance = dispatcher.get_balance(); - let _balance = dispatcher.get_balance(); - let _balance = dispatcher.get_balance(); + // contract A calls `get_balance` from contract B + let _balance = dispatcher_a + .call_other_contract( + contract_address_b.try_into().unwrap(), selector!("get_balance"), None, + ); - // Step after calling call_contract syscall (as top call) and - // storage read syscall (as inner call) + // Sycalls between step_b and step_c: + // top call: 1 x call contract syscall + // inner calls: 1 x storage read syscall, 1 x call contract syscall let step_c = get_current_vm_step(); - let expected_lower = step_b + (TOTAL_GET_BALANCE_CALL_STEPS) * 5 - STEPS_MARGIN; - let expected_upper = step_b + (TOTAL_GET_BALANCE_CALL_STEPS) * 5 + STEPS_MARGIN; + + let expected_steps_taken = 2 * CALL_CONTRACT_SYSCALL_STEPS + + 1 * STORAGE_READ_SYSCALL_STEPS + + 277; // 277 are steps from VM + let expected_lower = expected_steps_taken + step_b - STEPS_MARGIN; + let expected_upper = expected_steps_taken + step_b + STEPS_MARGIN; assert!( expected_lower <= step_c && step_c <= expected_upper, "step_c ({step_c}) not in [{expected_lower}, {expected_upper}]", ); } - - #[starknet::interface] - trait IHelloStarknet { - fn get_balance(self: @TContractState) -> felt252; - } "# ), Contract::from_code_path( From d37a05b31786eab2324748d628b460bcdc8262b7 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Mon, 10 Nov 2025 19:27:08 +0100 Subject: [PATCH 32/33] Fix test --- crates/forge/tests/integration/get_current_vm_step.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/forge/tests/integration/get_current_vm_step.rs b/crates/forge/tests/integration/get_current_vm_step.rs index cfde2ec430..33eca5bcc0 100644 --- a/crates/forge/tests/integration/get_current_vm_step.rs +++ b/crates/forge/tests/integration/get_current_vm_step.rs @@ -68,6 +68,17 @@ fn test_get_current_vm_step() { "step_c ({step_c}) not in [{expected_lower}, {expected_upper}]", ); } + + #[starknet::interface] + pub trait IHelloStarknet { + fn get_balance(self: @TContractState) -> felt252; + fn call_other_contract( + self: @TContractState, + other_contract_address: felt252, + selector: felt252, + calldata: Option>, + ) -> Span; + } "# ), Contract::from_code_path( From 13f1af4f02ad6fb2d399b62818b0e6b7b4aad7bc Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Mon, 10 Nov 2025 19:49:23 +0100 Subject: [PATCH 33/33] Reduce explanation --- .../appendix/snforge-library/testing/get_current_vm_step.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/appendix/snforge-library/testing/get_current_vm_step.md b/docs/src/appendix/snforge-library/testing/get_current_vm_step.md index 1ba5c5c3b9..e071e86ebb 100644 --- a/docs/src/appendix/snforge-library/testing/get_current_vm_step.md +++ b/docs/src/appendix/snforge-library/testing/get_current_vm_step.md @@ -8,13 +8,13 @@ fn get_current_vm_step() -> u32; ## Example -Let's consider a simple counter contract that increments a value stored in its storage. +Contract code: ```rust {{#include ../../../../listings/testing_reference/src/lib.cairo}} ``` -Now, let's define `setup` function which deploys this contract and increments the counter a few times and assert that `setup` function does not exceed a certain number of steps during its execution. This is particularly useful for performance testing and ensuring that our setup logic remains efficient. +Test code: ```rust {{#include ../../../../listings/testing_reference/tests/tests.cairo}} @@ -46,4 +46,4 @@ Failures:
-As we can see, the test fails because the `setup` function exceeded the allowed number of steps (100 in this case). +The test fails because the `setup` function exceeded the allowed number of steps (100 in this case).