Line data Source code
1 : #include "./fd_bn254_internal.h"
2 :
3 : #include "./fd_bn254_field.c"
4 : #include "./fd_bn254_field_ext.c"
5 : #include "./fd_bn254_g1.c"
6 : #include "./fd_bn254_g2.c"
7 : #include "./fd_bn254_pairing.c"
8 :
9 : /* Compress/Decompress */
10 :
11 : uchar *
12 : fd_bn254_g1_compress( uchar out[32],
13 30042 : uchar const in [64] ) {
14 30042 : fd_bn254_g1_t p[1] = { 0 };
15 30042 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_internal( p, in ) ) ) {
16 0 : return NULL;
17 0 : }
18 30042 : int is_inf = fd_bn254_g1_is_zero( p );
19 30042 : int flag_inf = in[32] & FLAG_INF;
20 :
21 : /* Serialize compressed point:
22 : https://github.com/arkworks-rs/algebra/blob/v0.4.2/ec/src/models/short_weierstrass/mod.rs#L122
23 :
24 : 1. If the infinity flags is set, return point at infinity
25 : 2. Else, copy x and set neg_y flag */
26 :
27 30042 : if( FD_UNLIKELY( is_inf ) ) {
28 0 : fd_memset( out, 0, 32 );
29 : /* The infinity flag in the result is set iff the infinity flag is set in the Y coordinate */
30 0 : out[0] = (uchar)( out[0] | flag_inf );
31 0 : return out;
32 0 : }
33 :
34 30042 : int is_neg = fd_bn254_fp_is_neg_nm( &p->Y );
35 30042 : memmove( out, in, 32 );
36 30042 : if( is_neg ) {
37 9 : out[0] = (uchar)( out[0] | FLAG_NEG );
38 9 : }
39 30042 : return out;
40 30042 : }
41 :
42 : uchar *
43 : fd_bn254_g1_decompress( uchar out[64],
44 30042 : uchar const in [32] ) {
45 : /* Special case: all zeros in => all zeros out, no flags */
46 30042 : const uchar zero[32] = { 0 };
47 30042 : if( fd_memeq( in, zero, 32 ) ) {
48 0 : return fd_memset( out, 0, 64UL );
49 0 : }
50 :
51 30042 : fd_bn254_fp_t x[1], x2[1], x3_plus_b[1], y[1];
52 30042 : int is_inf, is_neg;
53 30042 : if( FD_UNLIKELY( !fd_bn254_fp_frombytes_be_nm( x, in, &is_inf, &is_neg ) ) ) {
54 0 : return NULL;
55 0 : }
56 :
57 : /* Point at infinity.
58 : If the point at infinity flag is set (bit 6), return the point at
59 : infinity with no check on coords.
60 : https://github.com/arkworks-rs/algebra/blob/v0.4.2/ec/src/models/short_weierstrass/mod.rs#L156-L160
61 : */
62 30042 : if( is_inf ) {
63 0 : fd_memset( out, 0, 64UL );
64 : /* no flags */
65 0 : return out;
66 0 : }
67 :
68 30042 : fd_bn254_fp_to_mont( x, x );
69 30042 : fd_bn254_fp_sqr( x2, x );
70 30042 : fd_bn254_fp_mul( x3_plus_b, x2, x );
71 30042 : fd_bn254_fp_add( x3_plus_b, x3_plus_b, fd_bn254_const_b_mont );
72 30042 : if( FD_UNLIKELY( !fd_bn254_fp_sqrt( y, x3_plus_b ) ) ) {
73 0 : return NULL;
74 0 : }
75 :
76 30042 : fd_bn254_fp_from_mont( y, y );
77 30042 : if( is_neg != fd_bn254_fp_is_neg_nm( y ) ) {
78 3 : fd_bn254_fp_neg_nm( y, y );
79 3 : }
80 :
81 30042 : memmove( out, in, 32 ); out[0] &= FLAG_MASK;
82 30042 : fd_bn254_fp_tobytes_be_nm( &out[32], y );
83 : /* no flags */
84 30042 : return out;
85 30042 : }
86 :
87 : uchar *
88 : fd_bn254_g2_compress( uchar out[64],
89 30042 : uchar const in[128] ) {
90 30042 : fd_bn254_g2_t p[1] = { 0 };
91 30042 : if( FD_UNLIKELY( !fd_bn254_g2_frombytes_internal( p, in ) ) ) {
92 0 : return NULL;
93 0 : }
94 30042 : int is_inf = fd_bn254_g2_is_zero( p );
95 30042 : int flag_inf = in[64] & FLAG_INF;
96 :
97 : /* Serialize compressed point */
98 :
99 30042 : if( FD_UNLIKELY( is_inf ) ) {
100 0 : fd_memset( out, 0, 64 );
101 : /* The infinity flag in the result is set iff the infinity flag is set in the Y coordinate */
102 0 : out[0] = (uchar)( out[0] | flag_inf );
103 0 : return out;
104 0 : }
105 :
106 : /* Serialize x coordinate. The flags are on the 2nd element.
107 : https://github.com/arkworks-rs/algebra/blob/v0.4.2/ff/src/fields/models/quadratic_extension.rs#L700-L702 */
108 30042 : int is_neg = fd_bn254_fp2_is_neg_nm( &p->Y );
109 30042 : memmove( out, in, 64 );
110 30042 : if( is_neg ) {
111 18 : out[0] = (uchar)( out[0] | FLAG_NEG );
112 18 : }
113 30042 : return out;
114 30042 : }
115 :
116 : uchar *
117 : fd_bn254_g2_decompress( uchar out[128],
118 3042 : uchar const in [64] ) {
119 : /* Special case: all zeros in => all zeros out, no flags */
120 3042 : const uchar zero[64] = { 0 };
121 3042 : if( fd_memeq( in, zero, 64 ) ) {
122 0 : return fd_memset( out, 0, 128UL );
123 0 : }
124 :
125 3042 : fd_bn254_fp2_t x[1], x2[1], x3_plus_b[1], y[1];
126 3042 : int is_inf, is_neg;
127 3042 : if( FD_UNLIKELY( !fd_bn254_fp2_frombytes_be_nm( x, in, &is_inf, &is_neg ) ) ) {
128 0 : return NULL;
129 0 : }
130 :
131 : /* Point at infinity.
132 : If the point at infinity flag is set (bit 6), return the point at
133 : infinity with no check on coords.
134 : https://github.com/arkworks-rs/algebra/blob/v0.4.2/ec/src/models/short_weierstrass/mod.rs#L156-L160 */
135 3042 : if( is_inf ) {
136 0 : fd_memset( out, 0, 128UL );
137 : /* no flags */
138 0 : return out;
139 0 : }
140 :
141 3042 : fd_bn254_fp2_to_mont( x, x );
142 3042 : fd_bn254_fp2_sqr( x2, x );
143 3042 : fd_bn254_fp2_mul( x3_plus_b, x2, x );
144 3042 : fd_bn254_fp2_add( x3_plus_b, x3_plus_b, fd_bn254_const_twist_b_mont );
145 3042 : if( FD_UNLIKELY( !fd_bn254_fp2_sqrt( y, x3_plus_b ) ) ) {
146 0 : return NULL;
147 0 : }
148 :
149 3042 : fd_bn254_fp2_from_mont( y, y );
150 3042 : if( is_neg != fd_bn254_fp2_is_neg_nm( y ) ) {
151 27 : fd_bn254_fp2_neg_nm( y, y );
152 27 : }
153 :
154 3042 : memmove( out, in, 64 ); out[0] &= FLAG_MASK;
155 3042 : fd_bn254_fp2_tobytes_be_nm( &out[64], y );
156 : /* no flags */
157 3042 : return out;
158 3042 : }
159 :
160 : /* Ops */
161 :
162 : int
163 : fd_bn254_g1_add_syscall( uchar out[64],
164 : uchar const in[],
165 30033 : ulong in_sz ) {
166 : /* Expected 128-byte input (2 points). Pad input with 0s. */
167 30033 : if( FD_UNLIKELY( in_sz > 128UL ) ) {
168 0 : return -1;
169 0 : }
170 30033 : uchar FD_ALIGNED buf[128] = { 0 };
171 30033 : fd_memcpy( buf, in, in_sz );
172 :
173 : /* Validate inputs */
174 30033 : fd_bn254_g1_t r[1], a[1], b[1];
175 30033 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( a, &buf[ 0] ) ) ) {
176 0 : return -1;
177 0 : }
178 30033 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( b, &buf[64] ) ) ) {
179 0 : return -1;
180 0 : }
181 :
182 : /* Compute point add and serialize result */
183 30033 : fd_bn254_g1_affine_add( r, a, b );
184 30033 : fd_bn254_g1_tobytes( out, r );
185 30033 : return 0;
186 30033 : }
187 :
188 : int
189 : fd_bn254_g1_scalar_mul_syscall( uchar out[64],
190 : uchar const in[],
191 : ulong in_sz,
192 3120 : int check_correct_sz ) {
193 : /* Expected 96-byte input (1 point + 1 scalar). Pad input with 0s.
194 : Note: Agave checks for 128 bytes instead of 96. We have to do the same check.
195 : https://github.com/anza-xyz/agave/blob/v1.18.6/sdk/program/src/alt_bn128/mod.rs#L17
196 : Update: https://github.com/anza-xyz/agave/blob/d2df66d3/programs/bpf_loader/src/syscalls/mod.rs#L1654-L1658 */
197 3120 : ulong check_sz = check_correct_sz ? 96UL : 128UL;
198 3120 : if( FD_UNLIKELY( in_sz > check_sz ) ) {
199 0 : return -1;
200 0 : }
201 3120 : uchar FD_ALIGNED buf[96] = { 0 };
202 3120 : fd_memcpy( buf, in, fd_ulong_min( in_sz, 96UL ) );
203 :
204 : /* Validate inputs */
205 3120 : fd_bn254_g1_t r[1], a[1];
206 3120 : fd_bn254_scalar_t s[1];
207 3120 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( a, &buf[ 0] ) ) ) {
208 0 : return -1;
209 0 : }
210 :
211 : /* Scalar is big endian and NOT validated
212 : https://github.com/anza-xyz/agave/blob/v1.18.6/sdk/program/src/alt_bn128/mod.rs#L211-L214 */
213 3120 : fd_uint256_bswap( s, fd_type_pun_const( &buf[64] ) ); /* &buf[64] is always FD_ALIGNED */
214 : // no: if( FD_UNLIKELY( !fd_bn254_scalar_validate( s ) ) ) return -1;
215 :
216 : /* Compute scalar mul and serialize result */
217 3120 : fd_bn254_g1_scalar_mul( r, a, s );
218 3120 : fd_bn254_g1_tobytes( out, r );
219 3120 : return 0;
220 3120 : }
221 :
222 : int
223 : fd_bn254_pairing_is_one_syscall( uchar out[32],
224 : uchar const in[],
225 : ulong in_sz,
226 342 : int check_len ) {
227 : /* https://github.com/anza-xyz/agave/blob/v1.18.6/sdk/program/src/alt_bn128/mod.rs#L244
228 : Note: Solana had a bug where it checked if input.len().checked_rem(192).is_none(),
229 : which only fails when dividing by zero, so the check never triggered.
230 : When check_len is true, we properly validate that input size is a multiple of 192.
231 : This corresponds to the fix_alt_bn128_pairing_length_check feature gate. */
232 342 : if( check_len ) {
233 0 : if( FD_UNLIKELY( (in_sz % 192UL) != 0 ) ) {
234 0 : return -1; /* Invalid input length */
235 0 : }
236 0 : }
237 342 : ulong elements_len = in_sz / 192UL;
238 342 : fd_bn254_g1_t p[FD_BN254_PAIRING_BATCH_MAX];
239 342 : fd_bn254_g2_t q[FD_BN254_PAIRING_BATCH_MAX];
240 :
241 : /* Important: set r=1 so that the result of 0 pairings is 1. */
242 342 : fd_bn254_fp12_t r[1];
243 342 : fd_bn254_fp12_set_one( r );
244 :
245 342 : ulong sz=0;
246 1077 : for( ulong i=0; i<elements_len; i++ ) {
247 : /* G1: deserialize and check subgroup membership */
248 735 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( &p[sz], &in[i*192 ] ) ) ) {
249 0 : return -1;
250 0 : }
251 : /* G2: deserialize and check subgroup membership */
252 735 : if( FD_UNLIKELY( !fd_bn254_g2_frombytes_check_subgroup( &q[sz], &in[i*192+64] ) ) ) {
253 0 : return -1;
254 0 : }
255 : /* Skip any pair where either P or Q is the point at infinity */
256 735 : if( FD_UNLIKELY( fd_bn254_g1_is_zero(&p[sz]) || fd_bn254_g2_is_zero(&q[sz]) ) ) {
257 0 : continue;
258 0 : }
259 735 : ++sz;
260 : /* Compute the Miller loop and aggregate into r */
261 735 : if( sz==FD_BN254_PAIRING_BATCH_MAX || i==elements_len-1 ) {
262 342 : fd_bn254_fp12_t tmp[1];
263 342 : fd_bn254_miller_loop( tmp, p, q, sz );
264 342 : fd_bn254_fp12_mul( r, r, tmp );
265 342 : sz = 0;
266 342 : }
267 735 : }
268 342 : if( sz>0 ) {
269 0 : fd_bn254_fp12_t tmp[1];
270 0 : fd_bn254_miller_loop( tmp, p, q, sz );
271 0 : fd_bn254_fp12_mul( r, r, tmp );
272 0 : sz = 0;
273 0 : }
274 :
275 : /* Compute the final exponentiation */
276 342 : fd_bn254_final_exp( r, r );
277 :
278 : /* Output is 0 or 1, serialized as big endian uint256. */
279 342 : fd_memset( out, 0, 32 );
280 342 : if( FD_LIKELY( fd_bn254_fp12_is_one( r ) ) ) {
281 336 : out[31] = 1;
282 336 : }
283 342 : return 0;
284 342 : }
|