Skip to content

Commit 6f96a4a

Browse files
committed
Handle storage read/write syscalls in cheatable runtime
Towards #3331 commit-id:984675b6
1 parent eddea12 commit 6f96a4a

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,
@@ -240,6 +243,76 @@ pub fn get_block_hash_syscall(
240243
Ok(GetBlockHashResponse { block_hash })
241244
}
242245

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