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

refactor: split syscalls into separate files #344

Merged
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ tmp_venv/*
/.vscode
# Git hooks
/.husky
/.idea

# Python artifacts.
scripts/__pycache__
3 changes: 1 addition & 2 deletions crates/blockifier/src/execution/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ pub mod hint_processor;
mod secp;

#[cfg(test)]
#[path = "syscalls_test.rs"]
pub mod syscalls_test;
pub mod syscall_tests;

pub type SyscallResult<T> = Result<T, SyscallExecutionError>;
pub type WriteResponseResult = SyscallResult<()>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use pretty_assertions::assert_eq;
use starknet_api::felt;
use test_case::test_case;

use super::constants::REQUIRED_GAS_CALL_CONTRACT_TEST;
use crate::abi::abi_utils::selector_from_name;
use crate::context::ChainInfo;
use crate::execution::call_info::{CallExecution, Retdata};
use crate::execution::entry_point::CallEntryPoint;
use crate::retdata;
use crate::test_utils::contracts::FeatureContract;
use crate::test_utils::initial_test_state::test_state;
use crate::test_utils::{create_calldata, trivial_external_entry_point_new, CairoVersion, BALANCE};

#[test_case(
FeatureContract::TestContract(CairoVersion::Cairo1),
FeatureContract::TestContract(CairoVersion::Cairo1),
REQUIRED_GAS_CALL_CONTRACT_TEST;
"Call Contract between two contracts using VM"
)]
fn test_call_contract(
outer_contract: FeatureContract,
inner_contract: FeatureContract,
expected_gas: u64,
) {
let chain_info = &ChainInfo::create_for_testing();
let mut state = test_state(chain_info, BALANCE, &[(outer_contract, 1), (inner_contract, 1)]);

let outer_entry_point_selector = selector_from_name("test_call_contract");
let calldata = create_calldata(
inner_contract.get_instance_address(0),
"test_storage_read_write",
&[
felt!(405_u16), // Calldata: address.
felt!(48_u8), // Calldata: value.
],
);
let entry_point_call = CallEntryPoint {
entry_point_selector: outer_entry_point_selector,
calldata,
..trivial_external_entry_point_new(outer_contract)
};

assert_eq!(
entry_point_call.execute_directly(&mut state).unwrap().execution,
CallExecution {
retdata: retdata![felt!(48_u8)],
gas_consumed: expected_gas,
..CallExecution::default()
}
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub const REQUIRED_GAS_CALL_CONTRACT_TEST: u64 = 105680;
pub const REQUIRED_GAS_STORAGE_READ_WRITE_TEST: u64 = 27150;
pub const REQUIRED_GAS_LIBRARY_CALL_TEST: u64 = REQUIRED_GAS_CALL_CONTRACT_TEST;
152 changes: 152 additions & 0 deletions crates/blockifier/src/execution/syscalls/syscall_tests/deploy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use pretty_assertions::assert_eq;
use starknet_api::core::calculate_contract_address;
use starknet_api::transaction::{Calldata, ContractAddressSalt};
use starknet_api::{calldata, felt};
use test_case::test_case;

use crate::abi::abi_utils::selector_from_name;
use crate::context::ChainInfo;
use crate::execution::call_info::{CallExecution, Retdata};
use crate::execution::entry_point::CallEntryPoint;
use crate::retdata;
use crate::state::state_api::StateReader;
use crate::test_utils::contracts::FeatureContract;
use crate::test_utils::initial_test_state::test_state;
use crate::test_utils::{calldata_for_deploy_test, trivial_external_entry_point_new, CairoVersion};

#[test_case(FeatureContract::TestContract(CairoVersion::Cairo1);"VM")]
fn no_constructor(deployer_contract: FeatureContract) {
// TODO(Yoni): share the init code of the tests in this file.

let empty_contract = FeatureContract::Empty(CairoVersion::Cairo1);
let class_hash = empty_contract.get_class_hash();

let mut state = test_state(
&ChainInfo::create_for_testing(),
0,
&[(deployer_contract, 1), (empty_contract, 0)],
);

let calldata = calldata_for_deploy_test(class_hash, &[], true);
let entry_point_call = CallEntryPoint {
entry_point_selector: selector_from_name("test_deploy"),
calldata,
..trivial_external_entry_point_new(deployer_contract)
};
let deployed_contract_address = calculate_contract_address(
ContractAddressSalt::default(),
class_hash,
&calldata![],
deployer_contract.get_instance_address(0),
)
.unwrap();

let deploy_call = &entry_point_call.execute_directly(&mut state).unwrap().inner_calls[0];

assert_eq!(deploy_call.call.storage_address, deployed_contract_address);
assert_eq!(
deploy_call.execution,
CallExecution { retdata: retdata![], gas_consumed: 0, ..CallExecution::default() }
);
assert_eq!(state.get_class_hash_at(deployed_contract_address).unwrap(), class_hash);
}

#[test_case(FeatureContract::TestContract(CairoVersion::Cairo1);"VM")]
fn no_constructor_nonempty_calldata(deployer_contract: FeatureContract) {
let empty_contract = FeatureContract::Empty(CairoVersion::Cairo1);
let class_hash = empty_contract.get_class_hash();

let mut state = test_state(
&ChainInfo::create_for_testing(),
0,
&[(deployer_contract, 1), (empty_contract, 0)],
);

let calldata = calldata_for_deploy_test(class_hash, &[felt!(1_u8), felt!(1_u8)], true);

let entry_point_call = CallEntryPoint {
entry_point_selector: selector_from_name("test_deploy"),
calldata,
..trivial_external_entry_point_new(deployer_contract)
};

let error = entry_point_call.execute_directly(&mut state).unwrap_err().to_string();
assert!(error.contains(
"Invalid input: constructor_calldata; Cannot pass calldata to a contract with no \
constructor."
));
}

#[test_case(FeatureContract::TestContract(CairoVersion::Cairo1), 10140;"VM")]
fn with_constructor(deployer_contract: FeatureContract, expected_gas: u64) {
let empty_contract = FeatureContract::Empty(CairoVersion::Cairo1);
let mut state = test_state(
&ChainInfo::create_for_testing(),
0,
&[(deployer_contract, 1), (empty_contract, 0)],
);

let class_hash = deployer_contract.get_class_hash();
let constructor_calldata = vec![
felt!(1_u8), // Calldata: address.
felt!(1_u8), // Calldata: value.
];

let calldata = calldata_for_deploy_test(class_hash, &constructor_calldata, true);

let entry_point_call = CallEntryPoint {
entry_point_selector: selector_from_name("test_deploy"),
calldata,
..trivial_external_entry_point_new(deployer_contract)
};

// No errors expected.
let contract_address = calculate_contract_address(
ContractAddressSalt::default(),
class_hash,
&Calldata(constructor_calldata.clone().into()),
deployer_contract.get_instance_address(0),
)
.unwrap();
let deploy_call = &entry_point_call.execute_directly(&mut state).unwrap().inner_calls[0];

assert_eq!(deploy_call.call.storage_address, contract_address);
assert_eq!(
deploy_call.execution,
CallExecution {
retdata: retdata![constructor_calldata[0]],
gas_consumed: expected_gas,
..CallExecution::default()
}
);
assert_eq!(state.get_class_hash_at(contract_address).unwrap(), class_hash);
}

#[test_case(FeatureContract::TestContract(CairoVersion::Cairo1);"VM")]
fn to_unavailable_address(deployer_contract: FeatureContract) {
let empty_contract = FeatureContract::Empty(CairoVersion::Cairo1);
let mut state = test_state(
&ChainInfo::create_for_testing(),
0,
&[(deployer_contract, 1), (empty_contract, 0)],
);

let class_hash = deployer_contract.get_class_hash();
let constructor_calldata = vec![
felt!(1_u8), // Calldata: address.
felt!(1_u8), // Calldata: value.
];

let calldata = calldata_for_deploy_test(class_hash, &constructor_calldata, true);

let entry_point_call = CallEntryPoint {
entry_point_selector: selector_from_name("test_deploy"),
calldata,
..trivial_external_entry_point_new(deployer_contract)
};

entry_point_call.clone().execute_directly(&mut state).unwrap();
let error = entry_point_call.execute_directly(&mut state).unwrap_err().to_string();

assert!(error.contains("is unavailable for deployment."));
}
119 changes: 119 additions & 0 deletions crates/blockifier/src/execution/syscalls/syscall_tests/emit_event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use itertools::concat;
use pretty_assertions::assert_eq;
use starknet_api::felt;
use starknet_api::transaction::{Calldata, EventContent, EventData, EventKey};
use starknet_types_core::felt::Felt;
use test_case::test_case;

use crate::abi::abi_utils::selector_from_name;
use crate::context::ChainInfo;
use crate::execution::call_info::{CallExecution, CallInfo, OrderedEvent};
use crate::execution::entry_point::CallEntryPoint;
use crate::execution::errors::EntryPointExecutionError;
use crate::execution::syscalls::hint_processor::EmitEventError;
use crate::test_utils::contracts::FeatureContract;
use crate::test_utils::initial_test_state::test_state;
use crate::test_utils::{trivial_external_entry_point_new, CairoVersion, BALANCE};
use crate::versioned_constants::VersionedConstants;

const KEYS: [Felt; 2] = [Felt::from_hex_unchecked("0x2019"), Felt::from_hex_unchecked("0x2020")];
const DATA: [Felt; 3] = [
Felt::from_hex_unchecked("0x2021"),
Felt::from_hex_unchecked("0x2022"),
Felt::from_hex_unchecked("0x2023"),
];
const N_EMITTED_EVENTS: [Felt; 1] = [Felt::from_hex_unchecked("0x1")];

#[test_case(FeatureContract::TestContract(CairoVersion::Cairo1), 49860; "VM")]
fn positive_flow(test_contract: FeatureContract, expected_gas: u64) {
// TODO(Ori, 1/2/2024): Write an indicative expect message explaining why the conversion
// works.
let call_info = emit_events(test_contract, &N_EMITTED_EVENTS, &KEYS, &DATA).unwrap();
let event = EventContent {
keys: KEYS.into_iter().map(EventKey).collect(),
data: EventData(DATA.to_vec()),
};

assert_eq!(
call_info.execution,
CallExecution {
events: vec![OrderedEvent { order: 0, event }],
gas_consumed: expected_gas,
..Default::default()
}
);
}

#[test_case(FeatureContract::TestContract(CairoVersion::Cairo1); "VM")]
fn data_length_exceeds_limit(test_contract: FeatureContract) {
let versioned_constants = VersionedConstants::create_for_testing();

let max_event_data_length = versioned_constants.tx_event_limits.max_data_length;
let data_too_long = vec![felt!(2_u16); max_event_data_length + 1];
let error = emit_events(test_contract, &N_EMITTED_EVENTS, &KEYS, &data_too_long).unwrap_err();
let expected_error = EmitEventError::ExceedsMaxDataLength {
data_length: max_event_data_length + 1,
max_data_length: max_event_data_length,
};
assert!(error.to_string().contains(&expected_error.to_string()));
}

#[test_case(FeatureContract::TestContract(CairoVersion::Cairo1); "VM")]
fn keys_length_exceeds_limit(test_contract: FeatureContract) {
let versioned_constants = VersionedConstants::create_for_testing();

let max_event_keys_length = versioned_constants.tx_event_limits.max_keys_length;
let keys_too_long = vec![felt!(1_u16); max_event_keys_length + 1];
let error = emit_events(test_contract, &N_EMITTED_EVENTS, &keys_too_long, &DATA).unwrap_err();
let expected_error = EmitEventError::ExceedsMaxKeysLength {
keys_length: max_event_keys_length + 1,
max_keys_length: max_event_keys_length,
};

assert!(error.to_string().contains(&expected_error.to_string()));
}

#[test_case(FeatureContract::TestContract(CairoVersion::Cairo1); "VM")]
fn event_number_exceeds_limit(test_contract: FeatureContract) {
let versioned_constants = VersionedConstants::create_for_testing();

let max_n_emitted_events = versioned_constants.tx_event_limits.max_n_emitted_events;
let n_emitted_events_too_big = vec![felt!(
u16::try_from(max_n_emitted_events + 1).expect("Failed to convert usize to u16.")
)];
let error = emit_events(test_contract, &n_emitted_events_too_big, &KEYS, &DATA).unwrap_err();
let expected_error = EmitEventError::ExceedsMaxNumberOfEmittedEvents {
n_emitted_events: max_n_emitted_events + 1,
max_n_emitted_events,
};
assert!(error.to_string().contains(&expected_error.to_string()));
}

fn emit_events(
test_contract: FeatureContract,
n_emitted_events: &[Felt],
keys: &[Felt],
data: &[Felt],
) -> Result<CallInfo, EntryPointExecutionError> {
let chain_info = &ChainInfo::create_for_testing();
let mut state = test_state(chain_info, BALANCE, &[(test_contract, 1)]);
let calldata = Calldata(
concat(vec![
n_emitted_events.to_owned(),
vec![felt!(u16::try_from(keys.len()).expect("Failed to convert usize to u16."))],
keys.to_vec(),
vec![felt!(u16::try_from(data.len()).expect("Failed to convert usize to u16."))],
data.to_vec(),
])
.into(),
);

let entry_point_call = CallEntryPoint {
entry_point_selector: selector_from_name("test_emit_events"),
calldata,
..trivial_external_entry_point_new(test_contract)
};

let result = entry_point_call.execute_directly(&mut state);
result
}
Loading