Skip to content

Commit 79fb90b

Browse files
committed
Handle storage read/write syscalls in cheatable runtime
Towards #3331 commit-id:984675b6
1 parent 72c6f93 commit 79fb90b

File tree

2 files changed

+92
-1
lines changed

2 files changed

+92
-1
lines changed

crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/cheated_syscalls.rs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use blockifier::execution::syscalls::hint_processor::{
88
use blockifier::execution::syscalls::syscall_base::SyscallResult;
99
use blockifier::execution::syscalls::vm_syscall_utils::{
1010
CallContractRequest, DeployRequest, DeployResponse, EmptyRequest, GetBlockHashRequest,
11-
GetBlockHashResponse, GetExecutionInfoResponse, LibraryCallRequest, SyscallResponse,
11+
GetBlockHashResponse, GetExecutionInfoResponse, LibraryCallRequest, StorageReadRequest,
12+
StorageReadResponse, StorageWriteRequest, StorageWriteResponse, SyscallResponse,
1213
SyscallSelector, WriteResponseResult,
1314
};
1415
use blockifier::execution::{call_info::CallInfo, entry_point::ConstructorContext};
@@ -28,6 +29,8 @@ use blockifier::{
2829
};
2930
use cairo_vm::types::relocatable::Relocatable;
3031
use cairo_vm::vm::vm_core::VirtualMachine;
32+
use conversions::string::TryFromHexStr;
33+
use runtime::starknet::constants::TEST_ADDRESS;
3134
use starknet_api::core::calculate_contract_address;
3235
use starknet_api::{
3336
contract_class::EntryPointType,
@@ -245,6 +248,76 @@ pub fn get_block_hash_syscall(
245248
Ok(GetBlockHashResponse { block_hash })
246249
}
247250

251+
#[expect(clippy::needless_pass_by_value, clippy::result_large_err)]
252+
pub fn storage_read(
253+
request: StorageReadRequest,
254+
_vm: &mut VirtualMachine,
255+
syscall_handler: &mut SyscallHintProcessor<'_>,
256+
cheatnet_state: &mut CheatnetState,
257+
_remaining_gas: &mut u64,
258+
) -> SyscallResult<StorageReadResponse> {
259+
let original_storage_address = syscall_handler.base.call.storage_address;
260+
maybe_modify_storage_address(syscall_handler, cheatnet_state);
261+
262+
let value = syscall_handler
263+
.base
264+
.storage_read(request.address)
265+
.inspect_err(|_| {
266+
// Restore state on error before bubbling up
267+
syscall_handler.base.call.storage_address = original_storage_address;
268+
})?;
269+
270+
// Restore the original storage_address
271+
syscall_handler.base.call.storage_address = original_storage_address;
272+
273+
Ok(StorageReadResponse { value })
274+
}
275+
276+
#[expect(clippy::needless_pass_by_value, clippy::result_large_err)]
277+
pub fn storage_write(
278+
request: StorageWriteRequest,
279+
_vm: &mut VirtualMachine,
280+
syscall_handler: &mut SyscallHintProcessor<'_>,
281+
cheatnet_state: &mut CheatnetState,
282+
_remaining_gas: &mut u64,
283+
) -> SyscallResult<StorageWriteResponse> {
284+
let original_storage_address = syscall_handler.base.call.storage_address;
285+
maybe_modify_storage_address(syscall_handler, cheatnet_state);
286+
287+
syscall_handler
288+
.base
289+
.storage_write(request.address, request.value)
290+
.inspect_err(|_| {
291+
// Restore state on error before bubbling up
292+
syscall_handler.base.call.storage_address = original_storage_address;
293+
})?;
294+
295+
// Restore the original storage_address
296+
syscall_handler.base.call.storage_address = original_storage_address;
297+
298+
Ok(StorageWriteResponse {})
299+
}
300+
301+
// This logic is used to modify the storage address to enable using `contract_state_for_testing`
302+
// inside `interact_with_state` closure cheatcode.
303+
fn maybe_modify_storage_address(
304+
syscall_handler: &mut SyscallHintProcessor<'_>,
305+
cheatnet_state: &mut CheatnetState,
306+
) {
307+
let contract_address = syscall_handler.storage_address();
308+
let test_address =
309+
TryFromHexStr::try_from_hex_str(TEST_ADDRESS).expect("Failed to parse `TEST_ADDRESS`");
310+
311+
if contract_address != test_address {
312+
return;
313+
}
314+
315+
let cheated_data = cheatnet_state.get_cheated_data(contract_address);
316+
if let Some(actual_address) = cheated_data.contract_address {
317+
syscall_handler.base.call.storage_address = actual_address;
318+
}
319+
}
320+
248321
#[derive(Debug)]
249322
// crates/blockifier/src/execution/syscalls/mod.rs:127 (SingleSegmentResponse)
250323
// It is created here because fields in the original structure are private

crates/cheatnet/src/runtime_extensions/cheatable_starknet_runtime_extension.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,22 @@ impl<'a> ExtensionLogic for CheatableStarknetRuntimeExtension<'a> {
8787
SyscallSelector::GetBlockHash,
8888
)
8989
.map(|()| SyscallHandlingResult::Handled),
90+
SyscallSelector::StorageRead => self
91+
.execute_syscall(
92+
syscall_handler,
93+
vm,
94+
cheated_syscalls::storage_read,
95+
SyscallSelector::StorageRead,
96+
)
97+
.map(|()| SyscallHandlingResult::Handled),
98+
SyscallSelector::StorageWrite => self
99+
.execute_syscall(
100+
syscall_handler,
101+
vm,
102+
cheated_syscalls::storage_write,
103+
SyscallSelector::StorageWrite,
104+
)
105+
.map(|()| SyscallHandlingResult::Handled),
90106
_ => Ok(SyscallHandlingResult::Forwarded),
91107
}
92108
}
@@ -132,6 +148,8 @@ fn get_syscall_cost(
132148
SyscallSelector::Deploy => gas_costs.syscalls.deploy,
133149
SyscallSelector::GetExecutionInfo => gas_costs.syscalls.get_execution_info,
134150
SyscallSelector::GetBlockHash => gas_costs.syscalls.get_block_hash,
151+
SyscallSelector::StorageRead => gas_costs.syscalls.storage_read,
152+
SyscallSelector::StorageWrite => gas_costs.syscalls.storage_write,
135153
_ => unreachable!("Syscall has no associated cost"),
136154
}
137155
}

0 commit comments

Comments
 (0)