Skip to content
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

Continue cairo0 execution #96

Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 157 additions & 3 deletions src/execution/deprecated_syscall_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ use std::cell::RefCell;
use std::rc::Rc;

use blockifier::execution::execution_utils::ReadOnlySegments;
use cairo_vm::types::relocatable::Relocatable;
use cairo_type_derive::FieldOffsetGetters;
use cairo_vm::types::relocatable::{MaybeRelocatable, Relocatable};
use cairo_vm::vm::errors::hint_errors::HintError;
use cairo_vm::vm::vm_core::VirtualMachine;
use cairo_vm::Felt252;

use super::helper::ExecutionHelperWrapper;

Expand All @@ -21,6 +25,25 @@ pub struct DeprecatedOsSyscallHandlerWrapper {
pub deprecated_syscall_handler: Rc<RefCell<DeprecatedOsSyscallHandler>>,
}

#[derive(FieldOffsetGetters)]
pub struct CallContractRequest {
selector: Felt252,
contract_address: Felt252,
function_selector: Felt252,
calldata_size: Felt252,
calldata: Relocatable,
}
#[derive(FieldOffsetGetters)]
pub struct CallContractResponse {
retdata_size: Felt252,
retdata: Relocatable,
}
#[derive(FieldOffsetGetters)]
pub struct CallContract {
request: CallContractRequest,
response: CallContractResponse,
}

