Skip to content

Commit

Permalink
feat(blockifier): add library_call cairo native syscall
Browse files Browse the repository at this point in the history
  • Loading branch information
PearsonWhite committed Oct 30, 2024
1 parent 93ac2d0 commit 0ec1f46
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 36 deletions.
42 changes: 34 additions & 8 deletions crates/blockifier/src/execution/native/syscall_handler.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
use std::collections::HashSet;
use std::hash::RandomState;
use std::sync::Arc;

use cairo_native::starknet::{
ExecutionInfo, ExecutionInfoV2, Secp256k1Point, Secp256r1Point, StarknetSyscallHandler,
SyscallResult, U256,
};
use cairo_vm::vm::runners::cairo_runner::ExecutionResources;
use starknet_api::contract_class::EntryPointType;
use starknet_api::core::{ClassHash, EntryPointSelector};
use starknet_api::state::StorageKey;
use starknet_api::transaction::Calldata;
use starknet_types_core::felt::Felt;

use crate::execution::call_info::{CallInfo, OrderedEvent, OrderedL2ToL1Message, Retdata};
use crate::execution::entry_point::{CallEntryPoint, EntryPointExecutionContext};
use crate::execution::entry_point::{CallEntryPoint, CallType, EntryPointExecutionContext};
use crate::execution::native::utils::encode_str_as_felts;
use crate::execution::syscalls::hint_processor::{SyscallCounter, OUT_OF_GAS_ERROR};
use crate::execution::syscalls::SyscallSelector;
Expand Down Expand Up @@ -56,13 +60,11 @@ impl<'state> NativeSyscallHandler<'state> {
}
}

#[allow(dead_code)]
fn increment_syscall_count_by(&mut self, selector: &SyscallSelector, n: usize) {
let syscall_count = self.syscall_counter.entry(*selector).or_default();
*syscall_count += n
}

#[allow(dead_code)]
fn execute_inner_call(
&mut self,
entry_point: CallEntryPoint,
Expand Down Expand Up @@ -156,12 +158,36 @@ impl<'state> StarknetSyscallHandler for &mut NativeSyscallHandler<'state> {

fn library_call(
&mut self,
_class_hash: Felt,
_function_selector: Felt,
_calldata: &[Felt],
_remaining_gas: &mut u128,
class_hash: Felt,
function_selector: Felt,
calldata: &[Felt],
remaining_gas: &mut u128,
) -> SyscallResult<Vec<Felt>> {
todo!("Implement library_call syscall.");
self.substract_syscall_gas_cost(
remaining_gas,
SyscallSelector::LibraryCall,
self.context.gas_costs().library_call_gas_cost,
)?;

let class_hash = ClassHash(class_hash);

let wrapper_calldata = Calldata(Arc::new(calldata.to_vec()));

let entry_point = CallEntryPoint {
class_hash: Some(class_hash),
code_address: None,
entry_point_type: EntryPointType::External,
entry_point_selector: EntryPointSelector(function_selector),
calldata: wrapper_calldata,
// The call context remains the same in a library call.
storage_address: self.call.storage_address,
caller_address: self.call.caller_address,
call_type: CallType::Delegate,
initial_gas: u64::try_from(*remaining_gas)
.expect("Failed to convert gas (u128 -> u64)"),
};

Ok(self.execute_inner_call(entry_point, remaining_gas)?.0)
}

fn call_contract(
Expand Down
125 changes: 97 additions & 28 deletions crates/blockifier/src/execution/syscalls/syscall_tests/library_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ use crate::test_utils::{
};
use crate::versioned_constants::VersionedConstants;

#[cfg_attr(
feature = "cairo_native",
test_case(FeatureContract::TestContract(CairoVersion::Native), 189470; "Native")
)]
#[test_case(FeatureContract::TestContract(CairoVersion::Cairo1), REQUIRED_GAS_LIBRARY_CALL_TEST; "VM")]
fn test_library_call(test_contract: FeatureContract, expected_gas: u64) {
let chain_info = &ChainInfo::create_for_testing();
Expand Down Expand Up @@ -60,6 +64,10 @@ fn test_library_call(test_contract: FeatureContract, expected_gas: u64) {
);
}

#[cfg_attr(
feature = "cairo_native",
test_case(FeatureContract::TestContract(CairoVersion::Native); "Native")
)]
#[test_case(FeatureContract::TestContract(CairoVersion::Cairo1); "VM")]
fn test_library_call_assert_fails(test_contract: FeatureContract) {
let chain_info = &ChainInfo::create_for_testing();
Expand All @@ -81,14 +89,45 @@ fn test_library_call_assert_fails(test_contract: FeatureContract) {

let call_info = entry_point_call.execute_directly(&mut state).unwrap();
assert!(call_info.execution.failed);
assert_eq!(
format_panic_data(&call_info.execution.retdata.0),
"(0x7820213d2079 ('x != y'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'))"
);

let expected_err = match test_contract.cairo_version() {
CairoVersion::Cairo0 | CairoVersion::Cairo1 => {
"(0x7820213d2079 ('x != y'), 0x454e545259504f494e545f4641494c4544 \
('ENTRYPOINT_FAILED'))"
}
#[cfg(feature = "cairo_native")]
CairoVersion::Native => "0x7820213d2079 ('x != y')",
};
assert_eq!(format_panic_data(&call_info.execution.retdata.0), expected_err);
}

#[cfg_attr(
feature = "cairo_native",
test_case(FeatureContract::TestContract(CairoVersion::Native), 518110; "Native")
)]
#[test_case(FeatureContract::TestContract(CairoVersion::Cairo1), 478110; "VM")]
fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) {
// Todo(pwhite) 2024/10/28: Execution resources from the VM & Native are mesaured differently
// helper function to change the expected resource values from both of executions
// When gas is changed to be the same between VM and Native this should be removed.
#[cfg_attr(not(feature = "cairo_native"), allow(unused_variables))]
fn if_native<T>(test_contract: &FeatureContract) -> impl Fn(T, T) -> T + '_ {
move |native: T, non_native: T| {
#[cfg(feature = "cairo_native")]
{
if matches!(test_contract, FeatureContract::TestContract(CairoVersion::Native)) {
native
} else {
non_native
}
}
#[cfg(not(feature = "cairo_native"))]
{
non_native
}
}
}

