openvm_ecc_guest/
weierstrass.rs1use alloc::vec::Vec;
2use core::ops::{AddAssign, Mul};
3
4use openvm_algebra_guest::{Field, IntMod};
5
6use super::group::Group;
7
8pub trait WeierstrassPoint: Sized {
10 const CURVE_A: Self::Coordinate;
12 const CURVE_B: Self::Coordinate;
14 const IDENTITY: Self;
15
16 type Coordinate: Field;
17
18 fn as_le_bytes(&self) -> &[u8];
24
25 fn from_xy_unchecked(x: Self::Coordinate, y: Self::Coordinate) -> Self;
27 fn into_coords(self) -> (Self::Coordinate, Self::Coordinate);
28 fn x(&self) -> &Self::Coordinate;
29 fn y(&self) -> &Self::Coordinate;
30 fn x_mut(&mut self) -> &mut Self::Coordinate;
31 fn y_mut(&mut self) -> &mut Self::Coordinate;
32
33 fn add_ne_nonidentity(&self, p2: &Self) -> Self;
35 fn add_ne_assign_nonidentity(&mut self, p2: &Self);
37 fn sub_ne_nonidentity(&self, p2: &Self) -> Self;
39 fn sub_ne_assign_nonidentity(&mut self, p2: &Self);
41 fn double_nonidentity(&self) -> Self;
43 fn double_assign_nonidentity(&mut self);
45
46 fn from_xy(x: Self::Coordinate, y: Self::Coordinate) -> Option<Self>
47 where
48 for<'a> &'a Self::Coordinate: Mul<&'a Self::Coordinate, Output = Self::Coordinate>,
49 {
50 if x == Self::Coordinate::ZERO && y == Self::Coordinate::ZERO {
51 Some(Self::IDENTITY)
52 } else {
53 Self::from_xy_nonidentity(x, y)
54 }
55 }
56
57 fn from_xy_nonidentity(x: Self::Coordinate, y: Self::Coordinate) -> Option<Self>
58 where
59 for<'a> &'a Self::Coordinate: Mul<&'a Self::Coordinate, Output = Self::Coordinate>,
60 {
61 let lhs = &y * &y;
62 let rhs = &x * &x * &x + &Self::CURVE_A * &x + &Self::CURVE_B;
63 if lhs != rhs {
64 return None;
65 }
66 Some(Self::from_xy_unchecked(x, y))
67 }
68}
69
70pub struct DecompressionHint<T> {
75 pub possible: bool,
76 pub sqrt: T,
77}
78
79pub trait FromCompressed<Coordinate> {
80 fn decompress(x: Coordinate, rec_id: &u8) -> Option<Self>
88 where
89 Self: core::marker::Sized;
90
91 fn hint_decompress(x: &Coordinate, rec_id: &u8) -> Option<DecompressionHint<Coordinate>>;
102}
103
104pub trait IntrinsicCurve {
108 type Scalar: Clone;
109 type Point: Clone;
110
111 fn msm(coeffs: &[Self::Scalar], bases: &[Self::Point]) -> Self::Point;
115}
116
117pub struct CachedMulTable<'a, C: IntrinsicCurve> {
128 pub window_bits: usize,
131 pub bases: &'a [C::Point],
132 table: Vec<Vec<C::Point>>,
134 identity: C::Point,
136}
137
138impl<'a, C: IntrinsicCurve> CachedMulTable<'a, C>
139where
140 C::Point: WeierstrassPoint + Group,
141 C::Scalar: IntMod,
142{
143 pub fn new_with_prime_order(bases: &'a [C::Point], window_bits: usize) -> Self {
148 assert!(window_bits > 0);
149 let window_size = 1 << window_bits;
150 let table = bases
151 .iter()
152 .map(|base| {
153 if base.is_identity() {
154 vec![<C::Point as Group>::IDENTITY; window_size - 2]
155 } else {
156 let mut multiples = Vec::with_capacity(window_size - 2);
157 for _ in 0..window_size - 2 {
158 let multiple = multiples
163 .last()
164 .map(|last| WeierstrassPoint::add_ne_nonidentity(last, base))
165 .unwrap_or_else(|| base.double_nonidentity());
166 multiples.push(multiple);
167 }
168 multiples
169 }
170 })
171 .collect();
172
173 Self {
174 window_bits,
175 bases,
176 table,
177 identity: <C::Point as Group>::IDENTITY,
178 }
179 }
180
181 fn get_multiple(&self, base_idx: usize, scalar: usize) -> &C::Point {
182 if scalar == 0 {
183 &self.identity
184 } else if scalar == 1 {
185 unsafe { self.bases.get_unchecked(base_idx) }
186 } else {
187 unsafe { self.table.get_unchecked(base_idx).get_unchecked(scalar - 2) }
188 }
189 }
190
191 pub fn windowed_mul(&self, scalars: &[C::Scalar]) -> C::Point {
196 assert_eq!(8 % self.window_bits, 0);
197 assert_eq!(scalars.len(), self.bases.len());
198 let windows_per_byte = 8 / self.window_bits;
199
200 let num_windows = C::Scalar::NUM_LIMBS * windows_per_byte;
201 let mask = (1u8 << self.window_bits) - 1;
202
203 let mut limb_idx = C::Scalar::NUM_LIMBS;
206 let mut bit_idx = 0;
210
211 let mut res = <C::Point as Group>::IDENTITY;
212 for outer in 0..num_windows {
213 if bit_idx == 0 {
214 limb_idx -= 1;
215 bit_idx = 8 - self.window_bits;
216 } else {
217 bit_idx -= self.window_bits;
218 }
219
220 if outer != 0 {
221 for _ in 0..self.window_bits {
222 res.double_assign();
224 }
225 }
226 for (base_idx, scalar) in scalars.iter().enumerate() {
227 let scalar = (scalar.as_le_bytes()[limb_idx] >> bit_idx) & mask;
228 let summand = self.get_multiple(base_idx, scalar as usize);
229 res.add_assign(summand);
231 }
232 }
233 res
234 }
235}
236
237#[macro_export]
249macro_rules! impl_sw_affine {
250 ($struct_name:ident, $field:ty, $three:expr, $b:expr) => {
253 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
256 #[repr(transparent)]
257 pub struct $struct_name(AffinePoint<$field>);
258
259 impl $struct_name {
260 pub const fn new(x: $field, y: $field) -> Self {
261 Self(AffinePoint::new(x, y))
262 }
263 }
264
265 impl WeierstrassPoint for $struct_name {
266 const CURVE_A: $field = <$field>::ZERO;
267 const CURVE_B: $field = $b;
268 const IDENTITY: Self = Self(AffinePoint::new(<$field>::ZERO, <$field>::ZERO));
269
270 type Coordinate = $field;
271
272 fn as_le_bytes(&self) -> &[u8] {
274 unsafe {
275 &*core::ptr::slice_from_raw_parts(
276 self as *const Self as *const u8,
277 core::mem::size_of::<Self>(),
278 )
279 }
280 }
281 fn from_xy_unchecked(x: Self::Coordinate, y: Self::Coordinate) -> Self {
282 Self(AffinePoint::new(x, y))
283 }
284 fn into_coords(self) -> (Self::Coordinate, Self::Coordinate) {
285 (self.0.x, self.0.y)
286 }
287 fn x(&self) -> &Self::Coordinate {
288 &self.0.x
289 }
290 fn y(&self) -> &Self::Coordinate {
291 &self.0.y
292 }
293 fn x_mut(&mut self) -> &mut Self::Coordinate {
294 &mut self.0.x
295 }
296 fn y_mut(&mut self) -> &mut Self::Coordinate {
297 &mut self.0.y
298 }
299
300 fn double_nonidentity(&self) -> Self {
301 use ::openvm_algebra_guest::DivUnsafe;
302 let lambda = (&THREE * self.x() * self.x()).div_unsafe(self.y() + self.y());
305 let x3 = &lambda * &lambda - self.x() - self.x();
307 let y3 = lambda * (self.x() - &x3) - self.y();
309 Self(AffinePoint::new(x3, y3))
310 }
311
312 fn double_assign_nonidentity(&mut self) {
313 *self = self.double_nonidentity();
314 }
315
316 fn add_ne_nonidentity(&self, p2: &Self) -> Self {
317 use ::openvm_algebra_guest::DivUnsafe;
318 let lambda = (p2.y() - self.y()).div_unsafe(p2.x() - self.x());
322 let x3 = &lambda * &lambda - self.x() - p2.x();
323 let y3 = lambda * (self.x() - &x3) - self.y();
324 Self(AffinePoint::new(x3, y3))
325 }
326
327 fn add_ne_assign_nonidentity(&mut self, p2: &Self) {
328 *self = self.add_ne_nonidentity(p2);
329 }
330
331 fn sub_ne_nonidentity(&self, p2: &Self) -> Self {
332 use ::openvm_algebra_guest::DivUnsafe;
333 let lambda = (p2.y() + self.y()).div_unsafe(self.x() - p2.x());
337 let x3 = &lambda * &lambda - self.x() - p2.x();
338 let y3 = lambda * (self.x() - &x3) - self.y();
339 Self(AffinePoint::new(x3, y3))
340 }
341
342 fn sub_ne_assign_nonidentity(&mut self, p2: &Self) {
343 *self = self.sub_ne_nonidentity(p2);
344 }
345 }
346
347 impl core::ops::Neg for $struct_name {
348 type Output = Self;
349
350 fn neg(mut self) -> Self::Output {
351 self.0.y.neg_assign();
352 self
353 }
354 }
355
356 impl core::ops::Neg for &$struct_name {
357 type Output = $struct_name;
358
359 fn neg(self) -> Self::Output {
360 self.clone().neg()
361 }
362 }
363
364 impl From<$struct_name> for AffinePoint<$field> {
365 fn from(value: $struct_name) -> Self {
366 value.0
367 }
368 }
369
370 impl From<AffinePoint<$field>> for $struct_name {
371 fn from(value: AffinePoint<$field>) -> Self {
372 Self(value)
373 }
374 }
375 };
376}
377
378#[macro_export]
381macro_rules! impl_sw_group_ops {
382 ($struct_name:ident, $field:ty) => {
383 impl Group for $struct_name {
384 type SelfRef<'a> = &'a Self;
385
386 const IDENTITY: Self = <Self as WeierstrassPoint>::IDENTITY;
387
388 fn double(&self) -> Self {
389 if self.is_identity() {
390 self.clone()
391 } else {
392 self.double_nonidentity()
393 }
394 }
395
396 fn double_assign(&mut self) {
397 if !self.is_identity() {
398 self.double_assign_nonidentity();
399 }
400 }
401 }
402
403 impl core::ops::Add<&$struct_name> for $struct_name {
404 type Output = Self;
405
406 fn add(mut self, p2: &$struct_name) -> Self::Output {
407 use core::ops::AddAssign;
408 self.add_assign(p2);
409 self
410 }
411 }
412
413 impl core::ops::Add for $struct_name {
414 type Output = Self;
415
416 fn add(self, rhs: Self) -> Self::Output {
417 self.add(&rhs)
418 }
419 }
420
421 impl core::ops::Add<&$struct_name> for &$struct_name {
422 type Output = $struct_name;
423
424 fn add(self, p2: &$struct_name) -> Self::Output {
425 if self.is_identity() {
426 p2.clone()
427 } else if p2.is_identity() {
428 self.clone()
429 } else if self.x() == p2.x() {
430 if self.y() + p2.y() == <$field as openvm_algebra_guest::Field>::ZERO {
431 <$struct_name as WeierstrassPoint>::IDENTITY
432 } else {
433 self.double_nonidentity()
434 }
435 } else {
436 self.add_ne_nonidentity(p2)
437 }
438 }
439 }
440
441 impl core::ops::AddAssign<&$struct_name> for $struct_name {
442 fn add_assign(&mut self, p2: &$struct_name) {
443 if self.is_identity() {
444 *self = p2.clone();
445 } else if p2.is_identity() {
446 } else if self.x() == p2.x() {
448 if self.y() + p2.y() == <$field as openvm_algebra_guest::Field>::ZERO {
449 *self = <$struct_name as WeierstrassPoint>::IDENTITY;
450 } else {
451 self.double_assign_nonidentity();
452 }
453 } else {
454 self.add_ne_assign_nonidentity(p2);
455 }
456 }
457 }
458
459 impl core::ops::AddAssign for $struct_name {
460 fn add_assign(&mut self, rhs: Self) {
461 self.add_assign(&rhs);
462 }
463 }
464
465 impl core::ops::Sub<&$struct_name> for $struct_name {
466 type Output = Self;
467
468 fn sub(self, rhs: &$struct_name) -> Self::Output {
469 core::ops::Sub::sub(&self, rhs)
470 }
471 }
472
473 impl core::ops::Sub for $struct_name {
474 type Output = $struct_name;
475
476 fn sub(self, rhs: Self) -> Self::Output {
477 self.sub(&rhs)
478 }
479 }
480
481 impl core::ops::Sub<&$struct_name> for &$struct_name {
482 type Output = $struct_name;
483
484 fn sub(self, p2: &$struct_name) -> Self::Output {
485 if p2.is_identity() {
486 self.clone()
487 } else if self.is_identity() {
488 core::ops::Neg::neg(p2)
489 } else if self.x() == p2.x() {
490 if self.y() == p2.y() {
491 <$struct_name as WeierstrassPoint>::IDENTITY
492 } else {
493 self.double_nonidentity()
494 }
495 } else {
496 self.sub_ne_nonidentity(p2)
497 }
498 }
499 }
500
501 impl core::ops::SubAssign<&$struct_name> for $struct_name {
502 fn sub_assign(&mut self, p2: &$struct_name) {
503 if p2.is_identity() {
504 } else if self.is_identity() {
506 *self = core::ops::Neg::neg(p2);
507 } else if self.x() == p2.x() {
508 if self.y() == p2.y() {
509 *self = <$struct_name as WeierstrassPoint>::IDENTITY
510 } else {
511 self.double_assign_nonidentity();
512 }
513 } else {
514 self.sub_ne_assign_nonidentity(p2);
515 }
516 }
517 }
518
519 impl core::ops::SubAssign for $struct_name {
520 fn sub_assign(&mut self, rhs: Self) {
521 self.sub_assign(&rhs);
522 }
523 }
524 };
525}