Skip to content

Commit fde4822

Browse files
Arcticaepiotmag769
andauthored
Return mocked data, even if the contract does not exist (#653)
<!-- Reference any GitHub issues resolved by this PR --> Closes #645 ## Checklist <!-- Make sure all of these are complete --> - [x] Linked relevant issue - [x] Updated relevant documentation - [x] Added relevant tests - [x] Performed self-review of the code - [x] Added changes to `CHANGELOG.md` --------- Co-authored-by: Piotr Magiera <[email protected]>
1 parent 3a29c43 commit fde4822

File tree

5 files changed

+63
-43
lines changed

5 files changed

+63
-43
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
- `deploy_at` cheatcode
1515
- printing failures summary at the end of an execution
1616
- filtering tests now uses an absolute module tree path — it is possible to filter tests by module names, etc.
17+
- fixed mocking functions even if the contract does not exist
1718

1819
### Fixed
1920

crates/cheatnet/src/execution/cairo1_execution.rs

Lines changed: 2 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::collections::HashSet;
2-
31
use super::syscalls::CheatableSyscallHandler;
42
use crate::state::CheatcodeState;
53
use blockifier::{
@@ -10,23 +8,21 @@ use blockifier::{
108
},
119
contract_class::{ContractClassV1, EntryPointV1},
1210
entry_point::{
13-
CallEntryPoint, CallExecution, CallInfo, EntryPointExecutionContext,
14-
EntryPointExecutionResult, ExecutionResources, Retdata,
11+
CallEntryPoint, CallInfo, EntryPointExecutionContext, EntryPointExecutionResult,
12+
ExecutionResources,
1513
},
1614
errors::{EntryPointExecutionError, VirtualMachineExecutionError},
1715
execution_utils::Args,
1816
},
1917
state::state_api::State,
2018
};
21-
use cairo_vm::vm::runners::cairo_runner::ExecutionResources as VmExecutionResources;
2219
use cairo_vm::{
2320
hint_processor::hint_processor_definition::HintProcessor,
2421
vm::{
2522
runners::cairo_runner::{CairoArg, CairoRunner},
2623
vm_core::VirtualMachine,
2724
},
2825
};
29-
use starknet_api::hash::StarkFelt;
3026