let chain_info = &ChainInfo::create_for_testing();
let mut state = test_state(chain_info, BALANCE, &[(test_contract, 1)]);

Expand All @@ -109,7 +148,7 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) {
entry_point_selector: selector_from_name("test_nested_library_call"),
calldata: main_entry_point_calldata,
class_hash: Some(test_class_hash),
initial_gas: 9999906600,
initial_gas: if_native(&test_contract)(9999584180, 9999292440),
..trivial_external_entry_point_new(test_contract)
};
let nested_storage_entry_point = CallEntryPoint {
Expand All @@ -118,7 +157,7 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) {
class_hash: Some(test_class_hash),
code_address: None,
call_type: CallType::Delegate,
initial_gas: 9999597720,
initial_gas: if_native(&test_contract)(9999255300, 9998983560),
..trivial_external_entry_point_new(test_contract)
};
let library_entry_point = CallEntryPoint {
Expand All @@ -133,20 +172,35 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) {
class_hash: Some(test_class_hash),
code_address: None,
call_type: CallType::Delegate,
initial_gas: 9999749900,
initial_gas: if_native(&test_contract)(9999417480, 9999135740),
..trivial_external_entry_point_new(test_contract)
};
let storage_entry_point = CallEntryPoint {
calldata: calldata![felt!(key), felt!(value)],
initial_gas: 9999445780,
initial_gas: if_native(&test_contract)(9999093360, 9998831620),
..nested_storage_entry_point
};

let storage_entry_point_resources = ExecutionResources {
n_steps: 247,
n_memory_holes: 0,
builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 7)]),
};
let first_storage_entry_point_resources = if_native(&test_contract)(
ExecutionResources {
n_steps: 180,
n_memory_holes: 0,
builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 2)]),
},
ExecutionResources {
n_steps: 247,
n_memory_holes: 0,
builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 7)]),
},
);
let storage_entry_point_resources = if_native(&test_contract)(
ExecutionResources {
n_steps: 1202,
n_memory_holes: 0,
builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 19)]),
},
first_storage_entry_point_resources.clone(),
);

// The default VersionedConstants is used in the execute_directly call bellow.
let tracked_resource = test_contract.get_class().tracked_resource(
Expand All @@ -157,28 +211,35 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) {
call: nested_storage_entry_point,
execution: CallExecution {
retdata: retdata![felt!(value + 1)],
gas_consumed: REQUIRED_GAS_STORAGE_READ_WRITE_TEST,
gas_consumed: if_native(&test_contract)(27290, REQUIRED_GAS_STORAGE_READ_WRITE_TEST),
..CallExecution::default()
},
resources: storage_entry_point_resources.clone(),
resources: first_storage_entry_point_resources,
tracked_resource,
storage_read_values: vec![felt!(value + 1)],
accessed_storage_keys: HashSet::from([StorageKey(patricia_key!(key + 1))]),
..Default::default()
};

let library_call_resources = &get_syscall_resources(SyscallSelector::LibraryCall)
+ &ExecutionResources {
n_steps: 392,
let library_call_resources = if_native(&test_contract)(
ExecutionResources {
n_steps: 1022,
n_memory_holes: 0,
builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 15)]),
};
builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 17)]),
},
&get_syscall_resources(SyscallSelector::LibraryCall)
+ &ExecutionResources {
n_steps: 392,
n_memory_holes: 0,
builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 15)]),
},
);

let library_call_info = CallInfo {
call: library_entry_point,
execution: CallExecution {
retdata: retdata![felt!(value + 1)],
gas_consumed: REQUIRED_GAS_LIBRARY_CALL_TEST,
gas_consumed: if_native(&test_contract)(189470, REQUIRED_GAS_LIBRARY_CALL_TEST),
..CallExecution::default()
},
resources: library_call_resources,
Expand All @@ -191,7 +252,7 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) {
call: storage_entry_point,
execution: CallExecution {
retdata: retdata![felt!(value)],
gas_consumed: REQUIRED_GAS_STORAGE_READ_WRITE_TEST,
gas_consumed: if_native(&test_contract)(27290, REQUIRED_GAS_STORAGE_READ_WRITE_TEST),
..CallExecution::default()
},
resources: storage_entry_point_resources,
Expand All @@ -201,12 +262,20 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) {
..Default::default()
};

let main_call_resources = &(&get_syscall_resources(SyscallSelector::LibraryCall) * 3)
+ &ExecutionResources {
n_steps: 757,
n_memory_holes: 2,
builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 27)]),
};
let main_call_resources = if_native(&test_contract)(
ExecutionResources {
n_steps: 2886,
n_memory_holes: 0,
builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 49)]),
},
&(&get_syscall_resources(SyscallSelector::LibraryCall) * 3)
+ &ExecutionResources {
n_steps: 757,
n_memory_holes: 2,
builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 27)]),
},
);

let expected_call_info = CallInfo {
call: main_entry_point.clone(),
execution: CallExecution {
Expand Down

0 comments on commit 0ec1f46

Please sign in to comment.