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 28, 2024
1 parent f9d4b21 commit 75943a1
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 37 deletions.
43 changes: 34 additions & 9 deletions crates/blockifier/src/execution/native/syscall_handler.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::collections::HashSet;
use std::hash::RandomState;
use std::sync::Arc;

use cairo_native::starknet::{
ExecutionInfo,
Expand All @@ -11,12 +12,14 @@ use cairo_native::starknet::{
U256,
};
use cairo_vm::vm::runners::cairo_runner::ExecutionResources;
use starknet_api::core::{ContractAddress, EntryPointSelector};
use starknet_api::contract_class::EntryPointType;
use starknet_api::core::{ClassHash, ContractAddress, 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 @@ -69,13 +72,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 @@ -171,12 +172,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.contract_address,
caller_address: self.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
107 changes: 79 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,7 @@ use crate::test_utils::{
};
use crate::versioned_constants::VersionedConstants;

#[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 +61,7 @@ fn test_library_call(test_contract: FeatureContract, expected_gas: u64) {
);
}

#[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 +83,33 @@ 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'))"
}
CairoVersion::Native => "0x7820213d2079 ('x != y')",
};
assert_eq!(format_panic_data(&call_info.execution.retdata.0), expected_err);
}

#[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.
fn if_native<'a, T>(test_contract: &'a FeatureContract) -> impl Fn(T, T) -> T + 'a {
move |native: T, non_native: T| {
if matches!(test_contract, FeatureContract::TestContract(CairoVersion::Native)) {
native
} else {
non_native
}
}
}

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

Expand All @@ -109,7 +130,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 +139,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 +154,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 +193,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 +234,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 +244,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 75943a1

Please sign in to comment.