1extern crate proc_macro;
2
3use openvm_macros_common::MacroArgs;
4use proc_macro::TokenStream;
5use quote::format_ident;
6use syn::{
7 parse::{Parse, ParseStream},
8 parse_macro_input, ExprPath, LitStr, Token,
9};
10
11#[proc_macro]
31pub fn sw_declare(input: TokenStream) -> TokenStream {
32 let MacroArgs { items } = parse_macro_input!(input as MacroArgs);
33
34 let mut output = Vec::new();
35
36 let span = proc_macro::Span::call_site();
37
38 for item in items.into_iter() {
39 let struct_name_str = item.name.to_string();
40 let struct_name = syn::Ident::new(&struct_name_str, span.into());
41 let mut intmod_type: Option<syn::Path> = None;
42 let mut const_a: Option<syn::Expr> = None;
43 let mut const_b: Option<syn::Expr> = None;
44 for param in item.params {
45 match param.name.to_string().as_str() {
46 "mod_type" => {
48 if let syn::Expr::Path(ExprPath { path, .. }) = param.value {
49 intmod_type = Some(path)
50 } else {
51 return syn::Error::new_spanned(param.value, "Expected a type")
52 .to_compile_error()
53 .into();
54 }
55 }
56 "a" => {
57 const_a = Some(param.value);
60 }
61 "b" => {
62 const_b = Some(param.value);
65 }
66 _ => {
67 panic!("Unknown parameter {}", param.name);
68 }
69 }
70 }
71
72 let intmod_type = intmod_type.expect("mod_type parameter is required");
73 let const_a = const_a
75 .unwrap_or(syn::parse_quote!(<#intmod_type as openvm_algebra_guest::IntMod>::ZERO));
76 let const_b = const_b.expect("constant b coefficient is required");
77
78 macro_rules! create_extern_func {
79 ($name:ident) => {
80 let $name = syn::Ident::new(
81 &format!("{}_{}", stringify!($name), struct_name_str),
82 span.into(),
83 );
84 };
85 }
86 create_extern_func!(sw_add_ne_extern_func);
87 create_extern_func!(sw_double_extern_func);
88 create_extern_func!(sw_setup_extern_func);
89
90 let group_ops_mod_name = format_ident!("{}_ops", struct_name_str.to_lowercase());
91
92 let result = TokenStream::from(quote::quote_spanned! { span.into() =>
93 extern "C" {
94 fn #sw_add_ne_extern_func(rd: usize, rs1: usize, rs2: usize);
95 fn #sw_double_extern_func(rd: usize, rs1: usize);
96 fn #sw_setup_extern_func(uninit: *mut core::ffi::c_void, p1: *const u8, p2: *const u8);
97 }
98
99 #[derive(Eq, PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
100 #[repr(C)]
101 pub struct #struct_name {
102 x: #intmod_type,
103 y: #intmod_type,
104 }
105 #[allow(non_upper_case_globals)]
106
107 impl #struct_name {
108 const fn identity() -> Self {
109 Self {
110 x: <#intmod_type as openvm_algebra_guest::IntMod>::ZERO,
111 y: <#intmod_type as openvm_algebra_guest::IntMod>::ZERO,
112 }
113 }
114 #[inline(always)]
117 unsafe fn add_ne<const CHECK_SETUP: bool>(p1: &#struct_name, p2: &#struct_name) -> #struct_name {
118 #[cfg(not(target_os = "zkvm"))]
119 {
120 use openvm_algebra_guest::DivUnsafe;
121 let lambda = (&p2.y - &p1.y).div_unsafe(&p2.x - &p1.x);
122 let x3 = &lambda * &lambda - &p1.x - &p2.x;
123 let y3 = &lambda * &(&p1.x - &x3) - &p1.y;
124 #struct_name { x: x3, y: y3 }
125 }
126 #[cfg(target_os = "zkvm")]
127 {
128 if CHECK_SETUP {
129 Self::set_up_once();
130 }
131 let mut uninit: core::mem::MaybeUninit<#struct_name> = core::mem::MaybeUninit::uninit();
132 #sw_add_ne_extern_func(
133 uninit.as_mut_ptr() as usize,
134 p1 as *const #struct_name as usize,
135 p2 as *const #struct_name as usize
136 );
137 uninit.assume_init()
138 }
139 }
140
141 #[inline(always)]
142 unsafe fn add_ne_assign<const CHECK_SETUP: bool>(&mut self, p2: &#struct_name) {
143 #[cfg(not(target_os = "zkvm"))]
144 {
145 use openvm_algebra_guest::DivUnsafe;
146 let lambda = (&p2.y - &self.y).div_unsafe(&p2.x - &self.x);
147 let x3 = &lambda * &lambda - &self.x - &p2.x;
148 let y3 = &lambda * &(&self.x - &x3) - &self.y;
149 self.x = x3;
150 self.y = y3;
151 }
152 #[cfg(target_os = "zkvm")]
153 {
154 if CHECK_SETUP {
155 Self::set_up_once();
156 }
157 #sw_add_ne_extern_func(
158 self as *mut #struct_name as usize,
159 self as *const #struct_name as usize,
160 p2 as *const #struct_name as usize
161 );
162 }
163 }
164
165 #[inline(always)]
167 unsafe fn double_impl<const CHECK_SETUP: bool>(p: &#struct_name) -> #struct_name {
168 #[cfg(not(target_os = "zkvm"))]
169 {
170 use openvm_algebra_guest::DivUnsafe;
171 let curve_a: #intmod_type = #const_a;
172 let two = #intmod_type::from_u8(2);
173 let lambda = (&p.x * &p.x * #intmod_type::from_u8(3) + &curve_a).div_unsafe(&p.y * &two);
174 let x3 = &lambda * &lambda - &p.x * &two;
175 let y3 = &lambda * &(&p.x - &x3) - &p.y;
176 #struct_name { x: x3, y: y3 }
177 }
178 #[cfg(target_os = "zkvm")]
179 {
180 if CHECK_SETUP {
181 Self::set_up_once();
182 }
183 let mut uninit: core::mem::MaybeUninit<#struct_name> = core::mem::MaybeUninit::uninit();
184 #sw_double_extern_func(
185 uninit.as_mut_ptr() as usize,
186 p as *const #struct_name as usize,
187 );
188 uninit.assume_init()
189 }
190 }
191
192 #[inline(always)]
194 #[cfg(target_os = "zkvm")]
195 fn set_up_once() {
196 static is_setup: ::openvm_ecc_guest::once_cell::race::OnceBool = ::openvm_ecc_guest::once_cell::race::OnceBool::new();
197
198 is_setup.get_or_init(|| {
199 let modulus_bytes = <<Self as openvm_ecc_guest::weierstrass::WeierstrassPoint>::Coordinate as openvm_algebra_guest::IntMod>::MODULUS;
202 let mut one = [0u8; <<Self as openvm_ecc_guest::weierstrass::WeierstrassPoint>::Coordinate as openvm_algebra_guest::IntMod>::NUM_LIMBS];
203 one[0] = 1;
204 let curve_a_bytes = openvm_algebra_guest::IntMod::as_le_bytes(&<#struct_name as openvm_ecc_guest::weierstrass::WeierstrassPoint>::CURVE_A);
205 let p1 = [modulus_bytes.as_ref(), curve_a_bytes.as_ref()].concat();
207 let p2 = [one.as_ref(), one.as_ref()].concat();
209 let mut uninit: core::mem::MaybeUninit<[Self; 2]> = core::mem::MaybeUninit::uninit();
210
211 unsafe { #sw_setup_extern_func(uninit.as_mut_ptr() as *mut core::ffi::c_void, p1.as_ptr(), p2.as_ptr()); }
212 <#intmod_type as openvm_algebra_guest::IntMod>::set_up_once();
213 true
214 });
215 }
216
217 #[inline(always)]
218 #[cfg(not(target_os = "zkvm"))]
219 fn set_up_once() {
220 }
222
223 #[inline(always)]
224 fn is_identity_impl<const CHECK_SETUP: bool>(&self) -> bool {
225 use openvm_algebra_guest::IntMod;
226 unsafe {
228 self.x.eq_impl::<CHECK_SETUP>(&#intmod_type::ZERO) && self.y.eq_impl::<CHECK_SETUP>(&#intmod_type::ZERO)
229 }
230 }
231 }
232
233 impl ::openvm_ecc_guest::weierstrass::WeierstrassPoint for #struct_name {
234 const CURVE_A: #intmod_type = #const_a;
235 const CURVE_B: #intmod_type = #const_b;
236 const IDENTITY: Self = Self::identity();
237 type Coordinate = #intmod_type;
238
239 #[inline(always)]
242 fn as_le_bytes(&self) -> &[u8] {
243 unsafe { &*core::ptr::slice_from_raw_parts(self as *const Self as *const u8, <#intmod_type as openvm_algebra_guest::IntMod>::NUM_LIMBS * 2) }
244 }
245
246 #[inline(always)]
247 unsafe fn from_xy_unchecked(x: Self::Coordinate, y: Self::Coordinate) -> Self {
248 Self { x, y }
249 }
250
251 #[inline(always)]
252 fn x(&self) -> &Self::Coordinate {
253 &self.x
254 }
255
256 #[inline(always)]
257 fn y(&self) -> &Self::Coordinate {
258 &self.y
259 }
260
261 #[inline(always)]
262 fn x_mut(&mut self) -> &mut Self::Coordinate {
263 &mut self.x
264 }
265
266 #[inline(always)]
267 fn y_mut(&mut self) -> &mut Self::Coordinate {
268 &mut self.y
269 }
270
271 #[inline(always)]
272 fn into_coords(self) -> (Self::Coordinate, Self::Coordinate) {
273 (self.x, self.y)
274 }
275
276 #[inline(always)]
277 fn set_up_once() {
278 Self::set_up_once();
279 }
280
281 #[inline]
282 fn add_assign_impl<const CHECK_SETUP: bool>(&mut self, p2: &Self) {
283 use openvm_algebra_guest::IntMod;
284
285 if CHECK_SETUP {
286 #intmod_type::set_up_once();
288 }
289
290 if self.is_identity_impl::<CHECK_SETUP>() {
291 *self = p2.clone();
292 } else if p2.is_identity_impl::<CHECK_SETUP>() {
293 } else if unsafe { self.x.eq_impl::<false>(&p2.x) } { let sum_ys = unsafe { self.y.add_ref::<false>(&p2.y) };
296 if unsafe { IntMod::eq_impl::<false>(&sum_ys, &<#intmod_type as IntMod>::ZERO) } {
298 *self = Self::identity();
299 } else {
300 unsafe {
301 self.double_assign_nonidentity::<CHECK_SETUP>();
302 }
303 }
304 } else {
305 unsafe {
306 self.add_ne_assign_nonidentity::<CHECK_SETUP>(p2);
307 }
308 }
309 }
310
311 #[inline(always)]
312 fn double_assign_impl<const CHECK_SETUP: bool>(&mut self) {
313 if !self.is_identity_impl::<CHECK_SETUP>() {
314 unsafe {
315 self.double_assign_nonidentity::<CHECK_SETUP>();
316 }
317 }
318 }
319
320 #[inline(always)]
321 unsafe fn add_ne_nonidentity<const CHECK_SETUP: bool>(&self, p2: &Self) -> Self {
322 Self::add_ne::<CHECK_SETUP>(self, p2)
323 }
324
325 #[inline(always)]
326 unsafe fn add_ne_assign_nonidentity<const CHECK_SETUP: bool>(&mut self, p2: &Self) {
327 Self::add_ne_assign::<CHECK_SETUP>(self, p2);
328 }
329
330 #[inline(always)]
331 unsafe fn sub_ne_nonidentity<const CHECK_SETUP: bool>(&self, p2: &Self) -> Self {
332 Self::add_ne::<CHECK_SETUP>(self, &p2.clone().neg())
333 }
334
335 #[inline(always)]
336 unsafe fn sub_ne_assign_nonidentity<const CHECK_SETUP: bool>(&mut self, p2: &Self) {
337 Self::add_ne_assign::<CHECK_SETUP>(self, &p2.clone().neg());
338 }
339
340 #[inline(always)]
341 unsafe fn double_nonidentity<const CHECK_SETUP: bool>(&self) -> Self {
342 Self::double_impl::<CHECK_SETUP>(self)
343 }
344
345 #[inline(always)]
346 unsafe fn double_assign_nonidentity<const CHECK_SETUP: bool>(&mut self) {
347 #[cfg(not(target_os = "zkvm"))]
348 {
349 *self = Self::double_impl::<CHECK_SETUP>(self);
350 }
351 #[cfg(target_os = "zkvm")]
352 {
353 if CHECK_SETUP {
354 Self::set_up_once();
355 }
356 #sw_double_extern_func(
357 self as *mut #struct_name as usize,
358 self as *const #struct_name as usize
359 );
360 }
361 }
362 }
363
364 impl core::ops::Neg for #struct_name {
365 type Output = Self;
366
367 fn neg(self) -> Self::Output {
368 #struct_name {
369 x: self.x,
370 y: -self.y,
371 }
372 }
373 }
374
375 impl core::ops::Neg for &#struct_name {
376 type Output = #struct_name;
377
378 fn neg(self) -> #struct_name {
379 #struct_name {
380 x: self.x.clone(),
381 y: core::ops::Neg::neg(&self.y),
382 }
383 }
384 }
385
386 mod #group_ops_mod_name {
387 use ::openvm_ecc_guest::{weierstrass::{WeierstrassPoint, FromCompressed}, impl_sw_group_ops, algebra::IntMod};
388 use super::*;
389
390 impl_sw_group_ops!(#struct_name, #intmod_type);
391
392 impl FromCompressed<#intmod_type> for #struct_name {
393 fn decompress(x: #intmod_type, rec_id: &u8) -> Option<Self> {
394 use openvm_algebra_guest::Sqrt;
395 let y_squared = &x * &x * &x + &<#struct_name as ::openvm_ecc_guest::weierstrass::WeierstrassPoint>::CURVE_A * &x + &<#struct_name as ::openvm_ecc_guest::weierstrass::WeierstrassPoint>::CURVE_B;
396 let y = y_squared.sqrt();
397 match y {
398 None => None,
399 Some(y) => {
400 let correct_y = if y.as_le_bytes()[0] & 1 == *rec_id & 1 {
401 y
402 } else {
403 -y
404 };
405 if correct_y.as_le_bytes()[0] & 1 != *rec_id & 1 {
407 return None;
408 }
409 unsafe { Some(<#struct_name as ::openvm_ecc_guest::weierstrass::WeierstrassPoint>::from_xy_unchecked(x, correct_y)) }
411 }
412 }
413 }
414 }
415 }
416 });
417 output.push(result);
418 }
419
420 TokenStream::from_iter(output)
421}
422
423struct SwDefine {
424 items: Vec<String>,
425}
426
427impl Parse for SwDefine {
428 fn parse(input: ParseStream) -> syn::Result<Self> {
429 let items = input.parse_terminated(<LitStr as Parse>::parse, Token![,])?;
430 Ok(Self {
431 items: items.into_iter().map(|e| e.value()).collect(),
432 })
433 }
434}
435
436#[proc_macro]
437pub fn sw_init(input: TokenStream) -> TokenStream {
438 let SwDefine { items } = parse_macro_input!(input as SwDefine);
439
440 let mut externs = Vec::new();
441
442 let span = proc_macro::Span::call_site();
443
444 for (ec_idx, struct_id) in items.into_iter().enumerate() {
445 let add_ne_extern_func =
448 syn::Ident::new(&format!("sw_add_ne_extern_func_{struct_id}"), span.into());
449 let double_extern_func =
450 syn::Ident::new(&format!("sw_double_extern_func_{struct_id}"), span.into());
451 let setup_extern_func =
452 syn::Ident::new(&format!("sw_setup_extern_func_{struct_id}"), span.into());
453
454 externs.push(quote::quote_spanned! { span.into() =>
455 #[no_mangle]
456 extern "C" fn #add_ne_extern_func(rd: usize, rs1: usize, rs2: usize) {
457 openvm::platform::custom_insn_r!(
458 opcode = OPCODE,
459 funct3 = SW_FUNCT3 as usize,
460 funct7 = SwBaseFunct7::SwAddNe as usize + #ec_idx
461 * (SwBaseFunct7::SHORT_WEIERSTRASS_MAX_KINDS as usize),
462 rd = In rd,
463 rs1 = In rs1,
464 rs2 = In rs2
465 );
466 }
467
468 #[no_mangle]
469 extern "C" fn #double_extern_func(rd: usize, rs1: usize) {
470 openvm::platform::custom_insn_r!(
471 opcode = OPCODE,
472 funct3 = SW_FUNCT3 as usize,
473 funct7 = SwBaseFunct7::SwDouble as usize + #ec_idx
474 * (SwBaseFunct7::SHORT_WEIERSTRASS_MAX_KINDS as usize),
475 rd = In rd,
476 rs1 = In rs1,
477 rs2 = Const "x0"
478 );
479 }
480
481 #[no_mangle]
482 extern "C" fn #setup_extern_func(uninit: *mut core::ffi::c_void, p1: *const u8, p2: *const u8) {
483 #[cfg(target_os = "zkvm")]
484 {
485 openvm::platform::custom_insn_r!(
486 opcode = ::openvm_ecc_guest::OPCODE,
487 funct3 = ::openvm_ecc_guest::SW_FUNCT3 as usize,
488 funct7 = ::openvm_ecc_guest::SwBaseFunct7::SwSetup as usize
489 + #ec_idx
490 * (::openvm_ecc_guest::SwBaseFunct7::SHORT_WEIERSTRASS_MAX_KINDS as usize),
491 rd = In uninit,
492 rs1 = In p1,
493 rs2 = In p2
494 );
495 openvm::platform::custom_insn_r!(
496 opcode = ::openvm_ecc_guest::OPCODE,
497 funct3 = ::openvm_ecc_guest::SW_FUNCT3 as usize,
498 funct7 = ::openvm_ecc_guest::SwBaseFunct7::SwSetup as usize
499 + #ec_idx
500 * (::openvm_ecc_guest::SwBaseFunct7::SHORT_WEIERSTRASS_MAX_KINDS as usize),
501 rd = In uninit,
502 rs1 = In p1,
503 rs2 = Const "x0" );
505
506
507 }
508 }
509 });
510 }
511
512 TokenStream::from(quote::quote_spanned! { span.into() =>
513 #[allow(non_snake_case)]
514 #[cfg(target_os = "zkvm")]
515 mod openvm_intrinsics_ffi_2 {
516 use ::openvm_ecc_guest::{OPCODE, SW_FUNCT3, SwBaseFunct7};
517
518 #(#externs)*
519 }
520 })
521}