Line data Source code
1 : #include "./fd_bn254_field_inl.h"
2 :
3 : /* Compress/Decompress */
4 :
5 : uchar *
6 : fd_bn254_g1_compress( uchar out[32],
7 : uchar const in [64],
8 30099 : int big_endian ) {
9 30099 : fd_bn254_g1_t p[1] = { 0 };
10 30099 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_internal( p, in, big_endian ) ) ) {
11 0 : return NULL;
12 0 : }
13 30099 : int is_inf = fd_bn254_g1_is_zero( p );
14 30099 : int flag_inf = in[ big_endian ? 32 : 63 ] & FLAG_INF;
15 :
16 : /* Serialize compressed point:
17 : https://github.com/arkworks-rs/algebra/blob/v0.4.2/ec/src/models/short_weierstrass/mod.rs#L122
18 :
19 : 1. If the infinity flags is set, return point at infinity
20 : 2. Else, copy x and set neg_y flag */
21 :
22 30099 : if( FD_UNLIKELY( is_inf ) ) {
23 6 : fd_memset( out, 0, 32 );
24 : /* The infinity flag in the result is set iff the infinity flag is set in the Y coordinate */
25 6 : out[ big_endian ? 0 : 31 ] |= (uchar)flag_inf;
26 6 : return out;
27 6 : }
28 :
29 30093 : int is_neg = fd_bn254_fp_is_neg_nm( &p->Y );
30 30093 : fd_bn254_fp_tobytes_nm( out, &p->X, big_endian );
31 30093 : if( is_neg ) {
32 30024 : out[ big_endian ? 0 : 31 ] |= FLAG_NEG;
33 30024 : }
34 30093 : return out;
35 30099 : }
36 :
37 : uchar *
38 : fd_bn254_g1_decompress( uchar out[64],
39 : uchar const in [32],
40 30096 : int big_endian ) {
41 : /* Special case: all zeros in => all zeros out, no flags */
42 30096 : const uchar zero[32] = { 0 };
43 30096 : if( fd_memeq( in, zero, 32 ) ) {
44 3 : return fd_memset( out, 0, 64UL );
45 3 : }
46 :
47 30093 : fd_bn254_fp_t x_nm[1], x[1], x2[1], x3_plus_b[1], y[1];
48 30093 : int is_inf, is_neg;
49 30093 : if( FD_UNLIKELY( !fd_bn254_fp_frombytes_nm( x_nm, in, big_endian, &is_inf, &is_neg ) ) ) {
50 0 : return NULL;
51 0 : }
52 :
53 : /* Point at infinity.
54 : If the point at infinity flag is set (bit 6), return the point at
55 : infinity with no check on coords.
56 : https://github.com/arkworks-rs/algebra/blob/v0.4.2/ec/src/models/short_weierstrass/mod.rs#L156-L160
57 : */
58 30093 : if( is_inf ) {
59 0 : fd_memset( out, 0, 64UL );
60 : /* no flags */
61 0 : return out;
62 0 : }
63 :
64 30093 : fd_bn254_fp_to_mont( x, x_nm );
65 30093 : fd_bn254_fp_sqr( x2, x );
66 30093 : fd_bn254_fp_mul( x3_plus_b, x2, x );
67 30093 : fd_bn254_fp_add( x3_plus_b, x3_plus_b, fd_bn254_const_b_mont );
68 30093 : if( FD_UNLIKELY( !fd_bn254_fp_sqrt( y, x3_plus_b ) ) ) {
69 0 : return NULL;
70 0 : }
71 :
72 30093 : fd_bn254_fp_from_mont( y, y );
73 30093 : if( is_neg != fd_bn254_fp_is_neg_nm( y ) ) {
74 9 : fd_bn254_fp_neg_nm( y, y );
75 9 : }
76 :
77 30093 : fd_bn254_fp_tobytes_nm( out, x_nm, big_endian );
78 30093 : fd_bn254_fp_tobytes_nm( &out[32], y, big_endian );
79 : /* no flags */
80 30093 : return out;
81 30093 : }
82 :
83 : uchar *
84 : fd_bn254_g2_compress( uchar out[64],
85 : uchar const in[128],
86 30102 : int big_endian ) {
87 30102 : fd_bn254_g2_t p[1] = { 0 };
88 30102 : if( FD_UNLIKELY( !fd_bn254_g2_frombytes_internal( p, in, big_endian ) ) ) {
89 0 : return NULL;
90 0 : }
91 30102 : int is_inf = fd_bn254_g2_is_zero( p );
92 30102 : int flag_inf = in[ big_endian ? 64 : 127 ] & FLAG_INF;
93 :
94 : /* Serialize compressed point */
95 :
96 30102 : if( FD_UNLIKELY( is_inf ) ) {
97 9 : fd_memset( out, 0, 64 );
98 : /* The infinity flag in the result is set iff the infinity flag is set in the Y coordinate */
99 9 : out[ big_endian ? 0 : 63 ] |= (uchar)flag_inf;
100 9 : return out;
101 9 : }
102 :
103 : /* Serialize x coordinate. The flags are on the 2nd element.
104 : https://github.com/arkworks-rs/algebra/blob/v0.4.2/ff/src/fields/models/quadratic_extension.rs#L700-L702 */
105 30093 : int is_neg = fd_bn254_fp2_is_neg_nm( &p->Y );
106 30093 : fd_bn254_fp2_tobytes_nm( out, &p->X, big_endian );
107 30093 : if( is_neg ) {
108 30042 : out[ big_endian ? 0 : 63 ] |= FLAG_NEG;
109 30042 : }
110 30093 : return out;
111 30102 : }
112 :
113 : uchar *
114 : fd_bn254_g2_decompress( uchar out[128],
115 : uchar const in [64],
116 3099 : int big_endian ) {
117 : /* Special case: all zeros in => all zeros out, no flags */
118 3099 : const uchar zero[64] = { 0 };
119 3099 : if( fd_memeq( in, zero, 64 ) ) {
120 3 : return fd_memset( out, 0, 128UL );
121 3 : }
122 :
123 3096 : fd_bn254_fp2_t x_nm[1], x[1], x2[1], x3_plus_b[1], y[1];
124 3096 : int is_inf, is_neg;
125 3096 : if( FD_UNLIKELY( !fd_bn254_fp2_frombytes_nm( x_nm, in, big_endian, &is_inf, &is_neg ) ) ) {
126 0 : return NULL;
127 0 : }
128 :
129 : /* Point at infinity.
130 : If the point at infinity flag is set (bit 6), return the point at
131 : infinity with no check on coords.
132 : https://github.com/arkworks-rs/algebra/blob/v0.4.2/ec/src/models/short_weierstrass/mod.rs#L156-L160 */
133 3096 : if( is_inf ) {
134 3 : fd_memset( out, 0, 128UL );
135 : /* no flags */
136 3 : return out;
137 3 : }
138 :
139 3093 : fd_bn254_fp2_to_mont( x, x_nm );
140 3093 : fd_bn254_fp2_sqr( x2, x );
141 3093 : fd_bn254_fp2_mul( x3_plus_b, x2, x );
142 3093 : fd_bn254_fp2_add( x3_plus_b, x3_plus_b, fd_bn254_const_twist_b_mont );
143 3093 : if( FD_UNLIKELY( !fd_bn254_fp2_sqrt( y, x3_plus_b ) ) ) {
144 0 : return NULL;
145 0 : }
146 :
147 3093 : fd_bn254_fp2_from_mont( y, y );
148 3093 : if( is_neg != fd_bn254_fp2_is_neg_nm( y ) ) {
149 3060 : fd_bn254_fp2_neg_nm( y, y );
150 3060 : }
151 :
152 3093 : fd_bn254_fp2_tobytes_nm( out, x_nm, big_endian );
153 3093 : fd_bn254_fp2_tobytes_nm( &out[64], y, big_endian );
154 : /* no flags */
155 3093 : return out;
156 3093 : }
157 :
158 : /* Ops */
159 :
160 : int
161 : fd_bn254_g1_add_syscall( uchar out[64],
162 : uchar const in[],
163 : ulong in_sz,
164 30069 : int big_endian ) {
165 : /* Expected 128-byte input (2 points). Pad input with 0s (only big endian). */
166 30069 : if( FD_UNLIKELY( in_sz > 128UL ) ) {
167 0 : return -1;
168 0 : }
169 30069 : if( FD_UNLIKELY( !big_endian && in_sz != 128UL ) ) {
170 12 : return -1;
171 12 : }
172 30057 : uchar FD_ALIGNED buf[128] = { 0 };
173 30057 : fd_memcpy( buf, in, in_sz );
174 :
175 : /* Validate inputs */
176 30057 : fd_bn254_g1_t r[1], a[1], b[1];
177 30057 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( a, &buf[ 0], big_endian ) ) ) {
178 0 : return -1;
179 0 : }
180 30057 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( b, &buf[64], big_endian ) ) ) {
181 0 : return -1;
182 0 : }
183 :
184 : /* Compute point add and serialize result */
185 30057 : fd_bn254_g1_affine_add( r, a, b );
186 30057 : fd_bn254_g1_tobytes( out, r, big_endian );
187 30057 : return 0;
188 30057 : }
189 :
190 : int
191 : fd_bn254_g2_add_syscall( uchar out[128],
192 : uchar const in[],
193 : ulong in_sz,
194 30030 : int big_endian ) {
195 : /* Expected 256-byte input (2 points).
196 : https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/addition.rs#L234-L236 */
197 30030 : if( FD_UNLIKELY( in_sz != 256UL ) ) {
198 0 : return -1;
199 0 : }
200 30030 : uchar FD_ALIGNED buf[256] = { 0 };
201 30030 : fd_memcpy( buf, in, in_sz );
202 :
203 : /* Validate inputs (curve eq only, no subgroup)
204 : https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/addition.rs#L238-L250 */
205 30030 : fd_bn254_g2_t r[1], a[1], b[1];
206 30030 : if( FD_UNLIKELY( !fd_bn254_g2_frombytes_check_eq_only( a, &buf[ 0], big_endian ) ) ) {
207 0 : return -1;
208 0 : }
209 30030 : if( FD_UNLIKELY( !fd_bn254_g2_frombytes_check_eq_only( b, &buf[128], big_endian ) ) ) {
210 0 : return -1;
211 0 : }
212 :
213 : /* Compute point add and serialize result */
214 30030 : fd_bn254_g2_affine_add( r, a, b );
215 30030 : fd_bn254_g2_tobytes( out, r, big_endian );
216 30030 : return 0;
217 30030 : }
218 :
219 : int
220 : fd_bn254_g1_scalar_mul_syscall( uchar out[64],
221 : uchar const in[],
222 : ulong in_sz,
223 30132 : int big_endian ) {
224 : /* Expected 96-byte input (1 point + 1 scalar). Pad input with 0s (only big endian). */
225 30132 : if( FD_UNLIKELY( in_sz > 96UL ) ) {
226 0 : return -1;
227 0 : }
228 30132 : if( FD_UNLIKELY( !big_endian && in_sz != 96UL ) ) {
229 6 : return -1;
230 6 : }
231 30126 : uchar FD_ALIGNED buf[96] = { 0 };
232 30126 : fd_memcpy( buf, in, fd_ulong_min( in_sz, 96UL ) );
233 :
234 : /* Validate inputs */
235 30126 : fd_bn254_g1_t r[1], a[1];
236 30126 : fd_bn254_scalar_t s[1];
237 30126 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( a, &buf[ 0], big_endian ) ) ) {
238 0 : return -1;
239 0 : }
240 :
241 : /* Scalar is big endian and NOT validated
242 : https://github.com/anza-xyz/agave/blob/v1.18.6/sdk/program/src/alt_bn128/mod.rs#L211-L214 */
243 30126 : if( FD_BIG_ENDIAN_LIKELY( big_endian ) ) {
244 63 : fd_uint256_bswap( s, fd_type_pun_const( &buf[64] ) ); /* &buf[64] is always FD_ALIGNED */
245 30063 : } else {
246 30063 : memcpy( s, &buf[64], 32 );
247 30063 : }
248 : // no: if( FD_UNLIKELY( !fd_bn254_scalar_validate( s ) ) ) return -1;
249 :
250 : /* Compute scalar mul and serialize result */
251 30126 : fd_bn254_g1_scalar_mul( r, a, s );
252 30126 : fd_bn254_g1_tobytes( out, r, big_endian );
253 30126 : return 0;
254 30126 : }
255 :
256 : int
257 : fd_bn254_g2_scalar_mul_syscall( uchar out[128],
258 : uchar const in[],
259 : ulong in_sz,
260 330 : int big_endian ) {
261 : /* Expected 160-byte input (1 point + 1 scalar).
262 : https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/multiplication.rs#L248-L250 */
263 330 : if( FD_UNLIKELY( in_sz != 160UL ) ) {
264 0 : return -1;
265 0 : }
266 330 : uchar FD_ALIGNED buf[160] = { 0 };
267 330 : fd_memcpy( buf, in, 160UL );
268 :
269 : /* Validate point (curve equation and subgroup membership)
270 : https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/multiplication.rs#L252-L255 */
271 330 : fd_bn254_g2_t r[1], a[1];
272 330 : fd_bn254_scalar_t s[1];
273 330 : if( FD_UNLIKELY( !fd_bn254_g2_frombytes_check_subgroup( a, &buf[ 0], big_endian ) ) ) {
274 0 : return -1;
275 0 : }
276 :
277 : /* Scalar is little endian and NOT validated
278 : https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/multiplication.rs#L256-L272 */
279 330 : if( FD_BIG_ENDIAN_LIKELY( big_endian ) ) {
280 15 : fd_uint256_bswap( s, fd_type_pun_const( &buf[128] ) ); /* &buf[128] is always FD_ALIGNED */
281 315 : } else {
282 315 : memcpy( s, &buf[128], 32 );
283 315 : }
284 : // no: if( FD_UNLIKELY( !fd_bn254_scalar_validate( s ) ) ) return -1;
285 :
286 : /* Compute scalar mul and serialize result */
287 330 : fd_bn254_g2_scalar_mul( r, a, s );
288 330 : fd_bn254_g2_tobytes( out, r, big_endian );
289 330 : return 0;
290 330 : }
291 :
292 : int
293 : fd_bn254_pairing_is_one_syscall( uchar out[32],
294 : uchar const in[],
295 : ulong in_sz,
296 405 : int big_endian ) {
297 : /* https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/pairing.rs#L79 */
298 405 : if( FD_UNLIKELY( (in_sz % 192UL) != 0 ) ) {
299 15 : return -1; /* Invalid input length */
300 15 : }
301 390 : ulong elements_len = in_sz / 192UL;
302 390 : fd_bn254_g1_t p[FD_BN254_PAIRING_BATCH_MAX];
303 390 : fd_bn254_g2_t q[FD_BN254_PAIRING_BATCH_MAX];
304 :
305 : /* Important: set r=1 so that the result of 0 pairings is 1. */
306 390 : fd_bn254_fp12_t r[1];
307 390 : fd_bn254_fp12_set_one( r );
308 :
309 390 : ulong sz=0;
310 1266 : for( ulong i=0; i<elements_len; i++ ) {
311 : /* G1: deserialize and check subgroup membership */
312 876 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( &p[sz], &in[i*192 ], big_endian ) ) ) {
313 0 : return -1;
314 0 : }
315 : /* G2: deserialize and check subgroup membership */
316 876 : if( FD_UNLIKELY( !fd_bn254_g2_frombytes_check_subgroup( &q[sz], &in[i*192+64], big_endian ) ) ) {
317 0 : return -1;
318 0 : }
319 : /* Skip any pair where either P or Q is the point at infinity */
320 876 : if( FD_UNLIKELY( fd_bn254_g1_is_zero(&p[sz]) || fd_bn254_g2_is_zero(&q[sz]) ) ) {
321 0 : continue;
322 0 : }
323 876 : ++sz;
324 : /* Compute the Miller loop and aggregate into r */
325 876 : if( sz==FD_BN254_PAIRING_BATCH_MAX ) {
326 0 : fd_bn254_fp12_t tmp[1];
327 0 : fd_bn254_miller_loop( tmp, p, q, sz );
328 0 : fd_bn254_fp12_mul( r, r, tmp );
329 0 : sz = 0;
330 0 : }
331 876 : }
332 390 : if( sz>0 ) {
333 387 : fd_bn254_fp12_t tmp[1];
334 387 : fd_bn254_miller_loop( tmp, p, q, sz );
335 387 : fd_bn254_fp12_mul( r, r, tmp );
336 387 : sz = 0;
337 387 : }
338 :
339 : /* Compute the final exponentiation */
340 390 : fd_bn254_final_exp( r, r );
341 :
342 : /* Output is 0 or 1, serialized as big endian uint256. */
343 390 : fd_memset( out, 0, 32 );
344 390 : if( FD_LIKELY( fd_bn254_fp12_is_one( r ) ) ) {
345 378 : out[ big_endian ? 31 : 0 ] = 1;
346 378 : }
347 390 : return 0;
348 390 : }
|