1use crate::{
4 circuit::{bool::CBool, cs::{CS, RCS}, mux::c_mux3, num::CNum},
5 core::signal::Signal,
6 ff_uint::Num,
7 native::ecc::{EdwardsPoint, EdwardsPointEx, JubJubParams, MontgomeryPoint},
8};
9
10#[derive(Clone, Signal)]
11#[Value = "EdwardsPoint<C::Fr>"]
12pub struct CEdwardsPoint<C: CS> {
13 pub x: CNum<C>,
14 pub y: CNum<C>,
15}
16
17#[derive(Clone, Signal)]
18#[Value = "MontgomeryPoint<C::Fr>"]
19pub struct CMontgomeryPoint<C: CS> {
20 pub x: CNum<C>,
21 pub y: CNum<C>,
22}
23
24impl<C: CS> CEdwardsPoint<C> {
25 pub fn double<J: JubJubParams<Fr = C::Fr>>(&self, params: &J) -> Self {
26 let v = &self.x * &self.y;
27 let v2 = v.square();
28 let u = (&self.x + &self.y).square();
29 Self {
30 x: (&v * Num::from(2)).div_unchecked(&(Num::ONE + params.edwards_d() * &v2)),
31 y: (&u - &v * Num::from(2)).div_unchecked(&(Num::ONE - params.edwards_d() * &v2)),
32 }
33 }
34
35 pub fn mul_by_cofactor<J: JubJubParams<Fr = C::Fr>>(&self, params: &J) -> Self {
36 self.double(params).double(params).double(params)
37 }
38
39 pub fn add<J: JubJubParams<Fr = C::Fr>>(&self, p: &Self, params: &J) -> Self {
40 let v1 = &self.x * &p.y;
41 let v2 = &p.x * &self.y;
42 let v12 = &v1 * &v2;
43 let u = (&self.x + &self.y) * (&p.x + &p.y);
44 Self {
45 x: (&v1 + &v2).div_unchecked(&(Num::ONE + params.edwards_d() * &v12)),
46 y: (&u - &v1 - &v2).div_unchecked(&(Num::ONE - params.edwards_d() * &v12)),
47 }
48 }
49
50 pub fn assert_in_curve<J: JubJubParams<Fr = C::Fr>>(&self, params: &J) {
51 let x2 = self.x.square();
52 let y2 = self.y.square();
53
54 (params.edwards_d() * &x2 * &y2).assert_eq(&(&y2 - &x2 - Num::ONE));
55 }
56
57 pub fn assert_in_subgroup<J: JubJubParams<Fr = C::Fr>>(&self, params: &J) {
58 let preimage_value = self
59 .get_value()
60 .map(|p| p.mul(Num::from(8).checked_inv().unwrap(), params));
61 let preimage = self.derive_alloc::<Self>(preimage_value.as_ref());
62 preimage.assert_in_curve(params);
63 let preimage8 = preimage.mul_by_cofactor(params);
64
65 (&self.x - &preimage8.x).assert_zero();
66 (&self.y - &preimage8.y).assert_zero();
67 }
68
69 pub fn subgroup_decompress<J: JubJubParams<Fr = C::Fr>>(x: &CNum<C>, params: &J) -> Self {
70 let preimage_value = x.get_value().map(|x| {
71 EdwardsPoint::subgroup_decompress(x, params)
72 .unwrap_or(params.edwards_g().clone())
73 .mul(Num::from(8).checked_inv().unwrap(), params)
74 });
75 let preimage = CEdwardsPoint::alloc(x.get_cs(), preimage_value.as_ref());
76 preimage.assert_in_curve(params);
77 let preimage8 = preimage.mul_by_cofactor(params);
78 (x - &preimage8.x).assert_zero();
79 preimage8
80 }
81
82 pub fn into_montgomery(&self) -> CMontgomeryPoint<C> {
84 let x = (Num::ONE + &self.y).div_unchecked(&(Num::ONE - &self.y));
85 let y = x.div_unchecked(&self.x);
86 CMontgomeryPoint { x, y }
87 }
88
89 pub fn mul<J: JubJubParams<Fr = C::Fr>>(&self, bits: &[CBool<C>], params: &J) -> Self {
91 fn gen_table<C: CS, J: JubJubParams<Fr = C::Fr>>(
92 p: &EdwardsPointEx<C::Fr>,
93 params: &J,
94 ) -> Vec<Vec<Num<C::Fr>>> {
95 let mut x_col = vec![];
96 let mut y_col = vec![];
97 let mut q = p.clone();
98 for _ in 0..8 {
99 let MontgomeryPoint { x, y } = q.into_montgomery().unwrap();
100 x_col.push(x);
101 y_col.push(y);
102 q = q.add(&p, params);
103 }
104 vec![x_col, y_col]
105 }
106 let cs = self.get_cs();
107
108 match self.as_const() {
109 Some(c_base) => {
110 let c_base = c_base.into_extended();
111 let mut base = c_base;
112 if base.is_zero() {
113 self.derive_const(&EdwardsPoint::zero())
114 } else {
115 let bits_len = bits.len();
116 let zeros_len = (2 * bits_len) % 3;
117 let zero_bits = vec![CBool::from_const(cs, &false); zeros_len];
118 let all_bits = [bits, &zero_bits].concat();
119
120 let all_bits_len = all_bits.len();
121 let nwindows = all_bits_len / 3;
122
123 let mut acc = EdwardsPoint {
124 x: Num::ZERO,
125 y: -Num::ONE,
126 }
127 .into_extended();
128
129 for _ in 0..nwindows {
130 acc = acc.add(&base, params);
131 base = base.double().double().double();
132 }
133
134 let mp = acc.negate().into_montgomery().unwrap();
135
136 let mut acc = CMontgomeryPoint::from_const(cs, &mp);
137 let mut base = c_base;
138
139 for i in 0..nwindows {
140 let table = gen_table::<C, J>(&base, params);
141 let res = c_mux3(&all_bits[3 * i..3 * (i + 1)], &table);
142 let p = CMontgomeryPoint {
143 x: res[0].clone(),
144 y: res[1].clone(),
145 };
146 acc = acc.add(&p, params);
147 base = base.double().double().double();
148 }
149
150 let res = acc.into_edwards();
151 CEdwardsPoint {
152 x: -res.x,
153 y: -res.y,
154 }
155 }
156 }
157 _ => {
158 let base_is_zero = self.x.is_zero();
159 let dummy_point = CEdwardsPoint::from_const(cs, params.edwards_g());
160 let base_point = dummy_point.switch(&base_is_zero, self);
161
162 let mut base_point = base_point.into_montgomery();
163
164 let mut exponents = vec![base_point.clone()];
165
166 for _ in 1..bits.len() {
167 base_point = base_point.double(params);
168 exponents.push(base_point.clone());
169 }
170
171 let empty_acc = CMontgomeryPoint {
172 x: CNum::from_const(cs, &Num::ZERO),
173 y: CNum::from_const(cs, &Num::ZERO),
174 };
175 let mut acc = empty_acc.clone();
176
177 for i in 0..bits.len() {
178 let inc_acc = acc.add(&exponents[i], params);
179 acc = inc_acc.switch(&bits[i], &acc);
180 }
181
182 acc = empty_acc.switch(&base_is_zero, &acc);
183
184 let res = acc.into_edwards();
185 CEdwardsPoint {
186 x: -res.x,
187 y: -res.y,
188 }
189 }
190 }
191 }
192
193 pub fn from_scalar<J: JubJubParams<Fr = C::Fr>>(t: &CNum<C>, params: &J) -> Self {
195 fn check_and_get_y<C: CS, J: JubJubParams<Fr = C::Fr>>(
196 x: &CNum<C>,
197 t: &CNum<C>,
198 params: &J,
199 ) -> (CBool<C>, CNum<C>) {
200 let g = (x.square() * (x + params.montgomery_a()) + x) / params.montgomery_b();
201
202 let y_value = g.get_value().map(|g| {
203 let _y = match g.sqrt() {
204 Some(g_sqrt) => g_sqrt,
205 _ => (g * params.montgomery_u()).sqrt().unwrap(),
206 };
207 let _t = t.get_value().unwrap();
208 if (_y*_t).is_even() {
209 _y
210 } else {
211 -_y
212 }
213 });
214
215
216 let y:CNum<C> = x.derive_alloc(y_value.as_ref());
217
218 (&y*t).assert_even();
219
220
221 let y2 = y.square();
222
223 let is_square = (&g - &y2).is_zero();
224 let isnot_square = (&g * params.montgomery_u() - &y2).is_zero();
225
226 (&is_square ^ &isnot_square).assert_const(&true);
227 (is_square, y)
228 }
229
230 let t2g1 = t.square() * params.montgomery_u();
231
232 let x3 = -Num::ONE / params.montgomery_a() * (&t2g1 + Num::ONE);
233 let x2 = x3.div_unchecked(&t2g1);
234
235 let (is_valid, y2) = check_and_get_y(&x2, &t, params);
236 let (_, y3) = check_and_get_y(&x3, &t, params);
237
238 let x = x2.switch(&is_valid, &x3);
239 let y = y2.switch(&is_valid, &y3);
240
241 CMontgomeryPoint { x, y }
242 .into_edwards()
243 .mul_by_cofactor(params)
244 }
245}
246
247impl<C: CS> CMontgomeryPoint<C> {
248 pub fn double<J: JubJubParams<Fr = C::Fr>>(&self, params: &J) -> Self {
250 let x2 = self.x.square();
251 let l = (Num::from(3) * &x2 + Num::from(2) * params.montgomery_a() * &self.x + Num::ONE)
252 .div_unchecked(&(Num::from(2) * params.montgomery_b() * &self.y));
253 let b_l2 = params.montgomery_b() * &l.square();
254 let a = params.montgomery_a();
255
256 Self {
257 x: &b_l2 - &a - Num::from(2) * &self.x,
258 y: l * (Num::from(3) * &self.x + a - &b_l2) - &self.y,
259 }
260 }
261
262 pub fn add<J: JubJubParams<Fr = C::Fr>>(&self, p: &Self, params: &J) -> Self {
264 let l = (&p.y - &self.y).div_unchecked(&(&p.x - &self.x));
265 let b_l2 = params.montgomery_b() * &l.square();
266 let a = params.montgomery_a();
267
268 Self {
269 x: &b_l2 - &a - &self.x - &p.x,
270 y: l * (Num::from(2) * &self.x + &p.x + a - &b_l2) - &self.y,
271 }
272 }
273
274 pub fn into_edwards(&self) -> CEdwardsPoint<C> {
276 let y_is_zero = self.y.is_zero();
277 CEdwardsPoint {
278 x: self.x.div_unchecked(&(&self.y + y_is_zero.to_num())),
279 y: (&self.x - Num::ONE).div_unchecked(&(&self.x + Num::ONE)),
280 }
281 }
282}
283