1extern crate alloc;
2extern crate proc_macro;
3
4use std::sync::atomic::AtomicUsize;
5
6use num_bigint::BigUint;
7use num_prime::nt_funcs::is_prime;
8use openvm_macros_common::{string_to_bytes, MacroArgs};
9use proc_macro::TokenStream;
10use quote::format_ident;
11use syn::{
12 parse::{Parse, ParseStream},
13 parse_macro_input, LitStr, Token,
14};
15
16static MOD_IDX: AtomicUsize = AtomicUsize::new(0);
17
18#[proc_macro]
29pub fn moduli_declare(input: TokenStream) -> TokenStream {
30 let MacroArgs { items } = parse_macro_input!(input as MacroArgs);
31
32 let mut output = Vec::new();
33
34 let span = proc_macro::Span::call_site();
35
36 for item in items {
37 let struct_name = item.name.to_string();
38 let struct_name = syn::Ident::new(&struct_name, span.into());
39 let mut modulus: Option<String> = None;
40 for param in item.params {
41 match param.name.to_string().as_str() {
42 "modulus" => {
43 if let syn::Expr::Lit(syn::ExprLit {
44 lit: syn::Lit::Str(value),
45 ..
46 }) = param.value
47 {
48 modulus = Some(value.value());
49 } else {
50 return syn::Error::new_spanned(
51 param.value,
52 "Expected a string literal for macro argument `modulus`",
53 )
54 .to_compile_error()
55 .into();
56 }
57 }
58 _ => {
59 panic!("Unknown parameter {}", param.name);
60 }
61 }
62 }
63
64 let mod_idx = MOD_IDX.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
67
68 let modulus = modulus.expect("modulus parameter is required");
69 let modulus_bytes = string_to_bytes(&modulus);
70 let mut limbs = modulus_bytes.len();
71 let mut block_size = 32;
72
73 if limbs <= 32 {
74 limbs = 32;
75 } else if limbs <= 48 {
76 limbs = 48;
77 block_size = 16;
78 } else {
79 panic!("limbs must be at most 48");
80 }
81
82 let modulus_bytes = modulus_bytes
83 .into_iter()
84 .chain(vec![0u8; limbs])
85 .take(limbs)
86 .collect::<Vec<_>>();
87
88 let modulus_hex = modulus_bytes
89 .iter()
90 .rev()
91 .map(|x| format!("{x:02x}"))
92 .collect::<Vec<_>>()
93 .join("");
94 macro_rules! create_extern_func {
95 ($name:ident) => {
96 let $name = syn::Ident::new(
97 &format!("{}_{}", stringify!($name), modulus_hex),
98 span.into(),
99 );
100 };
101 }
102 create_extern_func!(add_extern_func);
103 create_extern_func!(sub_extern_func);
104 create_extern_func!(mul_extern_func);
105 create_extern_func!(div_extern_func);
106 create_extern_func!(is_eq_extern_func);
107 create_extern_func!(hint_sqrt_extern_func);
108 create_extern_func!(hint_non_qr_extern_func);
109 create_extern_func!(moduli_setup_extern_func);
110
111 let block_size = proc_macro::Literal::usize_unsuffixed(block_size);
112 let block_size = syn::Lit::new(block_size.to_string().parse::<_>().unwrap());
113
114 let module_name = format_ident!("algebra_impl_{}", mod_idx);
115
116 let result = TokenStream::from(quote::quote_spanned! { span.into() =>
117 #[derive(Clone, Eq, serde::Serialize, serde::Deserialize)]
129 #[repr(C, align(#block_size))]
130 pub struct #struct_name(#[serde(with = "openvm_algebra_guest::BigArray")] [u8; #limbs]);
131
132 extern "C" {
133 fn #add_extern_func(rd: usize, rs1: usize, rs2: usize);
134 fn #sub_extern_func(rd: usize, rs1: usize, rs2: usize);
135 fn #mul_extern_func(rd: usize, rs1: usize, rs2: usize);
136 fn #div_extern_func(rd: usize, rs1: usize, rs2: usize);
137 fn #is_eq_extern_func(rs1: usize, rs2: usize) -> bool;
138 fn #hint_sqrt_extern_func(rs1: usize);
139 fn #hint_non_qr_extern_func();
140 fn #moduli_setup_extern_func();
141 }
142
143 impl #struct_name {
144 #[inline(always)]
145 const fn from_const_u8(val: u8) -> Self {
146 let mut bytes = [0; #limbs];
147 bytes[0] = val;
148 Self(bytes)
149 }
150
151 pub const fn from_const_bytes(bytes: [u8; #limbs]) -> Self {
154 Self(bytes)
155 }
156
157 #[inline(always)]
158 fn add_assign_impl(&mut self, other: &Self) {
159 #[cfg(not(target_os = "zkvm"))]
160 {
161 *self = Self::from_biguint(
162 (self.as_biguint() + other.as_biguint()) % Self::modulus_biguint(),
163 );
164 }
165 #[cfg(target_os = "zkvm")]
166 {
167 Self::set_up_once();
168 unsafe {
169 #add_extern_func(
170 self as *mut Self as usize,
171 self as *const Self as usize,
172 other as *const Self as usize,
173 );
174 }
175 }
176 }
177
178 #[inline(always)]
179 fn sub_assign_impl(&mut self, other: &Self) {
180 #[cfg(not(target_os = "zkvm"))]
181 {
182 let modulus = Self::modulus_biguint();
183 *self = Self::from_biguint(
184 (self.as_biguint() + modulus.clone() - other.as_biguint()) % modulus,
185 );
186 }
187 #[cfg(target_os = "zkvm")]
188 {
189 Self::set_up_once();
190 unsafe {
191 #sub_extern_func(
192 self as *mut Self as usize,
193 self as *const Self as usize,
194 other as *const Self as usize,
195 );
196 }
197 }
198 }
199
200 #[inline(always)]
201 fn mul_assign_impl(&mut self, other: &Self) {
202 #[cfg(not(target_os = "zkvm"))]
203 {
204 *self = Self::from_biguint(
205 (self.as_biguint() * other.as_biguint()) % Self::modulus_biguint(),
206 );
207 }
208 #[cfg(target_os = "zkvm")]
209 {
210 Self::set_up_once();
211 unsafe {
212 #mul_extern_func(
213 self as *mut Self as usize,
214 self as *const Self as usize,
215 other as *const Self as usize,
216 );
217 }
218 }
219 }
220
221 #[inline(always)]
222 fn div_assign_unsafe_impl(&mut self, other: &Self) {
223 #[cfg(not(target_os = "zkvm"))]
224 {
225 let modulus = Self::modulus_biguint();
226 let inv = other.as_biguint().modinv(&modulus).unwrap();
227 *self = Self::from_biguint((self.as_biguint() * inv) % modulus);
228 }
229 #[cfg(target_os = "zkvm")]
230 {
231 Self::set_up_once();
232 unsafe {
233 #div_extern_func(
234 self as *mut Self as usize,
235 self as *const Self as usize,
236 other as *const Self as usize,
237 );
238 }
239 }
240 }
241
242 #[inline(always)]
245 unsafe fn add_refs_impl<const CHECK_SETUP: bool>(&self, other: &Self, dst_ptr: *mut Self) {
246 #[cfg(not(target_os = "zkvm"))]
247 {
248 let mut res = self.clone();
249 res += other;
250 let dst = unsafe { &mut *dst_ptr };
252 *dst = res;
253 }
254 #[cfg(target_os = "zkvm")]
255 {
256 if CHECK_SETUP {
257 Self::set_up_once();
258 }
259 #add_extern_func(
260 dst_ptr as usize,
261 self as *const #struct_name as usize,
262 other as *const #struct_name as usize,
263 );
264 }
265 }
266
267 #[inline(always)]
270 unsafe fn sub_refs_impl(&self, other: &Self, dst_ptr: *mut Self) {
271 #[cfg(not(target_os = "zkvm"))]
272 {
273 let mut res = self.clone();
274 res -= other;
275 let dst = unsafe { &mut *dst_ptr };
277 *dst = res;
278 }
279 #[cfg(target_os = "zkvm")]
280 {
281 Self::set_up_once();
282 unsafe {
283 #sub_extern_func(
284 dst_ptr as usize,
285 self as *const #struct_name as usize,
286 other as *const #struct_name as usize,
287 );
288 }
289 }
290 }
291
292 #[inline(always)]
295 unsafe fn mul_refs_impl(&self, other: &Self, dst_ptr: *mut Self) {
296 #[cfg(not(target_os = "zkvm"))]
297 {
298 let mut res = self.clone();
299 res *= other;
300 let dst = unsafe { &mut *dst_ptr };
302 *dst = res;
303 }
304 #[cfg(target_os = "zkvm")]
305 {
306 Self::set_up_once();
307 unsafe {
308 #mul_extern_func(
309 dst_ptr as usize,
310 self as *const #struct_name as usize,
311 other as *const #struct_name as usize,
312 );
313 }
314 }
315 }
316
317 #[inline(always)]
318 fn div_unsafe_refs_impl(&self, other: &Self) -> Self {
319 #[cfg(not(target_os = "zkvm"))]
320 {
321 let modulus = Self::modulus_biguint();
322 let inv = other.as_biguint().modinv(&modulus).unwrap();
323 Self::from_biguint((self.as_biguint() * inv) % modulus)
324 }
325 #[cfg(target_os = "zkvm")]
326 {
327 Self::set_up_once();
328 let mut uninit: core::mem::MaybeUninit<#struct_name> = core::mem::MaybeUninit::uninit();
329 unsafe {
330 #div_extern_func(
331 uninit.as_mut_ptr() as usize,
332 self as *const #struct_name as usize,
333 other as *const #struct_name as usize,
334 );
335 }
336 unsafe { uninit.assume_init() }
337 }
338 }
339
340 #[inline(always)]
341 unsafe fn eq_impl<const CHECK_SETUP: bool>(&self, other: &Self) -> bool {
342 #[cfg(not(target_os = "zkvm"))]
343 {
344 self.as_le_bytes() == other.as_le_bytes()
345 }
346 #[cfg(target_os = "zkvm")]
347 {
348 if CHECK_SETUP {
349 Self::set_up_once();
350 }
351 #is_eq_extern_func(self as *const #struct_name as usize, other as *const #struct_name as usize)
352 }
353 }
354
355 #[inline(always)]
357 #[cfg(target_os = "zkvm")]
358 fn set_up_once() {
359 static is_setup: ::openvm_algebra_guest::once_cell::race::OnceBool = ::openvm_algebra_guest::once_cell::race::OnceBool::new();
360 is_setup.get_or_init(|| {
361 unsafe { #moduli_setup_extern_func(); }
362 true
363 });
364 }
365 #[inline(always)]
366 #[cfg(not(target_os = "zkvm"))]
367 fn set_up_once() {
368 }
370 }
371
372 mod #module_name {
374 use openvm_algebra_guest::IntMod;
375
376 use super::#struct_name;
377
378 impl IntMod for #struct_name {
379 type Repr = [u8; #limbs];
380 type SelfRef<'a> = &'a Self;
381
382 const MODULUS: Self::Repr = [#(#modulus_bytes),*];
383
384 const ZERO: Self = Self([0; #limbs]);
385
386 const NUM_LIMBS: usize = #limbs;
387
388 const ONE: Self = Self::from_const_u8(1);
389
390 fn from_repr(repr: Self::Repr) -> Self {
391 Self(repr)
392 }
393
394 fn from_le_bytes(bytes: &[u8]) -> Option<Self> {
395 if bytes.len() != #limbs {
396 return None;
397 }
398 let elt = Self::from_le_bytes_unchecked(bytes);
399 if elt.is_reduced() {
400 Some(elt)
401 } else {
402 None
403 }
404 }
405
406 fn from_be_bytes(bytes: &[u8]) -> Option<Self> {
407 if bytes.len() != #limbs {
408 return None;
409 }
410 let elt = Self::from_be_bytes_unchecked(bytes);
411 if elt.is_reduced() {
412 Some(elt)
413 } else {
414 None
415 }
416 }
417
418 fn from_le_bytes_unchecked(bytes: &[u8]) -> Self {
419 let mut arr = [0u8; #limbs];
420 arr.copy_from_slice(bytes);
421 Self(arr)
422 }
423
424 fn from_be_bytes_unchecked(bytes: &[u8]) -> Self {
425 let mut arr = [0u8; #limbs];
426 for (a, b) in arr.iter_mut().zip(bytes.iter().rev()) {
427 *a = *b;
428 }
429 Self(arr)
430 }
431
432 fn from_u8(val: u8) -> Self {
433 Self::from_const_u8(val)
434 }
435
436 fn from_u32(val: u32) -> Self {
437 let mut bytes = [0; #limbs];
438 bytes[..4].copy_from_slice(&val.to_le_bytes());
439 Self(bytes)
440 }
441
442 fn from_u64(val: u64) -> Self {
443 let mut bytes = [0; #limbs];
444 bytes[..8].copy_from_slice(&val.to_le_bytes());
445 Self(bytes)
446 }
447
448 #[inline(always)]
449 fn as_le_bytes(&self) -> &[u8] {
450 &(self.0)
451 }
452
453 #[inline(always)]
454 fn to_be_bytes(&self) -> [u8; #limbs] {
455 core::array::from_fn(|i| self.0[#limbs - 1 - i])
456 }
457
458 #[cfg(not(target_os = "zkvm"))]
459 fn modulus_biguint() -> num_bigint::BigUint {
460 num_bigint::BigUint::from_bytes_le(&Self::MODULUS)
461 }
462
463 #[cfg(not(target_os = "zkvm"))]
464 fn from_biguint(biguint: num_bigint::BigUint) -> Self {
465 Self(openvm::utils::biguint_to_limbs(&biguint))
466 }
467
468 #[cfg(not(target_os = "zkvm"))]
469 fn as_biguint(&self) -> num_bigint::BigUint {
470 num_bigint::BigUint::from_bytes_le(self.as_le_bytes())
471 }
472
473 #[inline(always)]
474 fn neg_assign(&mut self) {
475 unsafe {
476 (#struct_name::ZERO).sub_refs_impl(self, self as *const Self as *mut Self);
479 }
480 }
481
482 #[inline(always)]
483 fn double_assign(&mut self) {
484 unsafe {
485 self.add_refs_impl::<true>(self, self as *const Self as *mut Self);
488 }
489 }
490
491 #[inline(always)]
492 fn square_assign(&mut self) {
493 unsafe {
494 self.mul_refs_impl(self, self as *const Self as *mut Self);
497 }
498 }
499
500 #[inline(always)]
501 fn double(&self) -> Self {
502 self + self
503 }
504
505 #[inline(always)]
506 fn square(&self) -> Self {
507 self * self
508 }
509
510 #[inline(always)]
511 fn cube(&self) -> Self {
512 &self.square() * self
513 }
514
515 fn assert_reduced(&self) {
520 let _ = core::hint::black_box(PartialEq::eq(self, self));
522 }
523
524 fn is_reduced(&self) -> bool {
525 for (x_limb, p_limb) in self.0.iter().rev().zip(Self::MODULUS.iter().rev()) {
527 if x_limb < p_limb {
528 return true;
529 } else if x_limb > p_limb {
530 return false;
531 }
532 }
533 false
535 }
536
537 #[inline(always)]
538 fn set_up_once() {
539 Self::set_up_once();
540 }
541
542 #[inline(always)]
543 unsafe fn eq_impl<const CHECK_SETUP: bool>(&self, other: &Self) -> bool {
544 Self::eq_impl::<CHECK_SETUP>(self, other)
545 }
546
547 #[inline(always)]
548 unsafe fn add_ref<const CHECK_SETUP: bool>(&self, other: &Self) -> Self {
549 let mut uninit: core::mem::MaybeUninit<#struct_name> = core::mem::MaybeUninit::uninit();
550 self.add_refs_impl::<CHECK_SETUP>(other, uninit.as_mut_ptr());
551 uninit.assume_init()
552 }
553 }
554
555 impl<'a> core::ops::AddAssign<&'a #struct_name> for #struct_name {
556 #[inline(always)]
557 fn add_assign(&mut self, other: &'a #struct_name) {
558 self.add_assign_impl(other);
559 }
560 }
561
562 impl core::ops::AddAssign for #struct_name {
563 #[inline(always)]
564 fn add_assign(&mut self, other: Self) {
565 self.add_assign_impl(&other);
566 }
567 }
568
569 impl core::ops::Add for #struct_name {
570 type Output = Self;
571 #[inline(always)]
572 fn add(mut self, other: Self) -> Self::Output {
573 self += other;
574 self
575 }
576 }
577
578 impl<'a> core::ops::Add<&'a #struct_name> for #struct_name {
579 type Output = Self;
580 #[inline(always)]
581 fn add(mut self, other: &'a #struct_name) -> Self::Output {
582 self += other;
583 self
584 }
585 }
586
587 impl<'a> core::ops::Add<&'a #struct_name> for &#struct_name {
588 type Output = #struct_name;
589 #[inline(always)]
590 fn add(self, other: &'a #struct_name) -> Self::Output {
591 unsafe { self.add_ref::<true>(other) }
593 }
594 }
595
596 impl<'a> core::ops::SubAssign<&'a #struct_name> for #struct_name {
597 #[inline(always)]
598 fn sub_assign(&mut self, other: &'a #struct_name) {
599 self.sub_assign_impl(other);
600 }
601 }
602
603 impl core::ops::SubAssign for #struct_name {
604 #[inline(always)]
605 fn sub_assign(&mut self, other: Self) {
606 self.sub_assign_impl(&other);
607 }
608 }
609
610 impl core::ops::Sub for #struct_name {
611 type Output = Self;
612 #[inline(always)]
613 fn sub(mut self, other: Self) -> Self::Output {
614 self -= other;
615 self
616 }
617 }
618
619 impl<'a> core::ops::Sub<&'a #struct_name> for #struct_name {
620 type Output = Self;
621 #[inline(always)]
622 fn sub(mut self, other: &'a #struct_name) -> Self::Output {
623 self -= other;
624 self
625 }
626 }
627
628 impl<'a> core::ops::Sub<&'a #struct_name> for &'a #struct_name {
629 type Output = #struct_name;
630 #[inline(always)]
631 fn sub(self, other: &'a #struct_name) -> Self::Output {
632 let mut uninit: core::mem::MaybeUninit<#struct_name> = core::mem::MaybeUninit::uninit();
633 unsafe {
634 self.sub_refs_impl(other, uninit.as_mut_ptr());
635 uninit.assume_init()
636 }
637 }
638 }
639
640 impl<'a> core::ops::MulAssign<&'a #struct_name> for #struct_name {
641 #[inline(always)]
642 fn mul_assign(&mut self, other: &'a #struct_name) {
643 self.mul_assign_impl(other);
644 }
645 }
646
647 impl core::ops::MulAssign for #struct_name {
648 #[inline(always)]
649 fn mul_assign(&mut self, other: Self) {
650 self.mul_assign_impl(&other);
651 }
652 }
653
654 impl core::ops::Mul for #struct_name {
655 type Output = Self;
656 #[inline(always)]
657 fn mul(mut self, other: Self) -> Self::Output {
658 self *= other;
659 self
660 }
661 }
662
663 impl<'a> core::ops::Mul<&'a #struct_name> for #struct_name {
664 type Output = Self;
665 #[inline(always)]
666 fn mul(mut self, other: &'a #struct_name) -> Self::Output {
667 self *= other;
668 self
669 }
670 }
671
672 impl<'a> core::ops::Mul<&'a #struct_name> for &#struct_name {
673 type Output = #struct_name;
674 #[inline(always)]
675 fn mul(self, other: &'a #struct_name) -> Self::Output {
676 let mut uninit: core::mem::MaybeUninit<#struct_name> = core::mem::MaybeUninit::uninit();
677 unsafe {
678 self.mul_refs_impl(other, uninit.as_mut_ptr());
679 uninit.assume_init()
680 }
681 }
682 }
683
684 impl<'a> openvm_algebra_guest::DivAssignUnsafe<&'a #struct_name> for #struct_name {
685 #[inline(always)]
687 fn div_assign_unsafe(&mut self, other: &'a #struct_name) {
688 self.div_assign_unsafe_impl(other);
689 }
690 }
691
692 impl openvm_algebra_guest::DivAssignUnsafe for #struct_name {
693 #[inline(always)]
695 fn div_assign_unsafe(&mut self, other: Self) {
696 self.div_assign_unsafe_impl(&other);
697 }
698 }
699
700 impl openvm_algebra_guest::DivUnsafe for #struct_name {
701 type Output = Self;
702 #[inline(always)]
704 fn div_unsafe(mut self, other: Self) -> Self::Output {
705 self.div_assign_unsafe_impl(&other);
706 self
707 }
708 }
709
710 impl<'a> openvm_algebra_guest::DivUnsafe<&'a #struct_name> for #struct_name {
711 type Output = Self;
712 #[inline(always)]
714 fn div_unsafe(mut self, other: &'a #struct_name) -> Self::Output {
715 self.div_assign_unsafe_impl(other);
716 self
717 }
718 }
719
720 impl<'a> openvm_algebra_guest::DivUnsafe<&'a #struct_name> for &#struct_name {
721 type Output = #struct_name;
722 #[inline(always)]
724 fn div_unsafe(self, other: &'a #struct_name) -> Self::Output {
725 self.div_unsafe_refs_impl(other)
726 }
727 }
728
729 impl PartialEq for #struct_name {
730 #[inline(always)]
731 fn eq(&self, other: &Self) -> bool {
732 unsafe { self.eq_impl::<true>(other) }
734 }
735 }
736
737 impl<'a> core::iter::Sum<&'a #struct_name> for #struct_name {
738 fn sum<I: Iterator<Item = &'a #struct_name>>(iter: I) -> Self {
739 iter.fold(Self::ZERO, |acc, x| &acc + x)
740 }
741 }
742
743 impl core::iter::Sum for #struct_name {
744 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
745 iter.fold(Self::ZERO, |acc, x| &acc + &x)
746 }
747 }
748
749 impl<'a> core::iter::Product<&'a #struct_name> for #struct_name {
750 fn product<I: Iterator<Item = &'a #struct_name>>(iter: I) -> Self {
751 iter.fold(Self::ONE, |acc, x| &acc * x)
752 }
753 }
754
755 impl core::iter::Product for #struct_name {
756 fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
757 iter.fold(Self::ONE, |acc, x| &acc * &x)
758 }
759 }
760
761 impl core::ops::Neg for #struct_name {
762 type Output = #struct_name;
763 #[inline(always)]
764 fn neg(self) -> Self::Output {
765 #struct_name::ZERO - &self
766 }
767 }
768
769 impl<'a> core::ops::Neg for &'a #struct_name {
770 type Output = #struct_name;
771 #[inline(always)]
772 fn neg(self) -> Self::Output {
773 #struct_name::ZERO - self
774 }
775 }
776
777 impl core::fmt::Debug for #struct_name {
778 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
779 write!(f, "{:?}", self.as_le_bytes())
780 }
781 }
782 }
783
784 impl openvm_algebra_guest::Reduce for #struct_name {
785 fn reduce_le_bytes(bytes: &[u8]) -> Self {
786 debug_assert!(
787 bytes.len() % #limbs == 0,
788 "reduce_le_bytes: input length {} is not a multiple of modulus byte size {}",
789 bytes.len(),
790 #limbs,
791 );
792 let mut res = <Self as openvm_algebra_guest::IntMod>::ZERO;
793 let mut base = <Self as openvm_algebra_guest::IntMod>::from_le_bytes_unchecked(&[255u8; #limbs]);
795 base += <Self as openvm_algebra_guest::IntMod>::ONE;
796 for chunk in bytes.chunks(#limbs).rev() {
797 let mut padded = [0u8; #limbs];
798 padded[..chunk.len()].copy_from_slice(chunk);
799 res = res * &base + <Self as openvm_algebra_guest::IntMod>::from_repr(padded);
800 }
801 openvm_algebra_guest::IntMod::assert_reduced(&res);
802 res
803 }
804 }
805 });
806
807 output.push(result);
808
809 let modulus_biguint = BigUint::from_bytes_le(&modulus_bytes);
810 let modulus_is_prime = is_prime(&modulus_biguint, None);
811
812 if modulus_is_prime.probably() {
813 let field_and_sqrt_impl = TokenStream::from(quote::quote_spanned! { span.into() =>
815 impl ::openvm_algebra_guest::Field for #struct_name {
816 const ZERO: Self = <Self as ::openvm_algebra_guest::IntMod>::ZERO;
817 const ONE: Self = <Self as ::openvm_algebra_guest::IntMod>::ONE;
818
819 type SelfRef<'a> = &'a Self;
820
821 fn double_assign(&mut self) {
822 ::openvm_algebra_guest::IntMod::double_assign(self);
823 }
824
825 fn square_assign(&mut self) {
826 ::openvm_algebra_guest::IntMod::square_assign(self);
827 }
828
829 }
830
831 impl openvm_algebra_guest::Sqrt for #struct_name {
832 fn sqrt(&self) -> Option<Self> {
837 match self.honest_host_sqrt() {
838 Some(Some(sqrt)) => Some(sqrt),
840 Some(None) => None,
842 None => {
844 loop {
846 openvm::io::println("ERROR: Square root hint is invalid. Entering infinite loop.");
847 }
848 }
849 }
850 }
851 }
852
853 impl #struct_name {
854 fn honest_host_sqrt(&self) -> Option<Option<Self>> {
858 if self == &<Self as ::openvm_algebra_guest::IntMod>::ZERO {
862 return Some(Some(<Self as ::openvm_algebra_guest::IntMod>::ZERO));
863 }
864
865 let (is_square, sqrt) = self.hint_sqrt_impl()?;
866
867 if is_square {
868 <Self as ::openvm_algebra_guest::IntMod>::assert_reduced(&sqrt);
870
871 if &(&sqrt * &sqrt) == self {
872 Some(Some(sqrt))
873 } else {
874 None
875 }
876 } else {
877 <Self as ::openvm_algebra_guest::IntMod>::assert_reduced(&sqrt);
879
880 if &sqrt * &sqrt == self * Self::get_non_qr() {
881 Some(None)
882 } else {
883 None
884 }
885 }
886 }
887
888
889 fn hint_sqrt_impl(&self) -> Option<(bool, Self)> {
893 #[cfg(not(target_os = "zkvm"))]
894 {
895 unimplemented!();
896 }
897 #[cfg(target_os = "zkvm")]
898 {
899 use ::openvm_algebra_guest::{openvm_custom_insn, openvm_rv32im_guest}; let is_square = core::mem::MaybeUninit::<u32>::uninit();
902 let mut sqrt = core::mem::MaybeUninit::<#struct_name>::uninit();
903 unsafe {
904 #hint_sqrt_extern_func(self as *const #struct_name as usize);
905 let is_square_ptr = is_square.as_ptr() as *const u32;
906 openvm_rv32im_guest::hint_store_u32!(is_square_ptr);
907 openvm_rv32im_guest::hint_buffer_chunked(sqrt.as_mut_ptr() as *mut u8, <#struct_name as ::openvm_algebra_guest::IntMod>::NUM_LIMBS / 4 as usize);
908 let is_square = is_square.assume_init();
909 if is_square == 0 || is_square == 1 {
910 Some((is_square == 1, sqrt.assume_init()))
911 } else {
912 None
913 }
914 }
915 }
916 }
917
918 fn init_non_qr() -> alloc::boxed::Box<#struct_name> {
920 #[cfg(not(target_os = "zkvm"))]
921 {
922 unimplemented!();
923 }
924 #[cfg(target_os = "zkvm")]
925 {
926 use ::openvm_algebra_guest::{openvm_custom_insn, openvm_rv32im_guest}; let mut non_qr_uninit = core::mem::MaybeUninit::<Self>::uninit();
929 let mut non_qr;
930 unsafe {
931 #hint_non_qr_extern_func();
932 let ptr = non_qr_uninit.as_mut_ptr() as *mut u8;
933 openvm_rv32im_guest::hint_buffer_chunked(ptr, <Self as ::openvm_algebra_guest::IntMod>::NUM_LIMBS / 4 as usize);
934 non_qr = non_qr_uninit.assume_init();
935 }
936 <Self as ::openvm_algebra_guest::IntMod>::assert_reduced(&non_qr);
938
939 use ::openvm_algebra_guest::{DivUnsafe, ExpBytes};
940 let exp = -<Self as ::openvm_algebra_guest::IntMod>::ONE.div_unsafe(Self::from_const_u8(2));
942 <Self as ::openvm_algebra_guest::IntMod>::assert_reduced(&exp);
943
944 if non_qr.exp_bytes(true, &<Self as ::openvm_algebra_guest::IntMod>::to_be_bytes(&exp)) != -<Self as ::openvm_algebra_guest::IntMod>::ONE
945 {
946 loop {
948 openvm::io::println("ERROR: Non quadratic residue hint is invalid. Entering infinite loop.");
949 }
950 }
951
952 alloc::boxed::Box::new(non_qr)
953 }
954 }
955
956 pub fn get_non_qr() -> &'static #struct_name {
958 static non_qr: ::openvm_algebra_guest::once_cell::race::OnceBox<#struct_name> = ::openvm_algebra_guest::once_cell::race::OnceBox::new();
959 &non_qr.get_or_init(Self::init_non_qr)
960 }
961 }
962 });
963
964 output.push(field_and_sqrt_impl);
965 }
966 }
967
968 TokenStream::from_iter(output)
969}
970
971struct ModuliDefine {
972 items: Vec<LitStr>,
973}
974
975impl Parse for ModuliDefine {
976 fn parse(input: ParseStream) -> syn::Result<Self> {
977 let items = input.parse_terminated(<LitStr as Parse>::parse, Token![,])?;
978 Ok(Self {
979 items: items.into_iter().collect(),
980 })
981 }
982}
983
984#[proc_macro]
985pub fn moduli_init(input: TokenStream) -> TokenStream {
986 let ModuliDefine { items } = parse_macro_input!(input as ModuliDefine);
987
988 let mut externs = Vec::new();
989
990 let mut two_modular_limbs_flattened_list = Vec::<u8>::new();
992 let mut limb_list_borders = vec![0usize];
994
995 let span = proc_macro::Span::call_site();
996
997 let mut max_block_size = 4;
998
999 for (mod_idx, item) in items.into_iter().enumerate() {
1000 let modulus = item.value();
1001 let modulus_bytes = string_to_bytes(&modulus);
1002 let mut limbs = modulus_bytes.len();
1003 let mut block_size = 32;
1004
1005 if limbs <= 32 {
1006 limbs = 32;
1007 } else if limbs <= 48 {
1008 limbs = 48;
1009 block_size = 16;
1010 } else {
1011 panic!("limbs must be at most 48");
1012 }
1013
1014 max_block_size = max_block_size.max(block_size);
1015
1016 let block_size = proc_macro::Literal::usize_unsuffixed(block_size);
1017 let block_size = syn::Lit::new(block_size.to_string().parse::<_>().unwrap());
1018
1019 let modulus_bytes = modulus_bytes
1020 .into_iter()
1021 .chain(vec![0u8; limbs])
1022 .take(limbs)
1023 .collect::<Vec<_>>();
1024
1025 let doubled_modulus = [modulus_bytes.clone(), modulus_bytes.clone()].concat();
1027 two_modular_limbs_flattened_list.extend(doubled_modulus);
1028 limb_list_borders.push(two_modular_limbs_flattened_list.len());
1029
1030 let modulus_hex = modulus_bytes
1031 .iter()
1032 .rev()
1033 .map(|x| format!("{x:02x}"))
1034 .collect::<Vec<_>>()
1035 .join("");
1036
1037 let setup_extern_func = syn::Ident::new(
1038 &format!("moduli_setup_extern_func_{modulus_hex}"),
1039 span.into(),
1040 );
1041
1042 for op_type in ["add", "sub", "mul", "div"] {
1043 let func_name =
1044 syn::Ident::new(&format!("{op_type}_extern_func_{modulus_hex}"), span.into());
1045 let mut chars = op_type.chars().collect::<Vec<_>>();
1046 chars[0] = chars[0].to_ascii_uppercase();
1047 let local_opcode = syn::Ident::new(
1048 &format!("{}Mod", chars.iter().collect::<String>()),
1049 span.into(),
1050 );
1051 externs.push(quote::quote_spanned! { span.into() =>
1052 #[no_mangle]
1053 extern "C" fn #func_name(rd: usize, rs1: usize, rs2: usize) {
1054 openvm::platform::custom_insn_r!(
1055 opcode = ::openvm_algebra_guest::OPCODE,
1056 funct3 = ::openvm_algebra_guest::MODULAR_ARITHMETIC_FUNCT3 as usize,
1057 funct7 = ::openvm_algebra_guest::ModArithBaseFunct7::#local_opcode as usize + #mod_idx * (::openvm_algebra_guest::ModArithBaseFunct7::MODULAR_ARITHMETIC_MAX_KINDS as usize),
1058 rd = In rd,
1059 rs1 = In rs1,
1060 rs2 = In rs2
1061 )
1062 }
1063 });
1064 }
1065
1066 let is_eq_extern_func =
1067 syn::Ident::new(&format!("is_eq_extern_func_{modulus_hex}"), span.into());
1068 externs.push(quote::quote_spanned! { span.into() =>
1069 #[no_mangle]
1070 extern "C" fn #is_eq_extern_func(rs1: usize, rs2: usize) -> bool {
1071 let mut x: u32;
1072 openvm::platform::custom_insn_r!(
1073 opcode = ::openvm_algebra_guest::OPCODE,
1074 funct3 = ::openvm_algebra_guest::MODULAR_ARITHMETIC_FUNCT3 as usize,
1075 funct7 = ::openvm_algebra_guest::ModArithBaseFunct7::IsEqMod as usize + #mod_idx * (::openvm_algebra_guest::ModArithBaseFunct7::MODULAR_ARITHMETIC_MAX_KINDS as usize),
1076 rd = Out x,
1077 rs1 = In rs1,
1078 rs2 = In rs2
1079 );
1080 x != 0
1081 }
1082 });
1083
1084 let hint_non_qr_extern_func = syn::Ident::new(
1085 &format!("hint_non_qr_extern_func_{modulus_hex}"),
1086 span.into(),
1087 );
1088 externs.push(quote::quote_spanned! { span.into() =>
1089 #[no_mangle]
1090 extern "C" fn #hint_non_qr_extern_func() {
1091 openvm::platform::custom_insn_r!(
1092 opcode = ::openvm_algebra_guest::OPCODE,
1093 funct3 = ::openvm_algebra_guest::MODULAR_ARITHMETIC_FUNCT3 as usize,
1094 funct7 = ::openvm_algebra_guest::ModArithBaseFunct7::HintNonQr as usize + #mod_idx * (::openvm_algebra_guest::ModArithBaseFunct7::MODULAR_ARITHMETIC_MAX_KINDS as usize),
1095 rd = Const "x0",
1096 rs1 = Const "x0",
1097 rs2 = Const "x0"
1098 );
1099 }
1100
1101
1102 });
1103
1104 let hint_sqrt_extern_func =
1107 syn::Ident::new(&format!("hint_sqrt_extern_func_{modulus_hex}"), span.into());
1108 externs.push(quote::quote_spanned! { span.into() =>
1109 #[no_mangle]
1110 extern "C" fn #hint_sqrt_extern_func(rs1: usize) {
1111 openvm::platform::custom_insn_r!(
1112 opcode = ::openvm_algebra_guest::OPCODE,
1113 funct3 = ::openvm_algebra_guest::MODULAR_ARITHMETIC_FUNCT3 as usize,
1114 funct7 = ::openvm_algebra_guest::ModArithBaseFunct7::HintSqrt as usize + #mod_idx * (::openvm_algebra_guest::ModArithBaseFunct7::MODULAR_ARITHMETIC_MAX_KINDS as usize),
1115 rd = Const "x0",
1116 rs1 = In rs1,
1117 rs2 = Const "x0"
1118 );
1119 }
1120 });
1121
1122 externs.push(quote::quote_spanned! { span.into() =>
1123 #[no_mangle]
1124 extern "C" fn #setup_extern_func() {
1125 #[cfg(target_os = "zkvm")]
1126 {
1127 #[repr(C, align(#block_size))]
1129 struct AlignedPlaceholder([u8; #limbs]);
1130
1131 const MODULUS_BYTES: AlignedPlaceholder = AlignedPlaceholder([#(#modulus_bytes),*]);
1132
1133 let mut uninit: core::mem::MaybeUninit<AlignedPlaceholder> = core::mem::MaybeUninit::uninit();
1136 openvm::platform::custom_insn_r!(
1137 opcode = ::openvm_algebra_guest::OPCODE,
1138 funct3 = ::openvm_algebra_guest::MODULAR_ARITHMETIC_FUNCT3,
1139 funct7 = ::openvm_algebra_guest::ModArithBaseFunct7::SetupMod as usize
1140 + #mod_idx
1141 * (::openvm_algebra_guest::ModArithBaseFunct7::MODULAR_ARITHMETIC_MAX_KINDS as usize),
1142 rd = In uninit.as_mut_ptr(),
1143 rs1 = In MODULUS_BYTES.0.as_ptr(),
1144 rs2 = Const "x0" );
1146 openvm::platform::custom_insn_r!(
1147 opcode = ::openvm_algebra_guest::OPCODE,
1148 funct3 = ::openvm_algebra_guest::MODULAR_ARITHMETIC_FUNCT3,
1149 funct7 = ::openvm_algebra_guest::ModArithBaseFunct7::SetupMod as usize
1150 + #mod_idx
1151 * (::openvm_algebra_guest::ModArithBaseFunct7::MODULAR_ARITHMETIC_MAX_KINDS as usize),
1152 rd = In uninit.as_mut_ptr(),
1153 rs1 = In MODULUS_BYTES.0.as_ptr(),
1154 rs2 = Const "x1" );
1156 unsafe {
1157 let mut tmp = uninit.as_mut_ptr() as usize;
1159 openvm::platform::custom_insn_r!(
1160 opcode = ::openvm_algebra_guest::OPCODE,
1161 funct3 = ::openvm_algebra_guest::MODULAR_ARITHMETIC_FUNCT3 as usize,
1162 funct7 = ::openvm_algebra_guest::ModArithBaseFunct7::SetupMod as usize
1163 + #mod_idx
1164 * (::openvm_algebra_guest::ModArithBaseFunct7::MODULAR_ARITHMETIC_MAX_KINDS as usize),
1165 rd = InOut tmp,
1166 rs1 = In MODULUS_BYTES.0.as_ptr(),
1167 rs2 = Const "x2" );
1169 }
1171 }
1172 }
1173 });
1174 }
1175
1176 let max_block_size = proc_macro::Literal::usize_unsuffixed(max_block_size);
1177 let max_block_size = syn::Lit::new(max_block_size.to_string().parse::<_>().unwrap());
1178
1179 let total_limbs_cnt = two_modular_limbs_flattened_list.len();
1180 let cnt_limbs_list_len = limb_list_borders.len();
1181 TokenStream::from(quote::quote_spanned! { span.into() =>
1182 #[allow(non_snake_case)]
1183 #[cfg(target_os = "zkvm")]
1184 mod openvm_intrinsics_ffi {
1185 #(#externs)*
1186 }
1187 #[allow(non_snake_case, non_upper_case_globals)]
1188 pub mod openvm_intrinsics_meta_do_not_type_this_by_yourself {
1189 #[repr(C, align(#max_block_size))]
1190 pub struct Aligned<T>(pub T);
1191
1192 pub const two_modular_limbs_list: Aligned<[u8; #total_limbs_cnt]> = Aligned([#(#two_modular_limbs_flattened_list),*]);
1193 pub const limb_list_borders: [usize; #cnt_limbs_list_len] = [#(#limb_list_borders),*];
1194 }
1195 })
1196}