1use binius_core::word::Word;
4use binius_frontend::{CircuitBuilder, Wire};
5
6use super::{
7 common::{coord_b, coord_beta, coord_field, coord_zero, pow_sqrt, scalar_field},
8 point::Secp256k1Affine,
9};
10use crate::bignum::{
11 BigUint, BigUintModPowHint, PseudoMersennePrimeField, assert_eq, biguint_eq, select,
12};
13
14pub struct Secp256k1 {
17 f_p: PseudoMersennePrimeField,
18 f_scalar: PseudoMersennePrimeField,
19 b: BigUint,
20}
21
22impl Secp256k1 {
23 pub fn new(b: &CircuitBuilder) -> Self {
26 let f_p = coord_field(b);
27 let f_scalar = scalar_field(b);
28 let b = coord_b(b);
29 Self { f_p, f_scalar, b }
30 }
31
32 pub const fn f_p(&self) -> &PseudoMersennePrimeField {
34 &self.f_p
35 }
36
37 pub const fn f_scalar(&self) -> &PseudoMersennePrimeField {
39 &self.f_scalar
40 }
41
42 pub fn assert_on_curve(&self, b: &CircuitBuilder, p: &Secp256k1Affine) {
46 let f_p = &self.f_p;
47
48 let x_pow2 = f_p.square(b, &p.x);
49 let x_pow3 = f_p.mul(b, &x_pow2, &p.x);
50
51 let y_pow2 = f_p.square(b, &p.y);
52 assert_eq(b, "secp256k1_on_curve", &y_pow2, &f_p.add(b, &x_pow3, &self.b));
53
54 b.assert_false("not_point_at_infinity", p.is_point_at_infinity);
55 }
56
57 pub fn recover(&self, b: &CircuitBuilder, r: &BigUint, recid_odd: Wire) -> Secp256k1Affine {
63 let f_p = self.f_p();
64
65 let y_squared = f_p.add(b, &f_p.mul(b, &f_p.square(b, r), r), &self.b);
67
68 let res_1_limbs =
70 BigUintModPowHint::call(b, &y_squared.limbs, &pow_sqrt(b).limbs, &f_p.modulus().limbs);
71 let res_1 = BigUint { limbs: res_1_limbs };
72 let res_2 = f_p.sub(b, &coord_zero(b), &res_1);
73
74 let res_1_low_limb = *res_1.limbs.first().expect("N_LIMBS > 0");
76 let odd_1 = b.shl(res_1_low_limb, (Word::BITS - 1) as u32);
77 let is_res_2 = b.bxor(odd_1, recid_odd);
78
79 let y = select(b, is_res_2, &res_2, &res_1);
80 let is_valid_point = biguint_eq(b, &f_p.square(b, &y), &y_squared);
81
82 Secp256k1Affine {
83 x: r.clone(),
84 y,
85 is_point_at_infinity: b.bnot(is_valid_point),
86 }
87 }
88
89 pub fn negate_if(
91 &self,
92 b: &CircuitBuilder,
93 cond: Wire,
94 p: &Secp256k1Affine,
95 ) -> Secp256k1Affine {
96 let neg_y = self.f_p.sub(b, &coord_zero(b), &p.y);
97 let y = select(b, cond, &neg_y, &p.y);
98 Secp256k1Affine {
99 x: p.x.clone(),
100 y,
101 is_point_at_infinity: p.is_point_at_infinity,
102 }
103 }
104
105 pub fn endomorphism(&self, b: &CircuitBuilder, p: &Secp256k1Affine) -> Secp256k1Affine {
107 let x = self.f_p.mul(b, &coord_beta(b), &p.x);
108 Secp256k1Affine {
109 x,
110 y: p.y.clone(),
111 is_point_at_infinity: p.is_point_at_infinity,
112 }
113 }
114
115 pub fn add(
122 &self,
123 b: &CircuitBuilder,
124 p1: &Secp256k1Affine,
125 p2: &Secp256k1Affine,
126 ) -> Secp256k1Affine {
127 let (addition_slope, x_diff_zero, y_diff_zero) = self.addition_slope(b, p1, p2);
128 let doubling_slope = self.doubling_slope(b, p1);
129
130 let pai_1 = p1.is_point_at_infinity;
131 let pai_2 = p2.is_point_at_infinity;
132
133 let slope = select(b, x_diff_zero, &doubling_slope, &addition_slope);
134 let (add_x, add_y) = self.sloped_add(b, &slope, p1, p2);
135
136 let x = select(b, pai_1, &p2.x, &select(b, pai_2, &p1.x, &add_x));
137 let y = select(b, pai_1, &p2.y, &select(b, pai_2, &p1.y, &add_y));
138
139 let pai_sum = b.band(x_diff_zero, b.bnot(y_diff_zero)); let is_point_at_infinity = b.select(pai_1, pai_2, b.select(pai_2, pai_1, pai_sum));
141
142 Secp256k1Affine {
143 x,
144 y,
145 is_point_at_infinity,
146 }
147 }
148
149 pub fn add_incomplete(
156 &self,
157 b: &CircuitBuilder,
158 p1: &Secp256k1Affine,
159 p2: &Secp256k1Affine,
160 ) -> Secp256k1Affine {
161 let (slope, x_diff_zero, y_diff_zero) = self.addition_slope(b, p1, p2);
162
163 let pai_1 = p1.is_point_at_infinity;
164 let pai_2 = p2.is_point_at_infinity;
165 let any_pai = b.bor(pai_1, pai_2);
166
167 let (add_x, add_y) = self.sloped_add(b, &slope, p1, p2);
168 let x = select(b, pai_1, &p2.x, &select(b, pai_2, &p1.x, &add_x));
169 let y = select(b, pai_1, &p2.y, &select(b, pai_2, &p1.y, &add_y));
170
171 let pai_sum = b.band(x_diff_zero, b.bnot(y_diff_zero)); let is_point_at_infinity = b.select(pai_1, pai_2, b.select(pai_2, pai_1, pai_sum));
173
174 b.assert_false("not_doubling", b.band(b.bnot(any_pai), b.band(x_diff_zero, y_diff_zero)));
175
176 Secp256k1Affine {
177 x,
178 y,
179 is_point_at_infinity,
180 }
181 }
182
183 pub fn double(&self, b: &CircuitBuilder, p: &Secp256k1Affine) -> Secp256k1Affine {
187 let slope = self.doubling_slope(b, p);
188 let (x, y) = self.sloped_add(b, &slope, p, p);
189 let is_point_at_infinity = p.is_point_at_infinity;
192 Secp256k1Affine {
193 x,
194 y,
195 is_point_at_infinity,
196 }
197 }
198
199 fn addition_slope(
200 &self,
201 b: &CircuitBuilder,
202 p1: &Secp256k1Affine,
203 p2: &Secp256k1Affine,
204 ) -> (BigUint, Wire, Wire) {
205 let f_p = &self.f_p;
206 let y_diff = f_p.sub(b, &p2.y, &p1.y);
208 let x_diff = f_p.sub(b, &p2.x, &p1.x);
209 let y_diff_zero = y_diff.is_zero(b);
210 let x_diff_zero = x_diff.is_zero(b);
211 let slope = f_p.div(b, &y_diff, &x_diff, b.bnot(x_diff_zero));
213 (slope, x_diff_zero, y_diff_zero)
214 }
215
216 fn doubling_slope(&self, b: &CircuitBuilder, p: &Secp256k1Affine) -> BigUint {
217 let f_p = &self.f_p;
218 let x_sqr = f_p.square(b, &p.x);
220 let x_sqr_by_3 = f_p.add(b, &f_p.add(b, &x_sqr, &x_sqr), &x_sqr);
221 let y_zero = p.y.is_zero(b);
223 let y_by_2 = f_p.add(b, &p.y, &p.y);
224 f_p.div(b, &x_sqr_by_3, &y_by_2, b.bnot(y_zero))
226 }
227
228 fn sloped_add(
229 &self,
230 b: &CircuitBuilder,
231 slope: &BigUint,
232 p1: &Secp256k1Affine,
233 p2: &Secp256k1Affine,
234 ) -> (BigUint, BigUint) {
235 let f_p = &self.f_p;
236 let slope_sqr = f_p.square(b, slope);
239 let x = f_p.sub(b, &f_p.sub(b, &slope_sqr, &p1.x), &p2.x);
240 let y = f_p.sub(b, &f_p.mul(b, slope, &f_p.sub(b, &p1.x, &x)), &p1.y);
241 (x, y)
242 }
243}