sp1_lib/utils.rs
1pub trait AffinePoint<const N: usize>: Clone + Sized {
2 /// The generator.
3 #[deprecated = "This const will have the `Self` type in the next major version."]
4 const GENERATOR: [u64; N];
5
6 const GENERATOR_T: Self;
7
8 /// Creates a new [`AffinePoint`] from the given limbs.
9 /// This function does not check that the inputs form a valid point. Only use this function
10 /// when the input is either trusted or previously validated.
11 fn new(limbs: [u64; N]) -> Self;
12
13 /// Creates a new [`AffinePoint`] that corresponds to the identity point.
14 fn identity() -> Self;
15
16 /// Returns a reference to the limbs.
17 fn limbs_ref(&self) -> &[u64; N];
18
19 /// Returns a mutable reference to the limbs. If the point is the infinity point, this will
20 /// panic.
21 fn limbs_mut(&mut self) -> &mut [u64; N];
22
23 fn is_identity(&self) -> bool;
24
25 /// Creates a new [`AffinePoint`] from the given x and y coordinates.
26 ///
27 /// The bytes are the concatenated little endian representations of the coordinates.
28 fn from(x: &[u8], y: &[u8]) -> Self {
29 debug_assert!(x.len() == N * 4);
30 debug_assert!(y.len() == N * 4);
31
32 let mut limbs = [0u64; N];
33 let x = bytes_to_words_le(x);
34 let y = bytes_to_words_le(y);
35
36 debug_assert!(x.len() == N / 2);
37 debug_assert!(y.len() == N / 2);
38
39 limbs[..(N / 2)].copy_from_slice(&x);
40 limbs[(N / 2)..].copy_from_slice(&y);
41 Self::new(limbs)
42 }
43
44 /// Creates a new [`AffinePoint`] from the given bytes in little endian.
45 /// This function does not check that the inputs form a valid point. Only use this function
46 /// when the input is either trusted or previously validated.
47 fn from_le_bytes(bytes: &[u8]) -> Self {
48 let limbs = bytes_to_words_le(bytes);
49 debug_assert!(limbs.len() == N);
50 Self::new(limbs.try_into().unwrap())
51 }
52
53 /// Creates a new [`AffinePoint`] from the given bytes in big endian.
54 fn to_le_bytes(&self) -> Vec<u8> {
55 let le_bytes = words_to_bytes_le(self.limbs_ref());
56 debug_assert!(le_bytes.len() == N * 8);
57 le_bytes
58 }
59
60 /// Adds the given [`AffinePoint`] to `self`.
61 fn add_assign(&mut self, other: &Self);
62
63 /// Adds the given [`AffinePoint`] to `self`. Can be optionally overridden to use a different
64 /// implementation of addition in multi-scalar multiplication, which is used in secp256k1
65 /// recovery.
66 fn complete_add_assign(&mut self, other: &Self) {
67 self.add_assign(other);
68 }
69
70 /// Doubles `self`.
71 fn double(&mut self);
72
73 /// Multiplies `self` by the given scalar.
74 fn mul_assign(&mut self, scalar: &[u64]) {
75 debug_assert_eq!(scalar.len(), N / 2);
76
77 let mut res: Self = Self::identity();
78 let mut temp = self.clone();
79
80 for &words in scalar.iter() {
81 for i in 0..u64::BITS {
82 if (words >> i) & 1 == 1 {
83 res.complete_add_assign(&temp);
84 }
85 temp.double();
86 }
87 }
88
89 *self = res;
90 }
91
92 /// Performs multi-scalar multiplication (MSM) on slices of bit vectors and points. Note:
93 /// a_bits_le and b_bits_le should be in little endian order.
94 fn multi_scalar_multiplication(
95 a_bits_le: &[bool],
96 a: Self,
97 b_bits_le: &[bool],
98 b: Self,
99 ) -> Self {
100 // The length of the bit vectors must be the same.
101 debug_assert!(a_bits_le.len() == b_bits_le.len());
102
103 let mut res: Self = Self::identity();
104 let mut temp_a = a.clone();
105 let mut temp_b = b.clone();
106 for (a_bit, b_bit) in a_bits_le.iter().zip(b_bits_le.iter()) {
107 if *a_bit {
108 res.complete_add_assign(&temp_a);
109 }
110 if *b_bit {
111 res.complete_add_assign(&temp_b);
112 }
113 temp_a.double();
114 temp_b.double();
115 }
116 res
117 }
118}
119
120/// Errors that can occur during scalar multiplication of an [`AffinePoint`].
121#[derive(Debug)]
122pub enum MulAssignError {
123 ScalarIsZero,
124}
125
126/// Converts a slice of words to a byte array in little endian.
127pub fn words_to_bytes_le(words: &[u64]) -> Vec<u8> {
128 words.iter().flat_map(|word| word.to_le_bytes()).collect::<Vec<_>>()
129}
130
131/// Converts a byte array in little endian to a slice of words.
132pub fn bytes_to_words_le(bytes: &[u8]) -> Vec<u64> {
133 bytes
134 .chunks_exact(8)
135 .map(|chunk| u64::from_le_bytes(chunk.try_into().unwrap()))
136 .collect::<Vec<_>>()
137}
138
139#[derive(Copy, Clone, Debug)]
140/// A representation of a point on a Weierstrass curve.
141pub enum WeierstrassPoint<const N: usize> {
142 Infinity,
143 Affine([u64; N]),
144}
145
146/// A trait for affine points on Weierstrass curves.
147pub trait WeierstrassAffinePoint<const N: usize>: AffinePoint<N> {
148 /// The infinity point representation of the Weierstrass curve. Typically an enum variant.
149 fn infinity() -> Self;
150
151 /// Returns true if the point is the infinity point.
152 fn is_infinity(&self) -> bool;
153
154 /// Performs the complete addition of two [`AffinePoint`]'s on a Weierstrass curve.
155 /// For an addition of two points P1 and P2, the cases are:
156 /// 1. P1 is infinity
157 /// 2. P2 is infinity
158 /// 3. P1 equals P2
159 /// 4. P1 is the negation of P2
160 /// 5. Default addition.
161 ///
162 /// Implements the complete addition cases according to the
163 /// [Zcash complete addition spec](https://zcash.github.io/halo2/design/gadgets/ecc/addition.html#complete-addition).
164 fn weierstrass_add_assign(&mut self, other: &Self) {
165 // Case 1: p1 is infinity.
166 if self.is_infinity() {
167 *self = other.clone();
168 return;
169 }
170
171 // Case 2: p2 is infinity.
172 if other.is_infinity() {
173 return;
174 }
175
176 // Once it's known the points are not infinity, their limbs can be safely used.
177 let p1 = self.limbs_mut();
178 let p2 = other.limbs_ref();
179
180 // Case 3: p1 equals p2.
181 if p1 == p2 {
182 self.double();
183 return;
184 }
185
186 // Case 4: p1 is the negation of p2.
187 // Note: If p1 and p2 are valid elliptic curve points, and p1.x == p2.x, that means that
188 // either p1.y == p2.y or p1.y + p2.y == p. Because we are past Case 4, we know that p1.y !=
189 // p2.y, so we can just check if p1.x == p2.x. Therefore, this implicitly checks that
190 // p1.x == p2.x AND p1.y + p2.y == p without modular negation.
191 if p1[..N / 2] == p2[..N / 2] {
192 *self = Self::infinity();
193 return;
194 }
195
196 // Case 5: Default addition.
197 self.add_assign(other);
198 }
199}