From 5411ed6af705e838c6a52423967c33fe5ab04a4e Mon Sep 17 00:00:00 2001 From: LucasLvy Date: Tue, 26 Sep 2023 17:50:38 +0200 Subject: [PATCH] feat(hints): add load compiled classes --- src/hints/hints_raw.rs | 6 ++ src/hints/mod.rs | 64 ++++++++++++++++++++- src/os_input.rs | 6 +- src/storage/mod.rs | 9 +++ src/storage/starknet.rs | 2 +- src/utils/hasher/pedersen.rs | 2 +- tests/common.rs | 4 +- tests/contracts/hint.cairo | 2 - tests/contracts/load_compiled_classes.cairo | 18 ++++++ tests/pie.rs | 6 +- tests/sharp.rs | 2 +- tests/snos.rs | 58 ++++++++++--------- 12 files changed, 137 insertions(+), 42 deletions(-) create mode 100644 tests/contracts/load_compiled_classes.cairo diff --git a/src/hints/hints_raw.rs b/src/hints/hints_raw.rs index 870ad49f0..7448fca70 100644 --- a/src/hints/hints_raw.rs +++ b/src/hints/hints_raw.rs @@ -5,6 +5,12 @@ os_input = StarknetOsInput.load(data=program_input) ids.initial_carried_outputs.messages_to_l1 = segments.add_temp_segment() ids.initial_carried_outputs.messages_to_l2 = segments.add_temp_segment()"#; +pub const LOAD_COMPILED_CLASS_FACTS: &str = r#"ids.compiled_class_facts = segments.add() +ids.n_compiled_class_facts = len(os_input.compiled_classes) +vm_enter_scope({ + 'compiled_class_facts': iter(os_input.compiled_classes.items()), +})"#; + pub const _VM_ENTER_SCOPE: &str = " # This hint shouldn't be whitelisted. vm_enter_scope(dict( diff --git a/src/hints/mod.rs b/src/hints/mod.rs index ba02d33d2..86e283ed9 100644 --- a/src/hints/mod.rs +++ b/src/hints/mod.rs @@ -1,19 +1,28 @@ +use std::any::Any; use std::collections::HashMap; use cairo_vm::felt::Felt252; -use cairo_vm::hint_processor::builtin_hint_processor::hint_utils::get_ptr_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; -mod hints_raw; use cairo_vm::serde::deserialize_program::ApTracking; use cairo_vm::types::exec_scope::ExecutionScopes; +use cairo_vm::types::relocatable::MaybeRelocatable; use cairo_vm::vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}; + use std::rc::Rc; use cairo_vm::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::{ BuiltinHintProcessor, HintFunc, }; +use crate::os_input::StarknetOsInput; +use crate::utils::hasher::pedersen::PedersenHasher; + +pub mod hints_raw; + pub fn sn_hint_processor() -> BuiltinHintProcessor { let mut hint_processor = BuiltinHintProcessor::new_empty(); @@ -59,3 +68,54 @@ pub fn starknet_os_input( Ok(()) } + +/* +Implements hint: + %{ + ids.compiled_class_facts = segments.add() + ids.n_compiled_class_facts = len(os_input.compiled_classes) + vm_enter_scope({ + 'compiled_class_facts': iter(os_input.compiled_classes.items()), + }) + %} +*/ +pub fn load_compiled_class_facts( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + // ids.compiled_class_facts = segments.add() + insert_value_from_var_name( + "compiled_class_facts", + vm.add_memory_segment(), + vm, + ids_data, + ap_tracking, + )?; + // Access the value of os_input which was loaded in a previous hint like that + // %{ os_input = ... %} + // Can't directly get os_input.compiled_classes so we need to get the whole os_input + let compiled_class_facts = exec_scopes + .get_ref::>("os_input")? + .compiled_classes + .clone(); + // ids.n_compiled_class_facts = len(os_input.compiled_classes) + insert_value_from_var_name( + "n_compiled_class_facts", + MaybeRelocatable::Int(Felt252::new(compiled_class_facts.len())), + vm, + ids_data, + ap_tracking, + )?; + // vm_enter_scope({ + // 'compiled_class_facts': iter(os_input.compiled_classes.items()), + // }) + let boxed_compiled_classes: Box = Box::new(compiled_class_facts.into_iter()); + exec_scopes.enter_scope(HashMap::from_iter(vec![( + "compiled_class_facts".to_string(), + boxed_compiled_classes, + )])); + Ok(()) +} diff --git a/src/os_input.rs b/src/os_input.rs index a2e6105fc..6f3ca607d 100644 --- a/src/os_input.rs +++ b/src/os_input.rs @@ -10,12 +10,12 @@ use crate::{ utils::{definitions::general_config::StarknetGeneralConfig, hasher::HasherT}, }; -#[derive(Serialize, Deserialize)] -struct StarknetOsInput { +#[derive(Serialize, Deserialize, Default)] +pub struct StarknetOsInput { contract_state_commitment_info: CommitmentInfo, contract_class_commitment_info: CommitmentInfo, deprecated_compiled_classes: HashMap, // TODO: Add contract_class module - compiled_classes: HashMap, // TODO: Add contract_class module + pub compiled_classes: HashMap, // TODO: Add contract_class module contracts: HashMap, class_hash_to_compiled_class_hash: HashMap, general_config: StarknetGeneralConfig, diff --git a/src/storage/mod.rs b/src/storage/mod.rs index d42c086a0..ae7c03ed5 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -12,6 +12,15 @@ pub trait Storage: Clone { fn get_value(&self, key: Vec) -> Option>; fn del_value(&self, key: Vec); } +impl Storage for () { + fn set_value(&self, _key: Vec, _value: Vec) {} + + fn get_value(&self, _key: Vec) -> Option> { + None + } + + fn del_value(&self, _key: Vec) {} +} pub trait DBObject: Serialize + for<'de> Deserialize<'de> { /// Method to get the database key for the object diff --git a/src/storage/starknet.rs b/src/storage/starknet.rs index a2256dd5c..3ffe7bce6 100644 --- a/src/storage/starknet.rs +++ b/src/storage/starknet.rs @@ -17,7 +17,7 @@ use super::{FactCheckingContext, Storage}; type CommitmentFacts = HashMap>; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Default)] pub struct CommitmentInfo { pub previous_root: Felt252, pub updated_root: Felt252, diff --git a/src/utils/hasher/pedersen.rs b/src/utils/hasher/pedersen.rs index 7ee6d4be7..2ad79f0f2 100644 --- a/src/utils/hasher/pedersen.rs +++ b/src/utils/hasher/pedersen.rs @@ -1,7 +1,7 @@ use super::HasherT; use cairo_felt::Felt252; /// The Pedersen hasher. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct PedersenHasher; diff --git a/tests/common.rs b/tests/common.rs index 30e329f5e..e6828da05 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -88,11 +88,9 @@ pub fn check_output_vs_python(program: &str, mut vm: VirtualMachine) { let python_output = unsafe { std::str::from_utf8_unchecked(&python_output.stdout) }.to_string(); let python_output = python_output .split('\n') - .into_iter() .skip_while(|&x| x != "Program output:") .skip(1) - .filter(|&x| !x.trim().is_empty()) - .into_iter(); + .filter(|&x| !x.trim().is_empty()); for (i, (rs, py)) in rs_output.zip(python_output).enumerate() { let py = py.to_string().trim().to_string(); pretty_assertions::assert_eq!(*rs, py, "Output #{i:} is different"); diff --git a/tests/contracts/hint.cairo b/tests/contracts/hint.cairo index be24253fd..f16350fbf 100644 --- a/tests/contracts/hint.cairo +++ b/tests/contracts/hint.cairo @@ -4,8 +4,6 @@ func main(output_ptr: felt*) -> (output_ptr: felt*) { tempvar a = 17; a = [output_ptr], ap++; - // Use custom hint to print the value of a - %{ print(ids.a) %} let output_ptr = output_ptr + 1; return(output_ptr = output_ptr); diff --git a/tests/contracts/load_compiled_classes.cairo b/tests/contracts/load_compiled_classes.cairo new file mode 100644 index 000000000..420c5ff4f --- /dev/null +++ b/tests/contracts/load_compiled_classes.cairo @@ -0,0 +1,18 @@ +%builtins output + +func main(output_ptr: felt*) -> (output_ptr: felt*) { + alloc_locals; + %{ mock_os_input %} + local compiled_class_facts; + local n_compiled_class_facts; + %{ + ids.compiled_class_facts = segments.add() + ids.n_compiled_class_facts = len(os_input.compiled_classes) + vm_enter_scope({ + 'compiled_class_facts': iter(os_input.compiled_classes.items()), + }) + %} + // When entering a scope we need to exit it afterwards otherwise the vm panics. + %{ vm_exit_scope() %} + return(output_ptr = output_ptr); +} diff --git a/tests/pie.rs b/tests/pie.rs index bf8824c12..95667e9a5 100644 --- a/tests/pie.rs +++ b/tests/pie.rs @@ -71,19 +71,19 @@ fn pie_execution_resources_ok(setup_pie: CairoPie) { fn pie_version_ok(setup_pie: CairoPie) { let version = setup_pie.version; - let version_s = serde_json::to_value(&version).unwrap(); + let version_s = serde_json::to_value(version).unwrap(); assert_eq!(version_s, json!({"cairo_pie": "1.1"})); } #[rstest] fn pie_memory_ok(setup_pie: CairoPie) { - let pie_s = serde_json::to_value(&setup_pie).unwrap(); + let pie_s = serde_json::to_value(setup_pie).unwrap(); assert_eq!(pie_s["memory"], "00000000000000800080ff7f018006400000000000000000000000000000000000000000000000000100000000000080640000000000000000000000000000000000000000000000000000000000000002000000000000800080fd7f0080024800000000000000000000000000000000000000000000000003000000000000800080ff7f018006400000000000000000000000000000000000000000000000000400000000000080c80000000000000000000000000000000000000000000000000000000000000005000000000000800080fd7f0180024800000000000000000000000000000000000000000000000006000000000000800080ff7f0180064000000000000000000000000000000000000000000000000007000000000000802c0100000000000000000000000000000000000000000000000000000000000008000000000000800080fd7f0280024800000000000000000000000000000000000000000000000009000000000000800080fd7f018026480000000000000000000000000000000000000000000000000a0000000000008003000000000000000000000000000000000000000000000000000000000000000b00000000000080fe7fff7fff7f8b20000000000000000000000000000000000000000000000000000000000080008000000000000001000000000000000000000000000000000000000000000000800100000000800080000000000080010000000000000000000000000000000000000000000000008002000000008000800000000000000200000000000000000000000000000000000000000000000080030000000080008064000000000000000000000000000000000000000000000000000000000000000400000000800080c80000000000000000000000000000000000000000000000000000000000000005000000008000802c0100000000000000000000000000000000000000000000000000000000000006000000008000800300000000000100000000000000000000000000000000000000000000000080000000000000018064000000000000000000000000000000000000000000000000000000000000000100000000000180c80000000000000000000000000000000000000000000000000000000000000002000000000001802c01000000000000000000000000000000000000000000000000000000000000"); } #[rstest] fn prepare_pie_ok(setup_pie: CairoPie) { - let disk_b64 = encode_pie(setup_pie.clone(), &Path::new("build/test.zip")); + let disk_b64 = encode_pie(setup_pie.clone(), Path::new("build/test.zip")); assert!(disk_b64.is_ok()); let mem_b64 = encode_pie_mem(setup_pie); diff --git a/tests/sharp.rs b/tests/sharp.rs index e8ba73d39..0b19d87b8 100644 --- a/tests/sharp.rs +++ b/tests/sharp.rs @@ -20,5 +20,5 @@ fn sharp_client_status() { let submit_resp = sharp_client.get_status(TEST_CAIRO_JOB_ID).unwrap(); assert_eq!(submit_resp.version.unwrap(), 1); - assert_eq!(submit_resp.validation_done.unwrap(), true); + assert!(submit_resp.validation_done.unwrap()); } diff --git a/tests/snos.rs b/tests/snos.rs index 027489201..6bdc5ebf4 100644 --- a/tests/snos.rs +++ b/tests/snos.rs @@ -10,6 +10,10 @@ use cairo_vm::serde::deserialize_program::ApTracking; use cairo_vm::types::exec_scope::ExecutionScopes; use cairo_vm::vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}; use common::{check_output_vs_python, compile_contracts}; +use snos::hints::hints_raw::*; +use snos::hints::load_compiled_class_facts; +use snos::os_input::StarknetOsInput; +use snos::utils::hasher::pedersen::PedersenHasher; use snos::SnOsRunner; use std::collections::HashMap; use std::fs; @@ -17,16 +21,19 @@ use std::rc::Rc; use rstest::*; -// Create the function that implements the custom hint -fn print_a_hint( - vm: &mut VirtualMachine, - _exec_scopes: &mut ExecutionScopes, - ids_data: &HashMap, - ap_tracking: &ApTracking, +/// Mocks an os input but not a good way of doing things as it won't work in python. +fn mock_os_input( + _vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + _ids_data: &HashMap, + _ap_tracking: &ApTracking, _constants: &HashMap, ) -> Result<(), HintError> { - let a = get_integer_from_var_name("a", vm, ids_data, ap_tracking)?; - println!("{}", a); + let mut os_input = StarknetOsInput::<(), PedersenHasher>::default(); + os_input + .compiled_classes + .insert(Felt252::new(2), Felt252::new(3)); + exec_scopes.insert_value("os_input", os_input); Ok(()) } @@ -35,22 +42,16 @@ fn print_a_hint( fn snos_ok() { let snos_runner = SnOsRunner::default(); let _runner_res = snos_runner.run(); - assert_eq!(4, 4); } #[rstest] -fn custom_hint_ok(_compile_contracts: ()) { +#[should_panic(expected = "Output #0 is different")] +fn test_different_outputs(_compile_contracts: ()) { let program_content = fs::read("build/hint.json").unwrap(); - // Wrap the Rust hint implementation in a Box smart pointer inside a HintFunc - let hint = HintFunc(Box::new(print_a_hint)); - //Instantiate the hint processor let mut hint_processor = BuiltinHintProcessor::new_empty(); - //Add the custom hint, together with the Python code - hint_processor.add_hint(String::from("print(ids.a)"), Rc::new(hint)); - //Run the cairo program let (_cairo_runner, virtual_machine) = cairo_run( &program_content, @@ -61,22 +62,27 @@ fn custom_hint_ok(_compile_contracts: ()) { &mut hint_processor, ) .expect("Couldn't run program"); - check_output_vs_python("build/hint.json", virtual_machine); + check_output_vs_python("build/different_output.json", virtual_machine); } #[rstest] -#[should_panic(expected = "Output #0 is different")] -fn test_different_outputs(_compile_contracts: ()) { - let program_content = fs::read("build/hint.json").unwrap(); - - // Wrap the Rust hint implementation in a Box smart pointer inside a HintFunc - let hint = HintFunc(Box::new(print_a_hint)); +fn load_compiled_classes_facts_test(_compile_contracts: ()) { + let program_path = "build/load_compiled_classes.json"; - //Instantiate the hint processor + // Instantiate the hint processor let mut hint_processor = BuiltinHintProcessor::new_empty(); //Add the custom hint, together with the Python code - hint_processor.add_hint(String::from("print(ids.a)"), Rc::new(hint)); + hint_processor.add_hint( + String::from("mock_os_input"), + Rc::new(HintFunc(Box::new(mock_os_input))), + ); + hint_processor.add_hint( + LOAD_COMPILED_CLASS_FACTS.to_owned(), + Rc::new(HintFunc(Box::new(load_compiled_class_facts))), + ); + + let program_content = fs::read(program_path).unwrap(); //Run the cairo program let (_cairo_runner, virtual_machine) = cairo_run( @@ -88,5 +94,5 @@ fn test_different_outputs(_compile_contracts: ()) { &mut hint_processor, ) .expect("Couldn't run program"); - check_output_vs_python("build/different_output.json", virtual_machine); + check_output_vs_python(program_path, virtual_machine); }