From fc82ee504783a99b451b3b7e34fc0320b8b3bf59 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 Mar 2023 16:36:31 -0300 Subject: [PATCH 01/34] Initial progress --- .../builtin_hint_processor/ec_utils.rs | 67 +++++++++++++++++++ .../builtin_hint_processor/hint_code.rs | 10 +++ .../builtin_hint_processor/mod.rs | 1 + 3 files changed, 78 insertions(+) create mode 100644 src/hint_processor/builtin_hint_processor/ec_utils.rs diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs new file mode 100644 index 0000000000..f427fa0ec8 --- /dev/null +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -0,0 +1,67 @@ +use crate::stdlib::{ + collections::HashMap, + prelude::*, + borrow::Cow, +}; +use crate::{ + hint_processor::{ + builtin_hint_processor::{ + hint_utils::{ + get_integer_from_var_name, get_relocatable_from_var_name, + }, + }, + hint_processor_definition::HintReference, + }, + serde::deserialize_program::ApTracking, + vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, +}; +use felt::Felt; + +#[derive(Debug, PartialEq)] +struct EcPoint<'a> { + x: Cow<'a, Felt>, + y: Cow<'a, Felt>, +} +impl EcPoint<'_> { + fn from_var_name<'a>( + name: &'a str, + vm: &'a VirtualMachine, + ids_data: &'a HashMap, + ap_tracking: &'a ApTracking, + ) -> Result, HintError> { + // Get first addr of EcPoint struct + let point_addr = get_relocatable_from_var_name(name, vm, ids_data, ap_tracking)?; + Ok(EcPoint { + x: vm.get_integer(point_addr).map_err(|_| { + HintError::IdentifierHasNoMember(name.to_string(), "x".to_string()) + })?, + y: vm.get_integer((point_addr + 1)?).map_err(|_| { + HintError::IdentifierHasNoMember(name.to_string(), "x".to_string()) + })?, + }) + } +} + + +// Implements hint: +// from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME +// from starkware.python.math_utils import random_ec_point +// from starkware.python.utils import to_bytes + +// # Define a seed for random_ec_point that's dependent on all the input, so that: +// # (1) The added point s is deterministic. +// # (2) It's hard to choose inputs for which the builtin will fail. +// seed = b"".join(map(to_bytes, [ids.p.x, ids.p.y, ids.m, ids.q.x, ids.q.y])) +// ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed) + +pub fn random_ec_point( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + let p = EcPoint::from_var_name("p", vm, ids_data, ap_tracking)?; + let q = EcPoint::from_var_name("q", vm, ids_data, ap_tracking)?; + let m = get_integer_from_var_name("m", vm, ids_data, ap_tracking)?; + let bytes: Vec = [p.x, p.y, m, q.x, q.y].iter().flat_map(|x| x.to_bytes_be()).collect(); + Ok(()) +} \ No newline at end of file diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index d529b9820f..42143c5ef1 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -541,5 +541,15 @@ pub(crate) const RELOCATE_SEGMENT: &str = pub(crate) const TEMPORARY_ARRAY: &str = r#"ids.temporary_array = segments.add_temp_segment()"#; pub(crate) const VERIFY_ECDSA_SIGNATURE: &str = r#"ecdsa_builtin.add_signature(ids.ecdsa_ptr.address_, (ids.signature_r, ids.signature_s))"#; + +pub(crate) const RANDOM_EC_POINT: &str = r#"from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME +from starkware.python.math_utils import random_ec_point +from starkware.python.utils import to_bytes + +# Define a seed for random_ec_point that's dependent on all the input, so that: +# (1) The added point s is deterministic. +# (2) It's hard to choose inputs for which the builtin will fail. +seed = b"".join(map(to_bytes, [ids.p.x, ids.p.y, ids.m, ids.q.x, ids.q.y])) +ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed)"#; #[cfg(feature = "skip_next_instruction_hint")] pub(crate) const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; diff --git a/src/hint_processor/builtin_hint_processor/mod.rs b/src/hint_processor/builtin_hint_processor/mod.rs index 32ff08b4f4..c8bcc69b46 100644 --- a/src/hint_processor/builtin_hint_processor/mod.rs +++ b/src/hint_processor/builtin_hint_processor/mod.rs @@ -23,3 +23,4 @@ pub mod skip_next_instruction; pub mod squash_dict_utils; pub mod uint256_utils; pub mod usort; +pub mod ec_utils; From 8005605fb31200c843e21a3f96d4910b1422fc9f Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 Mar 2023 17:58:06 -0300 Subject: [PATCH 02/34] Implement random_ec_point hint --- .../builtin_hint_processor/ec_utils.rs | 121 +++++++++++++++--- .../builtin_hint_processor/mod.rs | 2 +- src/vm/errors/hint_errors.rs | 2 + 3 files changed, 104 insertions(+), 21 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index f427fa0ec8..cbba29623e 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -1,14 +1,8 @@ -use crate::stdlib::{ - collections::HashMap, - prelude::*, - borrow::Cow, -}; +use crate::stdlib::{borrow::Cow, collections::HashMap, prelude::*}; use crate::{ hint_processor::{ - builtin_hint_processor::{ - hint_utils::{ - get_integer_from_var_name, get_relocatable_from_var_name, - }, + builtin_hint_processor::hint_utils::{ + get_integer_from_var_name, get_relocatable_from_var_name, }, hint_processor_definition::HintReference, }, @@ -16,6 +10,9 @@ use crate::{ vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, }; use felt::Felt; +use num_bigint::BigUint; +use num_traits::{Bounded, One, Pow}; +use sha2::{Digest, Sha256}; #[derive(Debug, PartialEq)] struct EcPoint<'a> { @@ -32,18 +29,17 @@ impl EcPoint<'_> { // Get first addr of EcPoint struct let point_addr = get_relocatable_from_var_name(name, vm, ids_data, ap_tracking)?; Ok(EcPoint { - x: vm.get_integer(point_addr).map_err(|_| { - HintError::IdentifierHasNoMember(name.to_string(), "x".to_string()) - })?, - y: vm.get_integer((point_addr + 1)?).map_err(|_| { - HintError::IdentifierHasNoMember(name.to_string(), "x".to_string()) - })?, + x: vm + .get_integer(point_addr) + .map_err(|_| HintError::IdentifierHasNoMember(name.to_string(), "x".to_string()))?, + y: vm + .get_integer((point_addr + 1)?) + .map_err(|_| HintError::IdentifierHasNoMember(name.to_string(), "x".to_string()))?, }) } } - -// Implements hint: +// Implements hint: // from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME // from starkware.python.math_utils import random_ec_point // from starkware.python.utils import to_bytes @@ -54,7 +50,7 @@ impl EcPoint<'_> { // seed = b"".join(map(to_bytes, [ids.p.x, ids.p.y, ids.m, ids.q.x, ids.q.y])) // ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed) -pub fn random_ec_point( +pub fn random_ec_point_hint( vm: &mut VirtualMachine, ids_data: &HashMap, ap_tracking: &ApTracking, @@ -62,6 +58,91 @@ pub fn random_ec_point( let p = EcPoint::from_var_name("p", vm, ids_data, ap_tracking)?; let q = EcPoint::from_var_name("q", vm, ids_data, ap_tracking)?; let m = get_integer_from_var_name("m", vm, ids_data, ap_tracking)?; - let bytes: Vec = [p.x, p.y, m, q.x, q.y].iter().flat_map(|x| x.to_bytes_be()).collect(); + let bytes: Vec = [p.x, p.y, m, q.x, q.y] + .iter() + .flat_map(|x| x.to_bytes_be()) + .collect(); + let (x, y) = random_ec_point(bytes)?; + let s_addr = get_relocatable_from_var_name("s", vm, ids_data, ap_tracking)?; + vm.insert_value(s_addr, x)?; + vm.insert_value((s_addr + 1)?, y)?; Ok(()) -} \ No newline at end of file +} + +// Returns a random non-zero point on the elliptic curve +// y^2 = x^3 + alpha * x + beta (mod field_prime). +// The point is created deterministically from the seed. +fn random_ec_point(seed_bytes: Vec) -> Result<(Felt, Felt), HintError> { + // Hash initial seed + let mut hasher = Sha256::new(); + hasher.update(seed_bytes); + let seed = hasher.finalize_reset().to_vec(); + for i in 0..100 { + // Calculate x + let mut i_bytes = (i as u64).to_be_bytes().to_vec(); + i_bytes.resize(10, 0); + let mut input = seed[1..8].to_vec(); + input.extend(i_bytes); + hasher.update(input); + let x = Felt::from_bytes_be(&hasher.finalize_reset()); + // Calculate y + let y_coef = (-1).pow(seed[0] & 1) as u32; + let y = recover_y(&x.to_biguint()); + if let Some(y) = y { + return Ok((x, Felt::from(y * y_coef))); + } + } + Err(HintError::RandomEcPointNotOnCurve) +} +const ALPHA: u32 = 3; //TODO GET THESE VALUES +const BETA: u32 = 4; + +// Recovers the corresponding y coordinate on the elliptic curve +// y^2 = x^3 + alpha * x + beta (mod field_prime) +// of a given x coordinate. +// Returns None if x is not the x coordinate of a point in the curve +fn recover_y(x: &BigUint) -> Option { + let y_squared: BigUint = x.pow(3_u32) + ALPHA * x + BETA; + if is_quad_residue(&y_squared) { + Some(y_squared.sqrt()) + } else { + None + } +} + +// Implementation adapted from sympy implementation +// Conditions: +// + prime is ommited as it will be CAIRO_PRIME +// + a >= 0 < prime (other cases ommited) +fn is_quad_residue(a: &BigUint) -> bool { + if a < &BigUint::from(2_u8) { + return true; + }; + a.modpow(&(Felt::max_value().to_biguint() / 2_u32), &Felt::prime()) == BigUint::one() +} + +// def random_ec_point( +// field_prime: int, alpha: int, beta: int, seed: Optional[bytes] = None +// ) -> Tuple[int, int]: +// """ +// Returns a random non-zero point on the elliptic curve +// y^2 = x^3 + alpha * x + beta (mod field_prime). +// If `seed` is not None, the point is created deterministically from the seed. +// """ +// if seed is not None: +// # If a seed is given, the function currently only extracts a 256-bit number from it. +// assert field_prime < 2**256, "Field prime must be less than 2^256." +// seed = sha256(seed).digest() +// for i in range(100): +// x = ( +// random.randrange(field_prime) +// if seed is None +// else int(sha256(seed[1:] + i.to_bytes(10, "little")).hexdigest(), 16) +// ) +// y_coef = (-1) ** (seed[0] & 1 if seed is not None else random.randrange(2)) +// try: +// y = recover_y(x, alpha, beta, field_prime) +// return x, (y_coef * y) % field_prime +// except NotOnCurveException: +// continue +// raise Exception("Could not find a point on the curve.") diff --git a/src/hint_processor/builtin_hint_processor/mod.rs b/src/hint_processor/builtin_hint_processor/mod.rs index c8bcc69b46..59ebe74c18 100644 --- a/src/hint_processor/builtin_hint_processor/mod.rs +++ b/src/hint_processor/builtin_hint_processor/mod.rs @@ -4,6 +4,7 @@ pub mod builtin_hint_processor_definition; pub mod cairo_keccak; pub mod dict_hint_utils; pub mod dict_manager; +pub mod ec_utils; pub mod find_element_hint; pub mod hint_code; pub mod hint_utils; @@ -23,4 +24,3 @@ pub mod skip_next_instruction; pub mod squash_dict_utils; pub mod uint256_utils; pub mod usort; -pub mod ec_utils; diff --git a/src/vm/errors/hint_errors.rs b/src/vm/errors/hint_errors.rs index e2ce4ec78d..9be002e085 100644 --- a/src/vm/errors/hint_errors.rs +++ b/src/vm/errors/hint_errors.rs @@ -171,4 +171,6 @@ pub enum HintError { AddSignatureNotAPublicKey(Relocatable), #[error(transparent)] Math(#[from] MathError), + #[error("random_ec_point: Could not find a point on the curve.")] + RandomEcPointNotOnCurve, } From 1045dd8c5ec73154a7e45dc6082c6ddf93fe7d17 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 Mar 2023 18:17:01 -0300 Subject: [PATCH 03/34] Add integration test --- cairo_programs/ec_op.cairo | 16 ++++++++++++++++ .../builtin_hint_processor_definition.rs | 5 +++++ tests/cairo_run_test.rs | 16 ++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 cairo_programs/ec_op.cairo diff --git a/cairo_programs/ec_op.cairo b/cairo_programs/ec_op.cairo new file mode 100644 index 0000000000..2f3908e6ec --- /dev/null +++ b/cairo_programs/ec_op.cairo @@ -0,0 +1,16 @@ +%builtins ec_op + +from starkware.cairo.common.cairo_builtins import EcOpBuiltin +from starkware.cairo.common.ec_point import EcPoint +from starkware.cairo.common.ec import ec_op + + +func main{ec_op_ptr: EcOpBuiltin*}() { + let p = EcPoint(3,0); + let m = 34; + let q = EcPoint(4,0); + let (r) = ec_op(p, m, q); + assert r.x = 3; + assert r.y = 0; + return (); +} diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index 6f94342a24..e83cdaef1d 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -66,6 +66,8 @@ use felt::Felt; #[cfg(feature = "skip_next_instruction_hint")] use crate::hint_processor::builtin_hint_processor::skip_next_instruction::skip_next_instruction; +use super::ec_utils::random_ec_point_hint; + pub struct HintProcessorData { pub code: String, pub ap_tracking: ApTracking, @@ -438,6 +440,9 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::VERIFY_ECDSA_SIGNATURE => { verify_ecdsa_signature(vm, &hint_data.ids_data, &hint_data.ap_tracking) } + hint_code::RANDOM_EC_POINT => { + random_ec_point_hint(vm, &hint_data.ids_data, &hint_data.ap_tracking) + } #[cfg(feature = "skip_next_instruction_hint")] hint_code::SKIP_NEXT_INSTRUCTION => skip_next_instruction(vm), code => Err(HintError::UnknownHint(code.to_string())), diff --git a/tests/cairo_run_test.rs b/tests/cairo_run_test.rs index c1bbb9f0a7..9efe1f3b8b 100644 --- a/tests/cairo_run_test.rs +++ b/tests/cairo_run_test.rs @@ -1408,3 +1408,19 @@ fn cairo_run_verify_signature_hint() { ) .expect("Couldn't run program"); } + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn cairo_run_ec_op() { + let mut hint_executor = BuiltinHintProcessor::new_empty(); + let cairo_run_config = cairo_run::CairoRunConfig { + layout: "all", + ..cairo_vm::cairo_run::CairoRunConfig::default() + }; + cairo_run::cairo_run( + include_bytes!("../cairo_programs/ec_op.json"), + &cairo_run_config, + &mut hint_executor, + ) + .expect("Couldn't run program"); +} From a6f3c6e298b3c80ae5bc4d93b2e70ce37f479734 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 Mar 2023 18:31:19 -0300 Subject: [PATCH 04/34] Fix alpha & beta values --- cairo_programs/ec_op.cairo | 7 +++++-- .../builtin_hint_processor/ec_utils.rs | 15 +++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/cairo_programs/ec_op.cairo b/cairo_programs/ec_op.cairo index 2f3908e6ec..9a86b30fe2 100644 --- a/cairo_programs/ec_op.cairo +++ b/cairo_programs/ec_op.cairo @@ -6,9 +6,12 @@ from starkware.cairo.common.ec import ec_op func main{ec_op_ptr: EcOpBuiltin*}() { - let p = EcPoint(3,0); + let p = EcPoint( + 0x6a4beaef5a93425b973179cdba0c9d42f30e01a5f1e2db73da0884b8d6756fc, + 0x72565ec81bc09ff53fbfad99324a92aa5b39fb58267e395e8abe36290ebf24f, + ); let m = 34; - let q = EcPoint(4,0); + let q = EcPoint(4,2); let (r) = ec_op(p, m, q); assert r.x = 3; assert r.y = 0; diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index cbba29623e..081cf1f04c 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -10,8 +10,9 @@ use crate::{ vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, }; use felt::Felt; +use lazy_static::lazy_static; use num_bigint::BigUint; -use num_traits::{Bounded, One, Pow}; +use num_traits::{Bounded, Num, One, Pow}; use sha2::{Digest, Sha256}; #[derive(Debug, PartialEq)] @@ -94,15 +95,21 @@ fn random_ec_point(seed_bytes: Vec) -> Result<(Felt, Felt), HintError> { } Err(HintError::RandomEcPointNotOnCurve) } -const ALPHA: u32 = 3; //TODO GET THESE VALUES -const BETA: u32 = 4; +const ALPHA: u32 = 1; +lazy_static! { + static ref BETA: BigUint = BigUint::from_str_radix( + "3141592653589793238462643383279502884197169399375105820974944592307816406665", + 10 + ) + .unwrap(); +} // Recovers the corresponding y coordinate on the elliptic curve // y^2 = x^3 + alpha * x + beta (mod field_prime) // of a given x coordinate. // Returns None if x is not the x coordinate of a point in the curve fn recover_y(x: &BigUint) -> Option { - let y_squared: BigUint = x.pow(3_u32) + ALPHA * x + BETA; + let y_squared: BigUint = x.pow(3_u32) + ALPHA * x + &*BETA; if is_quad_residue(&y_squared) { Some(y_squared.sqrt()) } else { From 94581dd52b6e89b4ca959a51fc577533c35a88d2 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 Mar 2023 18:32:13 -0300 Subject: [PATCH 05/34] Remove commented code --- .../builtin_hint_processor/ec_utils.rs | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index 081cf1f04c..b2df1cbbcd 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -127,29 +127,3 @@ fn is_quad_residue(a: &BigUint) -> bool { }; a.modpow(&(Felt::max_value().to_biguint() / 2_u32), &Felt::prime()) == BigUint::one() } - -// def random_ec_point( -// field_prime: int, alpha: int, beta: int, seed: Optional[bytes] = None -// ) -> Tuple[int, int]: -// """ -// Returns a random non-zero point on the elliptic curve -// y^2 = x^3 + alpha * x + beta (mod field_prime). -// If `seed` is not None, the point is created deterministically from the seed. -// """ -// if seed is not None: -// # If a seed is given, the function currently only extracts a 256-bit number from it. -// assert field_prime < 2**256, "Field prime must be less than 2^256." -// seed = sha256(seed).digest() -// for i in range(100): -// x = ( -// random.randrange(field_prime) -// if seed is None -// else int(sha256(seed[1:] + i.to_bytes(10, "little")).hexdigest(), 16) -// ) -// y_coef = (-1) ** (seed[0] & 1 if seed is not None else random.randrange(2)) -// try: -// y = recover_y(x, alpha, beta, field_prime) -// return x, (y_coef * y) % field_prime -// except NotOnCurveException: -// continue -// raise Exception("Could not find a point on the curve.") From c762ff7ad4826ae20ea88934541e66daec472f6b Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 Mar 2023 18:34:57 -0300 Subject: [PATCH 06/34] fix test program --- cairo_programs/ec_op.cairo | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cairo_programs/ec_op.cairo b/cairo_programs/ec_op.cairo index 9a86b30fe2..bf883ac8cf 100644 --- a/cairo_programs/ec_op.cairo +++ b/cairo_programs/ec_op.cairo @@ -11,9 +11,12 @@ func main{ec_op_ptr: EcOpBuiltin*}() { 0x72565ec81bc09ff53fbfad99324a92aa5b39fb58267e395e8abe36290ebf24f, ); let m = 34; - let q = EcPoint(4,2); + let q = EcPoint( + 0x654fd7e67a123dd13868093b3b7777f1ffef596c2e324f25ceaf9146698482c, + 0x4fad269cbf860980e38768fe9cb6b0b9ab03ee3fe84cfde2eccce597c874fd8, + ); let (r) = ec_op(p, m, q); - assert r.x = 3; - assert r.y = 0; + assert r.x = 108925483682366235368969256555281508851459278989259552980345066351008608800; + assert r.y = 1592365885972480102953613056006596671718206128324372995731808913669237079419; return (); } From dc4d8401054bfee22481ab891573f1ae0d72d58f Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 Mar 2023 19:11:14 -0300 Subject: [PATCH 07/34] Pad seed bytes --- src/hint_processor/builtin_hint_processor/ec_utils.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index b2df1cbbcd..829efa9ce0 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -61,7 +61,7 @@ pub fn random_ec_point_hint( let m = get_integer_from_var_name("m", vm, ids_data, ap_tracking)?; let bytes: Vec = [p.x, p.y, m, q.x, q.y] .iter() - .flat_map(|x| x.to_bytes_be()) + .flat_map(|x| to_padded_bytes(&x)) .collect(); let (x, y) = random_ec_point(bytes)?; let s_addr = get_relocatable_from_var_name("s", vm, ids_data, ap_tracking)?; @@ -70,14 +70,22 @@ pub fn random_ec_point_hint( Ok(()) } +fn to_padded_bytes(n: &Felt) -> Vec { + let mut bytes = n.to_bytes_be(); + bytes.resize(32, 0); + bytes +} + // Returns a random non-zero point on the elliptic curve // y^2 = x^3 + alpha * x + beta (mod field_prime). // The point is created deterministically from the seed. fn random_ec_point(seed_bytes: Vec) -> Result<(Felt, Felt), HintError> { // Hash initial seed let mut hasher = Sha256::new(); + println!("{:?}", &seed_bytes); hasher.update(seed_bytes); let seed = hasher.finalize_reset().to_vec(); + println!("{:?}", &seed); for i in 0..100 { // Calculate x let mut i_bytes = (i as u64).to_be_bytes().to_vec(); From 6bf8c08011041df3a979a4ac3dd4816c58559078 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 10:27:14 -0300 Subject: [PATCH 08/34] Pad left --- src/hint_processor/builtin_hint_processor/ec_utils.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index 829efa9ce0..a7066cb4b2 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -70,9 +70,11 @@ pub fn random_ec_point_hint( Ok(()) } +// Returns the Felt as a vec of bytes of len 32, pads left with zeros fn to_padded_bytes(n: &Felt) -> Vec { - let mut bytes = n.to_bytes_be(); - bytes.resize(32, 0); + let felt_to_bytes = n.to_bytes_be(); + let mut bytes: Vec = vec![0; 32 - felt_to_bytes.len()]; + bytes.extend(felt_to_bytes); bytes } @@ -82,10 +84,8 @@ fn to_padded_bytes(n: &Felt) -> Vec { fn random_ec_point(seed_bytes: Vec) -> Result<(Felt, Felt), HintError> { // Hash initial seed let mut hasher = Sha256::new(); - println!("{:?}", &seed_bytes); hasher.update(seed_bytes); let seed = hasher.finalize_reset().to_vec(); - println!("{:?}", &seed); for i in 0..100 { // Calculate x let mut i_bytes = (i as u64).to_be_bytes().to_vec(); From 493d399b38760e470c6015852225f64294382be3 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 11:21:05 -0300 Subject: [PATCH 09/34] Fix i padding --- src/hint_processor/builtin_hint_processor/ec_utils.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index a7066cb4b2..0c97afd947 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -88,10 +88,10 @@ fn random_ec_point(seed_bytes: Vec) -> Result<(Felt, Felt), HintError> { let seed = hasher.finalize_reset().to_vec(); for i in 0..100 { // Calculate x - let mut i_bytes = (i as u64).to_be_bytes().to_vec(); - i_bytes.resize(10, 0); - let mut input = seed[1..8].to_vec(); + let i_bytes = (i as u8).to_le_bytes(); + let mut input = seed[1..].to_vec(); input.extend(i_bytes); + input.extend(vec![0; 10 - i_bytes.len()]); hasher.update(input); let x = Felt::from_bytes_be(&hasher.finalize_reset()); // Calculate y From 46f7dddc54658f4a5bbe1ef279e7c0eff22cf99c Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 11:32:01 -0300 Subject: [PATCH 10/34] Fix x & y_coef --- src/hint_processor/builtin_hint_processor/ec_utils.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index 0c97afd947..4f338f0e71 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -12,6 +12,7 @@ use crate::{ use felt::Felt; use lazy_static::lazy_static; use num_bigint::BigUint; +use num_bigint::ToBigInt; use num_traits::{Bounded, Num, One, Pow}; use sha2::{Digest, Sha256}; @@ -93,12 +94,13 @@ fn random_ec_point(seed_bytes: Vec) -> Result<(Felt, Felt), HintError> { input.extend(i_bytes); input.extend(vec![0; 10 - i_bytes.len()]); hasher.update(input); - let x = Felt::from_bytes_be(&hasher.finalize_reset()); + let x = BigUint::from_bytes_be(&hasher.finalize_reset()); // Calculate y - let y_coef = (-1).pow(seed[0] & 1) as u32; - let y = recover_y(&x.to_biguint()); + let y_coef = (-1).pow(seed[0] & 1); + let y = recover_y(&x); if let Some(y) = y { - return Ok((x, Felt::from(y * y_coef))); + // Conversion from BigUint to BigInt doesnt fail + return Ok((Felt::from(x), Felt::from(y.to_bigint().unwrap() * y_coef))); } } Err(HintError::RandomEcPointNotOnCurve) From 79b75688c688d9c6b547c4ccd5278a765293e6bb Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 13:23:21 -0300 Subject: [PATCH 11/34] Copy sqrt implementation from PR #715 --- felt/src/bigint_felt.rs | 17 +++++ felt/src/lib.rs | 16 ++++- .../builtin_hint_processor/ec_utils.rs | 70 +++++++++++++++++-- 3 files changed, 96 insertions(+), 7 deletions(-) diff --git a/felt/src/bigint_felt.rs b/felt/src/bigint_felt.rs index e0e24acd64..84622a95d4 100644 --- a/felt/src/bigint_felt.rs +++ b/felt/src/bigint_felt.rs @@ -401,6 +401,23 @@ impl Sub for FeltBigInt { } } +impl<'a, const PH: u128, const PL: u128> Pow<&'a FeltBigInt> for &'a FeltBigInt { + type Output = FeltBigInt; + fn pow(self, rhs: Self) -> Self::Output { + FeltBigInt { + val: self.val.modpow(&rhs.val, &CAIRO_PRIME_BIGUINT), + } + } +} + +impl<'a, const PH: u128, const PL: u128> Pow> for &'a FeltBigInt { + type Output = FeltBigInt; + fn pow(self, rhs: Self::Output) -> Self::Output { + self.pow(&rhs) + } +} + + impl SubAssign for FeltBigInt { fn sub_assign(&mut self, rhs: Self) { *self = &*self - &rhs; diff --git a/felt/src/lib.rs b/felt/src/lib.rs index a94818c89b..e7a104a540 100644 --- a/felt/src/lib.rs +++ b/felt/src/lib.rs @@ -414,6 +414,15 @@ impl<'a> Pow for &'a Felt { } } +impl<'a> Pow<&'a Felt> for &'a Felt { + type Output = Felt; + fn pow(self, rhs: &'a Felt) -> Self::Output { + Self::Output { + value: (&self.value).pow(&rhs.value), + } + } +} + impl Div for Felt { type Output = Self; fn div(self, rhs: Self) -> Self { @@ -749,7 +758,8 @@ macro_rules! assert_felt_impl { fn assert_mul() {} fn assert_mul_ref<'a, T: Mul<&'a $type>>() {} fn assert_mul_assign_ref<'a, T: MulAssign<&'a $type>>() {} - fn assert_pow>() {} + fn assert_pow_u32>() {} + fn assert_pow_felt<'a, T: Pow<&'a $type>>() {} fn assert_div() {} fn assert_ref_div>() {} fn assert_rem() {} @@ -799,8 +809,8 @@ macro_rules! assert_felt_impl { assert_mul::<&$type>(); assert_mul_ref::<$type>(); assert_mul_assign_ref::<$type>(); - assert_pow::<$type>(); - assert_pow::<&$type>(); + assert_pow_u32::<$type>(); + assert_pow_felt::<&$type>(); assert_div::<$type>(); assert_div::<&$type>(); assert_ref_div::<&$type>(); diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index 4f338f0e71..05b399fe58 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -1,4 +1,5 @@ use crate::stdlib::{borrow::Cow, collections::HashMap, prelude::*}; +use crate::utils::CAIRO_PRIME; use crate::{ hint_processor::{ builtin_hint_processor::hint_utils::{ @@ -13,7 +14,7 @@ use felt::Felt; use lazy_static::lazy_static; use num_bigint::BigUint; use num_bigint::ToBigInt; -use num_traits::{Bounded, Num, One, Pow}; +use num_traits::{Bounded, Num, One, Pow, Zero}; use sha2::{Digest, Sha256}; #[derive(Debug, PartialEq)] @@ -98,6 +99,7 @@ fn random_ec_point(seed_bytes: Vec) -> Result<(Felt, Felt), HintError> { // Calculate y let y_coef = (-1).pow(seed[0] & 1); let y = recover_y(&x); + dbg!(&y); if let Some(y) = y { // Conversion from BigUint to BigInt doesnt fail return Ok((Felt::from(x), Felt::from(y.to_bigint().unwrap() * y_coef))); @@ -119,9 +121,10 @@ lazy_static! { // of a given x coordinate. // Returns None if x is not the x coordinate of a point in the curve fn recover_y(x: &BigUint) -> Option { - let y_squared: BigUint = x.pow(3_u32) + ALPHA * x + &*BETA; + let y_squared: BigUint = x.modpow(&BigUint::from(3_u32), &*CAIRO_PRIME) + ALPHA * x + &*BETA; + dbg!(&y_squared); if is_quad_residue(&y_squared) { - Some(y_squared.sqrt()) + Some(sqrt(&Felt::from(y_squared)).to_biguint()) } else { None } @@ -135,5 +138,64 @@ fn is_quad_residue(a: &BigUint) -> bool { if a < &BigUint::from(2_u8) { return true; }; - a.modpow(&(Felt::max_value().to_biguint() / 2_u32), &Felt::prime()) == BigUint::one() + a.modpow(&(Felt::max_value().to_biguint() / 2_u32), &*CAIRO_PRIME) == BigUint::one() +} + +// // Finds the minimum non-negative integer m such that (m*m) % p == n. +// // p = CAIRO_PRIME +// fn sqrt(num: BigUint){}; +// fn sqrt_mod_iter(num: BigUint) { +// let n = num.mod_floor(&*CAIRO_PRIME); +// if n.is_zero() { +// todo!(); +// } else { +// _sqrt_mod_prime_power(n); +// } +// } + +// // a, p = as_int(a), abs(as_int(p)) +// // if isprime(p): +// // a = a % p +// // if a == 0: +// // res = _sqrt_mod1(a, p, 1) +// // else: +// // res = _sqrt_mod_prime_power(a, p, 1) + +// // Find the solutions to ``x**2 = a mod p**k`` when ``a % p != 0`` +// // p = CAIRO_PRIME +// // k = 1 +// fn _sqrt_mod_prime_power(n: BigUint) { + +// } + +fn sqrt<'a>(n: &'a Felt) -> Felt { + // Based on Tonelli-Shanks' algorithm for finding square roots + // and sympy's library implementation of said algorithm. + if n.is_zero() || n.is_one() { + return n.clone(); + } + + let max_felt = Felt::max_value(); + let trailing_prime = Felt::max_value() >> 192; // 0x800000000000011 + let a = n.pow(&trailing_prime); + let d = (&Felt::new(3_i32)).pow(&trailing_prime); + let mut m = Felt::zero(); + let mut exponent = Felt::one() << 191_u32; + let mut adm; + for i in 0..192_u32 { + adm = &a * &(&d).pow(&m); + adm = (&adm).pow(&exponent); + exponent >>= 1; + // if adm ≡ -1 (mod CAIRO_PRIME) + if adm == max_felt { + m += Felt::one() << i; + } + } + let root_1 = n.pow(&((trailing_prime + 1_u32) >> 1)) * (&d).pow(&(m >> 1)); + let root_2 = &max_felt - &root_1 + 1_usize; + if root_1 < root_2 { + root_1 + } else { + root_2 + } } From fd922fb0146c401c9b04d50589a176674d7548b4 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 13:39:00 -0300 Subject: [PATCH 12/34] Move sqrt to math_utils + cleanup --- felt/src/bigint_felt.rs | 1 - .../builtin_hint_processor/ec_utils.rs | 65 +------------------ src/math_utils.rs | 40 ++++++++++-- 3 files changed, 39 insertions(+), 67 deletions(-) diff --git a/felt/src/bigint_felt.rs b/felt/src/bigint_felt.rs index 84622a95d4..95ecb5692f 100644 --- a/felt/src/bigint_felt.rs +++ b/felt/src/bigint_felt.rs @@ -417,7 +417,6 @@ impl<'a, const PH: u128, const PL: u128> Pow> for &'a FeltBig } } - impl SubAssign for FeltBigInt { fn sub_assign(&mut self, rhs: Self) { *self = &*self - &rhs; diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index 05b399fe58..afaed5a83b 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -14,9 +14,11 @@ use felt::Felt; use lazy_static::lazy_static; use num_bigint::BigUint; use num_bigint::ToBigInt; -use num_traits::{Bounded, Num, One, Pow, Zero}; +use num_traits::{Bounded, Num, One, Pow}; use sha2::{Digest, Sha256}; +use crate::math_utils::sqrt; + #[derive(Debug, PartialEq)] struct EcPoint<'a> { x: Cow<'a, Felt>, @@ -99,7 +101,6 @@ fn random_ec_point(seed_bytes: Vec) -> Result<(Felt, Felt), HintError> { // Calculate y let y_coef = (-1).pow(seed[0] & 1); let y = recover_y(&x); - dbg!(&y); if let Some(y) = y { // Conversion from BigUint to BigInt doesnt fail return Ok((Felt::from(x), Felt::from(y.to_bigint().unwrap() * y_coef))); @@ -122,7 +123,6 @@ lazy_static! { // Returns None if x is not the x coordinate of a point in the curve fn recover_y(x: &BigUint) -> Option { let y_squared: BigUint = x.modpow(&BigUint::from(3_u32), &*CAIRO_PRIME) + ALPHA * x + &*BETA; - dbg!(&y_squared); if is_quad_residue(&y_squared) { Some(sqrt(&Felt::from(y_squared)).to_biguint()) } else { @@ -140,62 +140,3 @@ fn is_quad_residue(a: &BigUint) -> bool { }; a.modpow(&(Felt::max_value().to_biguint() / 2_u32), &*CAIRO_PRIME) == BigUint::one() } - -// // Finds the minimum non-negative integer m such that (m*m) % p == n. -// // p = CAIRO_PRIME -// fn sqrt(num: BigUint){}; -// fn sqrt_mod_iter(num: BigUint) { -// let n = num.mod_floor(&*CAIRO_PRIME); -// if n.is_zero() { -// todo!(); -// } else { -// _sqrt_mod_prime_power(n); -// } -// } - -// // a, p = as_int(a), abs(as_int(p)) -// // if isprime(p): -// // a = a % p -// // if a == 0: -// // res = _sqrt_mod1(a, p, 1) -// // else: -// // res = _sqrt_mod_prime_power(a, p, 1) - -// // Find the solutions to ``x**2 = a mod p**k`` when ``a % p != 0`` -// // p = CAIRO_PRIME -// // k = 1 -// fn _sqrt_mod_prime_power(n: BigUint) { - -// } - -fn sqrt<'a>(n: &'a Felt) -> Felt { - // Based on Tonelli-Shanks' algorithm for finding square roots - // and sympy's library implementation of said algorithm. - if n.is_zero() || n.is_one() { - return n.clone(); - } - - let max_felt = Felt::max_value(); - let trailing_prime = Felt::max_value() >> 192; // 0x800000000000011 - let a = n.pow(&trailing_prime); - let d = (&Felt::new(3_i32)).pow(&trailing_prime); - let mut m = Felt::zero(); - let mut exponent = Felt::one() << 191_u32; - let mut adm; - for i in 0..192_u32 { - adm = &a * &(&d).pow(&m); - adm = (&adm).pow(&exponent); - exponent >>= 1; - // if adm ≡ -1 (mod CAIRO_PRIME) - if adm == max_felt { - m += Felt::one() << i; - } - } - let root_1 = n.pow(&((trailing_prime + 1_u32) >> 1)) * (&d).pow(&(m >> 1)); - let root_2 = &max_felt - &root_1 + 1_usize; - if root_1 < root_2 { - root_1 - } else { - root_2 - } -} diff --git a/src/math_utils.rs b/src/math_utils.rs index 0efd4293b5..eb9745d671 100644 --- a/src/math_utils.rs +++ b/src/math_utils.rs @@ -3,7 +3,7 @@ use crate::types::errors::math_errors::MathError; use felt::Felt; use num_bigint::{BigInt, BigUint}; use num_integer::Integer; -use num_traits::{One, Signed, Zero}; +use num_traits::{Bounded, One, Pow, Signed, Zero}; ///Returns the integer square root of the nonnegative integer n. ///This is the floor of the exact square root of n. @@ -28,7 +28,7 @@ pub fn isqrt(n: &BigUint) -> Result { y = (&x + n.div_floor(&x)).shr(1_u32); } - if !(&x.pow(2) <= n && n < &(&x + 1_u32).pow(2_u32)) { + if !(&BigUint::pow(&x, 2_u32) <= n && n < &BigUint::pow(&(&x + 1_u32), 2_u32)) { return Err(MathError::FailedToGetSqrt(n.clone())); }; Ok(x) @@ -157,6 +157,38 @@ pub fn ec_double_slope(point: &(BigInt, BigInt), alpha: &BigInt, prime: &BigInt) ) } +pub fn sqrt<'a>(n: &'a Felt) -> Felt { + // Based on Tonelli-Shanks' algorithm for finding square roots + // and sympy's library implementation of said algorithm. + if n.is_zero() || n.is_one() { + return n.clone(); + } + + let max_felt = Felt::max_value(); + let trailing_prime = Felt::max_value() >> 192; // 0x800000000000011 + let a = n.pow(&trailing_prime); + let d = (&Felt::new(3_i32)).pow(&trailing_prime); + let mut m = Felt::zero(); + let mut exponent = Felt::one() << 191_u32; + let mut adm; + for i in 0..192_u32 { + adm = &a * &(&d).pow(&m); + adm = (&adm).pow(&exponent); + exponent >>= 1; + // if adm ≡ -1 (mod CAIRO_PRIME) + if adm == max_felt { + m += Felt::one() << i; + } + } + let root_1 = n.pow(&((trailing_prime + 1_u32) >> 1)) * (&d).pow(&(m >> 1)); + let root_2 = &max_felt - &root_1 + 1_usize; + if root_1 < root_2 { + root_1 + } else { + root_2 + } +} + #[cfg(test)] mod tests { use super::*; @@ -540,7 +572,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn calculate_isqrt_b() { let n = biguint_str!("4573659632505831259480"); - assert_matches!(isqrt(&n.pow(2_u32)), Ok(num) if num == n); + assert_matches!(isqrt(&BigUint::pow(&n, 2_u32)), Ok(num) if num == n); } #[test] @@ -549,7 +581,7 @@ mod tests { let n = biguint_str!( "3618502788666131213697322783095070105623107215331596699973092056135872020481" ); - assert_matches!(isqrt(&n.pow(2_u32)), Ok(inner) if inner == n); + assert_matches!(isqrt(&BigUint::pow(&n, 2_u32)), Ok(inner) if inner == n); } #[test] From a87e40b711b4292bb7c5548f9f03f56bbaff9974 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 15:13:07 -0300 Subject: [PATCH 13/34] Add proptests --- Cargo.lock | 1 + Cargo.toml | 1 + felt/src/lib.rs | 19 +++++++++++++++++++ src/math_utils.rs | 18 ++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index a28f8f71cc..4b178eee62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -194,6 +194,7 @@ dependencies = [ "num-integer", "num-traits", "parse-hyperlinks", + "proptest", "rand_core", "rusty-hook", "serde", diff --git a/Cargo.toml b/Cargo.toml index 7794e20ad5..45814104fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,6 +83,7 @@ iai = "0.1" rusty-hook = "0.11" assert_matches = "1.5.0" criterion = { version = "0.3", features = ["html_reports"] } +proptest = "1.0.0" [[bench]] path = "bench/iai_benchmark.rs" diff --git a/felt/src/lib.rs b/felt/src/lib.rs index e7a104a540..1b57119bf0 100644 --- a/felt/src/lib.rs +++ b/felt/src/lib.rs @@ -1070,6 +1070,25 @@ mod test { prop_assert!(as_uint < p, "{}", as_uint); } + #[test] + #[allow(deprecated)] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + // Property-based test that ensures, for 100 values {x} that are randomly generated each time tests are run, that raising {x} to the {y}th power returns a result that is inside of the range [0, p]. + fn pow_felt_in_range(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)"){ + let base = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); + let exponent = Felt::parse_bytes(y.as_bytes(), 10).unwrap(); + let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); + + let result = Pow::pow(&base, &exponent); + let as_uint = result.to_biguint(); + prop_assert!(as_uint < p, "{}", as_uint); + + // test reference variant + let result: Felt = Pow::pow(&base, &exponent); + let as_uint = result.to_biguint(); + prop_assert!(as_uint < p, "{}", as_uint); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property based test that ensures, for 100 pairs of values {x} and {y} generated at random each time tests are run, that performing a Sum operation between them returns a result that is inside of the range [0, p]. diff --git a/src/math_utils.rs b/src/math_utils.rs index eb9745d671..c5064e1084 100644 --- a/src/math_utils.rs +++ b/src/math_utils.rs @@ -196,6 +196,7 @@ mod tests { use crate::utils::CAIRO_PRIME; use assert_matches::assert_matches; use num_traits::Num; + use proptest::prelude::*; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; @@ -597,4 +598,21 @@ mod tests { let y = BigInt::zero(); assert_matches!(safe_div_bigint(&x, &y), Err(MathError::DividedByZero)) } + + proptest! { + #[test] + // Test for sqrt of a quadratic residue. Result should be the minimum root. + fn sqrt_felt_test(ref x in "([1-9][0-9]*)") { + println!("{x}"); + let x = &Felt::parse_bytes(x.as_bytes(), 10).unwrap(); + let x_sq = x * x; + let sqrt = x_sq.sqrt(); + + if &sqrt != x { + assert_eq!(Felt::max_value() - sqrt + 1_usize, *x); + } else { + assert_eq!(&sqrt, x); + } + } + } } From 73ad0a9f92f43a51503e67564a7a44fa0ced0cbb Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 15:14:42 -0300 Subject: [PATCH 14/34] use constants in proptests --- felt/src/lib.rs | 58 ++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/felt/src/lib.rs b/felt/src/lib.rs index 1b57119bf0..33f7a1d124 100644 --- a/felt/src/lib.rs +++ b/felt/src/lib.rs @@ -862,7 +862,7 @@ mod test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property-based test that ensures, for 100 felt values that are randomly generated each time tests are run, that a new felt doesn't fall outside the range [0, p]. // In this and some of the following tests, The value of {x} can be either [0] or a very large number, in order to try to overflow the value of {p} and thus ensure the modular arithmetic is working correctly. - fn new_in_range(ref x in "(0|[1-9][0-9]*)") { + fn new_in_range(ref x in FELT_PATTERN) { let x = &Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); prop_assert!(&x.to_biguint() < p); @@ -872,7 +872,7 @@ mod test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property-based test that ensures, for 100 felt values that are randomly generated each time tests are run, that a felt created using Felt::from_bytes_be doesn't fall outside the range [0, p]. // In this and some of the following tests, The value of {x} can be either [0] or a very large number, in order to try to overflow the value of {p} and thus ensure the modular arithmetic is working correctly. - fn from_bytes_be_in_range(ref x in "(0|[1-9][0-9]*)") { + fn from_bytes_be_in_range(ref x in FELT_PATTERN) { let x = &Felt::from_bytes_be(x.as_bytes()); let max_felt = &Felt::max_value(); prop_assert!(x <= max_felt); @@ -882,7 +882,7 @@ mod test { #[allow(deprecated)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property-based test that ensures, for 100 felt values that are randomly generated each time tests are run, that the negative of a felt doesn't fall outside the range [0, p]. - fn neg_in_range(ref x in "(0|[1-9][0-9]*)") { + fn neg_in_range(ref x in FELT_PATTERN) { let x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -900,7 +900,7 @@ mod test { #[allow(deprecated)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property-based test that ensures, for 100 {x} and {y} values that are randomly generated each time tests are run, that a subtraction between two felts {x} and {y} and doesn't fall outside the range [0, p]. The values of {x} and {y} can be either [0] or a very large number. - fn sub_in_range(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)") { + fn sub_in_range(ref x in FELT_PATTERN, ref y in FELT_PATTERN) { let x = &Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = &Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -914,7 +914,7 @@ mod test { #[allow(deprecated)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property-based test that ensures, for 100 {x} and {y} values that are randomly generated each time tests are run, that a subtraction with assignment between two felts {x} and {y} and doesn't fall outside the range [0, p]. The values of {x} and {y} can be either [0] or a very large number. - fn sub_assign_in_range(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)") { + fn sub_assign_in_range(ref x in FELT_PATTERN, ref y in FELT_PATTERN) { let mut x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -933,7 +933,7 @@ mod test { #[allow(deprecated)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property-based test that ensures, for 100 {x} and {y} values that are randomly generated each time tests are run, that a multiplication between two felts {x} and {y} and doesn't fall outside the range [0, p]. The values of {x} and {y} can be either [0] or a very large number. - fn mul_in_range(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)") { + fn mul_in_range(ref x in FELT_PATTERN, ref y in FELT_PATTERN) { let x = &Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = &Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -947,7 +947,7 @@ mod test { #[allow(deprecated)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property-based test that ensures, for 100 pairs of {x} and {y} values that are randomly generated each time tests are run, that a multiplication with assignment between two felts {x} and {y} and doesn't fall outside the range [0, p]. The values of {x} and {y} can be either [0] or a very large number. - fn mul_assign_in_range(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)") { + fn mul_assign_in_range(ref x in FELT_PATTERN, ref y in FELT_PATTERN) { let mut x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = &Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -961,7 +961,7 @@ mod test { #[allow(deprecated)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property-based test that ensures, for 100 pairs of {x} and {y} values that are randomly generated each time tests are run, that the result of the division of {x} by {y} is the inverse multiplicative of {x} --that is, multiplying the result by {y} returns the original number {x}. The values of {x} and {y} can be either [0] or a very large number. - fn div_is_mul_inv(ref x in "(0|[1-9][0-9]*)", ref y in "[1-9][0-9]*") { + fn div_is_mul_inv(ref x in FELT_PATTERN, ref y in FELT_NON_ZERO_PATTERN) { let x = &Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = &Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -977,7 +977,7 @@ mod test { #[allow(deprecated)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property-based test that ensures, for 100 {value}s that are randomly generated each time tests are run, that performing a bit shift to the left by {shift_amount} of bits (between 0 and 999) returns a result that is inside of the range [0, p]. - fn shift_left_in_range(ref value in "(0|[1-9][0-9]*)", ref shift_amount in "[0-9]{1,3}"){ + fn shift_left_in_range(ref value in FELT_PATTERN, ref shift_amount in "[0-9]{1,3}"){ let value = Felt::parse_bytes(value.as_bytes(), 10).unwrap(); let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -993,7 +993,7 @@ mod test { #[allow(deprecated)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property-based test that ensures, for 100 {value}s that are randomly generated each time tests are run, that performing a bit shift to the right by {shift_amount} of bits (between 0 and 999) returns a result that is inside of the range [0, p]. - fn shift_right_in_range(ref value in "(0|[1-9][0-9]*)", ref shift_amount in "[0-9]{1,3}"){ + fn shift_right_in_range(ref value in FELT_PATTERN, ref shift_amount in "[0-9]{1,3}"){ let value = Felt::parse_bytes(value.as_bytes(), 10).unwrap(); let shift_amount:u32 = shift_amount.parse::().unwrap(); let result = (value >> shift_amount).to_biguint(); @@ -1006,7 +1006,7 @@ mod test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property-based test that ensures, for 100 {value}s that are randomly generated each time tests are run, that performing a bit shift to the right by {shift_amount} of bits (between 0 and 999), with assignment, returns a result that is inside of the range [0, p]. // "With assignment" means that the result of the operation is autommatically assigned to the variable value, replacing its previous content. - fn shift_right_assign_in_range(ref value in "(0|[1-9][0-9]*)", ref shift_amount in "[0-9]{1,3}"){ + fn shift_right_assign_in_range(ref value in FELT_PATTERN, ref shift_amount in "[0-9]{1,3}"){ let mut value = Felt::parse_bytes(value.as_bytes(), 10).unwrap(); let shift_amount:usize = shift_amount.parse::().unwrap(); let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -1018,7 +1018,7 @@ mod test { #[allow(deprecated)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property based test that ensures, for 100 pairs of values {x} and {y} generated at random each time tests are run, that performing a BitAnd operation between them returns a result that is inside of the range [0, p]. - fn bitand_in_range(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)"){ + fn bitand_in_range(ref x in FELT_PATTERN, ref y in FELT_PATTERN){ let x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -1031,7 +1031,7 @@ mod test { #[allow(deprecated)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property based test that ensures, for 100 pairs of values {x} and {y} generated at random each time tests are run, that performing a BitOr operation between them returns a result that is inside of the range [0, p]. - fn bitor_in_range(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)"){ + fn bitor_in_range(ref x in FELT_PATTERN, ref y in FELT_PATTERN){ let x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -1043,7 +1043,7 @@ mod test { #[allow(deprecated)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property based test that ensures, for 100 pairs of values {x} and {y} generated at random each time tests are run, that performing a BitXor operation between them returns a result that is inside of the range [0, p]. - fn bitxor_in_range(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)"){ + fn bitxor_in_range(ref x in FELT_PATTERN, ref y in FELT_PATTERN){ let x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -1055,7 +1055,7 @@ mod test { #[allow(deprecated)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property-based test that ensures, for 100 values {x} that are randomly generated each time tests are run, that raising {x} to the {y}th power returns a result that is inside of the range [0, p]. - fn pow_in_range(ref x in "(0|[1-9][0-9]*)", ref y in "[0-9]{1,2}"){ + fn pow_in_range(ref x in FELT_PATTERN, ref y in "[0-9]{1,2}"){ let base = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let exponent:u32 = y.parse()?; let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -1074,7 +1074,7 @@ mod test { #[allow(deprecated)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property-based test that ensures, for 100 values {x} that are randomly generated each time tests are run, that raising {x} to the {y}th power returns a result that is inside of the range [0, p]. - fn pow_felt_in_range(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)"){ + fn pow_felt_in_range(ref x in FELT_PATTERN, ref y in FELT_PATTERN){ let base = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let exponent = Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let p = BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -1092,7 +1092,7 @@ mod test { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property based test that ensures, for 100 pairs of values {x} and {y} generated at random each time tests are run, that performing a Sum operation between them returns a result that is inside of the range [0, p]. - fn sum_in_range(ref x in "[1-9][0-9]*", ref y in "[0-9][0-9]*"){ + fn sum_in_range(ref x in FELT_NON_ZERO_PATTERN, ref y in "[0-9][0-9]*"){ let x = &Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = &Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -1106,7 +1106,7 @@ mod test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property test to check that the remainder of a division between 100 pairs of values {x} and {y},generated at random each time tests are run, falls in the range [0, p]. x and y can either take the value of 0 or a large integer. // In Cairo, the result of x / y is defined to always satisfy the equation (x / y) * y == x, so the remainder is 0 most of the time. - fn rem_in_range(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)") { + fn rem_in_range(ref x in FELT_PATTERN, ref y in FELT_PATTERN) { let x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -1143,7 +1143,7 @@ mod test { #[test] // Property test to check that lcm(x, y) works. Since we're operating in a prime field, lcm // will just be the smaller number. - fn lcm_doesnt_panic(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)") { + fn lcm_doesnt_panic(ref x in FELT_PATTERN, ref y in FELT_PATTERN) { let x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let lcm = x.lcm(&y); @@ -1154,21 +1154,21 @@ mod test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // Property test to check that is_multiple_of(x, y) works. Since we're operating in a prime field, is_multiple_of // will always be true - fn is_multiple_of_doesnt_panic(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)") { + fn is_multiple_of_doesnt_panic(ref x in FELT_PATTERN, ref y in FELT_PATTERN) { let x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = Felt::parse_bytes(y.as_bytes(), 10).unwrap(); prop_assert!(x.is_multiple_of(&y)); } #[test] - fn divides_doesnt_panic(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)") { + fn divides_doesnt_panic(ref x in FELT_PATTERN, ref y in FELT_PATTERN) { let x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = Felt::parse_bytes(y.as_bytes(), 10).unwrap(); prop_assert!(x.divides(&y)); } #[test] - fn gcd_doesnt_panic(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)") { + fn gcd_doesnt_panic(ref x in FELT_PATTERN, ref y in FELT_PATTERN) { let x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let gcd1 = x.gcd(&y); @@ -1177,13 +1177,13 @@ mod test { } #[test] - fn is_even(ref x in "(0|[1-9][0-9]*)") { + fn is_even(ref x in FELT_PATTERN) { let x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); prop_assert_eq!(x.is_even(), x.to_biguint().is_even()); } #[test] - fn is_odd(ref x in "(0|[1-9][0-9]*)") { + fn is_odd(ref x in FELT_PATTERN) { let x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); prop_assert_eq!(x.is_odd(), x.to_biguint().is_odd()); } @@ -1195,7 +1195,7 @@ mod test { /// 0 + x = x ∀ x /// ``` #[test] - fn zero_additive_identity(ref x in "(0|[1-9][0-9]*)") { + fn zero_additive_identity(ref x in FELT_PATTERN) { let x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let zero = Felt::zero(); prop_assert_eq!(&x, &(&x + &zero)); @@ -1209,7 +1209,7 @@ mod test { /// 1 * x = x ∀ x /// ``` #[test] - fn one_multiplicative_identity(ref x in "(0|[1-9][0-9]*)") { + fn one_multiplicative_identity(ref x in FELT_PATTERN) { let x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let one = Felt::one(); prop_assert_eq!(&x, &(&x * &one)); @@ -1322,7 +1322,7 @@ mod test { } #[test] - fn add_in_range(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)") { + fn add_in_range(ref x in FELT_PATTERN, ref y in FELT_PATTERN) { let x = &Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = &Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); @@ -1333,7 +1333,7 @@ mod test { } #[test] - fn add_is_inv_sub(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)") { + fn add_is_inv_sub(ref x in FELT_PATTERN, ref y in FELT_PATTERN) { let x = &Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = &Felt::parse_bytes(y.as_bytes(), 10).unwrap(); @@ -1342,7 +1342,7 @@ mod test { } #[test] - fn add_assign_in_range(ref x in "(0|[1-9][0-9]*)", ref y in "(0|[1-9][0-9]*)") { + fn add_assign_in_range(ref x in FELT_PATTERN, ref y in FELT_PATTERN) { let mut x = Felt::parse_bytes(x.as_bytes(), 10).unwrap(); let y = Felt::parse_bytes(y.as_bytes(), 10).unwrap(); let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); From c6caa9b6da2409e8bf2c5c83535cb1f0a532c2d8 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 15:34:05 -0300 Subject: [PATCH 15/34] Add some tests --- .../builtin_hint_processor/ec_utils.rs | 60 +++++++++++++++++++ src/math_utils.rs | 15 +++++ 2 files changed, 75 insertions(+) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index afaed5a83b..99faf9330c 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -140,3 +140,63 @@ fn is_quad_residue(a: &BigUint) -> bool { }; a.modpow(&(Felt::max_value().to_biguint() / 2_u32), &*CAIRO_PRIME) == BigUint::one() } + +#[cfg(test)] +mod tests { + use num_traits::Zero; + + use super::*; + + #[test] + fn test_is_quad_residue_less_than_2() { + assert!(is_quad_residue(&BigUint::one())); + assert!(is_quad_residue(&BigUint::zero())); + } + + #[test] + fn test_is_quad_residue_false() { + assert!(!is_quad_residue( + &BigUint::from_str_radix( + "205857351767627712295703269674687767888261140702556021834663354704341414042", + 10 + ) + .unwrap() + )); + } + + #[test] + fn test_is_quad_residue_true() { + assert!(is_quad_residue( + &BigUint::from_str_radix( + "99957092485221722822822221624080199277265330641980989815386842231144616633668", + 10 + ) + .unwrap() + )); + } + + #[test] + fn test_recover_y_valid() { + let x = BigUint::from_str_radix( + "2497468900767850684421727063357792717599762502387246235265616708902555305129", + 10, + ) + .unwrap(); + let y = BigUint::from_str_radix( + "205857351767627712295703269674687767888261140702556021834663354704341414042", + 10, + ) + .unwrap(); + assert_eq!(recover_y(&x), Some(y)); + } + + #[test] + fn test_recover_y_invalid() { + let x = BigUint::from_str_radix( + "205857351767627712295703269674687767888261140702556021834663354704341414042", + 10, + ) + .unwrap(); + assert_eq!(recover_y(&x), None); + } +} diff --git a/src/math_utils.rs b/src/math_utils.rs index c5064e1084..6b70d374e3 100644 --- a/src/math_utils.rs +++ b/src/math_utils.rs @@ -599,6 +599,21 @@ mod tests { assert_matches!(safe_div_bigint(&x, &y), Err(MathError::DividedByZero)) } + #[test] + fn test_sqrt() { + let n = Felt::from_str_radix( + "99957092485221722822822221624080199277265330641980989815386842231144616633668", + 10, + ) + .unwrap(); + let expected_sqrt = Felt::from_str_radix( + "205857351767627712295703269674687767888261140702556021834663354704341414042", + 10, + ) + .unwrap(); + assert_eq!(sqrt(&n), expected_sqrt); + } + proptest! { #[test] // Test for sqrt of a quadratic residue. Result should be the minimum root. From 7feee1924047ee25cf070603978bb44f8539c0ae Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 15:46:48 -0300 Subject: [PATCH 16/34] Add test --- .../builtin_hint_processor/ec_utils.rs | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index 99faf9330c..77facd749f 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -67,7 +67,7 @@ pub fn random_ec_point_hint( .iter() .flat_map(|x| to_padded_bytes(&x)) .collect(); - let (x, y) = random_ec_point(bytes)?; + let (x, y) = random_ec_point_seeded(bytes)?; let s_addr = get_relocatable_from_var_name("s", vm, ids_data, ap_tracking)?; vm.insert_value(s_addr, x)?; vm.insert_value((s_addr + 1)?, y)?; @@ -85,7 +85,7 @@ fn to_padded_bytes(n: &Felt) -> Vec { // Returns a random non-zero point on the elliptic curve // y^2 = x^3 + alpha * x + beta (mod field_prime). // The point is created deterministically from the seed. -fn random_ec_point(seed_bytes: Vec) -> Result<(Felt, Felt), HintError> { +fn random_ec_point_seeded(seed_bytes: Vec) -> Result<(Felt, Felt), HintError> { // Hash initial seed let mut hasher = Sha256::new(); hasher.update(seed_bytes); @@ -199,4 +199,29 @@ mod tests { .unwrap(); assert_eq!(recover_y(&x), None); } + + #[test] + fn get_random_ec_point_seeded() { + let seed: Vec = vec![ + 6, 164, 190, 174, 245, 169, 52, 37, 185, 115, 23, 156, 219, 160, 201, 212, 47, 48, 224, + 26, 95, 30, 45, 183, 61, 160, 136, 75, 141, 103, 86, 252, 7, 37, 101, 236, 129, 188, 9, + 255, 83, 251, 250, 217, 147, 36, 169, 42, 165, 179, 159, 181, 130, 103, 227, 149, 232, + 171, 227, 98, 144, 235, 242, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 6, 84, 253, 126, 103, 161, 35, 221, 19, 134, + 128, 147, 179, 183, 119, 127, 31, 254, 245, 150, 194, 227, 36, 242, 92, 234, 249, 20, + 102, 152, 72, 44, 4, 250, 210, 105, 203, 248, 96, 152, 14, 56, 118, 143, 233, 203, 107, + 11, 154, 176, 62, 227, 254, 132, 207, 222, 46, 204, 206, 89, 124, 135, 79, 216, + ]; + let x = Felt::from_str_radix( + "2497468900767850684421727063357792717599762502387246235265616708902555305129", + 10, + ) + .unwrap(); + let y = Felt::from_str_radix( + "3412645436898503501401619513420382337734846074629040678138428701431530606439", + 10, + ) + .unwrap(); + assert_eq!(random_ec_point_seeded(seed).unwrap(), (x, y)); + } } From 2b63c802ddbf21bfae04175d3ac8975ddf2da653 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 16:32:17 -0300 Subject: [PATCH 17/34] Add test --- .../builtin_hint_processor/ec_utils.rs | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index 77facd749f..3b045eace5 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -143,8 +143,17 @@ fn is_quad_residue(a: &BigUint) -> bool { #[cfg(test)] mod tests { + use crate::any_box; + use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor; + use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::HintProcessorData; + use crate::hint_processor::hint_processor_definition::HintProcessor; + use crate::types::exec_scope::ExecutionScopes; use num_traits::Zero; + use crate::hint_processor::builtin_hint_processor::hint_code; + use crate::utils::test_utils::*; + use assert_matches::assert_matches; + use super::*; #[test] @@ -224,4 +233,81 @@ mod tests { .unwrap(); assert_eq!(random_ec_point_seeded(seed).unwrap(), (x, y)); } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_pow_prev_locs_exp_is_not_integer() { + let hint_code = hint_code::RANDOM_EC_POINT; + let mut vm = vm!(); + //Initialize fp + vm.run_context.fp = 6; + //Create hint_data + let ids_data = non_continuous_ids_data![("p", -6), ("q", -3), ("m", -4), ("s", 0)]; + //Insert ids.prev_locs.exp into memory as a RelocatableValue + /* p.x = 0x6a4beaef5a93425b973179cdba0c9d42f30e01a5f1e2db73da0884b8d6756fc + p.y = 0x72565ec81bc09ff53fbfad99324a92aa5b39fb58267e395e8abe36290ebf24f + m = 34 + q.x = 0x654fd7e67a123dd13868093b3b7777f1ffef596c2e324f25ceaf9146698482c + q.y = 0x4fad269cbf860980e38768fe9cb6b0b9ab03ee3fe84cfde2eccce597c874fd8 + */ + add_segments!(vm, 1); + vm.insert_value( + (1, 0).into(), + Felt::from_str_radix( + "0x6a4beaef5a93425b973179cdba0c9d42f30e01a5f1e2db73da0884b8d6756fc", + 16, + ) + .unwrap(), + ) + .unwrap(); + vm.insert_value( + (1, 1).into(), + Felt::from_str_radix( + "0x6a4beaef5a93425b973179cdba0c9d42f30e01a5f1e2db73da0884b8d6756fc", + 16, + ) + .unwrap(), + ) + .unwrap(); + vm.insert_value((1, 2).into(), Felt::from(34)).unwrap(); + vm.insert_value( + (1, 3).into(), + Felt::from_str_radix( + "0x654fd7e67a123dd13868093b3b7777f1ffef596c2e324f25ceaf9146698482c", + 16, + ) + .unwrap(), + ) + .unwrap(); + vm.insert_value( + (1, 4).into(), + Felt::from_str_radix( + "0x4fad269cbf860980e38768fe9cb6b0b9ab03ee3fe84cfde2eccce597c874fd8", + 16, + ) + .unwrap(), + ) + .unwrap(); + //Execute the hint + assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(())); + // Check post-hint memory values + // s.x = 108925483682366235368969256555281508851459278989259552980345066351008608800 + // s.y = 1592365885972480102953613056006596671718206128324372995731808913669237079419 + assert_eq!( + vm.get_integer((1, 5).into()).unwrap().as_ref(), + &Felt::from_str_radix( + "108925483682366235368969256555281508851459278989259552980345066351008608800", + 10 + ) + .unwrap() + ); + assert_eq!( + vm.get_integer((1, 6).into()).unwrap().as_ref(), + &Felt::from_str_radix( + "1592365885972480102953613056006596671718206128324372995731808913669237079419", + 10 + ) + .unwrap() + ); + } } From 12ecb361cfa07e7624bcd38d273fc68960118278 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 17:28:14 -0300 Subject: [PATCH 18/34] Add implementation for hint on chained_ec_op --- .../builtin_hint_processor_definition.rs | 5 +- .../builtin_hint_processor/ec_utils.rs | 60 ++++++++++++++++++- .../builtin_hint_processor/hint_code.rs | 27 +++++++++ src/vm/errors/hint_errors.rs | 2 + 4 files changed, 92 insertions(+), 2 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index e83cdaef1d..dfb5f2dd6a 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -66,7 +66,7 @@ use felt::Felt; #[cfg(feature = "skip_next_instruction_hint")] use crate::hint_processor::builtin_hint_processor::skip_next_instruction::skip_next_instruction; -use super::ec_utils::random_ec_point_hint; +use super::ec_utils::{chained_ec_op_random_ec_point_hint, random_ec_point_hint}; pub struct HintProcessorData { pub code: String, @@ -443,6 +443,9 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::RANDOM_EC_POINT => { random_ec_point_hint(vm, &hint_data.ids_data, &hint_data.ap_tracking) } + hint_code::CHAINED_EC_OP_RANDOM_EC_POINT => { + chained_ec_op_random_ec_point_hint(vm, &hint_data.ids_data, &hint_data.ap_tracking) + } #[cfg(feature = "skip_next_instruction_hint")] hint_code::SKIP_NEXT_INSTRUCTION => skip_next_instruction(vm), code => Err(HintError::UnknownHint(code.to_string())), diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index 3b045eace5..781f29a73f 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -14,11 +14,13 @@ use felt::Felt; use lazy_static::lazy_static; use num_bigint::BigUint; use num_bigint::ToBigInt; -use num_traits::{Bounded, Num, One, Pow}; +use num_traits::{Bounded, Num, One, Pow, ToPrimitive, Zero}; use sha2::{Digest, Sha256}; use crate::math_utils::sqrt; +use super::hint_utils::get_ptr_from_var_name; + #[derive(Debug, PartialEq)] struct EcPoint<'a> { x: Cow<'a, Felt>, @@ -74,6 +76,62 @@ pub fn random_ec_point_hint( Ok(()) } +// Implements hint: +// from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME +// from starkware.python.math_utils import random_ec_point +// from starkware.python.utils import to_bytes + +// n_elms = ids.len +// assert isinstance(n_elms, int) and n_elms >= 0, \ +// f'Invalid value for len. Got: {n_elms}.' +// if '__chained_ec_op_max_len' in globals(): +// assert n_elms <= __chained_ec_op_max_len, \ +// f'chained_ec_op() can only be used with len<={__chained_ec_op_max_len}. ' \ +// f'Got: n_elms={n_elms}.' + +// # Define a seed for random_ec_point that's dependent on all the input, so that: +// # (1) The added point s is deterministic. +// # (2) It's hard to choose inputs for which the builtin will fail. +// seed = b"".join( +// map( +// to_bytes, +// [ +// ids.p.x, +// ids.p.y, +// *memory.get_range(ids.m, n_elms), +// *memory.get_range(ids.q.address_, 2 * n_elms), +// ], +// ) +// ) +// ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed)" +pub fn chained_ec_op_random_ec_point_hint( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + let n_elms = get_integer_from_var_name("len", vm, ids_data, ap_tracking)?; + if n_elms.is_zero() || n_elms.to_usize().is_none() { + return Err(HintError::InvalidLenLalue(n_elms.into_owned())); + } + let n_elms = n_elms.to_usize().unwrap(); + let p = EcPoint::from_var_name("p", vm, ids_data, ap_tracking)?; + let m = get_ptr_from_var_name("m", vm, ids_data, ap_tracking)?; + let q = get_ptr_from_var_name("q", vm, ids_data, ap_tracking)?; + let m_range = vm.get_integer_range(m, n_elms)?; + let q_range = vm.get_integer_range(q, n_elms * 2)?; + let bytes: Vec = [p.x, p.y] + .iter() + .chain(m_range.iter()) + .chain(q_range.iter()) + .flat_map(|x| to_padded_bytes(&x)) + .collect(); + let (x, y) = random_ec_point_seeded(bytes)?; + let s_addr = get_relocatable_from_var_name("s", vm, ids_data, ap_tracking)?; + vm.insert_value(s_addr, x)?; + vm.insert_value((s_addr + 1)?, y)?; + Ok(()) +} + // Returns the Felt as a vec of bytes of len 32, pads left with zeros fn to_padded_bytes(n: &Felt) -> Vec { let felt_to_bytes = n.to_bytes_be(); diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index 42143c5ef1..abf9e9f6a7 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -551,5 +551,32 @@ from starkware.python.utils import to_bytes # (2) It's hard to choose inputs for which the builtin will fail. seed = b"".join(map(to_bytes, [ids.p.x, ids.p.y, ids.m, ids.q.x, ids.q.y])) ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed)"#; +pub(crate) const CHAINED_EC_OP_RANDOM_EC_POINT: &str = r#"from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME + from starkware.python.math_utils import random_ec_point + from starkware.python.utils import to_bytes + + n_elms = ids.len + assert isinstance(n_elms, int) and n_elms >= 0, \ + f'Invalid value for len. Got: {n_elms}.' + if '__chained_ec_op_max_len' in globals(): + assert n_elms <= __chained_ec_op_max_len, \ + f'chained_ec_op() can only be used with len<={__chained_ec_op_max_len}. ' \ + f'Got: n_elms={n_elms}.' + + # Define a seed for random_ec_point that's dependent on all the input, so that: + # (1) The added point s is deterministic. + # (2) It's hard to choose inputs for which the builtin will fail. + seed = b"".join( + map( + to_bytes, + [ + ids.p.x, + ids.p.y, + *memory.get_range(ids.m, n_elms), + *memory.get_range(ids.q.address_, 2 * n_elms), + ], + ) + ) + ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed)"#; #[cfg(feature = "skip_next_instruction_hint")] pub(crate) const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; diff --git a/src/vm/errors/hint_errors.rs b/src/vm/errors/hint_errors.rs index 9be002e085..56e2664f05 100644 --- a/src/vm/errors/hint_errors.rs +++ b/src/vm/errors/hint_errors.rs @@ -173,4 +173,6 @@ pub enum HintError { Math(#[from] MathError), #[error("random_ec_point: Could not find a point on the curve.")] RandomEcPointNotOnCurve, + #[error("Invalid value for len. Got: {0}.")] + InvalidLenLalue(Felt), } From 783772e2968a3ab6e5584b843d372d62c2533dc2 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 17:54:49 -0300 Subject: [PATCH 19/34] Fix string format --- .../builtin_hint_processor_definition.rs | 1 - .../builtin_hint_processor/hint_code.rs | 46 +++++++++---------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index dfb5f2dd6a..1b4b6475e5 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -137,7 +137,6 @@ impl HintProcessor for BuiltinHintProcessor { constants, ); } - match &*hint_data.code { hint_code::ADD_SEGMENT => add_segment(vm), hint_code::IS_NN => is_nn(vm, &hint_data.ids_data, &hint_data.ap_tracking), diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index abf9e9f6a7..10b954b44e 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -552,31 +552,31 @@ from starkware.python.utils import to_bytes seed = b"".join(map(to_bytes, [ids.p.x, ids.p.y, ids.m, ids.q.x, ids.q.y])) ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed)"#; pub(crate) const CHAINED_EC_OP_RANDOM_EC_POINT: &str = r#"from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME - from starkware.python.math_utils import random_ec_point - from starkware.python.utils import to_bytes +from starkware.python.math_utils import random_ec_point +from starkware.python.utils import to_bytes - n_elms = ids.len - assert isinstance(n_elms, int) and n_elms >= 0, \ - f'Invalid value for len. Got: {n_elms}.' - if '__chained_ec_op_max_len' in globals(): - assert n_elms <= __chained_ec_op_max_len, \ - f'chained_ec_op() can only be used with len<={__chained_ec_op_max_len}. ' \ - f'Got: n_elms={n_elms}.' +n_elms = ids.len +assert isinstance(n_elms, int) and n_elms >= 0, \ + f'Invalid value for len. Got: {n_elms}.' +if '__chained_ec_op_max_len' in globals(): + assert n_elms <= __chained_ec_op_max_len, \ + f'chained_ec_op() can only be used with len<={__chained_ec_op_max_len}. ' \ + f'Got: n_elms={n_elms}.' - # Define a seed for random_ec_point that's dependent on all the input, so that: - # (1) The added point s is deterministic. - # (2) It's hard to choose inputs for which the builtin will fail. - seed = b"".join( - map( - to_bytes, - [ - ids.p.x, - ids.p.y, - *memory.get_range(ids.m, n_elms), - *memory.get_range(ids.q.address_, 2 * n_elms), - ], - ) +# Define a seed for random_ec_point that's dependent on all the input, so that: +# (1) The added point s is deterministic. +# (2) It's hard to choose inputs for which the builtin will fail. +seed = b"".join( + map( + to_bytes, + [ + ids.p.x, + ids.p.y, + *memory.get_range(ids.m, n_elms), + *memory.get_range(ids.q.address_, 2 * n_elms), + ], ) - ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed)"#; +) +ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed)"#; #[cfg(feature = "skip_next_instruction_hint")] pub(crate) const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; From 57d68d0954a4d7fcc6eec499ad330fcc77ed99c9 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 18:25:03 -0300 Subject: [PATCH 20/34] Fix tests --- .../builtin_hint_processor/ec_utils.rs | 169 +++++++++++++++--- src/vm/errors/hint_errors.rs | 2 +- 2 files changed, 150 insertions(+), 21 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index 781f29a73f..e17b4ec599 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -111,7 +111,7 @@ pub fn chained_ec_op_random_ec_point_hint( ) -> Result<(), HintError> { let n_elms = get_integer_from_var_name("len", vm, ids_data, ap_tracking)?; if n_elms.is_zero() || n_elms.to_usize().is_none() { - return Err(HintError::InvalidLenLalue(n_elms.into_owned())); + return Err(HintError::InvalidLenValue(n_elms.into_owned())); } let n_elms = n_elms.to_usize().unwrap(); let p = EcPoint::from_var_name("p", vm, ids_data, ap_tracking)?; @@ -205,7 +205,9 @@ mod tests { use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor; use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::HintProcessorData; use crate::hint_processor::hint_processor_definition::HintProcessor; + use crate::relocatable; use crate::types::exec_scope::ExecutionScopes; + use crate::types::relocatable::Relocatable; use num_traits::Zero; use crate::hint_processor::builtin_hint_processor::hint_code; @@ -294,26 +296,26 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn run_pow_prev_locs_exp_is_not_integer() { + fn run_ec_op_random_ec_point_hint() { let hint_code = hint_code::RANDOM_EC_POINT; let mut vm = vm!(); //Initialize fp vm.run_context.fp = 6; //Create hint_data - let ids_data = non_continuous_ids_data![("p", -6), ("q", -3), ("m", -4), ("s", 0)]; + let ids_data = non_continuous_ids_data![("p", -6), ("q", -3), ("m", -4), ("s", -1)]; //Insert ids.prev_locs.exp into memory as a RelocatableValue - /* p.x = 0x6a4beaef5a93425b973179cdba0c9d42f30e01a5f1e2db73da0884b8d6756fc - p.y = 0x72565ec81bc09ff53fbfad99324a92aa5b39fb58267e395e8abe36290ebf24f + /* p.x = 3004956058830981475544150447242655232275382685012344776588097793621230049020 + p.y = 3232266734070744637901977159303149980795588196503166389060831401046564401743 m = 34 - q.x = 0x654fd7e67a123dd13868093b3b7777f1ffef596c2e324f25ceaf9146698482c - q.y = 0x4fad269cbf860980e38768fe9cb6b0b9ab03ee3fe84cfde2eccce597c874fd8 + q.x = 2864041794633455918387139831609347757720597354645583729611044800117714995244 + q.y = 2252415379535459416893084165764951913426528160630388985542241241048300343256 */ - add_segments!(vm, 1); + add_segments!(vm, 2); vm.insert_value( (1, 0).into(), Felt::from_str_radix( - "0x6a4beaef5a93425b973179cdba0c9d42f30e01a5f1e2db73da0884b8d6756fc", - 16, + "3004956058830981475544150447242655232275382685012344776588097793621230049020", + 10, ) .unwrap(), ) @@ -321,8 +323,8 @@ mod tests { vm.insert_value( (1, 1).into(), Felt::from_str_radix( - "0x6a4beaef5a93425b973179cdba0c9d42f30e01a5f1e2db73da0884b8d6756fc", - 16, + "3232266734070744637901977159303149980795588196503166389060831401046564401743", + 10, ) .unwrap(), ) @@ -331,8 +333,8 @@ mod tests { vm.insert_value( (1, 3).into(), Felt::from_str_radix( - "0x654fd7e67a123dd13868093b3b7777f1ffef596c2e324f25ceaf9146698482c", - 16, + "2864041794633455918387139831609347757720597354645583729611044800117714995244", + 10, ) .unwrap(), ) @@ -340,8 +342,8 @@ mod tests { vm.insert_value( (1, 4).into(), Felt::from_str_radix( - "0x4fad269cbf860980e38768fe9cb6b0b9ab03ee3fe84cfde2eccce597c874fd8", - 16, + "2252415379535459416893084165764951913426528160630388985542241241048300343256", + 10, ) .unwrap(), ) @@ -349,12 +351,12 @@ mod tests { //Execute the hint assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(())); // Check post-hint memory values - // s.x = 108925483682366235368969256555281508851459278989259552980345066351008608800 - // s.y = 1592365885972480102953613056006596671718206128324372995731808913669237079419 + // s.x = 96578541406087262240552119423829615463800550101008760434566010168435227837635 + // s.y = 3412645436898503501401619513420382337734846074629040678138428701431530606439 assert_eq!( vm.get_integer((1, 5).into()).unwrap().as_ref(), &Felt::from_str_radix( - "108925483682366235368969256555281508851459278989259552980345066351008608800", + "96578541406087262240552119423829615463800550101008760434566010168435227837635", 10 ) .unwrap() @@ -362,7 +364,134 @@ mod tests { assert_eq!( vm.get_integer((1, 6).into()).unwrap().as_ref(), &Felt::from_str_radix( - "1592365885972480102953613056006596671718206128324372995731808913669237079419", + "3412645436898503501401619513420382337734846074629040678138428701431530606439", + 10 + ) + .unwrap() + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_chained_ec_op_random_ec_point_hint() { + let hint_code = hint_code::CHAINED_EC_OP_RANDOM_EC_POINT; + let mut vm = vm!(); + //Initialize fp + vm.run_context.fp = 6; + //Create hint_data + let ids_data = + non_continuous_ids_data![("p", -6), ("m", -4), ("q", -3), ("len", -2), ("s", -1)]; + //Insert ids.prev_locs.exp into memory as a RelocatableValue + /* + p.x = 3004956058830981475544150447242655232275382685012344776588097793621230049020 + p.y = 3232266734070744637901977159303149980795588196503166389060831401046564401743 + _m = 34 + -q.x = 2864041794633455918387139831609347757720597354645583729611044800117714995244 + -q.y = 2252415379535459416893084165764951913426528160630388985542241241048300343256 + q = [q,q,q] + m = [m,m,m] + len = 3 + */ + add_segments!(vm, 4); + //p + vm.insert_value( + (1, 0).into(), + Felt::from_str_radix( + "3004956058830981475544150447242655232275382685012344776588097793621230049020", + 10, + ) + .unwrap(), + ) + .unwrap(); + vm.insert_value( + (1, 1).into(), + Felt::from_str_radix( + "3232266734070744637901977159303149980795588196503166389060831401046564401743", + 10, + ) + .unwrap(), + ) + .unwrap(); + //m + vm.insert_value((1, 2).into(), relocatable!(2, 0)).unwrap(); + vm.insert_value((2, 0).into(), Felt::from(34)).unwrap(); + vm.insert_value((2, 1).into(), Felt::from(34)).unwrap(); + vm.insert_value((2, 2).into(), Felt::from(34)).unwrap(); + //q + vm.insert_value((1, 3).into(), relocatable!(3, 0)).unwrap(); + vm.insert_value( + (3, 0).into(), + Felt::from_str_radix( + "2864041794633455918387139831609347757720597354645583729611044800117714995244", + 10, + ) + .unwrap(), + ) + .unwrap(); + vm.insert_value( + (3, 1).into(), + Felt::from_str_radix( + "2252415379535459416893084165764951913426528160630388985542241241048300343256", + 10, + ) + .unwrap(), + ) + .unwrap(); + vm.insert_value( + (3, 2).into(), + Felt::from_str_radix( + "2864041794633455918387139831609347757720597354645583729611044800117714995244", + 10, + ) + .unwrap(), + ) + .unwrap(); + vm.insert_value( + (3, 3).into(), + Felt::from_str_radix( + "2252415379535459416893084165764951913426528160630388985542241241048300343256", + 10, + ) + .unwrap(), + ) + .unwrap(); + vm.insert_value( + (3, 4).into(), + Felt::from_str_radix( + "2864041794633455918387139831609347757720597354645583729611044800117714995244", + 10, + ) + .unwrap(), + ) + .unwrap(); + vm.insert_value( + (3, 5).into(), + Felt::from_str_radix( + "2252415379535459416893084165764951913426528160630388985542241241048300343256", + 10, + ) + .unwrap(), + ) + .unwrap(); + //len + vm.insert_value((1, 4).into(), Felt::from(3)).unwrap(); + //Execute the hint + assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(())); + // Check post-hint memory values + // s.x = 1354562415074475070179359167082942891834423311678180448592849484844152837347 + // s.y = 907662328694455187848008017177970257426839229889571025406355869359245158736 + assert_eq!( + vm.get_integer((1, 5).into()).unwrap().as_ref(), + &Felt::from_str_radix( + "1354562415074475070179359167082942891834423311678180448592849484844152837347", + 10 + ) + .unwrap() + ); + assert_eq!( + vm.get_integer((1, 6).into()).unwrap().as_ref(), + &Felt::from_str_radix( + "907662328694455187848008017177970257426839229889571025406355869359245158736", 10 ) .unwrap() diff --git a/src/vm/errors/hint_errors.rs b/src/vm/errors/hint_errors.rs index 56e2664f05..025feb71a4 100644 --- a/src/vm/errors/hint_errors.rs +++ b/src/vm/errors/hint_errors.rs @@ -174,5 +174,5 @@ pub enum HintError { #[error("random_ec_point: Could not find a point on the curve.")] RandomEcPointNotOnCurve, #[error("Invalid value for len. Got: {0}.")] - InvalidLenLalue(Felt), + InvalidLenValue(Felt), } From cb35e0400dd5049574d32e029eac3fffc6d48b0d Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 18:25:45 -0300 Subject: [PATCH 21/34] Add test program --- cairo_programs/chained_ec_op.cairo | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 cairo_programs/chained_ec_op.cairo diff --git a/cairo_programs/chained_ec_op.cairo b/cairo_programs/chained_ec_op.cairo new file mode 100644 index 0000000000..d3af8fb9ee --- /dev/null +++ b/cairo_programs/chained_ec_op.cairo @@ -0,0 +1,38 @@ +%builtins ec_op + +from starkware.cairo.common.cairo_builtins import EcOpBuiltin +from starkware.cairo.common.ec_point import EcPoint +from starkware.cairo.common.ec import chained_ec_op +from starkware.cairo.common.alloc import alloc + + +func main{ec_op_ptr: EcOpBuiltin*}() { + let p = EcPoint( + 0x6a4beaef5a93425b973179cdba0c9d42f30e01a5f1e2db73da0884b8d6756fc, + 0x72565ec81bc09ff53fbfad99324a92aa5b39fb58267e395e8abe36290ebf24f, + ); + let q1 = EcPoint( + 0x654fd7e67a123dd13868093b3b7777f1ffef596c2e324f25ceaf9146698482c, + 0x4fad269cbf860980e38768fe9cb6b0b9ab03ee3fe84cfde2eccce597c874fd8, + ); + let q2 = EcPoint( + 0x654fd7e67a123dd13868093b3b7777f1ffef596c2e324f25ceaf9146698482c, + 0x4fad269cbf860980e38768fe9cb6b0b9ab03ee3fe84cfde2eccce597c874fd8, + ); + let q3 = EcPoint( + 0x654fd7e67a123dd13868093b3b7777f1ffef596c2e324f25ceaf9146698482c, + 0x4fad269cbf860980e38768fe9cb6b0b9ab03ee3fe84cfde2eccce597c874fd8, + ); + let q : EcPoint* = alloc(); + assert q[0] = q1; + assert q[1] = q2; + assert q[2] = q3; + let m : felt* = alloc(); + assert m[0] = 34; + assert m[1] = 34; + assert m[2] = 34; + let (r) = chained_ec_op(p, m, q, 3); + assert r.x = 3384892298291437283292800194657711696590239153368187334668717989522828417221; + assert r.y = 1522177177154723444905194991592642153940491339266976531102714535684279750063; + return (); +} \ No newline at end of file From 7d88bf03211b38c0c7618766ee464d81661084b5 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 18:39:41 -0300 Subject: [PATCH 22/34] Add test program --- cairo_programs/recover_y.cairo | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 cairo_programs/recover_y.cairo diff --git a/cairo_programs/recover_y.cairo b/cairo_programs/recover_y.cairo new file mode 100644 index 0000000000..3091d900be --- /dev/null +++ b/cairo_programs/recover_y.cairo @@ -0,0 +1,14 @@ +%builtins ec_op + +from starkware.cairo.common.cairo_builtins import EcOpBuiltin +from starkware.cairo.common.ec_point import EcPoint +from starkware.cairo.common.ec import recover_y + + +func main{ec_op_ptr: EcOpBuiltin*}() { + let x = 0x6a4beaef5a93425b973179cdba0c9d42f30e01a5f1e2db73da0884b8d6756fc; + let r: EcPoint = recover_y(x); + assert r.x = 0x6a4beaef5a93425b973179cdba0c9d42f30e01a5f1e2db73da0884b8d6756fc; + assert r.y = 0xda9a137e43f611ac0405266cdb56d55a4c604a7d981c6a17541c9d6f140db2; + return (); +} \ No newline at end of file From a2ed87e625a127999436a23d7e97f2fd8008a53a Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 18:39:58 -0300 Subject: [PATCH 23/34] Add impl for recover_y hint --- .../builtin_hint_processor_definition.rs | 3 ++- .../builtin_hint_processor/ec_utils.rs | 21 +++++++++++++++++++ .../builtin_hint_processor/hint_code.rs | 6 ++++++ src/vm/errors/hint_errors.rs | 2 ++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index 1b4b6475e5..49916f94b2 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -66,7 +66,7 @@ use felt::Felt; #[cfg(feature = "skip_next_instruction_hint")] use crate::hint_processor::builtin_hint_processor::skip_next_instruction::skip_next_instruction; -use super::ec_utils::{chained_ec_op_random_ec_point_hint, random_ec_point_hint}; +use super::ec_utils::{chained_ec_op_random_ec_point_hint, random_ec_point_hint, recover_y_hint}; pub struct HintProcessorData { pub code: String, @@ -445,6 +445,7 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::CHAINED_EC_OP_RANDOM_EC_POINT => { chained_ec_op_random_ec_point_hint(vm, &hint_data.ids_data, &hint_data.ap_tracking) } + hint_code::RECOVER_Y => recover_y_hint(vm, &hint_data.ids_data, &hint_data.ap_tracking), #[cfg(feature = "skip_next_instruction_hint")] hint_code::SKIP_NEXT_INSTRUCTION => skip_next_instruction(vm), code => Err(HintError::UnknownHint(code.to_string())), diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index e17b4ec599..ced3659020 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -132,6 +132,27 @@ pub fn chained_ec_op_random_ec_point_hint( Ok(()) } +// Implements hint: +// from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME +// from starkware.python.math_utils import recover_y +// ids.p.x = ids.x +// # This raises an exception if `x` is not on the curve. +// ids.p.y = recover_y(ids.x, ALPHA, BETA, FIELD_PRIME) +pub fn recover_y_hint( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + let p_x = get_integer_from_var_name("x", vm, ids_data, ap_tracking)?.into_owned(); + let p_addr = get_relocatable_from_var_name("p", vm, ids_data, ap_tracking)?; + vm.insert_value(p_addr, &p_x)?; + let p_y = Felt::from( + recover_y(&p_x.to_biguint()).ok_or_else(|| HintError::RecoverYPointNotOnCurve(p_x))?, + ); + vm.insert_value((p_addr + 1)?, p_y)?; + Ok(()) +} + // Returns the Felt as a vec of bytes of len 32, pads left with zeros fn to_padded_bytes(n: &Felt) -> Vec { let felt_to_bytes = n.to_bytes_be(); diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index 10b954b44e..295677edaa 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -578,5 +578,11 @@ seed = b"".join( ) ) ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed)"#; +pub(crate) const RECOVER_Y: &str = + "from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME +from starkware.python.math_utils import recover_y +ids.p.x = ids.x +# This raises an exception if `x` is not on the curve. +ids.p.y = recover_y(ids.x, ALPHA, BETA, FIELD_PRIME)"; #[cfg(feature = "skip_next_instruction_hint")] pub(crate) const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; diff --git a/src/vm/errors/hint_errors.rs b/src/vm/errors/hint_errors.rs index 025feb71a4..4a82c2bc9a 100644 --- a/src/vm/errors/hint_errors.rs +++ b/src/vm/errors/hint_errors.rs @@ -175,4 +175,6 @@ pub enum HintError { RandomEcPointNotOnCurve, #[error("Invalid value for len. Got: {0}.")] InvalidLenValue(Felt), + #[error("recover_y: {0} does not represent the x coordinate of a point on the curve.")] + RecoverYPointNotOnCurve(Felt), } From ac38803bd4167176c38d96850dc5d720458f6c3a Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 18:43:18 -0300 Subject: [PATCH 24/34] Add test for hint --- .../builtin_hint_processor/ec_utils.rs | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index ced3659020..dc23dbf58f 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -324,7 +324,6 @@ mod tests { vm.run_context.fp = 6; //Create hint_data let ids_data = non_continuous_ids_data![("p", -6), ("q", -3), ("m", -4), ("s", -1)]; - //Insert ids.prev_locs.exp into memory as a RelocatableValue /* p.x = 3004956058830981475544150447242655232275382685012344776588097793621230049020 p.y = 3232266734070744637901977159303149980795588196503166389060831401046564401743 m = 34 @@ -402,7 +401,6 @@ mod tests { //Create hint_data let ids_data = non_continuous_ids_data![("p", -6), ("m", -4), ("q", -3), ("len", -2), ("s", -1)]; - //Insert ids.prev_locs.exp into memory as a RelocatableValue /* p.x = 3004956058830981475544150447242655232275382685012344776588097793621230049020 p.y = 3232266734070744637901977159303149980795588196503166389060831401046564401743 @@ -518,4 +516,47 @@ mod tests { .unwrap() ); } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_recover_y_hint() { + let hint_code = hint_code::RECOVER_Y; + let mut vm = vm!(); + //Initialize fp + vm.run_context.fp = 3; + //Create hint_data + let ids_data = non_continuous_ids_data![("x", -3), ("p", -1)]; + // x = 3004956058830981475544150447242655232275382685012344776588097793621230049020 + add_segments!(vm, 2); + vm.insert_value( + (1, 0).into(), + Felt::from_str_radix( + "3004956058830981475544150447242655232275382685012344776588097793621230049020", + 10, + ) + .unwrap(), + ) + .unwrap(); + //Execute the hint + assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(())); + // Check post-hint memory values + // p.x = 3004956058830981475544150447242655232275382685012344776588097793621230049020 + // p.y = 386236054595386575795345623791920124827519018828430310912260655089307618738 + assert_eq!( + vm.get_integer((1, 2).into()).unwrap().as_ref(), + &Felt::from_str_radix( + "3004956058830981475544150447242655232275382685012344776588097793621230049020", + 10 + ) + .unwrap() + ); + assert_eq!( + vm.get_integer((1, 3).into()).unwrap().as_ref(), + &Felt::from_str_radix( + "386236054595386575795345623791920124827519018828430310912260655089307618738", + 10 + ) + .unwrap() + ); + } } From f69ef85570ee61b19fb2abaf7f5f8b7ca0d3e177 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 18:46:31 -0300 Subject: [PATCH 25/34] Add integration tests --- tests/cairo_run_test.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/cairo_run_test.rs b/tests/cairo_run_test.rs index 9efe1f3b8b..6e250ae8cf 100644 --- a/tests/cairo_run_test.rs +++ b/tests/cairo_run_test.rs @@ -1424,3 +1424,35 @@ fn cairo_run_ec_op() { ) .expect("Couldn't run program"); } + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn cairo_chained_run_ec_op() { + let mut hint_executor = BuiltinHintProcessor::new_empty(); + let cairo_run_config = cairo_run::CairoRunConfig { + layout: "all", + ..cairo_vm::cairo_run::CairoRunConfig::default() + }; + cairo_run::cairo_run( + include_bytes!("../cairo_programs/chained_ec_op.json"), + &cairo_run_config, + &mut hint_executor, + ) + .expect("Couldn't run program"); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn cairo_run_recover_y() { + let mut hint_executor = BuiltinHintProcessor::new_empty(); + let cairo_run_config = cairo_run::CairoRunConfig { + layout: "all", + ..cairo_vm::cairo_run::CairoRunConfig::default() + }; + cairo_run::cairo_run( + include_bytes!("../cairo_programs/recover_y.json"), + &cairo_run_config, + &mut hint_executor, + ) + .expect("Couldn't run program"); +} From f55e699b75ccb8d702cf255a294f11316cbcd7db Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 18:47:18 -0300 Subject: [PATCH 26/34] Clippy --- src/hint_processor/builtin_hint_processor/ec_utils.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index dc23dbf58f..3559ff7104 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -67,7 +67,7 @@ pub fn random_ec_point_hint( let m = get_integer_from_var_name("m", vm, ids_data, ap_tracking)?; let bytes: Vec = [p.x, p.y, m, q.x, q.y] .iter() - .flat_map(|x| to_padded_bytes(&x)) + .flat_map(|x| to_padded_bytes(x)) .collect(); let (x, y) = random_ec_point_seeded(bytes)?; let s_addr = get_relocatable_from_var_name("s", vm, ids_data, ap_tracking)?; @@ -123,7 +123,7 @@ pub fn chained_ec_op_random_ec_point_hint( .iter() .chain(m_range.iter()) .chain(q_range.iter()) - .flat_map(|x| to_padded_bytes(&x)) + .flat_map(|x| to_padded_bytes(x)) .collect(); let (x, y) = random_ec_point_seeded(bytes)?; let s_addr = get_relocatable_from_var_name("s", vm, ids_data, ap_tracking)?; @@ -201,7 +201,7 @@ lazy_static! { // of a given x coordinate. // Returns None if x is not the x coordinate of a point in the curve fn recover_y(x: &BigUint) -> Option { - let y_squared: BigUint = x.modpow(&BigUint::from(3_u32), &*CAIRO_PRIME) + ALPHA * x + &*BETA; + let y_squared: BigUint = x.modpow(&BigUint::from(3_u32), &CAIRO_PRIME) + ALPHA * x + &*BETA; if is_quad_residue(&y_squared) { Some(sqrt(&Felt::from(y_squared)).to_biguint()) } else { @@ -217,7 +217,7 @@ fn is_quad_residue(a: &BigUint) -> bool { if a < &BigUint::from(2_u8) { return true; }; - a.modpow(&(Felt::max_value().to_biguint() / 2_u32), &*CAIRO_PRIME) == BigUint::one() + a.modpow(&(Felt::max_value().to_biguint() / 2_u32), &CAIRO_PRIME) == BigUint::one() } #[cfg(test)] From 594cadfd1dd6c5880185ee5c986fa891a7374680 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 18:48:42 -0300 Subject: [PATCH 27/34] Clippy --- src/math_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math_utils.rs b/src/math_utils.rs index 6b70d374e3..5962cf4da0 100644 --- a/src/math_utils.rs +++ b/src/math_utils.rs @@ -157,7 +157,7 @@ pub fn ec_double_slope(point: &(BigInt, BigInt), alpha: &BigInt, prime: &BigInt) ) } -pub fn sqrt<'a>(n: &'a Felt) -> Felt { +pub fn sqrt(n: &Felt) -> Felt { // Based on Tonelli-Shanks' algorithm for finding square roots // and sympy's library implementation of said algorithm. if n.is_zero() || n.is_one() { From 48062518c205035ba4202f3496152c314e67285b Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 18:49:44 -0300 Subject: [PATCH 28/34] Add newline at EOf --- cairo_programs/chained_ec_op.cairo | 2 +- cairo_programs/recover_y.cairo | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cairo_programs/chained_ec_op.cairo b/cairo_programs/chained_ec_op.cairo index d3af8fb9ee..854879c39e 100644 --- a/cairo_programs/chained_ec_op.cairo +++ b/cairo_programs/chained_ec_op.cairo @@ -35,4 +35,4 @@ func main{ec_op_ptr: EcOpBuiltin*}() { assert r.x = 3384892298291437283292800194657711696590239153368187334668717989522828417221; assert r.y = 1522177177154723444905194991592642153940491339266976531102714535684279750063; return (); -} \ No newline at end of file +} diff --git a/cairo_programs/recover_y.cairo b/cairo_programs/recover_y.cairo index 3091d900be..6f935cb3e2 100644 --- a/cairo_programs/recover_y.cairo +++ b/cairo_programs/recover_y.cairo @@ -11,4 +11,4 @@ func main{ec_op_ptr: EcOpBuiltin*}() { assert r.x = 0x6a4beaef5a93425b973179cdba0c9d42f30e01a5f1e2db73da0884b8d6756fc; assert r.y = 0xda9a137e43f611ac0405266cdb56d55a4c604a7d981c6a17541c9d6f140db2; return (); -} \ No newline at end of file +} From f91f42746b414702727c6d7f43d0f314fea99fbf Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 Mar 2023 18:51:33 -0300 Subject: [PATCH 29/34] Remove unused trait impl --- felt/src/bigint_felt.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/felt/src/bigint_felt.rs b/felt/src/bigint_felt.rs index 95ecb5692f..7e1be38371 100644 --- a/felt/src/bigint_felt.rs +++ b/felt/src/bigint_felt.rs @@ -410,13 +410,6 @@ impl<'a, const PH: u128, const PL: u128> Pow<&'a FeltBigInt> for &'a Fel } } -impl<'a, const PH: u128, const PL: u128> Pow> for &'a FeltBigInt { - type Output = FeltBigInt; - fn pow(self, rhs: Self::Output) -> Self::Output { - self.pow(&rhs) - } -} - impl SubAssign for FeltBigInt { fn sub_assign(&mut self, rhs: Self) { *self = &*self - &rhs; From 026d629ea37aeab6f02b852dc6349cb0c0dc15b2 Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:48:57 -0300 Subject: [PATCH 30/34] Update src/hint_processor/builtin_hint_processor/ec_utils.rs Co-authored-by: Mario Rugiero --- src/hint_processor/builtin_hint_processor/ec_utils.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index 3559ff7104..d3f659d195 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -214,10 +214,8 @@ fn recover_y(x: &BigUint) -> Option { // + prime is ommited as it will be CAIRO_PRIME // + a >= 0 < prime (other cases ommited) fn is_quad_residue(a: &BigUint) -> bool { - if a < &BigUint::from(2_u8) { - return true; - }; - a.modpow(&(Felt::max_value().to_biguint() / 2_u32), &CAIRO_PRIME) == BigUint::one() + a.is_zero() || a.is_one() || + a.modpow(&(Felt::max_value().to_biguint() / 2_u32), &CAIRO_PRIME).is_one() } #[cfg(test)] From 5c0ad0dab6a52a4bd6482036fed48a1d14b24687 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 17 Mar 2023 17:14:07 -0300 Subject: [PATCH 31/34] Use constant for Felt::max / 2 --- src/hint_processor/builtin_hint_processor/ec_utils.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index 7b10af8ee5..8304dcc6a4 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -197,6 +197,7 @@ lazy_static! { 10 ) .unwrap(); + static ref FELT_MAX_HALVED: BigUint = Felt252::max_value().to_biguint() / 2_u32; } // Recovers the corresponding y coordinate on the elliptic curve @@ -217,10 +218,7 @@ fn recover_y(x: &BigUint) -> Option { // + prime is ommited as it will be CAIRO_PRIME // + a >= 0 < prime (other cases ommited) fn is_quad_residue(a: &BigUint) -> bool { - a.is_zero() - || a.is_one() - || a.modpow(&(Felt252::max_value().to_biguint() / 2_u32), &CAIRO_PRIME) - .is_one() + a.is_zero() || a.is_one() || a.modpow(&FELT_MAX_HALVED, &CAIRO_PRIME).is_one() } #[cfg(test)] From b6bc7df8a81cb11c7d9aa233de8886f720a287d5 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 17 Mar 2023 18:14:07 -0300 Subject: [PATCH 32/34] Add missing import --- src/hint_processor/builtin_hint_processor/ec_utils.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hint_processor/builtin_hint_processor/ec_utils.rs b/src/hint_processor/builtin_hint_processor/ec_utils.rs index 8304dcc6a4..e995b279a1 100644 --- a/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -238,6 +238,9 @@ mod tests { use super::*; + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::*; + #[test] fn test_is_quad_residue_less_than_2() { assert!(is_quad_residue(&BigUint::one())); From 861b2ef2d9dd6474624d12c3dec6a65814e98497 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 17 Mar 2023 18:17:38 -0300 Subject: [PATCH 33/34] Fix proptest dependency --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 45814104fb..75bdafb013 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,7 @@ felt = { package = "cairo-felt", path = "./felt", default-features = false } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3.34" assert_matches = "1.5.0" +proptest = "1.0.0" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] iai = "0.1" From 815b9a225840c4b26ff7f6bf24a604aba2a2f66c Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 17 Mar 2023 18:32:23 -0300 Subject: [PATCH 34/34] Try to fix wasm tests --- Cargo.toml | 1 - src/math_utils.rs | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 75bdafb013..45814104fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,6 @@ felt = { package = "cairo-felt", path = "./felt", default-features = false } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3.34" assert_matches = "1.5.0" -proptest = "1.0.0" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] iai = "0.1" diff --git a/src/math_utils.rs b/src/math_utils.rs index e9a6d92d9b..1f78a09c7c 100644 --- a/src/math_utils.rs +++ b/src/math_utils.rs @@ -196,6 +196,8 @@ mod tests { use crate::utils::CAIRO_PRIME; use assert_matches::assert_matches; use num_traits::Num; + + #[cfg(not(target_arch = "wasm32"))] use proptest::prelude::*; #[cfg(target_arch = "wasm32")] @@ -614,6 +616,7 @@ mod tests { assert_eq!(sqrt(&n), expected_sqrt); } + #[cfg(not(target_arch = "wasm32"))] proptest! { #[test] // Test for sqrt of a quadratic residue. Result should be the minimum root.