3127
// blockifier/src/execution/cairo1_execution.rs:48 (execute_entry_point_call)
3228
pub fn execute_entry_point_call_cairo1(
@@ -37,25 +33,6 @@ pub fn execute_entry_point_call_cairo1(
3733
resources: &mut ExecutionResources,
3834
context: &mut EntryPointExecutionContext,
3935
) -> EntryPointExecutionResult<CallInfo> {
40-
// region: Modified blockifier code
41-
if let Some(ret_data) = get_ret_data_by_call_entry_point(&call, cheatcode_state) {
42-
return Ok(CallInfo {
43-
call,
44-
execution: CallExecution {
45-
retdata: Retdata(ret_data.clone()),
46-
events: vec![],
47-
l2_to_l1_messages: vec![],
48-
failed: false,
49-
gas_consumed: 0,
50-
},
51-
vm_resources: VmExecutionResources::default(),
52-
inner_calls: vec![],
53-
storage_read_values: vec![],
54-
accessed_storage_keys: HashSet::new(),
55-
});
56-
}
57-
// endregion
58-
5936
let VmExecutionContext {
6037
mut runner,
6138
mut vm,
@@ -137,18 +114,3 @@ pub fn cheatable_run_entry_point(
137114

138115
Ok(())
139116
}
140-
141-
fn get_ret_data_by_call_entry_point<'a>(
142-
call: &CallEntryPoint,
143-
cheatcode_state: &'a CheatcodeState,
144-
) -> Option<&'a Vec<StarkFelt>> {
145-
if let Some(contract_address) = call.code_address {
146-
if let Some(contract_functions) = cheatcode_state.mocked_functions.get(&contract_address) {
147-
let entrypoint_selector = call.entry_point_selector;
148-
149-
let ret_data = contract_functions.get(&entrypoint_selector);
150-
return ret_data;
151-
}
152-
}
153-
None
154-
}

crates/cheatnet/src/execution/entry_point.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::cairo1_execution::execute_entry_point_call_cairo1;
22
use crate::state::CheatcodeState;
3+
use blockifier::execution::entry_point::{CallExecution, Retdata};
34
use blockifier::{
45
execution::{
56
contract_class::ContractClass,
@@ -12,12 +13,14 @@ use blockifier::{
1213
},
1314
state::state_api::State,
1415
};
16+
use cairo_vm::vm::runners::cairo_runner::ExecutionResources as VmExecutionResources;
1517
use starknet_api::{
1618
core::ClassHash,
1719
deprecated_contract_class::EntryPointType,
1820
hash::StarkFelt,
1921
transaction::{Calldata, TransactionVersion},
2022
};
23+
use std::collections::HashSet;
2124

2225
// blockifier/src/execution/entry_point.rs:180 (CallEntryPoint::execute)
2326
#[allow(clippy::module_name_repetitions)]
@@ -30,6 +33,9 @@ pub fn execute_call_entry_point(
3033
) -> EntryPointExecutionResult<CallInfo> {
3134
// region: Modified blockifier code
3235
// We skip recursion depth validation here.
36+
if let Some(ret_data) = get_ret_data_by_call_entry_point(entry_point, cheatcode_state) {
37+
return Ok(mocked_call_info(entry_point.clone(), ret_data.clone()));
38+
}
3339
// endregion
3440

3541
// Validate contract is deployed.
@@ -136,3 +142,35 @@ pub fn execute_constructor_entry_point(
136142
)
137143
// endregion
138144
}
145+
146+
fn get_ret_data_by_call_entry_point<'a>(
147+
call: &CallEntryPoint,
148+
cheatcode_state: &'a CheatcodeState,
149+
) -> Option<&'a Vec<StarkFelt>> {
150+
if let Some(contract_address) = call.code_address {
151+
if let Some(contract_functions) = cheatcode_state.mocked_functions.get(&contract_address) {
152+
let entrypoint_selector = call.entry_point_selector;
153+
154+
let ret_data = contract_functions.get(&entrypoint_selector);
155+
return ret_data;
156+
}
157+
}
158+
None
159+
}
160+
161+
fn mocked_call_info(call: CallEntryPoint, ret_data: Vec<StarkFelt>) -> CallInfo {
162+
CallInfo {
163+
call,
164+
execution: CallExecution {
165+
retdata: Retdata(ret_data),
166+
events: vec![],
167+
l2_to_l1_messages: vec![],
168+
failed: false,
169+
gas_consumed: 0,
170+
},
171+
vm_resources: VmExecutionResources::default(),
172+
inner_calls: vec![],
173+
storage_read_values: vec![],
174+
accessed_storage_keys: HashSet::new(),
175+
}
176+
}

crates/cheatnet/tests/cheatcodes/mock_call.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::{
66
use cairo_felt::Felt252;
77
use cheatnet::rpc::call_contract;
88
use conversions::StarknetConversions;
9+
use starknet_api::core::ContractAddress;
910

1011
#[test]
1112
fn mock_call_simple() {
@@ -358,3 +359,23 @@ fn mock_call_two_methods() {
358359

359360
assert_success!(output, ret_data);
360361
}
362+
363+
#[test]
364+
fn mock_call_nonexisting_contract() {
365+
let mut state = create_cheatnet_state();
366+
367+
let selector = felt_selector_from_name("get_thing");
368+
let ret_data = vec![Felt252::from(123)];
369+
370+
let contract_address = ContractAddress::from(218_u8);
371+
372+
state.start_mock_call(
373+
contract_address,
374+
&"get_thing".to_owned().to_felt252(),
375+
&ret_data,
376+
);
377+
378+
let output = call_contract(&contract_address, &selector, &[], &mut state).unwrap();
379+
380+
assert_success!(output, ret_data);
381+
}

docs/src/appendix/cheatcodes/start_mock_call.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55
Mocks contract call to a `function_name` of a contract at the given address. A call to function `function_name` will
66
return data provided in `ret_data` argument.
77

8-
If there is a contract deployed at the given address, mocked function won't be executed. Address with no contract can be
9-
mocked as well.
10-
Mock can be canceled with [`stop_mock_call`](./stop_mock_call.md).
8+
An address with no contract can be mocked as well. Mock can be canceled with [`stop_mock_call`](./stop_mock_call.md).
119

1210
- `contract_address` - target contract address
1311
- `function_name` - name of the function in a contract at the `contract_address` that will be mocked

0 commit comments

Comments
 (0)