diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 00a3727630..1c66883311 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,89 +1,40 @@ # Contribution Guideline Starknet Foundry is under active development and is open for contributions! -Want to get started? -Grab any [issue](https://github.com/foundry-rs/starknet-foundry/issues) labeled with `good-first-issue`! -Need some guidance? -Reach out to other developers on [Telegram](https://t.me/+d8ULaPxeRqlhMDNk) or open -a [GitHub discussion](https://github.com/foundry-rs/starknet-foundry/discussions)! +## Opening an issue -### Environment setup - -See [development guide](https://foundry-rs.github.io/starknet-foundry/development/environment-setup.html) in Starknet -Foundry book for environment setup. - -### Running Tests and Checks - -> ⚠️ Make sure you run `./scripts/install_devnet.sh` -> and then set [Scarb](https://docs.swmansion.com/scarb/) version -> [compatible](https://github.com/foundry-rs/starknet-foundry/releases) with both `snforge` and `sncast` -> after setting up the development environment, otherwise the tests will fail. - -Before creating a contribution, make sure your code passes the following checks - -```shell -$ cargo test -$ cargo fmt --check -$ cargo lint -$ typos -``` - -Otherwise, it won't be possible to merge your contribution. - -You can also run a specific set of tests, by directly running `cargo test`. +If you think something doesn't work or something is missing please open an issue! This way we can address this problem +and make Starknet Foundry better! +Before opening an issue, it is always a good idea to search existing +[issues](https://github.com/foundry-rs/starknet-foundry/issues) and verify if a similar one doesn't already exist. -For forge tests, make sure you are in `crates/forge` directory: -```shell -$ cargo test --lib # runs all unit tests -$ cargo test integration # runs all integration tests -$ cargo test e2e # runs all e2e tests -``` - -Similarly, to run sncast tests make sure you are in `crates/sncast` directory: - -```shell -$ cargo test --lib # runs lib unit tests -$ cargo test helpers # runs helpers unit tests -$ cargo test integration # runs all integration tests -$ cargo test e2e # runs all e2e tests -``` +## Contributing -Or to run cheatnet tests make sure you are in `crates/cheatnet` directory: +### Environment setup -```shell -$ cargo test --lib # runs lib unit tests -$ cargo test cheatcodes # runs all cheatcodes tests -$ cargo test starknet # runs all starknet tests -``` +See [development guide](https://foundry-rs.github.io/starknet-foundry/development/environment-setup.html) in Starknet +Foundry book for environment setup. -## Contributing +### Selecting an issue +If you are a first time contributor pick up any issue labeled as `good-first-issue`. Write a comment that you would like to +work on it and we will assign it to you. Need some guidance? Reach out to other developers on [Telegram](https://t.me/+d8ULaPxeRqlhMDNk). -Before you open a pull request, it is always a good idea to search -the [issues](https://github.com/foundry-rs/starknet-foundry/issues) and verify if the feature you would like -to add hasn't been already discussed. -We also appreciate creating a feature request before making a contribution, so it can be discussed before you get to -work. +If you are a more experienced Starknet Foundry contributor you can pick any issue labeled as `help-wanted`. Make sure to discuss the details with the core team beforehand. ### Writing Tests Please make sure the feature you are implementing is thoroughly tested with automatic tests. You can check existing tests in the repository to see the recommended approach to testing. -### Breaking Changes - -If the change you are introducing is changing or breaking the behavior of any already existing features, make sure to -include that information in the pull request description. - ### Pull Request Size Try to make your pull request self-contained, only introducing the necessary changes. If your feature is complicated, consider splitting the changes into meaningful parts and introducing them as separate pull requests. -While creating large pull requests usually will not prevent them from being merged, it may significantly increase review -time and increase the risk of complicated to resolve merge conflicts. +Creating very large pull requests may significantly increase review time or even prevent them from being merged. ### Contributions Related to Spelling and Grammar diff --git a/Cargo.lock b/Cargo.lock index 4bdd3dc5fa..e68b0060d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5263,7 +5263,7 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "trace-data" version = "0.2.0" -source = "git+https://github.com/software-mansion/cairo-profiler/?rev=3af0782#3af07829cca6ad36f753418b368693f5698264d6" +source = "git+https://github.com/software-mansion/cairo-profiler/?rev=0b9dafb#0b9dafbfdcc9237904738fece4e6c054323b528d" dependencies = [ "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 5a96e6752c..a88f0c4b32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,7 @@ regex = "1.10.4" serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.115" starknet = { git = "https://github.com/xJonathanLEI/starknet-rs", rev = "d980869" } -trace-data = { git = "https://github.com/software-mansion/cairo-profiler/", rev = "3af0782" } +trace-data = { git = "https://github.com/software-mansion/cairo-profiler/", rev = "0b9dafb" } tempfile = "3.10.1" thiserror = "1.0.58" ctor = "0.2.7" 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 e48fb39a77..ef02a93967 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 @@ -2,7 +2,7 @@ use std::cell::RefCell; use super::cairo1_execution::execute_entry_point_call_cairo1; use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::deprecated::cairo0_execution::execute_entry_point_call_cairo0; use crate::runtime_extensions::call_to_blockifier_runtime_extension::CheatnetState; -use crate::state::{CallTrace, CheatStatus}; +use crate::state::{CallTrace, CallTraceNode, CheatStatus}; use blockifier::execution::call_info::{CallExecution, Retdata}; use blockifier::{ execution::{ @@ -220,6 +220,9 @@ pub fn execute_constructor_entry_point( let contract_class = state.get_compiled_contract_class(ctor_context.class_hash)?; let Some(constructor_selector) = contract_class.constructor_selector() else { // Contract has no constructor. + cheatnet_state + .trace_data + .add_deploy_without_constructor_node(); return handle_empty_constructor(ctor_context, calldata, remaining_gas); }; @@ -278,18 +281,22 @@ fn mocked_call_info(call: CallEntryPoint, ret_data: Vec) -> CallInfo fn aggregate_nested_syscall_counters(trace: &Rc>) -> SyscallCounter { let mut result = SyscallCounter::new(); - for nested_call in &trace.borrow().nested_calls { - let sub_trace_counter = aggregate_syscall_counters(nested_call); - result = sum_syscall_counters(result, &sub_trace_counter); + for nested_call_node in &trace.borrow().nested_calls { + if let CallTraceNode::EntryPointCall(nested_call) = nested_call_node { + let sub_trace_counter = aggregate_syscall_counters(nested_call); + result = sum_syscall_counters(result, &sub_trace_counter); + } } result } fn aggregate_syscall_counters(trace: &Rc>) -> SyscallCounter { let mut result = trace.borrow().used_syscalls.clone(); - for nested_call in &trace.borrow().nested_calls { - let sub_trace_counter = aggregate_nested_syscall_counters(nested_call); - result = sum_syscall_counters(result, &sub_trace_counter); + for nested_call_node in &trace.borrow().nested_calls { + if let CallTraceNode::EntryPointCall(nested_call) = nested_call_node { + let sub_trace_counter = aggregate_nested_syscall_counters(nested_call); + result = sum_syscall_counters(result, &sub_trace_counter); + } } result } diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/rpc.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/rpc.rs index c553c4a6b8..2dab9df2f8 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/rpc.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/rpc.rs @@ -39,7 +39,7 @@ pub enum CallResult { Failure(CallFailure), } -/// Enum representing possible call failure and its' type. +/// Enum representing possible call failure and its type. /// `Panic` - Recoverable, meant to be caught by the user. /// `Error` - Unrecoverable, equivalent of panic! in rust. #[derive(Debug, Clone, Serialize, Deserialize)] 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 ee2a3a32a1..d0b5696712 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -1,4 +1,5 @@ use self::contracts_data::ContractsData; +use crate::state::CallTraceNode; use crate::{ runtime_extensions::{ call_to_blockifier_runtime_extension::{ @@ -686,10 +687,16 @@ fn handle_deploy_result( fn serialize_call_trace(call_trace: &CallTrace, output: &mut Vec) { serialize_call_entry_point(&call_trace.entry_point, output); - output.push(Felt252::from(call_trace.nested_calls.len())); + let visible_calls: Vec<_> = call_trace + .nested_calls + .iter() + .filter_map(CallTraceNode::extract_entry_point_call) + .collect(); + + output.push(Felt252::from(visible_calls.len())); - for call_trace in &call_trace.nested_calls { - serialize_call_trace(&call_trace.borrow(), output); + for call_trace_node in visible_calls { + serialize_call_trace(&call_trace_node.borrow(), output); } serialize_call_result(&call_trace.result, output); @@ -737,7 +744,7 @@ fn serialize_call_result(call_result: &CallResult, output: &mut Vec) { serialize_failure_data(0, panic_data.iter().cloned(), panic_data.len(), output); } CallFailure::Error { msg } => { - let data = ByteArray::from(msg.as_str()).serialize_with_magic(); + let data = ByteArray::from(msg.as_str()).serialize_no_magic(); let len = data.len(); serialize_failure_data(1, data, len, output); } @@ -820,6 +827,7 @@ pub fn update_top_call_execution_resources(runtime: &mut ForgeRuntime) { let nested_calls_syscalls = top_call .nested_calls .iter() + .filter_map(CallTraceNode::extract_entry_point_call) .fold(SyscallCounter::new(), |syscalls, trace| { sum_syscall_counters(syscalls, &trace.borrow().used_syscalls) }); diff --git a/crates/cheatnet/src/state.rs b/crates/cheatnet/src/state.rs index 15f4b11f40..58265c84ba 100644 --- a/crates/cheatnet/src/state.rs +++ b/crates/cheatnet/src/state.rs @@ -155,11 +155,43 @@ pub struct CallTrace { pub used_execution_resources: ExecutionResources, pub used_l1_resources: L1Resources, pub used_syscalls: SyscallCounter, - pub nested_calls: Vec>>, + pub nested_calls: Vec, pub result: CallResult, pub vm_trace: Option>, } +impl CallTrace { + fn default_successful_call() -> Self { + Self { + entry_point: Default::default(), + used_execution_resources: Default::default(), + used_l1_resources: Default::default(), + used_syscalls: Default::default(), + nested_calls: vec![], + result: CallResult::Success { ret_data: vec![] }, + vm_trace: None, + } + } +} + +/// Enum representing node of a trace of a call. +#[derive(Clone)] +pub enum CallTraceNode { + EntryPointCall(Rc>), + DeployWithoutConstructor, +} + +impl CallTraceNode { + #[must_use] + pub fn extract_entry_point_call(&self) -> Option<&Rc>> { + if let CallTraceNode::EntryPointCall(trace) = self { + Some(trace) + } else { + None + } + } +} + #[derive(Clone)] struct CallStackElement { // when we exit the call we use it to calculate resources used by the call @@ -259,12 +291,7 @@ impl Default for CheatnetState { test_code_entry_point.class_hash = Some(class_hash!(TEST_CONTRACT_CLASS_HASH)); let test_call = Rc::new(RefCell::new(CallTrace { entry_point: test_code_entry_point, - used_execution_resources: Default::default(), - used_l1_resources: Default::default(), - used_syscalls: Default::default(), - nested_calls: vec![], - result: CallResult::Success { ret_data: vec![] }, - vm_trace: None, + ..CallTrace::default_successful_call() })); Self { rolled_contracts: Default::default(), @@ -396,19 +423,14 @@ impl TraceData { ) { let new_call = Rc::new(RefCell::new(CallTrace { entry_point, - used_execution_resources: Default::default(), - used_l1_resources: Default::default(), - used_syscalls: Default::default(), - nested_calls: vec![], - result: CallResult::Success { ret_data: vec![] }, - vm_trace: None, + ..CallTrace::default_successful_call() })); let current_call = self.current_call_stack.top(); current_call .borrow_mut() .nested_calls - .push(new_call.clone()); + .push(CallTraceNode::EntryPointCall(new_call.clone())); self.current_call_stack .push(new_call, resources_used_before_call, cheated_data); @@ -446,6 +468,15 @@ impl TraceData { last_call.result = result; last_call.vm_trace = vm_trace; } + + pub fn add_deploy_without_constructor_node(&mut self) { + let current_call = self.current_call_stack.top(); + + current_call + .borrow_mut() + .nested_calls + .push(CallTraceNode::DeployWithoutConstructor); + } } fn get_cheat_for_contract( diff --git a/crates/forge-runner/src/build_trace_data.rs b/crates/forge-runner/src/build_trace_data.rs index 609369656f..7456ec41db 100644 --- a/crates/forge-runner/src/build_trace_data.rs +++ b/crates/forge-runner/src/build_trace_data.rs @@ -12,7 +12,7 @@ use cairo_vm::vm::runners::cairo_runner::ExecutionResources; use cairo_vm::vm::trace::trace_entry::TraceEntry; use cheatnet::constants::{TEST_CONTRACT_CLASS_HASH, TEST_ENTRY_POINT_SELECTOR}; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; -use cheatnet::state::CallTrace; +use cheatnet::state::{CallTrace, CallTraceNode}; use conversions::IntoConv; use itertools::Itertools; use starknet::core::utils::get_selector_from_name; @@ -22,7 +22,7 @@ use starknet_api::deprecated_contract_class::EntryPointType; use starknet_api::hash::StarkHash; use trace_data::{ CallEntryPoint as ProfilerCallEntryPoint, CallTrace as ProfilerCallTrace, - CallType as ProfilerCallType, ContractAddress, + CallTraceNode as ProfilerCallTraceNode, CallType as ProfilerCallType, ContractAddress, DeprecatedSyscallSelector as ProfilerDeprecatedSyscallSelector, EntryPointSelector, EntryPointType as ProfilerEntryPointType, ExecutionResources as ProfilerExecutionResources, TraceEntry as ProfilerTraceEntry, VmExecutionResources, @@ -55,12 +55,24 @@ pub fn build_profiler_call_trace( nested_calls: value .nested_calls .iter() - .map(|c| build_profiler_call_trace(c, contracts_data)) + .map(|c| build_profiler_call_trace_node(c, contracts_data)) .collect(), vm_trace, } } +fn build_profiler_call_trace_node( + value: &CallTraceNode, + contracts_data: &ContractsData, +) -> ProfilerCallTraceNode { + match value { + CallTraceNode::EntryPointCall(trace) => { + ProfilerCallTraceNode::EntryPointCall(build_profiler_call_trace(trace, contracts_data)) + } + CallTraceNode::DeployWithoutConstructor => ProfilerCallTraceNode::DeployWithoutConstructor, + } +} + #[must_use] pub fn build_profiler_execution_resources( execution_resources: ExecutionResources, diff --git a/crates/forge/tests/data/trace/tests/test_trace.cairo b/crates/forge/tests/data/trace/tests/test_trace.cairo index 0ce23922e8..78831eeeec 100644 --- a/crates/forge/tests/data/trace/tests/test_trace.cairo +++ b/crates/forge/tests/data/trace/tests/test_trace.cairo @@ -8,7 +8,7 @@ use trace_info::{ #[test] #[feature("safe_dispatcher")] -fn test_trace_print() { +fn test_trace() { let sc = declare("SimpleContract").unwrap(); let (contract_address_A, _) = sc.deploy(@array![]).unwrap(); diff --git a/crates/forge/tests/e2e/build_profile.rs b/crates/forge/tests/e2e/build_profile.rs index 6b60af0550..2d83025d91 100644 --- a/crates/forge/tests/e2e/build_profile.rs +++ b/crates/forge/tests/e2e/build_profile.rs @@ -2,6 +2,7 @@ use super::common::runner::{setup_package, test_runner}; use forge_runner::profiler_api::PROFILE_DIR; #[test] +#[ignore] // TODO(#1991): remove ignore when new profiler is released fn simple_package_build_profile() { let temp = setup_package("simple_package"); diff --git a/crates/forge/tests/e2e/build_trace_data.rs b/crates/forge/tests/e2e/build_trace_data.rs index a6c3039e19..9830d3c435 100644 --- a/crates/forge/tests/e2e/build_trace_data.rs +++ b/crates/forge/tests/e2e/build_trace_data.rs @@ -1,7 +1,8 @@ use super::common::runner::{setup_package, test_runner}; +use crate::e2e::common::get_trace_from_trace_node; use forge_runner::build_trace_data::{TEST_CODE_CONTRACT_NAME, TEST_CODE_FUNCTION_NAME, TRACE_DIR}; use std::fs; -use trace_data::CallTrace as ProfilerCallTrace; +use trace_data::{CallTrace as ProfilerCallTrace, CallTraceNode as ProfilerCallTraceNode}; #[test] fn simple_package_save_trace() { @@ -50,7 +51,7 @@ fn trace_has_contract_and_function_names() { let trace_data = fs::read_to_string( temp.join(TRACE_DIR) - .join("tests::test_trace::test_trace_print.json"), + .join("tests::test_trace::test_trace.json"), ) .unwrap(); @@ -65,7 +66,7 @@ fn trace_has_contract_and_function_names() { call_trace.entry_point.function_name, Some(String::from(TEST_CODE_FUNCTION_NAME)) ); - assert_contract_and_function_names(&call_trace.nested_calls[0]); + assert_contract_and_function_names(get_trace_from_trace_node(&call_trace.nested_calls[3])); } fn assert_contract_and_function_names(trace: &ProfilerCallTrace) { @@ -79,8 +80,8 @@ fn assert_contract_and_function_names(trace: &ProfilerCallTrace) { Some(String::from("execute_calls")) ); - for sub_trace in &trace.nested_calls { - assert_contract_and_function_names(sub_trace); + for sub_trace_node in &trace.nested_calls { + assert_contract_and_function_names(get_trace_from_trace_node(sub_trace_node)); } } @@ -97,7 +98,7 @@ fn trace_has_vm_trace() { let trace_data = fs::read_to_string( temp.join(TRACE_DIR) - .join("tests::test_trace::test_trace_print.json"), + .join("tests::test_trace::test_trace.json"), ) .unwrap(); @@ -112,7 +113,44 @@ fn assert_vm_trace_exists(trace: &ProfilerCallTrace) { trace.vm_trace.is_some() || trace.entry_point.function_name == Some(String::from("fail")) ); - for sub_trace in &trace.nested_calls { - assert_vm_trace_exists(sub_trace); + for sub_trace_node in &trace.nested_calls { + if let ProfilerCallTraceNode::EntryPointCall(sub_trace) = sub_trace_node { + assert_vm_trace_exists(sub_trace); + } } } + +#[test] +fn trace_has_deploy_with_no_constructor_phantom_nodes() { + let temp = setup_package("trace"); + let snapbox = test_runner(&temp); + + snapbox + .arg("--save-trace-data") + .current_dir(&temp) + .assert() + .success(); + + let trace_data = fs::read_to_string( + temp.join(TRACE_DIR) + .join("tests::test_trace::test_trace.json"), + ) + .unwrap(); + + let call_trace: ProfilerCallTrace = + serde_json::from_str(&trace_data).expect("Failed to parse call_trace"); + + // 3 first calls are deploys with empty constructors + matches!( + call_trace.nested_calls[0], + trace_data::CallTraceNode::DeployWithoutConstructor + ); + matches!( + call_trace.nested_calls[1], + trace_data::CallTraceNode::DeployWithoutConstructor + ); + matches!( + call_trace.nested_calls[2], + trace_data::CallTraceNode::DeployWithoutConstructor + ); +} diff --git a/crates/forge/tests/e2e/common/mod.rs b/crates/forge/tests/e2e/common/mod.rs index 748377a9c8..575b63a851 100644 --- a/crates/forge/tests/e2e/common/mod.rs +++ b/crates/forge/tests/e2e/common/mod.rs @@ -1 +1,11 @@ +use trace_data::{CallTrace as ProfilerCallTrace, CallTraceNode as ProfilerCallTraceNode}; + pub mod runner; + +pub fn get_trace_from_trace_node(trace_node: &ProfilerCallTraceNode) -> &ProfilerCallTrace { + if let ProfilerCallTraceNode::EntryPointCall(trace) = trace_node { + trace + } else { + panic!("Deploy without constructor node was not expected") + } +} diff --git a/crates/forge/tests/e2e/trace_print.rs b/crates/forge/tests/e2e/trace_print.rs index d82c18c757..ca880163d7 100644 --- a/crates/forge/tests/e2e/trace_print.rs +++ b/crates/forge/tests/e2e/trace_print.rs @@ -89,7 +89,7 @@ fn trace_info_print() { ] Call Result: Success: [] - [PASS] tests::test_trace::test_trace_print (gas: [..] + [PASS] tests::test_trace::test_trace (gas: [..] Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out "}, ); diff --git a/crates/forge/tests/e2e/trace_resources.rs b/crates/forge/tests/e2e/trace_resources.rs index 5e256c7c3e..e14067106b 100644 --- a/crates/forge/tests/e2e/trace_resources.rs +++ b/crates/forge/tests/e2e/trace_resources.rs @@ -1,9 +1,10 @@ use super::common::runner::{setup_package, test_runner}; +use crate::e2e::common::get_trace_from_trace_node; use assert_fs::TempDir; use forge_runner::build_trace_data::TRACE_DIR; use std::{collections::HashMap, fs}; use trace_data::{ - CallTrace as ProfilerCallTrace, + CallTrace as ProfilerCallTrace, CallTraceNode as ProfilerCallTraceNode, DeprecatedSyscallSelector::{ CallContract, Deploy, EmitEvent, GetBlockHash, GetExecutionInfo, Keccak, LibraryCall, SendMessageToL1, StorageRead, StorageWrite, @@ -75,8 +76,10 @@ fn check_vm_resources_and_easily_unifiable_syscalls( call_trace: &ProfilerCallTrace, ) -> &ProfilerExecutionResources { let mut child_resources = vec![]; - for call in &call_trace.nested_calls { - child_resources.push(check_vm_resources_and_easily_unifiable_syscalls(call)); + for call_node in &call_trace.nested_calls { + if let trace_data::CallTraceNode::EntryPointCall(call) = call_node { + child_resources.push(check_vm_resources_and_easily_unifiable_syscalls(call)); + } } let mut sum_child_resources = ProfilerExecutionResources::default(); @@ -146,83 +149,86 @@ fn assert_l2_l1_messages(call_trace: &ProfilerCallTrace) { ); } -// When sth fails in the functions below and you didn't change anything in the cairo code it is a BUG. +// When sth fails in the functions below, and you didn't change anything in the cairo code it is a BUG. // If you changed the corresponding cairo code count the expected occurrences of syscalls manually first, then assert them. // TL;DR: DON't mindlessly change numbers to fix the tests if they ever fail. fn check_call(test_call_trace: &ProfilerCallTrace) { assert_not_easily_unifiable_syscalls(test_call_trace, 14, 8, 1); - let regular_call = &test_call_trace.nested_calls[1]; + let regular_call = get_trace_from_trace_node(&test_call_trace.nested_calls[4]); assert_not_easily_unifiable_syscalls(regular_call, 2, 1, 0); - let from_proxy = ®ular_call.nested_calls[0]; + let from_proxy = get_trace_from_trace_node(®ular_call.nested_calls[1]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); - let with_libcall = &test_call_trace.nested_calls[2]; + let with_libcall = get_trace_from_trace_node(&test_call_trace.nested_calls[5]); assert_not_easily_unifiable_syscalls(with_libcall, 2, 0, 1); - let from_proxy = &with_libcall.nested_calls[0]; + let from_proxy = get_trace_from_trace_node(&with_libcall.nested_calls[1]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); - let call_two = &test_call_trace.nested_calls[3]; + let call_two = get_trace_from_trace_node(&test_call_trace.nested_calls[6]); assert_not_easily_unifiable_syscalls(call_two, 3, 2, 0); - let from_proxy = &call_two.nested_calls[0]; + let from_proxy = get_trace_from_trace_node(&call_two.nested_calls[0]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); - let from_proxy_dummy = &call_two.nested_calls[1]; + let from_proxy_dummy = get_trace_from_trace_node(&call_two.nested_calls[2]); assert_not_easily_unifiable_syscalls(from_proxy_dummy, 1, 0, 0); - let from_proxy = &test_call_trace.nested_calls[4]; + let from_proxy = get_trace_from_trace_node(&test_call_trace.nested_calls[7]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); } fn check_deploy(test_call_trace: &ProfilerCallTrace) { assert_not_easily_unifiable_syscalls(test_call_trace, 14, 4, 0); - for deploy_proxy in &test_call_trace.nested_calls { - assert_not_easily_unifiable_syscalls(deploy_proxy, 2, 1, 0); + for deploy_proxy_node in &test_call_trace.nested_calls { + if let ProfilerCallTraceNode::EntryPointCall(deploy_proxy) = deploy_proxy_node { + assert_not_easily_unifiable_syscalls(deploy_proxy, 2, 1, 0); - let from_proxy = &deploy_proxy.nested_calls[0]; - assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); + let from_proxy = get_trace_from_trace_node(&deploy_proxy.nested_calls[1]); + assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); + } } } fn check_l1_handler(test_call_trace: &ProfilerCallTrace) { assert_not_easily_unifiable_syscalls(test_call_trace, 8, 3, 0); - let handle_l1 = &test_call_trace.nested_calls[1]; + let handle_l1 = get_trace_from_trace_node(&test_call_trace.nested_calls[3]); assert_not_easily_unifiable_syscalls(handle_l1, 3, 2, 0); - let regular_call = &handle_l1.nested_calls[0]; + let regular_call = get_trace_from_trace_node(&handle_l1.nested_calls[1]); assert_not_easily_unifiable_syscalls(regular_call, 2, 1, 0); - let from_proxy = ®ular_call.nested_calls[0]; + let from_proxy = get_trace_from_trace_node(®ular_call.nested_calls[1]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); } fn check_libcall(test_call_trace: &ProfilerCallTrace) { assert_not_easily_unifiable_syscalls(test_call_trace, 11, 3, 5); - let regular_call = &test_call_trace.nested_calls[0]; + let regular_call = get_trace_from_trace_node(&test_call_trace.nested_calls[3]); + assert_not_easily_unifiable_syscalls(regular_call, 2, 1, 0); - let from_proxy = ®ular_call.nested_calls[0]; + let from_proxy = get_trace_from_trace_node(®ular_call.nested_calls[1]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); - let with_libcall = &test_call_trace.nested_calls[1]; + let with_libcall = get_trace_from_trace_node(&test_call_trace.nested_calls[4]); assert_not_easily_unifiable_syscalls(with_libcall, 2, 0, 1); - let call_two = &test_call_trace.nested_calls[2]; + let call_two = get_trace_from_trace_node(&test_call_trace.nested_calls[5]); assert_not_easily_unifiable_syscalls(call_two, 3, 2, 0); - let from_proxy = &call_two.nested_calls[0]; + let from_proxy = get_trace_from_trace_node(&call_two.nested_calls[0]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); - let from_proxy_dummy = &call_two.nested_calls[1]; + let from_proxy_dummy = get_trace_from_trace_node(&call_two.nested_calls[2]); assert_not_easily_unifiable_syscalls(from_proxy_dummy, 1, 0, 0); - let from_proxy = &test_call_trace.nested_calls[3]; + let from_proxy = get_trace_from_trace_node(&test_call_trace.nested_calls[6]); assert_not_easily_unifiable_syscalls(from_proxy, 1, 0, 0); } diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 6291be0f5f..7b2c0dd7d2 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -46,24 +46,15 @@ * [Cheatcodes Reference](appendix/cheatcodes.md) * [CheatTarget](appendix/cheatcodes/cheat_target.md) * [CheatSpan](appendix/cheatcodes/cheat_span.md) + * [caller_address](appendix/cheatcodes/caller_address.md) + * [block_number](appendix/cheatcodes/block_number.md) * [block_timestamp](appendix/cheatcodes/block_timestamp.md) * [mock_call](appendix/cheatcodes/mock_call.md) - * [caller_address](appendix/cheatcodes/caller_address/README.md) - * [prank](appendix/cheatcodes/caller_address/prank.md) - * [start_prank](appendix/cheatcodes/caller_address/start_prank.md) - * [stop_prank](appendix/cheatcodes/caller_address/stop_prank.md) + * [tx_info](appendix/cheatcodes/tx_info.md) * [sequencer_address](appendix/cheatcodes/sequencer_address/README.md) * [elect](appendix/cheatcodes/sequencer_address/elect.md) * [start_elect](appendix/cheatcodes/sequencer_address/start_elect.md) * [stop_elect](appendix/cheatcodes/sequencer_address/stop_elect.md) - * [block_number](appendix/cheatcodes/block_number/README.md) - * [roll](appendix/cheatcodes/block_number/roll.md) - * [start_roll](appendix/cheatcodes/block_number/start_roll.md) - * [stop_roll](appendix/cheatcodes/block_number/stop_roll.md) - * [tx_info](appendix/cheatcodes/tx_info/README.md) - * [spoof](appendix/cheatcodes/tx_info/spoof.md) - * [start_spoof](appendix/cheatcodes/tx_info/start_spoof.md) - * [stop_spoof](appendix/cheatcodes/tx_info/stop_spoof.md) * [get_class_hash](appendix/cheatcodes/get_class_hash.md) * [replace_bytecode](appendix/cheatcodes/replace_bytecode.md) * [l1_handler_execute](appendix/cheatcodes/l1_handler_execute.md) diff --git a/docs/src/appendix/cheatcodes.md b/docs/src/appendix/cheatcodes.md index 39e69afab4..8dc6f789a4 100644 --- a/docs/src/appendix/cheatcodes.md +++ b/docs/src/appendix/cheatcodes.md @@ -2,21 +2,21 @@ - [`CheatTarget`](cheatcodes/cheat_target.md) - enum for selecting contracts to target with cheatcodes - [`CheatSpan`](cheatcodes/cheat_span.md) - enum for specifying the number of target calls for a cheat -- [`prank`](cheatcodes/caller_address/prank.md) - changes the caller address for contracts, for a number of calls -- [`start_prank`](cheatcodes/caller_address/start_prank.md) - changes the caller address for contracts -- [`stop_prank`](cheatcodes/caller_address/stop_prank.md) - cancels the `prank` / `start_prank` for contracts -- [`roll`](cheatcodes/block_number/roll.md) - changes the block number for contracts, for a number of calls -- [`start_roll`](cheatcodes/block_number/start_roll.md) - changes the block number for contracts -- [`stop_roll`](cheatcodes/block_number/stop_roll.md) - cancels the `roll` / `start_roll` for contracts +- [`prank`](cheatcodes/caller_address#prank) - changes the caller address for contracts, for a number of calls +- [`start_prank`](cheatcodes/caller_address#start_prank) - changes the caller address for contracts +- [`stop_prank`](cheatcodes/caller_address#stop_prank) - cancels the `prank` / `start_prank` for contracts +- [`roll`](cheatcodes/block_number#roll) - changes the block number for contracts, for a number of calls +- [`start_roll`](cheatcodes/block_number#start_roll) - changes the block number for contracts +- [`stop_roll`](cheatcodes/block_number#stop_roll) - cancels the `roll` / `start_roll` for contracts - [`warp`](cheatcodes/block_timestamp#warp) - changes the block timestamp for contracts, for a number of calls - [`start_warp`](cheatcodes/block_timestamp#start_warp) - changes the block timestamp for contracts - [`stop_warp`](cheatcodes/block_timestamp#stop_warp) - cancels the `warp` / `start_warp` for contracts - [`elect`](cheatcodes/sequencer_address/elect.md) - changes the sequencer address for contracts, for a number of calls - [`start_elect`](cheatcodes/sequencer_address/start_elect.md) - changes the sequencer address for contracts - [`stop_elect`](cheatcodes/sequencer_address/stop_elect.md) - cancels the `elect` / `start_elect` for contracts -- [`spoof`](cheatcodes/tx_info/spoof.md) - changes the transaction context for contracts, for a number of calls -- [`start_spoof`](cheatcodes/tx_info/start_spoof.md) - changes the transaction context for contracts -- [`stop_spoof`](cheatcodes/tx_info/stop_spoof.md) - cancels the `spoof` / `start_spoof` for contracts +- [`spoof`](cheatcodes/tx_info#spoof) - changes the transaction context for contracts, for a number of calls +- [`start_spoof`](cheatcodes/tx_info#start_spoof) - changes the transaction context for contracts +- [`stop_spoof`](cheatcodes/tx_info#stop_spoof) - cancels the `spoof` / `start_spoof` for contracts - [`mock_call`](cheatcodes/mock_call.md#mock_call) - mocks a number of contract calls to an entry point - [`start_mock_call`](cheatcodes/mock_call.md#start_mock_call) - mocks contract call to an entry point - [`stop_mock_call`](cheatcodes/mock_call.md#stop_mock_call) - cancels the `mock_call` / `start_mock_call` for an entry point diff --git a/docs/src/appendix/cheatcodes/block_number.md b/docs/src/appendix/cheatcodes/block_number.md new file mode 100644 index 0000000000..6433f2ac87 --- /dev/null +++ b/docs/src/appendix/cheatcodes/block_number.md @@ -0,0 +1,18 @@ +# `block_number` + +Cheatcodes modifying `block_number`: + +## `roll` +> `fn roll(target: CheatTarget, block_number: u64, span: CheatSpan)` + +Changes the block number for the given target and span. + +## `start_roll` +> `fn start_roll(target: CheatTarget, block_number: u64)` + +Changes the block number for the given target. + +# `stop_roll` +> `fn stop_roll(target: CheatTarget)` + +Cancels the `roll` / `start_roll` for the given target. \ No newline at end of file diff --git a/docs/src/appendix/cheatcodes/block_number/README.md b/docs/src/appendix/cheatcodes/block_number/README.md deleted file mode 100644 index 2f75d4c7e0..0000000000 --- a/docs/src/appendix/cheatcodes/block_number/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# `block_number` - -Cheatcodes modifying `block_number`: - -* [`roll`](./roll.md) - changes the block number for the given target and span -* [`start_roll`](./start_roll.md) - changes the block number for the given target until [`stop_roll`](./stop_roll.md) is called -* [`stop_roll`](./stop_roll.md) - cancels the [`roll`](./roll.md) / [`start_roll`](./start_roll.md) for the given target diff --git a/docs/src/appendix/cheatcodes/block_number/roll.md b/docs/src/appendix/cheatcodes/block_number/roll.md deleted file mode 100644 index de6e47c18e..0000000000 --- a/docs/src/appendix/cheatcodes/block_number/roll.md +++ /dev/null @@ -1,10 +0,0 @@ -# `roll` - -> `fn roll(target: CheatTarget, block_number: u64, span: CheatSpan)` - -Changes the block number for the given target and span. -This change can be canceled with [`stop_roll`](./stop_roll.md). - -- `target` - instance of [`CheatTarget`](../cheat_target.md) specifying which contracts to roll -- `block_number` - block number to be set -- `span` - instance of [`CheatSpan`](../cheat_span.md) specifying the number of target calls with the cheat applied diff --git a/docs/src/appendix/cheatcodes/block_number/start_roll.md b/docs/src/appendix/cheatcodes/block_number/start_roll.md deleted file mode 100644 index 1619bcc1a2..0000000000 --- a/docs/src/appendix/cheatcodes/block_number/start_roll.md +++ /dev/null @@ -1,49 +0,0 @@ -# `start_roll` - -> `fn start_roll(target: CheatTarget, block_number: u64)` - -Changes the block number for the given target. -The change can be canceled with [`stop_roll`](./stop_roll.md). - -- `target` - instance of [`CheatTarget`](../cheat_target.md) specifying which contracts to roll -- `block_number` - block number to be set - -For contract implementation: - -```rust -// ... -#[storage] -struct Storage { - // ... - stored_block_number: u64 -} - -#[abi(embed_v0)] -impl IContractImpl of IContract { - fn set_block_number(ref self: ContractState) { - self.stored_block_number.write(starknet::get_block_info().unbox().block_number); - } - - fn get_block_number(self: @ContractState) -> u64 { - self.stored_block_number.read() - } -} -// ... -``` - -We can use `start_roll` in a test to change the block number for contracts: - -```rust -use snforge_std::{start_roll, CheatTarget}; - -#[test] -fn test_roll() { - // ... - - start_roll(CheatTarget::One(contract_address), 234); - - dispatcher.set_block_number(); - let new_block_number = dispatcher.get_block_number(); - assert(new_block_number == 234, 'Wrong block number'); -} -``` diff --git a/docs/src/appendix/cheatcodes/block_number/stop_roll.md b/docs/src/appendix/cheatcodes/block_number/stop_roll.md deleted file mode 100644 index 8144b558d1..0000000000 --- a/docs/src/appendix/cheatcodes/block_number/stop_roll.md +++ /dev/null @@ -1,20 +0,0 @@ -# `stop_roll` - -> `fn stop_roll(target: CheatTarget)` - -Cancels the [`roll`](./roll.md) / [`start_roll`](./start_roll.md) for the given target. - -- `target` - instance of [`CheatTarget`](../cheat_target.md) specifying which contracts to stop rolling - -```rust -use snforge_std::{stop_roll, CheatTarget}; - -#[test] -fn test_roll() { - // ... - - stop_roll(CheatTarget::One(contract_address)); - - // ... -} -``` diff --git a/docs/src/appendix/cheatcodes/caller_address.md b/docs/src/appendix/cheatcodes/caller_address.md new file mode 100644 index 0000000000..bfb636eed3 --- /dev/null +++ b/docs/src/appendix/cheatcodes/caller_address.md @@ -0,0 +1,19 @@ +# `caller_address` + +Cheatcodes modifying `caller_address`: + +## `prank` +> `fn prank(target: CheatTarget, caller_address: ContractAddress, span: CheatSpan)` + +Changes the caller address for the given target and span. + + +## `start_prank` +> `fn start_prank(target: CheatTarget, caller_address: ContractAddress)` + +Changes the caller address for the given target. + +## `stop_prank` +> `fn stop_prank(target: CheatTarget)` + +Cancels the `prank` / `start_prank` for the given target. diff --git a/docs/src/appendix/cheatcodes/caller_address/README.md b/docs/src/appendix/cheatcodes/caller_address/README.md deleted file mode 100644 index 3d9e217225..0000000000 --- a/docs/src/appendix/cheatcodes/caller_address/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# `caller_address` - -Cheatcodes modifying `caller_address`: - -* [`prank`](./prank.md) - changes the caller address for the given target and span -* [`start_prank`](./start_prank.md) - changes the caller address for the given target until [`stop_prank`](./stop_prank.md) is called -* [`stop_prank`](./stop_prank.md) - cancels the [`prank`](./prank.md) / [`start_prank`](./start_prank.md) for the given target diff --git a/docs/src/appendix/cheatcodes/caller_address/prank.md b/docs/src/appendix/cheatcodes/caller_address/prank.md deleted file mode 100644 index 29f4d9ede1..0000000000 --- a/docs/src/appendix/cheatcodes/caller_address/prank.md +++ /dev/null @@ -1,10 +0,0 @@ -# `prank` - -> `fn prank(target: CheatTarget, caller_address: ContractAddress, span: CheatSpan)` - -Changes the caller address for the given target and span. -This change can be canceled with [`stop_prank`](./stop_prank.md). - -- `target` - instance of [`CheatTarget`](../cheat_target.md) specifying which contracts to prank -- `caller_address` - caller address to be set -- `span` - instance of [`CheatSpan`](../cheat_span.md) specifying the number of target calls with the cheat applied diff --git a/docs/src/appendix/cheatcodes/caller_address/start_prank.md b/docs/src/appendix/cheatcodes/caller_address/start_prank.md deleted file mode 100644 index fd505ab44a..0000000000 --- a/docs/src/appendix/cheatcodes/caller_address/start_prank.md +++ /dev/null @@ -1,51 +0,0 @@ -# `start_prank` - -> `fn start_prank(target: CheatTarget, caller_address: ContractAddress)` - -Changes the caller address for the given target. -This change can be canceled with [`stop_prank`](./stop_prank.md). - -- `target` - instance of [`CheatTarget`](../cheat_target.md) specifying which contracts to prank. -- `caller_address` - caller address to be set - -For contract implementation: - -```rust -// ... -#[storage] -struct Storage { - // ... - stored_caller_address: ContractAddress -} - -#[abi(embed_v0)] -impl IContractImpl of IContract { - fn set_caller_address(ref self: ContractState) { - self.stored_caller_address.write(starknet::get_caller_address()); - } - - fn get_caller_address(self: @ContractState) -> ContractAddress { - self.stored_caller_address.read() - } -} -// ... -``` - -We can use `start_prank` in a test to change the caller address for a given contract: - -```rust -use snforge_std::{start_prank, CheatTarget}; - -#[test] -fn test_prank() { - // ... - - let caller_address: ContractAddress = 123.try_into().unwrap(); - - start_prank(CheatTarget::One(contract_address), caller_address); - - dispatcher.set_caller_address(); - let caller_address = dispatcher.get_caller_address(); - assert(caller_address.into() == 123, 'Wrong caller address'); -} -``` diff --git a/docs/src/appendix/cheatcodes/caller_address/stop_prank.md b/docs/src/appendix/cheatcodes/caller_address/stop_prank.md deleted file mode 100644 index 1fae3039a3..0000000000 --- a/docs/src/appendix/cheatcodes/caller_address/stop_prank.md +++ /dev/null @@ -1,20 +0,0 @@ -# `stop_prank` - -> `fn stop_prank(target: CheatTarget)` - -Cancels the [`prank`](./prank.md) / [`start_prank`](./start_prank.md) for the given target. - -- `contract_address` - target contract address - -```rust -use snforge_std::{stop_prank, CheatTarget}; - -#[test] -fn test_prank() { - // ... - - stop_prank(CheatTarget::One(contract_address)); - - // ... -} -``` diff --git a/docs/src/appendix/cheatcodes/tx_info.md b/docs/src/appendix/cheatcodes/tx_info.md new file mode 100644 index 0000000000..a8ed1ed932 --- /dev/null +++ b/docs/src/appendix/cheatcodes/tx_info.md @@ -0,0 +1,55 @@ +# `tx_info` + +Cheatcodes modifying `tx_info`: + +## `spoof` + +> `fn spoof(target: CheatTarget, tx_info_mock: TxInfoMock, span: CheatSpan)` + +Changes `TxInfo` returned by `get_tx_info()` for the targeted contract and span. + +## `start_spoof` + +> `fn start_spoof(target: CheatTarget, tx_info_mock: TxInfoMock)` + +Changes `TxInfo` returned by `get_tx_info()` for the targeted contract until the spoof is canceled with `stop_spoof`. + +## `stop_spoof` + +> `fn stop_spoof(target: CheatTarget)` + +Cancels the `spoof` / `start_spoof` for the given target. + + +## `TxInfoMock` & `TxInfoMockTrait` + +A structure used for setting individual fields in `TxInfo` +All fields are optional, with optional value meaning as defined: +- `None` means that the field is going to be reset to the initial value +- `Some(n)` means that the value will be set to the `n` value +``` +struct TxInfoMock { + version: Option, + account_contract_address: Option, + max_fee: Option, + signature: Option>, + transaction_hash: Option, + chain_id: Option, + nonce: Option, + // starknet::info::v2::TxInfo fields + resource_bounds: Option>, + tip: Option, + paymaster_data: Option>, + nonce_data_availability_mode: Option, + fee_data_availability_mode: Option, + account_deployment_data: Option>, +} +``` + +Returns a default object initialized with Option::None for each field +Useful for setting only a few of fields instead of all of them +``` +trait TxInfoMockTrait { + fn default() -> TxInfoMock; +} +``` \ No newline at end of file diff --git a/docs/src/appendix/cheatcodes/tx_info/README.md b/docs/src/appendix/cheatcodes/tx_info/README.md deleted file mode 100644 index f357d5015f..0000000000 --- a/docs/src/appendix/cheatcodes/tx_info/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# `tx_info` - -Cheatcodes modifying `tx_info`: - -* [`spoof`](./spoof.md) - changes `TxInfo` returned by `get_tx_info()` for the given target and span -* [`start_spoof`](./start_spoof.md) - Changes `TxInfo` returned by `get_tx_info()` for the given target until [`stop_spoof`](./stop_spoof.md) is called -* [`stop_spoof`](./stop_spoof.md) - cancels the [`spoof`](./spoof.md) / [`start_spoof`](./start_spoof.md) for the given target diff --git a/docs/src/appendix/cheatcodes/tx_info/spoof.md b/docs/src/appendix/cheatcodes/tx_info/spoof.md deleted file mode 100644 index a98cc71b98..0000000000 --- a/docs/src/appendix/cheatcodes/tx_info/spoof.md +++ /dev/null @@ -1,10 +0,0 @@ -# `spoof` - -> `fn spoof(target: CheatTarget, tx_info_mock: TxInfoMock, span: CheatSpan)` - -Changes `TxInfo` returned by `get_tx_info()` for the targeted contract and span. -This change can be canceled with [`stop_spoof`](./stop_spoof.md). - -- `target` - instance of [`CheatTarget`](../cheat_target.md) specifying which contracts to spoof -- `tx_info_mock` - a struct with same structure as `TxInfo` (returned by `get_tx_info()`) -- `span` - instance of [`CheatSpan`](../cheat_span.md) specifying the number of target calls with the cheat applied diff --git a/docs/src/appendix/cheatcodes/tx_info/start_spoof.md b/docs/src/appendix/cheatcodes/tx_info/start_spoof.md deleted file mode 100644 index 9e3e65c492..0000000000 --- a/docs/src/appendix/cheatcodes/tx_info/start_spoof.md +++ /dev/null @@ -1,93 +0,0 @@ -# `start_spoof` - -> `fn start_spoof(target: CheatTarget, tx_info_mock: TxInfoMock)` - -Changes `TxInfo` returned by `get_tx_info()` for the targeted contract until the spoof is canceled -with [stop_spoof](./stop_spoof.md). - -- `target` - instance of [`CheatTarget`](../cheat_target.md) specifying which contracts to spoof -- `tx_info_mock` - a struct with same structure as `TxInfo` (returned by `get_tx_info()`) - -To mock the field of `TxInfo`, set the corresponding field of `TxInfoMock` to `Some(mocked_value)`. Setting the field to `None` will use a default value - the field will not be mocked. Using `None` will also cancel current mock for that field. See below for practical example. - -> 📝 **Note** -> -> To get access to fields from `starknet::info::v2::TxInfo`, you can use -> `get_execution_info_v2_syscall().unwrap_syscall().unbox().tx_info.unbox()` - -```rust -struct TxInfoMock { - version: Option, - account_contract_address: Option, - max_fee: Option, - signature: Option>, - transaction_hash: Option, - chain_id: Option, - nonce: Option, - // starknet::info::v2::TxInfo fields - resource_bounds: Option>, - tip: Option, - paymaster_data: Option>, - nonce_data_availability_mode: Option, - fee_data_availability_mode: Option, - account_deployment_data: Option>, -} - -trait TxInfoMockTrait { - // Returns a default object initialized with Option::None for each field - fn default() -> TxInfoMock; -} -``` - -For contract implementation: - -```rust -// ... -#[storage] -struct Storage { - stored_hash: felt252 -} - -#[abi(embed_v0)] -impl IContractImpl of IContract { - fn store_tx_hash(ref self: ContractState) { - let tx_info = get_tx_info().unbox(); - self.stored_hash.write(tx_info.transaction_hash); - } - - fn get_stored_tx_hash(self: @ContractState) -> felt252 { - self.stored_hash.read() - } -} -// ... -``` - -```rust -use snforge_std::{ start_spoof, CheatTarget }; - -#[test] -fn test_spoof() { - // ... - let tx_hash_before_mock = dispatcher.get_stored_tx_hash(); - - // Change transaction_hash to 1234 - // All other fields of `TxInfo` remain unchanged - let mut tx_info = TxInfoMockTrait::default(); - tx_info.transaction_hash = Option::Some(1234); - - start_spoof(CheatTarget::One(contract_address), tx_info); - - dispatcher.store_tx_hash(); - - let tx_hash = dispatcher.get_stored_tx_hash(); - assert(tx_hash == 1234, 'tx_hash should be mocked'); - - // Cancel tx_info.transaction_hash mocking by setting the field to `None` - // All other fields of `TxInfo` remain unchanged - start_spoof(CheatTarget::One(contract_address), TxInfoMockTrait::default()); - dispatcher.store_tx_hash(); - - let tx_hash = dispatcher.get_stored_tx_hash(); - assert(tx_hash == tx_hash_before_mock, 'tx_hash was not reverted'); -} -``` diff --git a/docs/src/appendix/cheatcodes/tx_info/stop_spoof.md b/docs/src/appendix/cheatcodes/tx_info/stop_spoof.md deleted file mode 100644 index 1479d3eb08..0000000000 --- a/docs/src/appendix/cheatcodes/tx_info/stop_spoof.md +++ /dev/null @@ -1,20 +0,0 @@ -# `stop_spoof` - -> `fn stop_spoof(target: CheatTarget)` - -Cancels the [`spoof`](./spoof.md) / [`start_spoof`](./start_spoof.md) for the given target. - -- `target` - instance of [`CheatTarget`](../cheat_target.md) specifying which contracts to stop spoofing - -```rust -use snforge_std::{ stop_spoof, CheatTarget }; - -#[test] -fn test_spoof() { - // ... - - stop_spoof(CheatTarget::One(contract_address)); - - // ... -} -``` diff --git a/docs/src/development/environment-setup.md b/docs/src/development/environment-setup.md index 90fddf3f22..a7c447aa0e 100644 --- a/docs/src/development/environment-setup.md +++ b/docs/src/development/environment-setup.md @@ -1,10 +1,11 @@ # Environment Setup - > 💡 **Info** -> -> This setup is for development of Starknet Foundry. -> -> If you don't wish to contribute, you can omit these instructions. +> This tutorial is only relevant if you wish to contribute to Starknet Foundry. +> If you plan to only use it as a tool for your project, you can skip this part. + +## Prerequisites + +### Rust Install the latest stable [Rust](https://www.rust-lang.org/tools/install) version. If you already have Rust installed make sure to upgrade it by running @@ -13,39 +14,47 @@ If you already have Rust installed make sure to upgrade it by running $ rustup update ``` -To verify that project was cloned and set up correctly, you can run +### Scarb +You can read more about installing Scarb [here](https://docs.swmansion.com/scarb/download.html) -```shell -$ cargo check -``` +Please make sure you're using Scarb installed via [asdf](https://asdf-vm.com/) - otherwise some tests may fail. +> To verify, run: +> +> ```shell +> $ which scarb +> ``` +> the result of which should be: +> ```shell +> $HOME/.asdf/shims/scarb +> ``` +> +> If you previously installed scarb using official installer, you may need to remove this installation or modify your PATH to make sure asdf installed one is always used. -## Running Tests -> 📝 **Note** +> ❗️ **Warning** +> +> If you haven't pushed your branch to the remote yet (you've been working only locally), two tests will fail: > -> Make sure you run `./scripts/install_devnet.sh` -> and then set [Scarb](https://docs.swmansion.com/scarb/) version -> [compatible](https://github.com/foundry-rs/starknet-foundry/releases) with both `snforge` and `sncast` -> and use the newest [universal-sierra-compiler](https://github.com/software-mansion/universal-sierra-compiler) -> after setting up the development environment, otherwise the tests will fail. +> - `e2e::running::init_new_project_test` +> - `e2e::running::simple_package_with_git_dependency` +> +> After pushing the branch to the remote, those tests should pass. + +### Starknet Devnet +To install it run `./scripts/install_devnet.sh` + +### Universal sierra compiler +Install the latest [universal-sierra-compiler](https://github.com/software-mansion/universal-sierra-compiler) version. + + +## Running Tests Tests can be run with: ```shell $ cargo test ``` -> 💡 **Info** -> -> Please make sure you're using scarb installed via asdf - otherwise some tests may fail. -> To verify, run: -> -> ```shell -> $ which scarb -> $HOME/.asdf/shims/scarb -> ``` -> -> If you previously installed scarb using official installer, you may need to remove this installation or modify your PATH to make sure asdf installed one > is always used. ## Formatting and Lints @@ -82,3 +91,7 @@ Some typos can be automatically fixed by running ```shell $ typos -w ``` + +## Contributing + +Read the general contribution guideline [here](https://github.com/foundry-rs/starknet-foundry/blob/master/CONTRIBUTING.md) diff --git a/snforge_std/src/cheatcodes.cairo b/snforge_std/src/cheatcodes.cairo index 882f46e083..db3e7df856 100644 --- a/snforge_std/src/cheatcodes.cairo +++ b/snforge_std/src/cheatcodes.cairo @@ -30,6 +30,10 @@ fn test_address() -> ContractAddress { contract_address_const::<469394814521890341860918960550914>() } +/// Changes the block number for the given target and span. +/// - `target` - instance of `CheatTarget` specifying which contracts to roll +/// - `block_number` - block number to be set +/// - `span` - instance of `CheatSpan` specifying the number of target calls with the cheat applied fn roll(target: CheatTarget, block_number: u64, span: CheatSpan) { validate_cheat_target_and_span(@target, @span); @@ -40,16 +44,26 @@ fn roll(target: CheatTarget, block_number: u64, span: CheatSpan) { handle_cheatcode(cheatcode::<'roll'>(inputs.span())); } +/// Changes the block number for the given target. +/// - `target` - instance of `CheatTarget` specifying which contracts to roll +/// - `block_number` - block number to be set fn start_roll(target: CheatTarget, block_number: u64) { roll(target, block_number, CheatSpan::Indefinite); } + +/// Cancels the `roll` / `start_roll` for the given target. +/// - `target` - instance of `CheatTarget` specifying which contracts to stop rolling fn stop_roll(target: CheatTarget) { let mut inputs = array![]; target.serialize(ref inputs); handle_cheatcode(cheatcode::<'stop_roll'>(inputs.span())); } +/// Changes the caller address for the given target and span. +/// - `target` - instance of `CheatTarget` specifying which contracts to prank +/// - `caller_address` - caller address to be set +/// - `span` - instance of `CheatSpan` specifying the number of target calls with the cheat applied fn prank(target: CheatTarget, caller_address: ContractAddress, span: CheatSpan) { validate_cheat_target_and_span(@target, @span); @@ -60,10 +74,16 @@ fn prank(target: CheatTarget, caller_address: ContractAddress, span: CheatSpan) handle_cheatcode(cheatcode::<'prank'>(inputs.span())); } +/// Changes the caller address for the given target. +/// This change can be canceled with `stop_prank`. +/// - `target` - instance of `CheatTarget` specifying which contracts to prank +/// - `caller_address` - caller address to be set fn start_prank(target: CheatTarget, caller_address: ContractAddress) { prank(target, caller_address, CheatSpan::Indefinite); } +/// Cancels the `prank` / `start_prank` for the given target. +/// - `target` - instance of `CheatTarget` specifying which contracts to stop pranking fn stop_prank(target: CheatTarget) { let mut inputs = array![]; target.serialize(ref inputs); diff --git a/snforge_std/src/cheatcodes/tx_info.cairo b/snforge_std/src/cheatcodes/tx_info.cairo index 69b1a76702..3bcf9ecbda 100644 --- a/snforge_std/src/cheatcodes/tx_info.cairo +++ b/snforge_std/src/cheatcodes/tx_info.cairo @@ -3,6 +3,11 @@ use starknet::info::v2::ResourceBounds; use snforge_std::cheatcodes::{CheatSpan, CheatTarget, validate_cheat_target_and_span}; use super::super::_cheatcode::handle_cheatcode; + +/// A structure used for setting individual fields in `TxInfo` +/// All fields are optional, with optional value meaning as defined: +/// - `None` means that the field is going to be reset to the initial value +/// - `Some(value)` means that the field will be set to `value` #[derive(Copy, Drop, Serde)] struct TxInfoMock { version: Option, @@ -22,6 +27,8 @@ struct TxInfoMock { } trait TxInfoMockTrait { + /// Returns a default object initialized with Option::None for each field + /// Useful for setting only a few of fields instead of all of them fn default() -> TxInfoMock; } @@ -45,6 +52,10 @@ impl TxInfoMockImpl of TxInfoMockTrait { } } +/// Changes `TxInfo` returned by `get_tx_info()` for the targeted contract and span. +/// - `target` - instance of `CheatTarget` specifying which contracts to spoof +/// - `tx_info_mock` - a struct with same structure as `TxInfo` (returned by `get_tx_info()`) +/// - `span` - instance of `CheatSpan` specifying the number of target calls with the cheat applied fn spoof(target: CheatTarget, tx_info_mock: TxInfoMock, span: CheatSpan) { validate_cheat_target_and_span(@target, @span); @@ -55,10 +66,15 @@ fn spoof(target: CheatTarget, tx_info_mock: TxInfoMock, span: CheatSpan) { handle_cheatcode(cheatcode::<'spoof'>(inputs.span())); } +/// Changes `TxInfo` returned by `get_tx_info()` for the targeted contract until the spoof is canceled with `stop_spoof`. +/// - `target` - instance of `CheatTarget` specifying which contracts to spoof +/// - `tx_info_mock` - a struct with same structure as `TxInfo` (returned by `get_tx_info()`) fn start_spoof(target: CheatTarget, tx_info_mock: TxInfoMock) { spoof(target, tx_info_mock, CheatSpan::Indefinite); } +/// Cancels the `spoof` / `start_spoof` for the given target. +/// - `target` - instance of `CheatTarget` specifying which contracts to stop spoofing fn stop_spoof(target: CheatTarget) { let mut inputs = array![]; target.serialize(ref inputs);