diff --git a/CHANGELOG.md b/CHANGELOG.md index 2493219658..b737d5152d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### Upcoming Changes +* feat: Add hint `U256InvModN` to `Cairo1HintProcessor` [#1744](https://github.com/lambdaclass/cairo-vm/pull/1744) + * perf: use a more compact representation for `MemoryCell` [#1672](https://github.com/lambdaclass/cairo-vm/pull/1672) * BREAKING: `Memory::get_value` will now always return `Cow::Owned` variants, code that relied on `Cow::Borrowed` may break diff --git a/cairo1-run/src/main.rs b/cairo1-run/src/main.rs index 5ca02bda21..716673a740 100644 --- a/cairo1-run/src/main.rs +++ b/cairo1-run/src/main.rs @@ -267,6 +267,11 @@ mod tests { use rstest::rstest; #[rstest] + #[case( + "ecdsa_recover.cairo", + "3490001189944926769628658346285649224182856084131963744896357527096042836716", + None + )] #[case("tensor_new.cairo", "[1 2] [1 false 1 true]", None)] #[case("bytes31_ret.cairo", "123", None)] #[case("null_ret.cairo", "null", None)] diff --git a/cairo_programs/cairo-1-programs/ecdsa_recover.cairo b/cairo_programs/cairo-1-programs/ecdsa_recover.cairo new file mode 100644 index 0000000000..8af5d0d96f --- /dev/null +++ b/cairo_programs/cairo-1-programs/ecdsa_recover.cairo @@ -0,0 +1,7 @@ + +fn main() -> felt252 { + let message_hash: felt252 = 0x503f4bea29baee10b22a7f10bdc82dda071c977c1f25b8f3973d34e6b03b2c; + let signature_r: felt252 = 0xbe96d72eb4f94078192c2e84d5230cde2a70f4b45c8797e2c907acff5060bb; + let signature_s: felt252 = 0x677ae6bba6daf00d2631fab14c8acf24be6579f9d9e98f67aa7f2770e57a1f5; + core::ecdsa::recover_public_key(:message_hash, :signature_r, :signature_s, y_parity: false).unwrap() +} diff --git a/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs b/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs index 1cfc100939..d05006912a 100644 --- a/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs +++ b/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs @@ -25,9 +25,9 @@ use cairo_lang_casm::{ use core::any::Any; use core::ops::Shl; -use num_bigint::BigUint; -use num_integer::Integer; -use num_traits::ToPrimitive; +use num_bigint::{BigInt, BigUint}; +use num_integer::{ExtendedGcd, Integer}; +use num_traits::{Signed, ToPrimitive}; /// Execution scope for constant memory allocation. struct MemoryExecScope { @@ -242,6 +242,30 @@ impl Cairo1HintProcessor { vm, dividend0, dividend1, dividend2, dividend3, divisor0, divisor1, quotient0, quotient1, quotient2, quotient3, remainder0, remainder1, ), + Hint::Core(CoreHintBase::Core(CoreHint::U256InvModN { + b0, + b1, + n0, + n1, + g0_or_no_inv, + g1_option, + s_or_r0, + s_or_r1, + t_or_k0, + t_or_k1, + })) => self.u256_inv_mod_n( + vm, + b0, + b1, + n0, + n1, + g0_or_no_inv, + g1_option, + s_or_r0, + s_or_r1, + t_or_k0, + t_or_k1, + ), hint => Err(HintError::UnknownHint( format!("{:?}", hint).into_boxed_str(), @@ -397,7 +421,6 @@ impl Cairo1HintProcessor { dict_manager_exec_scope.finalize_segment(vm, dict_address) } - #[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)] fn uint256_div_mod( &self, @@ -1076,6 +1099,77 @@ impl Cairo1HintProcessor { ) .map_err(HintError::from) } + + #[allow(clippy::too_many_arguments)] + fn u256_inv_mod_n( + &self, + vm: &mut VirtualMachine, + b0: &ResOperand, + b1: &ResOperand, + n0: &ResOperand, + n1: &ResOperand, + g0_or_no_inv: &CellRef, + g1_option: &CellRef, + s_or_r0: &CellRef, + s_or_r1: &CellRef, + t_or_k0: &CellRef, + t_or_k1: &CellRef, + ) -> Result<(), HintError> { + let pow_2_128 = BigInt::from(u128::MAX) + 1u32; + let b0 = get_val(vm, b0)?.to_bigint(); + let b1 = get_val(vm, b1)?.to_bigint(); + let n0 = get_val(vm, n0)?.to_bigint(); + let n1 = get_val(vm, n1)?.to_bigint(); + let b: BigInt = b0.clone() + b1.clone().shl(128); + let n: BigInt = n0 + n1.shl(128); + let ExtendedGcd { + gcd: mut g, + x: _, + y: mut r, + } = n.extended_gcd(&b); + if n == 1.into() { + vm.insert_value(cell_ref_to_relocatable(s_or_r0, vm)?, Felt252::from(b0))?; + vm.insert_value(cell_ref_to_relocatable(s_or_r1, vm)?, Felt252::from(b1))?; + vm.insert_value(cell_ref_to_relocatable(t_or_k0, vm)?, Felt252::from(1))?; + vm.insert_value(cell_ref_to_relocatable(t_or_k1, vm)?, Felt252::from(0))?; + vm.insert_value(cell_ref_to_relocatable(g0_or_no_inv, vm)?, Felt252::from(1))?; + vm.insert_value(cell_ref_to_relocatable(g1_option, vm)?, Felt252::from(0))?; + } else if g != 1.into() { + // This makes sure `g0_or_no_inv` is always non-zero in the no inverse case. + if g.is_even() { + g = 2u32.into(); + } + let (limb1, limb0) = (&b / &g).div_rem(&pow_2_128); + vm.insert_value(cell_ref_to_relocatable(s_or_r0, vm)?, Felt252::from(limb0))?; + vm.insert_value(cell_ref_to_relocatable(s_or_r1, vm)?, Felt252::from(limb1))?; + let (limb1, limb0) = (&n / &g).div_rem(&pow_2_128); + vm.insert_value(cell_ref_to_relocatable(t_or_k0, vm)?, Felt252::from(limb0))?; + vm.insert_value(cell_ref_to_relocatable(t_or_k1, vm)?, Felt252::from(limb1))?; + let (limb1, limb0) = g.div_rem(&pow_2_128); + vm.insert_value( + cell_ref_to_relocatable(g0_or_no_inv, vm)?, + Felt252::from(limb0), + )?; + vm.insert_value( + cell_ref_to_relocatable(g1_option, vm)?, + Felt252::from(limb1), + )?; + } else { + r %= &n; + if r.is_negative() { + r += &n; + } + let k: BigInt = (&r * b - 1) / n; + let (limb1, limb0) = r.div_rem(&pow_2_128); + vm.insert_value(cell_ref_to_relocatable(s_or_r0, vm)?, Felt252::from(limb0))?; + vm.insert_value(cell_ref_to_relocatable(s_or_r1, vm)?, Felt252::from(limb1))?; + let (limb1, limb0) = k.div_rem(&pow_2_128); + vm.insert_value(cell_ref_to_relocatable(t_or_k0, vm)?, Felt252::from(limb0))?; + vm.insert_value(cell_ref_to_relocatable(t_or_k1, vm)?, Felt252::from(limb1))?; + vm.insert_value(cell_ref_to_relocatable(g0_or_no_inv, vm)?, Felt252::from(0))?; + } + Ok(()) + } } impl HintProcessorLogic for Cairo1HintProcessor {