Skip to main content

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}