From 375e85d808ab5dc4b1c020f6aa4b93955caa9a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ignacio=20Gonz=C3=A1lez?= Date: Wed, 26 Apr 2023 15:09:03 -0300 Subject: [PATCH] Add assert le find small arcs (#406) * Add get_segment_arena_index * Add assert_le_find_small_arcs --- Cargo.lock | 50 ++++--- crates/cairo-1-hint-processor/Cargo.toml | 1 + .../src/dict_manager.rs | 125 ++++++++++++++++++ .../src/hint_processor.rs | 95 ++++++++++++- crates/cairo-1-hint-processor/src/lib.rs | 1 + 5 files changed, 253 insertions(+), 19 deletions(-) create mode 100644 crates/cairo-1-hint-processor/src/dict_manager.rs diff --git a/Cargo.lock b/Cargo.lock index 8f963f979..efb38d5aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -588,6 +588,7 @@ version = "0.1.0" dependencies = [ "cairo-felt 0.3.0-rc1 (git+https://github.com/lambdaclass/cairo-rs?rev=725c17e6b4c50ecf9fbb0113ecf172d858372954)", "cairo-lang-casm", + "cairo-lang-utils 0.1.0", "cairo-vm", "num-integer", "num-traits 0.2.15", @@ -623,7 +624,7 @@ name = "cairo-lang-casm" version = "1.0.0-alpha.7" source = "git+https://github.com/starkware-libs/cairo#a647d9863bb9c98a68760a8bc6553da88adf5de8" dependencies = [ - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "indoc 2.0.1", "num-bigint", "num-traits 0.2.15", @@ -648,7 +649,7 @@ dependencies = [ "cairo-lang-sierra", "cairo-lang-sierra-generator", "cairo-lang-syntax", - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "clap", "log", "salsa", @@ -670,7 +671,7 @@ dependencies = [ "cairo-lang-filesystem", "cairo-lang-parser", "cairo-lang-syntax", - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "indexmap", "itertools", "salsa", @@ -683,7 +684,7 @@ version = "1.0.0-alpha.7" source = "git+https://github.com/starkware-libs/cairo#a647d9863bb9c98a68760a8bc6553da88adf5de8" dependencies = [ "cairo-lang-filesystem", - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "itertools", "salsa", ] @@ -693,7 +694,7 @@ name = "cairo-lang-eq-solver" version = "1.0.0-alpha.7" source = "git+https://github.com/starkware-libs/cairo#a647d9863bb9c98a68760a8bc6553da88adf5de8" dependencies = [ - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "good_lp", "indexmap", "itertools", @@ -705,7 +706,7 @@ version = "1.0.0-alpha.7" source = "git+https://github.com/starkware-libs/cairo#a647d9863bb9c98a68760a8bc6553da88adf5de8" dependencies = [ "cairo-lang-debug", - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "path-clean", "salsa", "serde", @@ -725,7 +726,7 @@ dependencies = [ "cairo-lang-proc-macros", "cairo-lang-semantic", "cairo-lang-syntax", - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "id-arena", "indexmap", "itertools", @@ -745,7 +746,7 @@ dependencies = [ "cairo-lang-filesystem", "cairo-lang-syntax", "cairo-lang-syntax-codegen", - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "colored", "itertools", "log", @@ -764,7 +765,7 @@ dependencies = [ "cairo-lang-parser", "cairo-lang-semantic", "cairo-lang-syntax", - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "indoc 2.0.1", "itertools", "salsa", @@ -806,7 +807,7 @@ dependencies = [ "cairo-lang-parser", "cairo-lang-proc-macros", "cairo-lang-syntax", - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "id-arena", "itertools", "log", @@ -822,7 +823,7 @@ name = "cairo-lang-sierra" version = "1.0.0-alpha.7" source = "git+https://github.com/starkware-libs/cairo#a647d9863bb9c98a68760a8bc6553da88adf5de8" dependencies = [ - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "const-fnv1a-hash", "convert_case 0.6.0", "derivative", @@ -846,7 +847,7 @@ source = "git+https://github.com/starkware-libs/cairo#a647d9863bb9c98a68760a8bc6 dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "itertools", "thiserror", ] @@ -858,7 +859,7 @@ source = "git+https://github.com/starkware-libs/cairo#a647d9863bb9c98a68760a8bc6 dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "itertools", "thiserror", ] @@ -879,7 +880,7 @@ dependencies = [ "cairo-lang-semantic", "cairo-lang-sierra", "cairo-lang-syntax", - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "id-arena", "indexmap", "itertools", @@ -900,7 +901,7 @@ dependencies = [ "cairo-lang-sierra", "cairo-lang-sierra-ap-change", "cairo-lang-sierra-gas", - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "clap", "indoc 2.0.1", "itertools", @@ -931,7 +932,7 @@ dependencies = [ "cairo-lang-sierra-generator", "cairo-lang-sierra-to-casm", "cairo-lang-syntax", - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "clap", "convert_case 0.6.0", "genco", @@ -956,7 +957,7 @@ source = "git+https://github.com/starkware-libs/cairo#a647d9863bb9c98a68760a8bc6 dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "salsa", "smol_str", ] @@ -966,12 +967,25 @@ name = "cairo-lang-syntax-codegen" version = "1.0.0-alpha.7" source = "git+https://github.com/starkware-libs/cairo#a647d9863bb9c98a68760a8bc6553da88adf5de8" dependencies = [ - "cairo-lang-utils", + "cairo-lang-utils 1.0.0-alpha.7", "genco", "log", "xshell", ] +[[package]] +name = "cairo-lang-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7196aada3418cb09e7aa2b39f0a6bb660e09adccf565d340457bbcbeef439106" +dependencies = [ + "chrono", + "env_logger", + "indexmap", + "itertools", + "log", +] + [[package]] name = "cairo-lang-utils" version = "1.0.0-alpha.7" diff --git a/crates/cairo-1-hint-processor/Cargo.toml b/crates/cairo-1-hint-processor/Cargo.toml index 036cf118b..e6ecbfcad 100644 --- a/crates/cairo-1-hint-processor/Cargo.toml +++ b/crates/cairo-1-hint-processor/Cargo.toml @@ -7,5 +7,6 @@ edition = "2021" cairo-rs = { git="https://github.com/lambdaclass/cairo-rs/", rev = "725c17e6b4c50ecf9fbb0113ecf172d858372954", package = "cairo-vm" } felt = { git="https://github.com/lambdaclass/cairo-rs/", rev = "725c17e6b4c50ecf9fbb0113ecf172d858372954", package = "cairo-felt" } cairo-lang-casm = { git = "https://github.com/starkware-libs/cairo" } +cairo-lang-utils = "0.1.0" num-integer = "0.1.45" num-traits = "0.2.15" diff --git a/crates/cairo-1-hint-processor/src/dict_manager.rs b/crates/cairo-1-hint-processor/src/dict_manager.rs new file mode 100644 index 000000000..937fe49fb --- /dev/null +++ b/crates/cairo-1-hint-processor/src/dict_manager.rs @@ -0,0 +1,125 @@ +#![allow(dead_code)] //TODO: remove after implementing all hints + +use std::collections::HashMap; + +use cairo_rs::{types::relocatable::Relocatable, vm::vm_core::VirtualMachine}; +use felt::Felt252; + +/// Stores the data of a specific dictionary. +pub struct DictTrackerExecScope { + /// The data of the dictionary. + data: HashMap, + /// The index of the dictionary in the dict_infos segment. + #[allow(dead_code)] + idx: usize, +} + +/// Helper object to allocate, track and destruct all dictionaries in the run. +#[derive(Default)] +pub struct DictManagerExecScope { + /// Maps between a segment index and the DictTrackerExecScope associated with it. + trackers: HashMap, +} + +impl DictTrackerExecScope { + /// Creates a new tracker placed in index `idx` in the dict_infos segment. + pub fn new(idx: usize) -> Self { + Self { + data: HashMap::default(), + idx, + } + } +} + +impl DictManagerExecScope { + pub const DICT_DEFAULT_VALUE: usize = 0; + + /// Allocates a new segment for a new dictionary and return the start of the segment. + pub fn new_default_dict(&mut self, vm: &mut VirtualMachine) -> Relocatable { + let dict_segment = vm.add_memory_segment(); + assert!( + self.trackers + .insert( + dict_segment.segment_index, + DictTrackerExecScope::new(self.trackers.len()) + ) + .is_none(), + "Segment index already in use." + ); + dict_segment + } + + /// Returns a reference for a dict tracker corresponding to a given pointer to a dict segment. + fn get_dict_tracker(&self, dict_end: Relocatable) -> &DictTrackerExecScope { + self.trackers + .get(&dict_end.segment_index) + .expect("The given value does not point to a known dictionary.") + } + + /// Returns a mut reference for a dict tracker corresponding to a given pointer to a dict + /// segment. + fn get_dict_tracker_mut(&mut self, dict_end: Relocatable) -> &mut DictTrackerExecScope { + self.trackers + .get_mut(&dict_end.segment_index) + .expect("The given value does not point to a known dictionary.") + } + + /// Returns the index of the dict tracker corresponding to a given pointer to a dict segment. + pub fn get_dict_infos_index(&self, dict_end: Relocatable) -> usize { + self.get_dict_tracker(dict_end).idx + } + + /// Inserts a value to the dict tracker corresponding to a given pointer to a dict segment. + pub fn insert_to_tracker(&mut self, dict_end: Relocatable, key: Felt252, value: Felt252) { + self.get_dict_tracker_mut(dict_end).data.insert(key, value); + } + + /// Gets a value from the dict tracker corresponding to a given pointer to a dict segment. + /// None if the key does not exist in the tracker data. + pub fn get_from_tracker(&self, dict_end: Relocatable, key: &Felt252) -> Option { + self.get_dict_tracker(dict_end).data.get(key).cloned() + } +} + +/// Helper object for the management of dict_squash hints. +#[derive(Default, Debug)] +pub struct DictSquashExecScope { + /// A map from key to the list of indices accessing it, each list in reverse order. + pub access_indices: HashMap>, + /// Descending list of keys. + pub keys: Vec, +} + +impl DictSquashExecScope { + /// Returns the current key to process. + pub fn current_key(&self) -> Option { + self.keys.last().cloned() + } + + /// Returns and removes the current key, and its access indices. Should be called when only the + /// last key access is in the corresponding indices list. + pub fn pop_current_key(&mut self) -> Option { + let key_accesses = self.access_indices.remove(&self.current_key().unwrap()); + assert!( + key_accesses.unwrap().len() == 1, + "Key popped but not all accesses were processed." + ); + self.keys.pop() + } + + /// Returns a reference to the access indices list of the current key. + pub fn current_access_indices(&mut self) -> Option<&mut Vec> { + let current_key = self.current_key()?; + self.access_indices.get_mut(¤t_key) + } + + /// Returns a reference to the last index in the current access indices list. + pub fn current_access_index(&mut self) -> Option<&Felt252> { + self.current_access_indices()?.last() + } + + /// Returns and removes the current access index. + pub fn pop_current_access_index(&mut self) -> Option { + self.current_access_indices()?.pop() + } +} diff --git a/crates/cairo-1-hint-processor/src/hint_processor.rs b/crates/cairo-1-hint-processor/src/hint_processor.rs index 628deeb81..67bf00ea9 100644 --- a/crates/cairo-1-hint-processor/src/hint_processor.rs +++ b/crates/cairo-1-hint-processor/src/hint_processor.rs @@ -1,7 +1,8 @@ use cairo_lang_casm::{ hints::Hint, - operand::{CellRef, DerefOrImmediate, Operation, Register, ResOperand}, + operand::{BinOpOperand, CellRef, DerefOrImmediate, Operation, Register, ResOperand}, }; +use cairo_lang_utils::extract_matches; use cairo_rs::{ hint_processor::hint_processor_definition::HintProcessor, types::exec_scope::ExecutionScopes, @@ -15,6 +16,8 @@ use num_integer::Integer; use num_traits::identities::Zero; use std::{collections::HashMap, ops::Mul}; +use crate::dict_manager::DictManagerExecScope; + /// HintProcessor for Cairo 1 compiler hints. struct Cairo1HintProcessor {} @@ -72,6 +75,25 @@ fn res_operand_get_val( } } +fn extract_buffer(buffer: &ResOperand) -> (&CellRef, Felt252) { + let (cell, base_offset) = match buffer { + ResOperand::Deref(cell) => (cell, 0.into()), + ResOperand::BinOp(BinOpOperand { + op: Operation::Add, + a, + b, + }) => ( + a, + extract_matches!(b, DerefOrImmediate::Immediate) + .clone() + .value + .into(), + ), + _ => panic!("Illegal argument for a buffer."), + }; + (cell, base_offset) +} + impl Cairo1HintProcessor { fn alloc_segment(&mut self, vm: &mut VirtualMachine, dst: &CellRef) -> Result<(), HintError> { let segment = vm.add_memory_segment(); @@ -138,6 +160,48 @@ impl Cairo1HintProcessor { .map_err(HintError::from) } + fn assert_le_find_small_arcs( + &self, + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + range_check_ptr: &ResOperand, + a: &ResOperand, + b: &ResOperand, + ) -> Result<(), HintError> { + let a_val = res_operand_get_val(vm, a)?; + let b_val = res_operand_get_val(vm, b)?; + let mut lengths_and_indices = vec![ + (a_val.clone(), 0), + (b_val.clone() - a_val, 1), + (Felt252::from(-1) - b_val, 2), + ]; + lengths_and_indices.sort(); + exec_scopes.assign_or_update_variable("excluded_arc", Box::new(lengths_and_indices[2].1)); + // ceil((PRIME / 3) / 2 ** 128). + let prime_over_3_high = 3544607988759775765608368578435044694_u128; + // ceil((PRIME / 2) / 2 ** 128). + let prime_over_2_high = 5316911983139663648412552867652567041_u128; + let (range_check_base, range_check_offset) = extract_buffer(range_check_ptr); + let range_check_ptr = get_ptr(vm, range_check_base, &range_check_offset)?; + vm.insert_value( + range_check_ptr, + Felt252::from(lengths_and_indices[0].0.to_biguint() % prime_over_3_high), + )?; + vm.insert_value( + (range_check_ptr + 1)?, + Felt252::from(lengths_and_indices[0].0.to_biguint() / prime_over_3_high), + )?; + vm.insert_value( + (range_check_ptr + 2)?, + Felt252::from(lengths_and_indices[1].0.to_biguint() % prime_over_2_high), + )?; + vm.insert_value( + (range_check_ptr + 3)?, + Felt252::from(lengths_and_indices[1].0.to_biguint() / prime_over_2_high), + ) + .map_err(HintError::from) + } + fn div_mod( &self, vm: &mut VirtualMachine, @@ -161,6 +225,26 @@ impl Cairo1HintProcessor { .map_err(HintError::from) } + fn get_segment_arena_index( + &self, + vm: &mut VirtualMachine, + exec_scopes: &ExecutionScopes, + dict_end_ptr: &ResOperand, + dict_index: &CellRef, + ) -> Result<(), HintError> { + let (dict_base, dict_offset) = extract_buffer(dict_end_ptr); + let dict_address = get_ptr(vm, dict_base, &dict_offset)?; + let dict_manager_exec_scope = exec_scopes + .get_ref::("dict_manager_exec_scope") + .expect("Trying to read from a dict while dict manager was not initialized."); + let dict_infos_index = dict_manager_exec_scope.get_dict_infos_index(dict_address); + vm.insert_value( + cell_ref_to_relocatable(dict_index, vm), + Felt252::from(dict_infos_index), + ) + .map_err(HintError::from) + } + #[allow(clippy::too_many_arguments)] fn uint256_div_mod( &self, @@ -297,8 +381,17 @@ impl HintProcessor for Cairo1HintProcessor { ) -> Result<(), HintError> { let hint = hint_data.downcast_ref::().unwrap(); match hint { + Hint::GetSegmentArenaIndex { + dict_end_ptr, + dict_index, + } => self.get_segment_arena_index(vm, exec_scopes, dict_end_ptr, dict_index), Hint::AllocSegment { dst } => self.alloc_segment(vm, dst), Hint::TestLessThan { lhs, rhs, dst } => self.test_less_than(vm, lhs, rhs, dst), + Hint::AssertLeFindSmallArcs { + range_check_ptr, + a, + b, + } => self.assert_le_find_small_arcs(vm, exec_scopes, range_check_ptr, a, b), Hint::SquareRoot { value, dst } => self.square_root(vm, value, dst), Hint::TestLessThanOrEqual { lhs, rhs, dst } => { self.test_less_than_or_equal(vm, lhs, rhs, dst) diff --git a/crates/cairo-1-hint-processor/src/lib.rs b/crates/cairo-1-hint-processor/src/lib.rs index 93c7ec76e..f84c0dce1 100644 --- a/crates/cairo-1-hint-processor/src/lib.rs +++ b/crates/cairo-1-hint-processor/src/lib.rs @@ -1 +1,2 @@ +mod dict_manager; pub mod hint_processor;