From 92cbf063609511a6b16517f29737fc0d799e76e2 Mon Sep 17 00:00:00 2001 From: whichqua Date: Fri, 2 Aug 2024 17:39:39 +0300 Subject: [PATCH 1/4] feat: secp-related hints Problem: we need an implementation of the hints used by the Starknet OS in the secp syscalls. These hints rely on private primitives in `cairo-vm` and need to be implemented here. Solution: this PR adds an implementation of all the hints that require `cairo-vm` primitives in the `cairo-vm` repository. --- CHANGELOG.md | 1 + .../builtin_hint_processor_definition.rs | 85 +++ .../builtin_hint_processor/secp/ec_utils.rs | 8 +- .../builtin_hint_processor/secp/hints.rs | 677 ++++++++++++++++++ .../builtin_hint_processor/secp/mod.rs | 1 + .../builtin_hint_processor/secp/secp_utils.rs | 16 +- .../builtin_hint_processor/secp/signature.rs | 2 + 7 files changed, 785 insertions(+), 5 deletions(-) create mode 100644 vm/src/hint_processor/builtin_hint_processor/secp/hints.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fab13c1fb..34cfaaa1fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Upcoming Changes * chore: bump `cairo-lang-` dependencies to 2.7.1 [#1823](https://github.com/lambdaclass/cairo-vm/pull/1823) +* feat: Implement `SECP related` hints #### [1.0.1] - 2024-08-12 diff --git a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index fcc6ad0dcc..6ea3084a02 100644 --- a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -8,6 +8,7 @@ use super::{ field_arithmetic::{u256_get_square_root, u384_get_square_root, uint384_div}, mod_circuit::{run_p_mod_circuit, run_p_mod_circuit_with_large_batch_size}, secp::{ + self, ec_utils::{ compute_doubling_slope_external_consts, compute_slope_and_assing_secp_p, ec_double_assign_new_y, ec_mul_inner, ec_negate_embedded_secp_p, @@ -874,6 +875,90 @@ impl HintProcessorLogic for BuiltinHintProcessor { constants, exec_scopes, ), + secp::hints::SECP_R1_GET_POINT_FROM_X => secp::hints::r1_get_point_from_x( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + secp::hints::SECP_DOUBLE_ASSIGN_NEW_X => secp::hints::double_assign_new_x( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + secp::hints::GENERATE_NIBBLES => secp::hints::generate_nibbles( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + secp::hints::FAST_SECP_ADD_ASSIGN_NEW_Y => secp::hints::fast_secp_add_assign_new_y( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + secp::hints::WRITE_NIBBLES_TO_MEM => secp::hints::write_nibbles_to_mem( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + secp::hints::COMPUTE_IDS_HIGH_LOW => secp::hints::compute_ids_high_low( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + secp::hints::COMPUTE_Q_MOD_PRIME => secp::hints::compute_q_mod_prime( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + secp::hints::IS_ON_CURVE_2 => secp::hints::is_on_curve_2( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + secp::hints::SECP_REDUCE => secp::hints::reduce_value( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + secp::hints::SECP_REDUCE_X => secp::hints::reduce_x( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + secp::hints::COMPUTE_VALUE_DIV_MOD => secp::hints::compute_value_div_mod( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + secp::hints::WRITE_DIVMOD_SEGMENT => secp::hints::write_div_mod_segment( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), code => Err(HintError::UnknownHint(code.to_string().into_boxed_str())), } } diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs b/vm/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs index fdafd49e07..4eb99ac6d4 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -27,12 +27,12 @@ use num_traits::{One, ToPrimitive, Zero}; use super::secp_utils::SECP256R1_P; #[derive(Debug, PartialEq)] -struct EcPoint<'a> { - x: BigInt3<'a>, - y: BigInt3<'a>, +pub(crate) struct EcPoint<'a> { + pub(crate) x: BigInt3<'a>, + pub(crate) y: BigInt3<'a>, } impl EcPoint<'_> { - fn from_var_name<'a>( + pub(crate) fn from_var_name<'a>( name: &'a str, vm: &'a VirtualMachine, ids_data: &'a HashMap, diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/hints.rs b/vm/src/hint_processor/builtin_hint_processor/secp/hints.rs new file mode 100644 index 0000000000..b181b6ae8e --- /dev/null +++ b/vm/src/hint_processor/builtin_hint_processor/secp/hints.rs @@ -0,0 +1,677 @@ +use crate::stdlib::{ + collections::HashMap, + ops::Deref, + ops::{Add, Mul, Rem}, + prelude::*, +}; + +use crate::hint_processor::builtin_hint_processor::hint_utils::{ + get_constant_from_var_name, get_integer_from_var_name, get_relocatable_from_var_name, + insert_value_from_var_name, +}; +use crate::hint_processor::builtin_hint_processor::uint256_utils::Uint256; +use crate::hint_processor::hint_processor_definition::HintReference; +use crate::math_utils::{div_mod, signed_felt}; +use crate::serde::deserialize_program::ApTracking; +use crate::types::errors::math_errors::MathError; +use crate::types::exec_scope::ExecutionScopes; +use crate::types::relocatable::MaybeRelocatable; +use crate::vm::errors::hint_errors::HintError; +use crate::vm::vm_core::VirtualMachine; +use crate::Felt252; +use num_bigint::{BigInt, BigUint}; +use num_integer::Integer; +use num_traits::Zero; +use num_traits::{FromPrimitive, One}; + +use super::bigint_utils::{BigInt3, Uint384}; +use super::ec_utils::EcPoint; +use super::secp_utils::{BLS_BASE, BLS_PRIME, SECP256R1_ALPHA, SECP256R1_B, SECP256R1_P, SECP_P}; + +pub const SECP_REDUCE: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack +value = pack(ids.x, PRIME) % SECP256R1_P"#; +pub fn reduce_value( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let x = Uint384::from_var_name("x", vm, ids_data, ap_tracking)?.pack86(); + exec_scopes.insert_value("value", x.mod_floor(&SECP256R1_P)); + Ok(()) +} + +pub const SECP_REDUCE_X: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack + +x = pack(ids.x, PRIME) % SECP256R1_P"#; +pub fn reduce_x( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let x = Uint384::from_var_name("x", vm, ids_data, ap_tracking)?.pack86(); + exec_scopes.insert_value("x", x.mod_floor(&SECP256R1_P)); + Ok(()) +} + +pub const COMPUTE_Q_MOD_PRIME: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack + +q, r = divmod(pack(ids.val, PRIME), SECP256R1_P) +assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." +ids.q = q % PRIME"#; +pub fn compute_q_mod_prime( + vm: &mut VirtualMachine, + _exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let val = Uint384::from_var_name("val", vm, ids_data, ap_tracking)?.pack86(); + let (q, r) = val.div_mod_floor(&SECP256R1_P); + if !r.is_zero() { + return Err(HintError::SecpVerifyZero(Box::new(val))); + } + insert_value_from_var_name("q", Felt252::from(&q), vm, ids_data, ap_tracking)?; + Ok(()) +} + +pub const COMPUTE_IDS_HIGH_LOW: &str = r#"from starkware.cairo.common.math_utils import as_int + +# Correctness check. +value = as_int(ids.value, PRIME) % PRIME +assert value < ids.UPPER_BOUND, f'{value} is outside of the range [0, 2**165).' + +# Calculation for the assertion. +ids.high, ids.low = divmod(ids.value, ids.SHIFT)"#; +pub fn compute_ids_high_low( + vm: &mut VirtualMachine, + _exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + constants: &HashMap, +) -> Result<(), HintError> { + const UPPER_BOUND: &str = "starkware.cairo.common.math.assert_250_bit.UPPER_BOUND"; + const SHIFT: &str = "starkware.cairo.common.math.assert_250_bit.SHIFT"; + //Declare constant values + let upper_bound = constants + .get(UPPER_BOUND) + .map_or_else(|| get_constant_from_var_name("UPPER_BOUND", constants), Ok)?; + let shift = constants + .get(SHIFT) + .map_or_else(|| get_constant_from_var_name("SHIFT", constants), Ok)?; + let value = Felt252::from(&signed_felt(get_integer_from_var_name( + "value", + vm, + ids_data, + ap_tracking, + )?)); + if &value > upper_bound { + return Err(HintError::ValueOutside250BitRange(Box::new(value))); + } + + let (high, low) = value.div_rem(&shift.try_into().map_err(|_| MathError::DividedByZero)?); + insert_value_from_var_name("high", high, vm, ids_data, ap_tracking)?; + insert_value_from_var_name("low", low, vm, ids_data, ap_tracking)?; + Ok(()) +} + +pub const SECP_R1_GET_POINT_FROM_X: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP256R1, pack +from starkware.python.math_utils import y_squared_from_x + +y_square_int = y_squared_from_x( + x=pack(ids.x, SECP256R1.prime), + alpha=SECP256R1.alpha, + beta=SECP256R1.beta, + field_prime=SECP256R1.prime, +) + +# Note that (y_square_int ** ((SECP256R1.prime + 1) / 4)) ** 2 = +# = y_square_int ** ((SECP256R1.prime + 1) / 2) = +# = y_square_int ** ((SECP256R1.prime - 1) / 2 + 1) = +# = y_square_int * y_square_int ** ((SECP256R1.prime - 1) / 2) = y_square_int * {+/-}1. +y = pow(y_square_int, (SECP256R1.prime + 1) // 4, SECP256R1.prime) + +# We need to decide whether to take y or prime - y. +if ids.v % 2 == y % 2: + value = y +else: + value = (-y) % SECP256R1.prime"#; + +pub fn r1_get_point_from_x( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + exec_scopes.insert_value::("SECP256R1_P", SECP256R1_P.clone()); + + // def y_squared_from_x(x: int, alpha: int, beta: int, field_prime: int) -> int: + // """ + // Computes y^2 using the curve equation: + // y^2 = x^3 + alpha * x + beta (mod field_prime) + // """ + // return (pow(x, 3, field_prime) + alpha * x + beta) % field_prime + fn y_squared_from_x(x: &BigInt, alpha: &BigInt, beta: &BigInt, field_prime: &BigInt) -> BigInt { + // Compute x^3 (mod field_prime) + let x_cubed = x.modpow(&BigInt::from(3), field_prime); + + // Compute alpha * x + let alpha_x = alpha.mul(x); + + // Compute y^2 = (x^3 + alpha * x + beta) % field_prime + x_cubed.add(&alpha_x).add(beta).rem(field_prime) + } + + // prime = curve.prime + // y_squared = y_squared_from_x( + // x=x, + // alpha=curve.alpha, + // beta=curve.beta, + // field_prime=prime, + // ) + + // y = pow(y_squared, (prime + 1) // 4, prime) + // if (y & 1) != request.y_parity: + // y = (-y) % prime + + let x = Uint384::from_var_name("x", vm, ids_data, ap_tracking)? + .pack86() + .mod_floor(&SECP256R1_P); + + let y_square_int = y_squared_from_x(&x, &SECP256R1_ALPHA, &SECP256R1_B, &SECP256R1_P); + exec_scopes.insert_value::("y_square_int", y_square_int.clone()); + + // Calculate (prime + 1) // 4 + let exp = (SECP256R1_P.to_owned() + BigInt::one()).div_floor(&BigInt::from(4)); + // Calculate pow(y_square_int, exp, prime) + let y = y_square_int.modpow(&exp, &SECP256R1_P); + exec_scopes.insert_value::("y", y.clone()); + + let v = get_integer_from_var_name("v", vm, ids_data, ap_tracking)?.to_biguint(); + if v.is_even() == y.is_even() { + exec_scopes.insert_value("value", y); + } else { + let value = (-y).mod_floor(&SECP256R1_P); + exec_scopes.insert_value("value", value); + } + Ok(()) +} + +pub const IS_ON_CURVE_2: &str = r#"ids.is_on_curve = (y * y) % SECP256R1.prime == y_square_int"#; + +pub fn is_on_curve_2( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let y: BigInt = exec_scopes.get("y")?; + let y_square_int: BigInt = exec_scopes.get("y_square_int")?; + + let is_on_curve = ((y.pow(2)) % SECP256R1_P.to_owned()) == y_square_int; + insert_value_from_var_name( + "is_on_curve", + Felt252::from(is_on_curve), + vm, + ids_data, + ap_tracking, + )?; + + Ok(()) +} + +pub const SECP_DOUBLE_ASSIGN_NEW_X: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack + +slope = pack(ids.slope, SECP256R1_P) +x = pack(ids.point.x, SECP256R1_P) +y = pack(ids.point.y, SECP256R1_P) + +value = new_x = (pow(slope, 2, SECP256R1_P) - 2 * x) % SECP256R1_P"#; + +pub fn double_assign_new_x( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + exec_scopes.insert_value::("SECP256R1_P", SECP256R1_P.clone()); + //ids.slope + let slope = BigInt3::from_var_name("slope", vm, ids_data, ap_tracking)?; + //ids.point + let point = EcPoint::from_var_name("point", vm, ids_data, ap_tracking)?; + + let slope = slope.pack86().mod_floor(&SECP256R1_P); + let x = point.x.pack86().mod_floor(&SECP256R1_P); + let y = point.y.pack86().mod_floor(&SECP256R1_P); + + let value = + (slope.modpow(&(2usize.into()), &SECP256R1_P) - (&x << 1u32)).mod_floor(&SECP256R1_P); + + //Assign variables to vm scope + exec_scopes.insert_value("slope", slope); + exec_scopes.insert_value("x", x); + exec_scopes.insert_value("y", y); + exec_scopes.insert_value("value", value.clone()); + exec_scopes.insert_value("new_x", value); + Ok(()) +} + +pub const GENERATE_NIBBLES: &str = r#"num = (ids.scalar.high << 128) + ids.scalar.low +nibbles = [(num >> i) & 0xf for i in range(0, 256, 4)] +ids.first_nibble = nibbles.pop() +ids.last_nibble = nibbles[0]"#; +pub fn generate_nibbles( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let num = Uint256::from_var_name("scalar", vm, ids_data, ap_tracking)?.pack(); + + // Generate nibbles + let mut nibbles: Vec = (0..256) + .step_by(4) + .map(|i| ((&num >> i) & BigUint::from_u8(0xf).unwrap())) + .map(|s: BigUint| s.into()) + .collect(); + + // ids.first_nibble = nibbles.pop() + let first_nibble = nibbles.pop().unwrap(); + + insert_value_from_var_name("first_nibble", first_nibble, vm, ids_data, ap_tracking)?; + + // ids.last_nibble = nibbles[0] + let last_nibble = *nibbles.get(0).unwrap(); + insert_value_from_var_name("last_nibble", last_nibble, vm, ids_data, ap_tracking)?; + exec_scopes.insert_value("nibbles", nibbles); + Ok(()) +} + +pub const FAST_SECP_ADD_ASSIGN_NEW_Y: &str = + r#"value = new_y = (slope * (x - new_x) - y) % SECP256R1_P"#; +pub fn fast_secp_add_assign_new_y( + _vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + _ids_data: &HashMap, + _ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + //Get variables from vm scope + let (slope, x, new_x, y, secp_p) = ( + exec_scopes.get::("slope")?, + exec_scopes.get::("x")?, + exec_scopes.get::("new_x")?, + exec_scopes.get::("y")?, + SECP256R1_P.deref(), + ); + let value = (slope * (x - new_x) - y).mod_floor(secp_p); + exec_scopes.insert_value("value", value.clone()); + exec_scopes.insert_value("new_y", value); + + Ok(()) +} + +pub const WRITE_NIBBLES_TO_MEM: &str = r#"memory[fp + 0] = to_felt_or_relocatable(nibbles.pop())"#; + +pub fn write_nibbles_to_mem( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + _ids_data: &HashMap, + _ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let nibbles: &mut Vec = exec_scopes.get_mut_list_ref("nibbles")?; + let nibble = nibbles.pop().unwrap(); + vm.insert_value((vm.get_fp() + 0)?, nibble)?; + + Ok(()) +} + +pub const COMPUTE_VALUE_DIV_MOD: &str = r#"from starkware.python.math_utils import div_mod + +value = div_mod(1, x, SECP256R1_P)"#; +pub fn compute_value_div_mod( + _vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + _ids_data: &HashMap, + _ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + //Get variables from vm scope + let x = exec_scopes.get_ref::("x")?; + + let value = div_mod(&BigInt::one(), x, &SECP256R1_P)?; + exec_scopes.insert_value("value", value); + + Ok(()) +} + +pub const WRITE_DIVMOD_SEGMENT: &str = r#"from starkware.starknet.core.os.data_availability.bls_utils import BLS_PRIME, pack, split + +a = pack(ids.a, PRIME) +b = pack(ids.b, PRIME) + +q, r = divmod(a * b, BLS_PRIME) + +# By the assumption: |a|, |b| < 2**104 * ((2**86) ** 2 + 2**86 + 1) < 2**276.001. +# Therefore |q| <= |ab| / BLS_PRIME < 2**299. +# Hence the absolute value of the high limb of split(q) < 2**127. +segments.write_arg(ids.q.address_, split(q)) +segments.write_arg(ids.res.address_, split(r))"#; + +pub fn write_div_mod_segment( + vm: &mut VirtualMachine, + _exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let a = bls_pack( + &BigInt3::from_var_name("a", vm, ids_data, ap_tracking)?, + &SECP_P, + ); + let b = bls_pack( + &BigInt3::from_var_name("b", vm, ids_data, ap_tracking)?, + &SECP_P, + ); + let (q, r) = (a * b).div_mod_floor(&BLS_PRIME); + let q_reloc = get_relocatable_from_var_name("q", vm, ids_data, ap_tracking)?; + let res_reloc = get_relocatable_from_var_name("res", vm, ids_data, ap_tracking)?; + + let q_arg: Vec = bls_split(&q) + .into_iter() + .map(|ref n| Felt252::from(n).into()) + .collect::>(); + let res_arg: Vec = bls_split(&r) + .into_iter() + .map(|ref n| Felt252::from(n).into()) + .collect::>(); + vm.write_arg(q_reloc, &q_arg).map_err(HintError::Memory)?; + vm.write_arg(res_reloc, &res_arg) + .map_err(HintError::Memory)?; + Ok(()) +} + +fn bls_split(num: &BigInt) -> Vec { + use num_traits::Signed; + let mut num = num.clone(); + let mut a = Vec::new(); + for _ in 0..2 { + let residue = num.clone() % BLS_BASE.deref(); + num /= BLS_BASE.deref(); + a.push(residue); + } + a.push(num.clone()); + assert!(num.abs() < BigInt::from_u128(1 << 127).unwrap()); + a +} + +fn as_int(value: &BigInt, prime: &BigInt) -> BigInt { + let half_prime = prime.clone() / 2u32; + if value > &half_prime { + value - prime + } else { + value.clone() + } +} + +fn bls_pack(z: &BigInt3, prime: &BigInt) -> BigInt { + let limbs = &z.limbs; + limbs + .iter() + .enumerate() + .fold(BigInt::zero(), |acc, (i, limb)| { + let limb_as_int = as_int(&limb.to_bigint(), prime); + acc + limb_as_int * &BLS_BASE.pow(i as u32) + }) +} + +#[cfg(test)] +mod tests { + + use assert_matches::assert_matches; + + use crate::utils::test_utils::*; + + use super::*; + + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::*; + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_is_on_curve_2() { + let mut vm = VirtualMachine::new(false); + vm.set_fp(1); + let ids_data = non_continuous_ids_data![("is_on_curve", -1)]; + vm.segments = segments![((1, 0), 1)]; + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + let y = BigInt::from(1234); + let y_square_int = y.clone() * y.clone(); + + exec_scopes.insert_value("y", y); + exec_scopes.insert_value("y_square_int", y_square_int); + + is_on_curve_2( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &Default::default(), + ) + .expect("is_on_curve2() failed"); + + let is_on_curve: Felt252 = + get_integer_from_var_name("is_on_curve", &vm, &ids_data, &ap_tracking) + .expect("is_on_curve2 should be put in ids_data"); + assert_eq!(is_on_curve, 1.into()); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_compute_q_mod_prime() { + let mut vm = VirtualMachine::new(false); + + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + vm.run_context.fp = 9; + //Create hint data + let ids_data = non_continuous_ids_data![("val", -5), ("q", 0)]; + vm.segments = segments![((1, 4), 0), ((1, 5), 0), ((1, 6), 0)]; + compute_q_mod_prime( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &Default::default(), + ) + .expect("compute_q_mod_prime() failed"); + + let q: Felt252 = get_integer_from_var_name("q", &vm, &ids_data, &ap_tracking) + .expect("compute_q_mod_prime should have put 'q' in ids_data"); + assert_eq!(q, Felt252::from(0)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_compute_ids_high_low() { + let mut vm = VirtualMachine::new(false); + + let value = BigInt::from(25); + let shift = BigInt::from(12); + + vm.set_fp(14); + let ids_data = non_continuous_ids_data![ + ("UPPER_BOUND", -14), + ("value", -11), + ("high", -8), + ("low", -5), + ("SHIFT", -2) + ]; + + vm.segments = segments!( + //UPPER_BOUND + ((1, 0), 18446744069414584321), + ((1, 1), 0), + ((1, 2), 0), + //value + ((1, 3), 25), + ((1, 4), 0), + ((1, 5), 0), + //high + ((1, 6), 2), + ((1, 7), 0), + ((1, 8), 0), + //low + ((1, 9), 1), + ((1, 10), 0), + ((1, 11), 0), + //SHIFT + ((1, 12), 12), + ((1, 13), 0), + ((1, 14), 0) + ); + + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + let constants = HashMap::from([ + ( + "UPPER_BOUND".to_string(), + Felt252::from(18446744069414584321_u128), + ), + ("SHIFT".to_string(), Felt252::from(12)), + ]); + compute_ids_high_low( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &constants, + ) + .expect("compute_ids_high_low() failed"); + + let high: Felt252 = get_integer_from_var_name("high", &vm, &ids_data, &ap_tracking) + .expect("compute_ids_high_low should have put 'high' in ids_data"); + let low: Felt252 = get_integer_from_var_name("low", &vm, &ids_data, &ap_tracking) + .expect("compute_ids_high_low should have put 'low' in ids_data"); + + let (expected_high, expected_low) = value.div_rem(&shift); + assert_eq!(high, Felt252::from(expected_high)); + assert_eq!(low, Felt252::from(expected_low)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_calculate_value() { + let mut vm = VirtualMachine::new(false); + vm.set_fp(10); + + let ids_data = non_continuous_ids_data![("x", -10), ("v", -7)]; + vm.segments = segments!( + // X + ((1, 0), 18446744069414584321), + ((1, 1), 0), + ((1, 2), 0), + // v + ((1, 3), 1), + ((1, 4), 0), + ((1, 5), 0), + ); + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + let x = BigInt::from(18446744069414584321u128); // Example x value + let v = BigInt::from(1); // Example v value (must be 0 or 1 for even/odd check) + + let constants = HashMap::new(); + + r1_get_point_from_x( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &constants, + ) + .expect("calculate_value() failed"); + + let value: BigInt = exec_scopes + .get("value") + .expect("value should be calculated and stored in exec_scopes"); + + // Compute y_squared_from_x(x) + let y_square_int = (x.modpow(&BigInt::from(3), &SECP256R1_P) + + SECP256R1_ALPHA.deref() * &x + + SECP256R1_B.deref()) + .mod_floor(&SECP256R1_P); + + // Calculate y = pow(y_square_int, (SECP256R1_P + 1) // 4, SECP256R1_P) + let exp = (SECP256R1_P.deref() + BigInt::one()).div_floor(&BigInt::from(4)); + let y = y_square_int.modpow(&exp, &SECP256R1_P); + + // Determine the expected value based on the parity of v and y + let expected_value = if v.is_even() == y.is_even() { + y + } else { + (-y).mod_floor(&SECP256R1_P) + }; + + assert_eq!(value, expected_value); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_pack_x_prime() { + let mut vm = VirtualMachine::new(false); + + //Initialize fp + vm.run_context.fp = 10; + + //Create hint data + let ids_data = non_continuous_ids_data![("x", -5)]; + + vm.segments = segments![ + ((1, 5), ("132181232131231239112312312313213083892150", 10)), + ((1, 6), 10), + ((1, 7), 10) + ]; + + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + reduce_value( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &Default::default(), + ) + .expect("pack_x_prime() failed"); + + assert_matches!( + exec_scopes.get::("value"), + Ok(x) if x == bigint_str!( + "59863107065205964761754162760883789350782881856141750" + ) + ); + } +} diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/mod.rs b/vm/src/hint_processor/builtin_hint_processor/secp/mod.rs index bb98b7868a..665bf8e4a7 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/mod.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/mod.rs @@ -1,5 +1,6 @@ pub mod bigint_utils; pub mod ec_utils; pub mod field_utils; +pub mod hints; pub mod secp_utils; pub mod signature; diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs b/vm/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs index 4457c975b3..ba1e73aa9a 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs @@ -6,7 +6,7 @@ use crate::vm::errors::hint_errors::HintError; use lazy_static::lazy_static; use num_bigint::{BigInt, BigUint}; -use num_traits::Zero; +use num_traits::{FromPrimitive, Num, Zero}; // Constants in package "starkware.cairo.common.cairo_secp.constants". pub const BASE_86: &str = "starkware.cairo.common.cairo_secp.constants.BASE"; @@ -66,6 +66,20 @@ lazy_static! { pub(crate) static ref SECP256R1_ALPHA: BigInt = BigInt::from_str( "115792089210356248762697446949407573530086143415290314195533631308867097853948" ).unwrap(); + pub(crate) static ref SECP256R1_B: BigInt = BigInt::from_str_radix( + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", + 16, + ) + .unwrap(); + + pub(crate) static ref BLS_PRIME: BigInt = BigInt::from_str( + "52435875175126190479447740508185965837690552500527637822603658699938581184513" + ) + .unwrap(); + + pub(crate) static ref BLS_BASE: BigInt = BigInt::from_u64(2).unwrap().pow(86); + + } /* diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/signature.rs b/vm/src/hint_processor/builtin_hint_processor/secp/signature.rs index 57d72b5bba..b495ab073f 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/signature.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/signature.rs @@ -118,8 +118,10 @@ pub fn get_point_from_x( .pack86() .mod_floor(&SECP_P); let y_cube_int = (x_cube_int + beta).mod_floor(&SECP_P); + exec_scopes.insert_value("y_square_int", y_cube_int.clone()); // Divide by 4 let mut y = y_cube_int.modpow(&(&*SECP_P + 1_u32).shr(2_u32), &SECP_P); + exec_scopes.insert_value::("y", y.clone()); let v = get_integer_from_var_name("v", vm, ids_data, ap_tracking)?.to_bigint(); if v.is_even() != y.is_even() { From 40ebe36e64ffa429ff18cba8ef0bd7d2c122ac12 Mon Sep 17 00:00:00 2001 From: Olivier Desenfans Date: Thu, 29 Aug 2024 13:59:32 +0200 Subject: [PATCH 2/4] PR link in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34cfaaa1fd..7c9f5851f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ #### Upcoming Changes * chore: bump `cairo-lang-` dependencies to 2.7.1 [#1823](https://github.com/lambdaclass/cairo-vm/pull/1823) -* feat: Implement `SECP related` hints +* feat: Implement `SECP related` hints [#1829](https://github.com/lambdaclass/cairo-vm/pull/1829) #### [1.0.1] - 2024-08-12 From e2198f07b37de2808e7b826467977fdb93f32703 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 9 Sep 2024 15:48:35 -0600 Subject: [PATCH 3/4] Impl eval_circuit hint --- .../cairo_1_hint_processor/hint_processor.rs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs b/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs index 98bb0a1547..5d8b49f823 100644 --- a/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs +++ b/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs @@ -274,6 +274,9 @@ impl Cairo1HintProcessor { t_or_k0, t_or_k1, ), + Hint::Core(CoreHintBase::Core(CoreHint::EvalCircuit { n_add_mods, add_mod_builtin, n_mul_mods, mul_mod_builtin } )) => { + self.eval_circuit(vm, n_add_mods, add_mod_builtin, n_mul_mods, mul_mod_builtin) + }, Hint::Starknet(StarknetHint::Cheatcode { selector, .. }) => { let selector = &selector.value.to_bytes_be().1; let selector = crate::stdlib::str::from_utf8(selector).map_err(|_| { @@ -1192,6 +1195,33 @@ impl Cairo1HintProcessor { } Ok(()) } + + #[allow(clippy::too_many_arguments)] + fn eval_circuit( + &self, + vm: &mut VirtualMachine, + n_add_mods: &ResOperand, + add_mod_builtin: &ResOperand, + n_mul_mods: &ResOperand, + mul_mod_builtin: &ResOperand, + ) -> Result<(), HintError> { + let n_add_mods: usize = get_val(vm, n_add_mods)? + .to_bigint() + .try_into() + .map_err(|_| HintError::BigintToU32Fail)?; + let add_mod_builtin = as_relocatable(vm, add_mod_builtin)?; + let n_mul_mods: usize = get_val(vm, n_mul_mods)? + .to_bigint() + .try_into() + .map_err(|_| HintError::BigintToU32Fail)?; + let mul_mod_builtin = as_relocatable(vm, mul_mod_builtin)?; + + vm.mod_builtin_fill_memory( + Some((add_mod_builtin, n_add_mods)), + Some((mul_mod_builtin, n_mul_mods)), + None, + ).map_err(HintError::from) + } } impl HintProcessorLogic for Cairo1HintProcessor { From 679f4124f8bc2120e54f1f3fbe4fed23ae9eb765 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Wed, 11 Sep 2024 14:45:40 -0600 Subject: [PATCH 4/4] Use cairo's impl of eval_circuit --- .../cairo_1_hint_processor/circuit.rs | 276 ++++++++++++++++++ .../cairo_1_hint_processor/hint_processor.rs | 8 +- .../cairo_1_hint_processor/mod.rs | 1 + 3 files changed, 279 insertions(+), 6 deletions(-) create mode 100644 vm/src/hint_processor/cairo_1_hint_processor/circuit.rs diff --git a/vm/src/hint_processor/cairo_1_hint_processor/circuit.rs b/vm/src/hint_processor/cairo_1_hint_processor/circuit.rs new file mode 100644 index 0000000000..ac916e8a92 --- /dev/null +++ b/vm/src/hint_processor/cairo_1_hint_processor/circuit.rs @@ -0,0 +1,276 @@ +use std::array; +use std::ops::{Deref, Shl}; + +use num_bigint::{BigInt, BigUint, ToBigInt}; +use num_integer::{ExtendedGcd, Integer}; +use num_traits::{One, Signed, Zero}; +use starknet_types_core::felt::Felt as Felt252; + +use crate::types::relocatable::{MaybeRelocatable, Relocatable}; +use crate::vm::errors::hint_errors::HintError; +use crate::vm::vm_core::VirtualMachine; + + +// These constants are copied from `cairo-lang-sierra` crate +/// The number of limbs used to represent a single value in the circuit. +pub const VALUE_SIZE: usize = 4; +/// The size of the AddMod and MulMod builtin instances. +pub const MOD_BUILTIN_INSTANCE_SIZE: usize = 7; +/// A gate is defined by 3 offsets, the first two are the inputs and the third is the output. +pub const OFFSETS_PER_GATE: usize = 3; + +struct CircuitInstance<'a> { + vm: &'a mut VirtualMachine, + values_ptr: Relocatable, + add_mod_offsets: Relocatable, + mul_mod_offsets: Relocatable, + modulus: BigUint, +} + +impl CircuitInstance<'_> { + /// Given an address of an offset, gives the corresponding address in the values buffer. + fn get_value_ptr(&self, offset_addr: Relocatable) -> Relocatable { + (self.values_ptr + self.vm.get_integer(offset_addr).unwrap().as_ref()).unwrap() + } + + /// Reads a value from the values buffer. + fn read_circuit_value(&mut self, offset_addr: Relocatable) -> Option { + let addr = self.get_value_ptr(offset_addr); + read_circuit_value(self.vm, addr) + } + + /// Writes a value to the values buffer. + fn write_circuit_value(&mut self, offset_addr: Relocatable, value: BigUint) { + let addr = self.get_value_ptr(offset_addr); + write_circuit_value(self.vm, addr, value); + } + + /// Reads a values from the location specified by `index` in the `add_mod_offsets` buffer. + fn read_addmod_value(&mut self, index: usize) -> Option { + self.read_circuit_value((self.add_mod_offsets + index).unwrap()) + } + + /// Writes `value` to the location specified by `index` in the `add_mod_offsets` buffer. + fn write_addmod_value(&mut self, index: usize, value: BigUint) { + self.write_circuit_value((self.add_mod_offsets + index).unwrap(), value) + } + + /// Reads a values from the location specified by `index` in the `mul_mod_offsets` buffer. + fn get_mulmod_value(&mut self, index: usize) -> Option { + self.read_circuit_value((self.mul_mod_offsets + index).unwrap()) + } + + /// Writes `value` to the location specified by `index` in the `mul_mod_offsets` buffer. + fn write_mulmod_value(&mut self, index: usize, value: BigUint) { + self.write_circuit_value((self.mul_mod_offsets + index).unwrap(), value) + } + + /// Fills the values for an add mod gate. + /// Returns true if all the inputs are ready and the values were filled successfully, false + /// otherwise. + fn fill_add_gate(&mut self, index: usize) -> bool { + let lhs = self.read_addmod_value(index); + let rhs = self.read_addmod_value(index + 1); + + match (lhs, rhs) { + (Some(lhs), Some(rhs)) => { + let res = (lhs + rhs).mod_floor(&self.modulus); + self.write_addmod_value(index + 2, res); + true + } + + (None, Some(rhs)) => { + // lhs + rhs = res => lhs = res - rhs + if let Some(res) = self.read_addmod_value(index + 2) { + self.write_addmod_value( + index, + (res + &self.modulus - rhs).mod_floor(&self.modulus), + ); + true + } else { + false + } + } + + _ => false, + } + } + + /// Fills the values for a mul mod gate. + /// Assumes all the inputs are ready and the modulus is not zero or one. + /// Returns true if the values were filled successfully, returns false if its an inverse + /// operation and input is not invertible. + fn fill_mul_gate(&mut self, index: usize) -> bool { + let lhs = self.get_mulmod_value(index); + let rhs = self.get_mulmod_value(index + 1); + + match (lhs, rhs) { + (Some(lhs), Some(rhs)) => { + let res = (lhs * rhs).mod_floor(&self.modulus); + self.write_mulmod_value(index + 2, res); + true + } + (None, Some(rhs)) => { + let (success, res) = invert_or_nullify(rhs, &self.modulus); + self.write_mulmod_value(index, res); + success + } + _ => panic!("Unexpected None value in fill_mul_gate"), + } + } +} + +/// Reads a circuit value from the memory. +fn read_circuit_value(vm: &mut VirtualMachine, addr: Relocatable) -> Option { + let mut res = BigUint::zero(); + for i in (0..VALUE_SIZE).rev() { + let addr_i = (addr + i).unwrap(); + match vm.get_maybe(&addr_i) { + Some(MaybeRelocatable::Int(limb)) => res = res.shl(96) + limb.to_biguint(), + _ => return None, + } + } + Some(res) +} + +/// Writes a circuit value from the memory. +fn write_circuit_value(vm: &mut VirtualMachine, addr: Relocatable, mut value: BigUint) { + for i in 0..VALUE_SIZE { + let (new_value, rem) = value.div_rem(&BigUint::from(1_u32).shl(96)); + let addr_i = (addr + i).unwrap(); + vm.insert_value(addr_i, Felt252::from(rem)).unwrap(); + value = new_value; + } +} + +/// Fills the values for a circuit. +/// +/// Returns the first mul gate index that failed to fill its values or `n_mul_mods` if all gates +/// were filled successfully. +pub fn fill_values( + vm: &mut VirtualMachine, + values_ptr: Relocatable, + add_mod_offsets: Relocatable, + n_add_mods: usize, + mul_mod_offsets: Relocatable, + n_mul_mods: usize, + modulus_ptr: Relocatable, +) -> usize { + let modulus = read_circuit_value(vm, modulus_ptr).unwrap(); + let mut c = CircuitInstance { vm, values_ptr, add_mod_offsets, mul_mod_offsets, modulus }; + + let mut addmod_idx = 0; + let mut first_failure_idx = n_mul_mods; + + let mut mulmod_idx = 0; + loop { + while addmod_idx < n_add_mods { + if !c.fill_add_gate(3 * addmod_idx) { + break; + } + addmod_idx += 1; + } + + if mulmod_idx == n_mul_mods { + break; + } + + let success = c.fill_mul_gate(3 * mulmod_idx); + if !success && first_failure_idx == n_mul_mods { + first_failure_idx = mulmod_idx; + } + mulmod_idx += 1; + } + + first_failure_idx +} + +/// Returns (value % modulus). +fn positive_modulus(value: &BigInt, modulus: &BigUint) -> BigUint { + let value_mod = value.magnitude().mod_floor(modulus); + if value.is_negative() { modulus - value_mod } else { value_mod } +} + +/// Given a value and a modulus, either finds its inverse or finds a non-zero value that nullifies +/// the value. +/// +/// If the value is invertible, returns (true, inverse), otherwise returns (false, nullifier). +fn invert_or_nullify(value: BigUint, modulus: &BigUint) -> (bool, BigUint) { + let ExtendedGcd::<_> { gcd, x, y: _ } = + value.to_bigint().unwrap().extended_gcd(&modulus.to_bigint().unwrap()); + + let gcd = gcd.to_biguint().unwrap(); + if gcd.is_one() { + return (true, positive_modulus(&x, modulus)); + } + let nullifier = modulus / gcd; + // Note that gcd divides the value, so value * nullifier = value * (modulus / gcd) = + // (value // gcd) * modulus = 0 (mod modulus) + (false, nullifier) +} + +/// Fills the instances of a mod builtin. +pub fn fill_instances( + vm: &mut VirtualMachine, + builtin_ptr: Relocatable, + n_instances: usize, + modulus: [Felt252; VALUE_SIZE], + values_ptr: Relocatable, + mut offsets_ptr: Relocatable, +) -> Result<(), HintError> { + for i in 0..n_instances { + let instance_ptr = (builtin_ptr + i * MOD_BUILTIN_INSTANCE_SIZE)?; + + for (idx, value) in modulus.iter().enumerate() { + vm.insert_value((instance_ptr + idx)?, *value)?; + } + + vm.insert_value((instance_ptr + 4)?, values_ptr)?; + vm.insert_value((instance_ptr + 5)?, offsets_ptr)?; + offsets_ptr += OFFSETS_PER_GATE; + vm.insert_value((instance_ptr + 6)?, n_instances - i)?; + } + + Ok(()) +} + +/// Evaluates a circuit and fills the builtin instances and the values buffer. +/// +/// Returns the first mul gate index that failed to fill its values or `n_mul_mods` if all gates +/// were filled successfully. +pub fn eval_circuit( + vm: &mut VirtualMachine, + add_mod_builtin: Relocatable, + n_add_mods: usize, + mul_mod_builtin: Relocatable, + n_mul_mods: usize, +) -> Result<(), HintError> { + let modulus_ptr = mul_mod_builtin; + // The offset of the values pointer inside an instance of the builtins. + let values_offset = 4; + // The offset of the offsets pointer inside an instance of the builtins. + let offsets_offset = 5; + let values_ptr = vm.get_relocatable((mul_mod_builtin + values_offset)?)?; + + let mul_mod_offsets = vm.get_relocatable((mul_mod_builtin + offsets_offset)?)?; + let add_mod_offsets = if n_add_mods == 0 { + mul_mod_offsets + } else { + vm.get_relocatable((add_mod_builtin + offsets_offset)?)? + }; + let n_computed_gates = fill_values( + vm, + values_ptr, + add_mod_offsets, + n_add_mods, + mul_mod_offsets, + n_mul_mods, + modulus_ptr, + ); + + let modulus: [Felt252; 4] = + array::from_fn(|i| *vm.get_integer((modulus_ptr + i).unwrap()).unwrap().deref()); + fill_instances(vm, add_mod_builtin, n_add_mods, modulus, values_ptr, add_mod_offsets)?; + fill_instances(vm, mul_mod_builtin, n_computed_gates, modulus, values_ptr, mul_mod_offsets)?; + Ok(()) +} diff --git a/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs b/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs index 5d8b49f823..8ee065aa02 100644 --- a/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs +++ b/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs @@ -1,3 +1,4 @@ +use super::circuit::eval_circuit; use super::dict_manager::DictManagerExecScope; use super::hint_processor_utils::*; use crate::any_box; @@ -1196,7 +1197,6 @@ impl Cairo1HintProcessor { Ok(()) } - #[allow(clippy::too_many_arguments)] fn eval_circuit( &self, vm: &mut VirtualMachine, @@ -1216,11 +1216,7 @@ impl Cairo1HintProcessor { .map_err(|_| HintError::BigintToU32Fail)?; let mul_mod_builtin = as_relocatable(vm, mul_mod_builtin)?; - vm.mod_builtin_fill_memory( - Some((add_mod_builtin, n_add_mods)), - Some((mul_mod_builtin, n_mul_mods)), - None, - ).map_err(HintError::from) + eval_circuit(vm, add_mod_builtin, n_add_mods, mul_mod_builtin, n_mul_mods) } } diff --git a/vm/src/hint_processor/cairo_1_hint_processor/mod.rs b/vm/src/hint_processor/cairo_1_hint_processor/mod.rs index 29d5f47bd3..0c0f49a29f 100644 --- a/vm/src/hint_processor/cairo_1_hint_processor/mod.rs +++ b/vm/src/hint_processor/cairo_1_hint_processor/mod.rs @@ -1,3 +1,4 @@ pub mod dict_manager; pub mod hint_processor; pub mod hint_processor_utils; +pub mod circuit;