use std::fmt::LowerHex; use std::fs; use std::path::Path; use std::process::{Child, Command}; use cairo_lang_starknet::casm_contract_class::CasmContractClass; use hyper::{Body, Response}; use starknet_core::random_number_generator::generate_u32_random_number; use starknet_rs_core::types::contract::SierraClass; use starknet_rs_core::types::{ExecutionResult, FieldElement, FlattenedSierraClass}; use starknet_rs_providers::Provider; use starknet_rs_signers::LocalWallet; use starknet_types::contract_class::compute_casm_class_hash; pub async fn get_json_body(resp: Response) -> serde_json::Value { let resp_body = resp.into_body(); let resp_body_bytes = hyper::body::to_bytes(resp_body).await.unwrap(); serde_json::from_slice(&resp_body_bytes).unwrap() } pub async fn get_string_body(resp: Response) -> String { let resp_body = resp.into_body(); let body_bytes = hyper::body::to_bytes(resp_body).await.unwrap(); String::from_utf8(body_bytes.to_vec()).unwrap() } /// dummy testing value pub fn get_deployable_account_signer() -> LocalWallet { let new_account_private_key = "0xc248668388dbe9acdfa3bc734cc2d57a"; starknet_rs_signers::LocalWallet::from(starknet_rs_signers::SigningKey::from_secret_scalar( FieldElement::from_hex_be(new_account_private_key).unwrap(), )) } /// resolve a path relative to the current directory (starknet-server) pub fn resolve_path(relative_path: &str) -> String { let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); format!("{manifest_dir}/{relative_path}") } pub fn remove_file(path: &str) { let file_path = Path::new(path); if file_path.exists() { fs::remove_file(file_path).expect("Could not remove file"); } } pub fn load_json(path: &str) -> T { let reader = std::fs::File::open(path).unwrap(); let loaded: T = serde_json::from_reader(reader).unwrap(); loaded } pub fn get_flattened_sierra_contract_and_casm_hash( sierra_path: &str, ) -> (FlattenedSierraClass, FieldElement) { let sierra_string = std::fs::read_to_string(sierra_path).unwrap(); let sierra_class: SierraClass = serde_json::from_str(&sierra_string).unwrap(); let contract_class: cairo_lang_starknet::contract_class::ContractClass = serde_json::from_str(&sierra_string).unwrap(); let casm_contract_class = CasmContractClass::from_contract_class(contract_class, false).unwrap(); let compiled_class_hash = compute_casm_class_hash(&casm_contract_class).unwrap(); (sierra_class.flatten().unwrap(), compiled_class_hash.into()) } pub fn get_messaging_contract_in_sierra_and_compiled_class_hash() -> (FlattenedSierraClass, FieldElement) { let sierra_path = concat!(env!("CARGO_MANIFEST_DIR"), "/test_data/cairo1/messaging/cairo_1_l1l2.sierra"); get_flattened_sierra_contract_and_casm_hash(sierra_path) } pub fn get_messaging_lib_in_sierra_and_compiled_class_hash() -> (FlattenedSierraClass, FieldElement) { let sierra_path = concat!(env!("CARGO_MANIFEST_DIR"), "/test_data/cairo1/messaging/cairo_1_l1l2_lib.sierra"); get_flattened_sierra_contract_and_casm_hash(sierra_path) } pub fn get_events_contract_in_sierra_and_compiled_class_hash() -> (FlattenedSierraClass, FieldElement) { let events_sierra_path = concat!( env!("CARGO_MANIFEST_DIR"), "/test_data/cairo1/events/events_2.0.1_compiler.sierra" ); get_flattened_sierra_contract_and_casm_hash(events_sierra_path) } pub fn get_timestamp_contract_in_sierra_and_compiled_class_hash() -> (FlattenedSierraClass, FieldElement) { let timestamp_sierra_path = concat!(env!("CARGO_MANIFEST_DIR"), "/test_data/cairo1/timestamp/timestamp.json"); get_flattened_sierra_contract_and_casm_hash(timestamp_sierra_path) } pub async fn assert_tx_successful(tx_hash: &FieldElement, client: &T) { let receipt = client.get_transaction_receipt(tx_hash).await.unwrap(); match receipt.execution_result() { ExecutionResult::Succeeded => (), other => panic!("Should have succeeded; got: {other:?}"), } } pub async fn assert_tx_reverted( tx_hash: &FieldElement, client: &T, expected_failure_reasons: &[&str], ) { let receipt = client.get_transaction_receipt(tx_hash).await.unwrap(); match receipt.execution_result() { ExecutionResult::Reverted { reason } => { for expected_reason in expected_failure_reasons { reason.contains(expected_reason); } } other => panic!("Should have reverted; got: {other:?}; receipt: {receipt:?}"), } } pub fn to_hex_felt(value: &T) -> String { format!("{value:#x}") } pub fn to_num_as_hex(value: &T) -> String { format!("{value:#x}") } pub fn iter_to_hex_felt(iterable: &[T]) -> Vec { iterable.iter().map(to_hex_felt).collect() } pub fn get_unix_timestamp_as_seconds() -> u64 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .expect("should get current UNIX timestamp") .as_secs() } pub async fn send_ctrl_c_signal_and_wait(process: &Child) { send_ctrl_c_signal(process).await; std::thread::sleep(std::time::Duration::from_secs(1)); } async fn send_ctrl_c_signal(process: &Child) { #[cfg(windows)] { // To send SIGINT signal on windows, windows-kill is needed let mut kill = Command::new("windows-kill") .args(["-SIGINT", process.id().to_string().as_str()]) .spawn() .unwrap(); kill.wait().unwrap(); } #[cfg(unix)] { let mut kill = Command::new("kill") .args(["-s", "SIGINT", process.id().to_string().as_str()]) .spawn() .unwrap(); kill.wait().unwrap(); } } /// Wrapper of file name which attempts to delete the file when the variable is dropped. /// Appends a random sequence to the file name base to make it unique. /// Prevents name collisions - no need to come up with unique names for files (e.g. when dumping). /// Automatically deletes the underlying file when the variable is dropped - no need to remember /// deleting. pub struct UniqueAutoDeletableFile { pub path: String, } impl UniqueAutoDeletableFile { /// Appends a random sequence to the name_base to make it unique /// Unlike [NamedTempFile](https://docs.rs/tempfile/latest/tempfile/struct.NamedTempFile.html), /// it doesn't create the file. pub fn new(name_base: &str) -> Self { Self { path: format!("{name_base}-{}", generate_u32_random_number()) } } } impl Drop for UniqueAutoDeletableFile { fn drop(&mut self) { remove_file(&self.path) } } #[cfg(test)] mod test_unique_auto_deletable_file { use std::path::Path; use super::UniqueAutoDeletableFile; #[test] fn test_deleted() { let file = UniqueAutoDeletableFile::new("foo"); let saved_file_path = file.path.clone(); assert!(!Path::new(&file.path).exists()); std::fs::File::create(&file.path).unwrap(); assert!(Path::new(&file.path).exists()); drop(file); assert!(!Path::new(&saved_file_path).exists()); } #[test] fn test_dropping_successful_if_file_not_created() { let file = UniqueAutoDeletableFile::new("foo"); drop(file); // if everything ok, the test should just exit successfully } #[test] fn test_file_names_unique() { let common_prefix = "foo"; // run it many times to increase the probability of being secure for _ in 0..1_000_000 { let file1 = UniqueAutoDeletableFile::new(common_prefix); let file2 = UniqueAutoDeletableFile::new(common_prefix); assert_ne!(file1.path, file2.path); } } }