-
Notifications
You must be signed in to change notification settings - Fork 240
Add get_current_vm_step
#3868
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add get_current_vm_step
#3868
Changes from all commits
b55a950
8a8a80f
073c95f
77dd8b9
5672903
53494da
5dd5177
ab287a4
d3ceb43
7af1256
feed6d5
2afa790
9b7e7a2
30d1404
b5c1de0
57deb3b
a674c9d
8b22af2
7254799
86b73b0
660ec59
65c6297
c691cb1
113bc5f
cbdd37e
d84fe26
e4b5c13
b94c072
3a999a8
48c9e68
7f6a893
195544a
ec48ca9
4aab58a
d17f252
d37a05b
13f1af4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add some kind of sanity check where we assert specific value of used steps (or a delta of steps)? So we detect eventually regressions if these change significantly.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see that we have a test below that does this for contracts but let's have one for top level calls as well.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've extended
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, let's add a test where we have inner calls and these inner calls do some expensive computation (not just increment some counter, but call some more expensive syscalls). Check if steps value is "large enough" there.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should be able to roughly estimate the steps value from fee mechanism docs https://docs.starknet.io/learn/protocol/fees#vm-resources similarly as we do in our gas integration tests and check if values in the test are roughly withing the expected range.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Responded in comment above. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| 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; | ||
|
|
||
| #[test] | ||
| fn test_get_current_vm_step() { | ||
| let test = test_case!( | ||
| indoc!( | ||
| r#" | ||
| use snforge_std::testing::get_current_vm_step; | ||
| use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; | ||
|
|
||
|
|
||
| const STEPS_MARGIN: u32 = 100; | ||
|
|
||
| // 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; | ||
|
|
||
| #[test] | ||
| fn check_current_vm_step() { | ||
| let contract = declare("HelloStarknet").unwrap().contract_class(); | ||
| let step_a = get_current_vm_step(); | ||
|
|
||
| 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_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_a = IHelloStarknetDispatcher { contract_address: contract_address_a }; | ||
|
|
||
| // 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, | ||
| ); | ||
|
|
||
| // 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_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] | ||
| pub trait IHelloStarknet<TContractState> { | ||
| fn get_balance(self: @TContractState) -> felt252; | ||
| fn call_other_contract( | ||
| self: @TContractState, | ||
| other_contract_address: felt252, | ||
| selector: felt252, | ||
| calldata: Option<Array<felt252>>, | ||
| ) -> Span<felt252>; | ||
| } | ||
| "# | ||
| ), | ||
| Contract::from_code_path( | ||
| "HelloStarknet".to_string(), | ||
| Path::new("tests/data/contracts/hello_starknet.cairo"), | ||
| ) | ||
| .unwrap() | ||
| ); | ||
|
|
||
| let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); | ||
|
|
||
| assert_passed(&result); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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]] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| #[starknet::interface] | ||
| pub trait ICounter<TContractState> { | ||
| fn increment(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<ContractState> { | ||
| fn increment(ref self: ContractState) { | ||
| let current_value = self.i.read(); | ||
| self.i.write(current_value + 1); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| use snforge_std::testing::get_current_vm_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_vm_step(); | ||
| setup(); | ||
| let steps_end = get_current_vm_step(); | ||
|
|
||
| // Assert that setup used no more than 100 steps | ||
| assert!(steps_end - steps_start <= 100); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # `testing` Module | ||
|
|
||
| Module containing functions useful for testing. | ||
|
|
||
| ## Functions | ||
|
|
||
| * [`get_current_vm_step`](./testing/get_current_vm_step.md) |
Uh oh!
There was an error while loading. Please reload this page.