impl DeprecatedOsSyscallHandlerWrapper {
// TODO(#69): implement the syscalls
pub fn new(exec_wrapper: ExecutionHelperWrapper, syscall_ptr: Relocatable) -> Self {
Expand All @@ -32,8 +55,37 @@ impl DeprecatedOsSyscallHandlerWrapper {
})),
}
}
pub fn call_contract(&self, syscall_ptr: Relocatable) {
println!("call_contract (TODO): {}", syscall_ptr);
pub fn call_contract(&self, syscall_ptr: Relocatable, vm: &mut VirtualMachine) -> Result<(), HintError> {
let sys_hand = self.deprecated_syscall_handler.as_ref().borrow();
let result = sys_hand
.exec_wrapper
.execution_helper
.as_ref()
.borrow_mut()
.result_iter
.next()
.expect("A call execution should have a corresponding result");

let response_offset = CallContract::response_offset() * 5; // TODO: response_offset() doesn't seem to take sizeof(CallContractRequest) into account
let retdata_size_offset = response_offset + CallContractResponse::retdata_size_offset();
let retdata_offset = response_offset + CallContractResponse::retdata_offset();

vm.insert_value((syscall_ptr + retdata_size_offset).unwrap(), result.retdata.0.len()).unwrap();
let new_segment = vm.add_temporary_segment();
let retdata = result
.retdata
.0
.iter()
.map(|sf| {
// TODO: better way to StarkFelt -> Felt252?
let felt = Felt252::from_hex(&sf.to_string()).unwrap();
notlesh marked this conversation as resolved.
Show resolved Hide resolved
MaybeRelocatable::Int(felt)
})
.collect();
vm.load_data(new_segment, &retdata)?;
vm.insert_value((syscall_ptr + retdata_offset).unwrap(), new_segment)?;

Ok(())
}
pub fn delegate_call(&self, syscall_ptr: Relocatable) {
println!("delegate_call (TODO): {}", syscall_ptr);
Expand Down Expand Up @@ -99,3 +151,105 @@ impl DeprecatedOsSyscallHandlerWrapper {
self.deprecated_syscall_handler.as_ref().borrow().syscall_ptr
}
}

#[cfg(test)]
mod test {
use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::Arc;

use blockifier::block_context::{BlockContext, FeeTokenAddresses, GasPrices};
use blockifier::execution::call_info::Retdata;
use blockifier::execution::entry_point_execution::CallResult;
use cairo_vm::serde::deserialize_program::ApTracking;
use cairo_vm::types::exec_scope::ExecutionScopes;
use cairo_vm::types::relocatable::{MaybeRelocatable, Relocatable};
use cairo_vm::vm::vm_core::VirtualMachine;
use cairo_vm::Felt252;
use rstest::{fixture, rstest};
use starknet_api::block::{BlockNumber, BlockTimestamp};
use starknet_api::core::{ChainId, ContractAddress, PatriciaKey};
use starknet_api::hash::{StarkFelt, StarkHash};
use starknet_api::{contract_address, patricia_key};

use crate::execution::deprecated_syscall_handler::DeprecatedOsSyscallHandlerWrapper;
use crate::execution::helper::ExecutionHelperWrapper;
use crate::hints::vars;

#[fixture]
fn block_context() -> BlockContext {
BlockContext {
chain_id: ChainId("SN_GOERLI".to_string()),
block_number: BlockNumber(1_000_000),
block_timestamp: BlockTimestamp(1_704_067_200),
sequencer_address: contract_address!("0x0"),
fee_token_addresses: FeeTokenAddresses {
eth_fee_token_address: contract_address!("0x1"),
strk_fee_token_address: contract_address!("0x2"),
},
vm_resource_fee_cost: Arc::new(HashMap::new()),
gas_prices: GasPrices { eth_l1_gas_price: 1, strk_l1_gas_price: 1 },
invoke_tx_max_n_steps: 1,
validate_max_n_steps: 1,
max_recursion_depth: 50,
}
}

#[rstest]
fn test_call_contract(block_context: BlockContext) {
let mut vm = VirtualMachine::new(false);
vm.set_fp(1);
vm.add_memory_segment();
vm.add_memory_segment();

let syscall_ptr = vm.add_memory_segment();

let mut exec_scopes = ExecutionScopes::new();

let execution_infos = Default::default();
let exec_helper = ExecutionHelperWrapper::new(execution_infos, &block_context);

// insert a call result for call_contract to replay. it should insert this into a new temporary
// segment and insert its size somewhere in syscall_ptr.
let call_results = vec![CallResult {
failed: false,
retdata: Retdata(vec![StarkFelt::THREE, StarkFelt::TWO, StarkFelt::ONE]),
gas_consumed: 1,
}];
exec_helper.execution_helper.as_ref().borrow_mut().result_iter = call_results.into_iter();

let exec_helper_box = Box::new(exec_helper);
exec_scopes.insert_box(vars::scopes::EXECUTION_HELPER, exec_helper_box.clone());

let syscall_handler = DeprecatedOsSyscallHandlerWrapper::new(*exec_helper_box, syscall_ptr);

syscall_handler.call_contract(syscall_ptr, &mut vm).unwrap();

// syscall_ptr should have been filled out syscall_ptr segment with a CallContractResponse
let syscall_data_raw = vm.get_range(syscall_ptr, 7); // TODO: derive from struct size?
let expected_temp_segment = Relocatable { segment_index: -1, offset: 0 };
assert_eq!(
syscall_data_raw,
vec![
None,
None,
None,
None,
None,
Some(Cow::Borrowed(&MaybeRelocatable::Int(Felt252::THREE))),
Some(Cow::Borrowed(&MaybeRelocatable::RelocatableValue(expected_temp_segment))),
]
);

// the retdata should have been copied into the temp segment
let retdata_raw = vm.get_range(expected_temp_segment, 3);
assert_eq!(
retdata_raw,
vec![
Some(Cow::Borrowed(&MaybeRelocatable::Int(Felt252::THREE))),
Some(Cow::Borrowed(&MaybeRelocatable::Int(Felt252::TWO))),
Some(Cow::Borrowed(&MaybeRelocatable::Int(Felt252::ONE))),
]
);
}
}
4 changes: 1 addition & 3 deletions src/execution/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,7 @@ impl ExecutionHelperWrapper {
pub fn exit_call(&mut self) {
let mut eh_ref = self.execution_helper.as_ref().borrow_mut();
eh_ref.call_execution_info_ptr = None;
// TODO: uncomment
println!("don't forget to uncomment!");
// assert_iterators_exhausted(&eh_ref);
assert_iterators_exhausted(&eh_ref);
assert!(eh_ref.call_info.is_some());
eh_ref.call_info = None;
}
Expand Down
42 changes: 42 additions & 0 deletions src/hints/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use cairo_vm::vm::errors::hint_errors::HintError;
use cairo_vm::vm::vm_core::VirtualMachine;
use cairo_vm::Felt252;
use indoc::indoc;
use num_traits::ToPrimitive;

use crate::cairo_types::structs::{EntryPointReturnValues, ExecutionContext};
use crate::execution::deprecated_syscall_handler::DeprecatedOsSyscallHandlerWrapper;
Expand Down Expand Up @@ -835,3 +836,44 @@ pub fn check_execution(

Ok(())
}

pub const COMPARE_RETURN_VALUE: &str = indoc! {r#"
# Check that the actual return value matches the expected one.
expected = memory.get_range(
addr=ids.call_response.retdata, size=ids.call_response.retdata_size
)
actual = memory.get_range(addr=ids.retdata, size=ids.retdata_size)

assert expected == actual, f'Return value mismatch expected={expected}, actual={actual}.'"#
};

pub fn compare_return_value(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
_constants: &HashMap<String, Felt252>,
) -> Result<(), HintError> {
let call_response = get_ptr_from_var_name("call_response", vm, ids_data, ap_tracking)?;
// the first field in call_data is the size
let size = vm.get_integer(call_response)?.into_owned().to_usize().unwrap();
// the second field is immediately afterward, but needs to be dereferenced
let expected_retdata_ptr = vm.get_relocatable((call_response + 1)?)?;
let expected = vm.get_range(expected_retdata_ptr, size);

let ids_retdata = get_ptr_from_var_name("retdata", vm, ids_data, ap_tracking)?;
let ids_retdata_size =
get_integer_from_var_name("retdata_size", vm, ids_data, ap_tracking)?.into_owned().to_usize().unwrap();

let actual = vm.get_range(ids_retdata, ids_retdata_size);

if expected != actual {
println!("expected: {:?}", expected);
println!("actual: {:?}", actual);

// assert_eq!(expected, actual, "Return value mismatch");
return Err(HintError::AssertNotEqualFail(Box::new((call_response.into(), ids_retdata.into()))));
}

Ok(())
}
7 changes: 5 additions & 2 deletions src/hints/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub mod syscalls;
#[cfg(test)]
mod tests;
mod unimplemented;
mod vars;
pub mod vars;

type HintImpl = fn(
&mut VirtualMachine,
Expand All @@ -43,7 +43,7 @@ type HintImpl = fn(
&HashMap<String, Felt252>,
) -> Result<(), HintError>;

static HINTS: [(&str, HintImpl); 87] = [
static HINTS: [(&str, HintImpl); 90] = [
(INITIALIZE_CLASS_HASHES, initialize_class_hashes),
(INITIALIZE_STATE_CHANGES, initialize_state_changes),
(IS_N_GE_TWO, is_n_ge_two),
Expand Down Expand Up @@ -78,6 +78,7 @@ static HINTS: [(&str, HintImpl); 87] = [
(execution::ASSERT_TRANSACTION_HASH, execution::assert_transaction_hash),
(execution::CHECK_IS_DEPRECATED, execution::check_is_deprecated),
(execution::CHECK_EXECUTION, execution::check_execution),
(execution::COMPARE_RETURN_VALUE, execution::compare_return_value),
(execution::CONTRACT_ADDRESS, execution::contract_address),
(execution::END_TX, execution::end_tx),
(execution::ENTER_CALL, execution::enter_call),
Expand Down Expand Up @@ -125,11 +126,13 @@ static HINTS: [(&str, HintImpl); 87] = [
(syscalls::GET_TX_SIGNATURE, syscalls::get_tx_signature),
(syscalls::LIBRARY, syscalls::library_call),
(syscalls::LIBRARY_CALL_L1_HANDLER, syscalls::library_call_l1_handler),
(syscalls::OS_LOGGER_ENTER_SYSCALL_PREPRARE_EXIT_SYSCALL, syscalls::os_logger_enter_syscall_preprare_exit_syscall),
(syscalls::REPLACE_CLASS, syscalls::replace_class),
(syscalls::SEND_MESSAGE_TO_L1, syscalls::send_message_to_l1),
(syscalls::STORAGE_READ, syscalls::storage_read),
(syscalls::STORAGE_WRITE, syscalls::storage_write),
(syscalls::SET_SYSCALL_PTR, syscalls::set_syscall_ptr),
(syscalls::EXIT_SYSCALL, syscalls::exit_syscall),
(BREAKPOINT, breakpoint),
];

Expand Down
54 changes: 50 additions & 4 deletions src/hints/syscalls.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::collections::HashMap;

use cairo_vm::hint_processor::builtin_hint_processor::hint_utils::{get_ptr_from_var_name, insert_value_from_var_name};
use cairo_vm::hint_processor::builtin_hint_processor::hint_utils::{
get_ptr_from_var_name, insert_value_from_var_name,
};
use cairo_vm::hint_processor::hint_processor_definition::HintReference;
use cairo_vm::serde::deserialize_program::ApTracking;
use cairo_vm::types::exec_scope::ExecutionScopes;
Expand All @@ -25,7 +27,7 @@ pub fn call_contract(
let syscall_handler = exec_scopes.get::<DeprecatedOsSyscallHandlerWrapper>("syscall_handler")?;
let syscall_ptr = get_ptr_from_var_name("syscall_ptr", vm, ids_data, ap_tracking)?;

syscall_handler.call_contract(syscall_ptr);
syscall_handler.call_contract(syscall_ptr, vm)?;

Ok(())
}
Expand Down Expand Up @@ -352,6 +354,50 @@ pub fn set_syscall_ptr(
Ok(())
}

pub const OS_LOGGER_ENTER_SYSCALL_PREPRARE_EXIT_SYSCALL: &str = indoc! {r#"
execution_helper.os_logger.enter_syscall(
n_steps=current_step,
builtin_ptrs=ids.builtin_ptrs,
deprecated=True,
selector=ids.selector,
range_check_ptr=ids.range_check_ptr,
)

# Prepare a short callable to save code duplication.
exit_syscall = lambda selector: execution_helper.os_logger.exit_syscall(
n_steps=current_step,
builtin_ptrs=ids.builtin_ptrs,
range_check_ptr=ids.range_check_ptr,
selector=selector,
)"#
};
pub fn os_logger_enter_syscall_preprare_exit_syscall(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
_constants: &HashMap<String, Felt252>,
) -> Result<(), HintError> {
println!("TODO: os_logger enter/exit calls");

Ok(())
}

pub const EXIT_SYSCALL: &str = indoc! {r#"
exit_syscall(selector=ids.CALL_CONTRACT_SELECTOR)"#
};
pub fn exit_syscall(
_vm: &mut VirtualMachine,
_exec_scopes: &mut ExecutionScopes,
_ids_data: &HashMap<String, HintReference>,
_ap_tracking: &ApTracking,
_constants: &HashMap<String, Felt252>,
) -> Result<(), HintError> {
println!("exit_syscall hint: TODO");

Ok(())
}

#[cfg(test)]
mod tests {
use blockifier::block_context::BlockContext;
Expand All @@ -367,7 +413,7 @@ mod tests {
let syscall_ptr = Relocatable::from((0, 0));
let execution_infos = vec![];
let exec_helper = ExecutionHelperWrapper::new(execution_infos, &block_context);
let syscall_handler = OsSyscallHandlerWrapper::new(exec_helper, syscall_ptr);
let syscall_handler = OsSyscallHandlerWrapper::new(exec_helper);

let mut exec_scopes = ExecutionScopes::new();
exec_scopes.insert_value(vars::scopes::SYSCALL_HANDLER, syscall_handler);
Expand Down Expand Up @@ -399,6 +445,6 @@ mod tests {
assert_eq!(syscall_ptr, Relocatable::from((3, 0)));

let syscall_handler: OsSyscallHandlerWrapper = exec_scopes.get(vars::scopes::SYSCALL_HANDLER).unwrap();
assert_eq!(syscall_handler.syscall_ptr(), syscall_ptr);
assert_eq!(syscall_handler.syscall_ptr(), Some(syscall_ptr));
}
}
11 changes: 0 additions & 11 deletions src/hints/unimplemented.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,17 +253,6 @@ const SET_CONTRACT_ADDRESS: &str = indoc! {r#"
#[allow(unused)]
const SET_AP_TO_NONCE_ARG_SEGMENT: &str = "memory[ap] = to_felt_or_relocatable(segments.gen_arg([tx.nonce]))";

#[allow(unused)]
const COMPARE_RETURN_VALUE: &str = indoc! {r#"
# Check that the actual return value matches the expected one.
expected = memory.get_range(
addr=ids.call_response.retdata, size=ids.call_response.retdata_size
)
actual = memory.get_range(addr=ids.retdata, size=ids.retdata_size)

assert expected == actual, f'Return value mismatch expected={expected}, actual={actual}.'"#
};

#[allow(unused)]
const SPLIT_DESCEND: &str = "ids.length, ids.word = descend";

Expand Down