diff --git a/Cargo.lock b/Cargo.lock index 4e7d19603..0f6ef4d72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3102,6 +3102,7 @@ dependencies = [ "indoc", "lazy_static", "log", + "num-bigint", "num-integer", "num-traits 0.2.17", "pretty_assertions", diff --git a/Cargo.toml b/Cargo.toml index a7fbdcc77..e10df0076 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ indexmap = "1.9.2" indoc = "2" lazy_static = "1.4.0" log = "0.4.19" +num-bigint = "0.4" num-integer = "0.1.45" num-traits = "0.2.16" regex = "1.10.0" diff --git a/src/hints/mod.rs b/src/hints/mod.rs index 5e2f28368..2ee017fa6 100644 --- a/src/hints/mod.rs +++ b/src/hints/mod.rs @@ -2,11 +2,10 @@ pub mod block_context; pub mod builtins; pub mod execution; pub mod syscalls; -mod unimplemented; -mod vars; - #[cfg(test)] mod tests; +mod unimplemented; +mod vars; use std::collections::{HashMap, HashSet}; @@ -25,6 +24,7 @@ use cairo_vm::vm::runners::cairo_runner::{ResourceTracker, RunResources}; use cairo_vm::vm::vm_core::VirtualMachine; use cairo_vm::Felt252; use indoc::indoc; +use num_bigint::BigInt; use crate::config::DEFAULT_INPUT_PATH; use crate::io::input::StarknetOsInput; @@ -37,7 +37,7 @@ type HintImpl = fn( &HashMap, ) -> Result<(), HintError>; -static HINTS: [(&str, HintImpl); 50] = [ +static HINTS: [(&str, HintImpl); 51] = [ // (BREAKPOINT, breakpoint), (STARKNET_OS_INPUT, starknet_os_input), (INITIALIZE_STATE_CHANGES, initialize_state_changes), @@ -88,6 +88,7 @@ static HINTS: [(&str, HintImpl); 50] = [ (syscalls::SEND_MESSAGE_TO_L1, syscalls::send_message_to_l1), (syscalls::STORAGE_READ, syscalls::storage_read), (syscalls::STORAGE_WRITE, syscalls::storage_write), + (IS_ON_CURVE, is_on_curve), (IS_N_GE_TWO, is_n_ge_two), ]; @@ -355,3 +356,22 @@ pub fn is_n_ge_two( insert_value_into_ap(vm, value)?; Ok(()) } + +pub const IS_ON_CURVE: &str = "ids.is_on_curve = (y * y) % SECP_P == y_square_int"; +pub fn is_on_curve( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let y: BigInt = exec_scopes.get(vars::ids::Y)?; + let y_square_int: BigInt = exec_scopes.get(vars::ids::Y_SQUARE_INT)?; + let sec_p: BigInt = exec_scopes.get(vars::ids::SECP_P)?; + + let is_on_curve = (y.clone() * y) % sec_p == y_square_int; + let is_on_curve: Felt252 = if is_on_curve { Felt252::ONE } else { Felt252::ZERO }; + insert_value_from_var_name(vars::ids::IS_ON_CURVE, is_on_curve, vm, ids_data, ap_tracking)?; + + Ok(()) +} diff --git a/src/hints/tests.rs b/src/hints/tests.rs index 141c5fe0b..8d811d58d 100644 --- a/src/hints/tests.rs +++ b/src/hints/tests.rs @@ -2,6 +2,7 @@ mod tests { use cairo_vm::serde::deserialize_program::ApTracking; use cairo_vm::types::exec_scope::ExecutionScopes; + use num_bigint::BigInt; use rstest::rstest; use crate::hints::*; @@ -29,6 +30,41 @@ mod tests { } }; } + + #[test] + fn test_is_on_curve() { + let mut vm = VirtualMachine::new(false); + vm.set_fp(1); + vm.add_memory_segment(); + vm.add_memory_segment(); + + let ids_data = ids_data![vars::ids::IS_ON_CURVE]; + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + let y = BigInt::from(1234); + let y_square_int = y.clone() * y.clone(); + + exec_scopes.insert_value(vars::ids::Y, y); + exec_scopes.insert_value(vars::ids::Y_SQUARE_INT, y_square_int); + + // TODO: use an appropriate constant for SECP_P. Also see TODO in `fn is_on_curve` -- should it be + // in exec_scopes to begin with, or should it implicitly exist in the hint itself? + use std::str::FromStr; + let secp_p = + BigInt::from_str("115792089237316195423570985008687907853269984665640564039457584007908834671663").unwrap(); + exec_scopes.insert_value(vars::ids::SECP_P, secp_p); + + is_on_curve(&mut vm, &mut exec_scopes, &ids_data, &ap_tracking, &Default::default()) + .expect("is_on_curve() failed"); + + let is_on_curve: Felt252 = get_integer_from_var_name(vars::ids::IS_ON_CURVE, &vm, &ids_data, &ap_tracking) + .expect("is_on_curve should be put in ids_data") + .into_owned(); + assert_eq!(is_on_curve, 1.into()); + } + #[rstest] #[case(Felt252::TWO, Felt252::ONE)] #[case(Felt252::THREE, Felt252::ONE)] diff --git a/src/hints/unimplemented.rs b/src/hints/unimplemented.rs index a0a17a626..07d2741e5 100644 --- a/src/hints/unimplemented.rs +++ b/src/hints/unimplemented.rs @@ -1,8 +1,5 @@ use indoc::indoc; -#[allow(unused)] -const IS_ON_CURVE: &str = "ids.is_on_curve = (y * y) % SECP_P == y_square_int"; - #[allow(unused)] const CACHE_CONTRACT_STORAGE: &str = indoc! {r#" # Make sure the value is cached (by reading it), to be used later on for the diff --git a/src/hints/vars.rs b/src/hints/vars.rs index bae0dd4f9..0fcebda84 100644 --- a/src/hints/vars.rs +++ b/src/hints/vars.rs @@ -3,7 +3,11 @@ pub mod scopes { } pub mod ids { + pub const IS_ON_CURVE: &str = "is_on_curve"; pub const OS_CONTEXT: &str = "os_context"; + pub const SECP_P: &str = "SECP_P"; pub const SYSCALL_PTR: &str = "syscall_ptr"; + pub const Y: &str = "y"; + pub const Y_SQUARE_INT: &str = "y_square_int"; pub const N: &str = "n"; }