Skip to content

Commit

Permalink
feat(hints): add load compiled classes
Browse files Browse the repository at this point in the history
  • Loading branch information
0xLucqs committed Sep 27, 2023
1 parent 587b561 commit 5411ed6
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 42 deletions.
6 changes: 6 additions & 0 deletions src/hints/hints_raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
64 changes: 62 additions & 2 deletions src/hints/mod.rs
Original file line number Diff line number Diff line change
@@ -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();

Expand Down Expand Up @@ -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<String, HintReference>,
ap_tracking: &ApTracking,
_constants: &HashMap<String, Felt252>,
) -> 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::<StarknetOsInput<(), PedersenHasher>>("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<dyn Any> = Box::new(compiled_class_facts.into_iter());
exec_scopes.enter_scope(HashMap::from_iter(vec![(
"compiled_class_facts".to_string(),
boxed_compiled_classes,
)]));
Ok(())
}
6 changes: 3 additions & 3 deletions src/os_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ use crate::{
utils::{definitions::general_config::StarknetGeneralConfig, hasher::HasherT},
};

#[derive(Serialize, Deserialize)]
struct StarknetOsInput<S: Storage, H: HasherT> {
#[derive(Serialize, Deserialize, Default)]
pub struct StarknetOsInput<S: Storage, H: HasherT> {
contract_state_commitment_info: CommitmentInfo<S, H>,
contract_class_commitment_info: CommitmentInfo<S, H>,
deprecated_compiled_classes: HashMap<Felt252, Felt252>, // TODO: Add contract_class module
compiled_classes: HashMap<Felt252, Felt252>, // TODO: Add contract_class module
pub compiled_classes: HashMap<Felt252, Felt252>, // TODO: Add contract_class module
contracts: HashMap<Felt252, ContractState>,
class_hash_to_compiled_class_hash: HashMap<Felt252, Felt252>,
general_config: StarknetGeneralConfig,
Expand Down
9 changes: 9 additions & 0 deletions src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ pub trait Storage: Clone {
fn get_value(&self, key: Vec<u8>) -> Option<Vec<u8>>;
fn del_value(&self, key: Vec<u8>);
}
impl Storage for () {
fn set_value(&self, _key: Vec<u8>, _value: Vec<u8>) {}

fn get_value(&self, _key: Vec<u8>) -> Option<Vec<u8>> {
None
}

fn del_value(&self, _key: Vec<u8>) {}
}

pub trait DBObject: Serialize + for<'de> Deserialize<'de> {
/// Method to get the database key for the object
Expand Down
2 changes: 1 addition & 1 deletion src/storage/starknet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use super::{FactCheckingContext, Storage};

type CommitmentFacts = HashMap<Felt252, Vec<Felt252>>;

#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Default)]
pub struct CommitmentInfo<S: Storage, H: HasherT> {
pub previous_root: Felt252,
pub updated_root: Felt252,
Expand Down
2 changes: 1 addition & 1 deletion src/utils/hasher/pedersen.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
4 changes: 1 addition & 3 deletions tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
2 changes: 0 additions & 2 deletions tests/contracts/hint.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
18 changes: 18 additions & 0 deletions tests/contracts/load_compiled_classes.cairo
Original file line number Diff line number Diff line change
@@ -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);
}
6 changes: 3 additions & 3 deletions tests/pie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion tests/sharp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
58 changes: 32 additions & 26 deletions tests/snos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,30 @@ 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;
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<String, HintReference>,
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<String, HintReference>,
_ap_tracking: &ApTracking,
_constants: &HashMap<String, Felt252>,
) -> 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(())
}

Expand All @@ -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,
Expand All @@ -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(
Expand All @@ -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);
}

0 comments on commit 5411ed6

Please sign in to comment.