Line data Source code
1 : #include "fd_vm_syscall.h"
2 : #include "../../runtime/fd_bank.h"
3 : #include "../../../ballet/ed25519/fd_curve25519.h"
4 : #include "../../../ballet/ed25519/fd_ristretto255.h"
5 : #include "../../../ballet/bls/fd_bls12_381.h"
6 :
7 : int
8 : fd_vm_syscall_sol_curve_validate_point( /**/ void * _vm,
9 : /**/ ulong curve_id,
10 : /**/ ulong point_addr,
11 : FD_PARAM_UNUSED ulong r3,
12 : FD_PARAM_UNUSED ulong r4,
13 505 : FD_PARAM_UNUSED ulong r5 ) {
14 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L871 */
15 505 : fd_vm_t * vm = (fd_vm_t *)_vm;
16 505 : ulong ret = 1UL; /* by default return Ok(1) == error */
17 :
18 : /* BLS12-381 syscalls are under feature gate enable_bls12_381_syscall.
19 : To clean up the feature gate after activation, just remove this block
20 : (the rest of the function will behave correctly). */
21 505 : {
22 505 : if( FD_UNLIKELY(
23 505 : !FD_FEATURE_ACTIVE_BANK( vm->instr_ctx->bank, enable_bls12_381_syscall )
24 505 : && ( curve_id==FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_BE
25 505 : || curve_id==FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_LE
26 505 : || curve_id==FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_BE
27 505 : || curve_id==FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_LE )
28 505 : ) ) {
29 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
30 0 : return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
31 0 : }
32 505 : }
33 :
34 505 : uchar const * point = NULL;
35 505 : switch( curve_id ) {
36 :
37 152 : case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS:
38 :
39 152 : FD_VM_CU_UPDATE( vm, FD_VM_CURVE_EDWARDS_VALIDATE_POINT_COST );
40 151 : point = FD_VM_MEM_HADDR_LD( vm, point_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
41 0 : ret = (ulong)!fd_ed25519_point_validate( point ); /* 0 if valid point, 1 if not */
42 150 : break;
43 :
44 148 : case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO:
45 :
46 148 : FD_VM_CU_UPDATE( vm, FD_VM_CURVE_RISTRETTO_VALIDATE_POINT_COST );
47 147 : point = FD_VM_MEM_HADDR_LD( vm, point_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
48 0 : ret = (ulong)!fd_ristretto255_point_validate( point ); /* 0 if valid point, 1 if not */
49 146 : break;
50 :
51 47 : case FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_BE:
52 103 : case FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_LE: {
53 :
54 103 : int big_endian = ( curve_id & 0x80 ) ? 1 : 0;
55 103 : FD_VM_CU_UPDATE( vm, FD_VM_CURVE_BLS12_381_G1_VALIDATE_COST );
56 103 : point = FD_VM_MEM_HADDR_LD( vm, point_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ );
57 0 : ret = (ulong)!fd_bls12_381_g1_validate_syscall( point, big_endian ); /* 0 if valid point, 1 if not */
58 103 : } break;
59 :
60 43 : case FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_BE:
61 99 : case FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_LE: {
62 :
63 99 : int big_endian = ( curve_id & 0x80 ) ? 1 : 0;
64 99 : FD_VM_CU_UPDATE( vm, FD_VM_CURVE_BLS12_381_G2_VALIDATE_COST );
65 99 : point = FD_VM_MEM_HADDR_LD( vm, point_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ );
66 0 : ret = (ulong)!fd_bls12_381_g2_validate_syscall( point, big_endian ); /* 0 if valid point, 1 if not */
67 99 : } break;
68 :
69 3 : default:
70 : /* https://github.com/anza-xyz/agave/blob/5b3390b99a6e7665439c623062c1a1dda2803524/programs/bpf_loader/src/syscalls/mod.rs#L919-L928 */
71 3 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
72 3 : return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
73 505 : }
74 :
75 498 : vm->reg[0] = ret;
76 498 : return FD_VM_SUCCESS;
77 505 : }
78 :
79 : int
80 : fd_vm_syscall_sol_curve_group_op( void * _vm,
81 : ulong curve_id,
82 : ulong group_op,
83 : ulong left_input_addr,
84 : ulong right_input_addr,
85 647 : ulong result_point_addr ) {
86 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L928 */
87 647 : fd_vm_t * vm = (fd_vm_t *)_vm;
88 647 : ulong ret = 1UL; /* by default return Ok(1) == error */
89 :
90 : /* BLS12-381 syscalls are under feature gate enable_bls12_381_syscall.
91 : To clean up the feature gate after activation, just remove this block
92 : (the rest of the function will behave correctly). */
93 647 : {
94 647 : if( FD_UNLIKELY(
95 647 : !FD_FEATURE_ACTIVE_BANK( vm->instr_ctx->bank, enable_bls12_381_syscall )
96 647 : && ( curve_id==FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_BE
97 647 : || curve_id==FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_LE
98 647 : || curve_id==FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_BE
99 647 : || curve_id==FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_LE )
100 647 : ) ) {
101 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
102 0 : return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
103 0 : }
104 647 : }
105 :
106 : /* Note: we don't strictly follow the Rust implementation, but instead combine
107 : common code across switch cases. Similar to fd_vm_syscall_sol_alt_bn128_group_op. */
108 :
109 : /* MATCH_ID_OP allows us to unify 2 switch/case into 1.
110 : For better readability, we also temp define EDWARDS, RISTRETTO.
111 :
112 : The first time we check that both curve_id and group_op are valid
113 : with 2 nested switch/case. Using MATCH_ID_OP leads to undesidered
114 : edge cases. The second time, when we know that curve_id and group_op
115 : are correct, then we can use MATCH_ID_OP and a single switch/case. */
116 1356 : #define MATCH_ID_OP(crv_id,grp_op) ((crv_id << 4) | grp_op)
117 647 : #define EDWARDS FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS
118 647 : #define RISTRETTO FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO
119 647 : #define BLS_G1_BE FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_BE
120 647 : #define BLS_G1_LE FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_LE
121 647 : #define BLS_G2_BE FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_BE
122 647 : #define BLS_G2_LE FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_LE
123 :
124 647 : ulong cost = 0UL;
125 647 : ulong inputL_sz = 32UL;
126 647 : ulong inputR_sz = 32UL;
127 647 : switch( curve_id ) {
128 :
129 203 : case EDWARDS:
130 203 : switch( group_op ) {
131 :
132 61 : case FD_VM_SYSCALL_SOL_CURVE_ADD:
133 61 : cost = FD_VM_CURVE_EDWARDS_ADD_COST;
134 61 : break;
135 :
136 53 : case FD_VM_SYSCALL_SOL_CURVE_SUB:
137 53 : cost = FD_VM_CURVE_EDWARDS_SUBTRACT_COST;
138 53 : break;
139 :
140 87 : case FD_VM_SYSCALL_SOL_CURVE_MUL:
141 87 : cost = FD_VM_CURVE_EDWARDS_MULTIPLY_COST;
142 87 : break;
143 :
144 2 : default:
145 2 : goto invalid_error;
146 203 : }
147 201 : break;
148 :
149 201 : case RISTRETTO:
150 170 : switch( group_op ) {
151 :
152 48 : case FD_VM_SYSCALL_SOL_CURVE_ADD:
153 48 : cost = FD_VM_CURVE_RISTRETTO_ADD_COST;
154 48 : break;
155 :
156 54 : case FD_VM_SYSCALL_SOL_CURVE_SUB:
157 54 : cost = FD_VM_CURVE_RISTRETTO_SUBTRACT_COST;
158 54 : break;
159 :
160 66 : case FD_VM_SYSCALL_SOL_CURVE_MUL:
161 66 : cost = FD_VM_CURVE_RISTRETTO_MULTIPLY_COST;
162 66 : break;
163 :
164 2 : default:
165 2 : goto invalid_error;
166 170 : }
167 168 : break;
168 :
169 : /* BLS12-381 G1 */
170 168 : case BLS_G1_BE:
171 138 : case BLS_G1_LE:
172 138 : switch( group_op ) {
173 :
174 41 : case FD_VM_SYSCALL_SOL_CURVE_ADD:
175 41 : cost = FD_VM_CURVE_BLS12_381_G1_ADD_COST;
176 41 : inputL_sz = FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ;
177 41 : inputR_sz = FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ;
178 41 : break;
179 :
180 49 : case FD_VM_SYSCALL_SOL_CURVE_SUB:
181 49 : cost = FD_VM_CURVE_BLS12_381_G1_SUB_COST;
182 49 : inputL_sz = FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ;
183 49 : inputR_sz = FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ;
184 49 : break;
185 :
186 47 : case FD_VM_SYSCALL_SOL_CURVE_MUL:
187 47 : cost = FD_VM_CURVE_BLS12_381_G1_MUL_COST;
188 : /* inputL_sz = 32UL // scalar */
189 47 : inputR_sz = FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ;
190 47 : break;
191 :
192 1 : default:
193 1 : goto invalid_error;
194 138 : }
195 137 : break;
196 :
197 : /* BLS12-381 G2 */
198 137 : case BLS_G2_BE:
199 134 : case BLS_G2_LE:
200 134 : switch( group_op ) {
201 :
202 44 : case FD_VM_SYSCALL_SOL_CURVE_ADD:
203 44 : cost = FD_VM_CURVE_BLS12_381_G2_ADD_COST;
204 44 : inputL_sz = FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ;
205 44 : inputR_sz = FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ;
206 44 : break;
207 :
208 41 : case FD_VM_SYSCALL_SOL_CURVE_SUB:
209 41 : cost = FD_VM_CURVE_BLS12_381_G2_SUB_COST;
210 41 : inputL_sz = FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ;
211 41 : inputR_sz = FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ;
212 41 : break;
213 :
214 48 : case FD_VM_SYSCALL_SOL_CURVE_MUL:
215 48 : cost = FD_VM_CURVE_BLS12_381_G2_MUL_COST;
216 : /* inputL_sz = 32UL // scalar */
217 48 : inputR_sz = FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ;
218 48 : break;
219 :
220 1 : default:
221 1 : goto invalid_error;
222 134 : }
223 133 : break;
224 :
225 133 : default:
226 3 : goto invalid_error;
227 647 : }
228 :
229 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L944-L947 */
230 1272 : FD_VM_CU_UPDATE( vm, cost );
231 :
232 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L949-L958 */
233 :
234 : /* Note: left_input_addr is a point for add, sub, BUT it's a scalar for mul. */
235 1887 : uchar const * inputL = FD_VM_MEM_HADDR_LD( vm, left_input_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, inputL_sz );
236 1853 : uchar const * inputR = FD_VM_MEM_HADDR_LD( vm, right_input_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, inputR_sz );
237 :
238 613 : int big_endian = ( curve_id & 0x80 ) ? 1 : 0;
239 :
240 1853 : switch( MATCH_ID_OP( curve_id, group_op ) ) {
241 :
242 58 : case MATCH_ID_OP( EDWARDS, FD_VM_SYSCALL_SOL_CURVE_ADD ): {
243 58 : fd_ed25519_point_t p0[1], p1[1], r[1];
244 58 : if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p0, inputL ) ) ) {
245 7 : goto soft_error;
246 7 : }
247 51 : if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p1, inputR ) ) ) {
248 2 : goto soft_error;
249 2 : }
250 :
251 49 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_point_addr, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
252 46 : fd_ed25519_point_add( r, p0, p1 );
253 46 : fd_ed25519_point_tobytes( result, r );
254 46 : ret = 0UL;
255 46 : break;
256 49 : }
257 :
258 50 : case MATCH_ID_OP( EDWARDS, FD_VM_SYSCALL_SOL_CURVE_SUB ): {
259 50 : fd_ed25519_point_t p0[1], p1[1], r[1];
260 50 : if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p0, inputL ) ) ) {
261 4 : goto soft_error;
262 4 : }
263 46 : if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p1, inputR ) ) ) {
264 3 : goto soft_error;
265 3 : }
266 :
267 43 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_point_addr, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
268 35 : fd_ed25519_point_sub( r, p0, p1 );
269 35 : fd_ed25519_point_tobytes( result, r );
270 35 : ret = 0UL;
271 35 : break;
272 43 : }
273 :
274 84 : case MATCH_ID_OP( EDWARDS, FD_VM_SYSCALL_SOL_CURVE_MUL ): {
275 84 : fd_ed25519_point_t p[1], r[1];
276 84 : if( FD_UNLIKELY( !fd_curve25519_scalar_validate( inputL ) ) ) {
277 13 : goto soft_error;
278 13 : }
279 71 : if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p, inputR ) ) ) {
280 2 : goto soft_error;
281 2 : }
282 :
283 69 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_point_addr, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
284 62 : fd_ed25519_scalar_mul( r, inputL, p );
285 62 : fd_ed25519_point_tobytes( result, r );
286 62 : ret = 0UL;
287 62 : break;
288 69 : }
289 :
290 45 : case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_ADD ): {
291 45 : fd_ristretto255_point_t p0[1], p1[1], r[1];
292 45 : if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p0, inputL ) ) ) {
293 3 : goto soft_error;
294 3 : }
295 42 : if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p1, inputR ) ) ) {
296 1 : goto soft_error;
297 1 : }
298 :
299 41 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_point_addr, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
300 35 : fd_ristretto255_point_add( r, p0, p1 );
301 35 : fd_ristretto255_point_tobytes( result, r );
302 35 : ret = 0UL;
303 35 : break;
304 41 : }
305 :
306 51 : case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_SUB ): {
307 51 : fd_ristretto255_point_t p0[1], p1[1], r[1];
308 51 : if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p0, inputL ) ) ) {
309 4 : goto soft_error;
310 4 : }
311 47 : if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p1, inputR ) ) ) {
312 1 : goto soft_error;
313 1 : }
314 :
315 46 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_point_addr, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
316 41 : fd_ristretto255_point_sub( r, p0, p1 );
317 41 : fd_ristretto255_point_tobytes( result, r );
318 41 : ret = 0UL;
319 41 : break;
320 46 : }
321 :
322 63 : case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_MUL ): {
323 63 : fd_ristretto255_point_t p[1], r[1];
324 63 : if( FD_UNLIKELY( !fd_curve25519_scalar_validate( inputL ) ) ) {
325 8 : goto soft_error;
326 8 : }
327 55 : if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p, inputR ) ) ) {
328 10 : goto soft_error;
329 10 : }
330 :
331 45 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_point_addr, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
332 42 : fd_ristretto255_scalar_mul( r, inputL, p );
333 42 : fd_ristretto255_point_tobytes( result, r );
334 42 : ret = 0UL;
335 42 : break;
336 45 : }
337 :
338 : /* BLS12-381 G1 */
339 :
340 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/syscalls/src/lib.rs#L1453 */
341 17 : case MATCH_ID_OP( BLS_G1_BE, FD_VM_SYSCALL_SOL_CURVE_ADD ):
342 40 : case MATCH_ID_OP( BLS_G1_LE, FD_VM_SYSCALL_SOL_CURVE_ADD ): {
343 40 : uchar _result[ FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ ];
344 : /* Compute add */
345 40 : if( FD_LIKELY( fd_bls12_381_g1_add_syscall( _result, inputL, inputR, big_endian )==0 ) ) {
346 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/syscalls/src/lib.rs#L1474 */
347 30 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_point_addr, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ );
348 29 : memcpy( result, _result, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ );
349 29 : ret = 0UL; /* success */
350 29 : }
351 39 : break;
352 40 : }
353 :
354 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/syscalls/src/lib.rs#L1485 */
355 39 : case MATCH_ID_OP( BLS_G1_BE, FD_VM_SYSCALL_SOL_CURVE_SUB ):
356 48 : case MATCH_ID_OP( BLS_G1_LE, FD_VM_SYSCALL_SOL_CURVE_SUB ): {
357 48 : uchar _result[ FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ ];
358 : /* Compute sub */
359 48 : if( FD_LIKELY( fd_bls12_381_g1_sub_syscall( _result, inputL, inputR, big_endian )==0 ) ) {
360 32 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_point_addr, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ );
361 32 : memcpy( result, _result, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ );
362 32 : ret = 0UL; /* success */
363 32 : }
364 48 : break;
365 48 : }
366 :
367 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/syscalls/src/lib.rs#L1519 */
368 48 : case MATCH_ID_OP( BLS_G1_BE, FD_VM_SYSCALL_SOL_CURVE_MUL ):
369 46 : case MATCH_ID_OP( BLS_G1_LE, FD_VM_SYSCALL_SOL_CURVE_MUL ): {
370 46 : uchar _result[ FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ ];
371 : /* Compute mul */
372 46 : if( FD_LIKELY( fd_bls12_381_g1_mul_syscall( _result, inputL, inputR, big_endian )==0 ) ) {
373 32 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_point_addr, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ );
374 30 : memcpy( result, _result, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ );
375 30 : ret = 0UL; /* success */
376 30 : }
377 44 : break;
378 46 : }
379 :
380 : /* BLS12-381 G2 */
381 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/syscalls/src/lib.rs#L1566 */
382 44 : case MATCH_ID_OP( BLS_G2_BE, FD_VM_SYSCALL_SOL_CURVE_ADD ):
383 43 : case MATCH_ID_OP( BLS_G2_LE, FD_VM_SYSCALL_SOL_CURVE_ADD ): {
384 43 : uchar _result[ FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ ];
385 : /* Compute add */
386 43 : if( FD_LIKELY( fd_bls12_381_g2_add_syscall( _result, inputL, inputR, big_endian )==0 ) ) {
387 29 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_point_addr, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ );
388 28 : memcpy( result, _result, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ );
389 28 : ret = 0UL; /* success */
390 28 : }
391 42 : break;
392 43 : }
393 :
394 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/syscalls/src/lib.rs#L1598 */
395 42 : case MATCH_ID_OP( BLS_G2_BE, FD_VM_SYSCALL_SOL_CURVE_SUB ):
396 40 : case MATCH_ID_OP( BLS_G2_LE, FD_VM_SYSCALL_SOL_CURVE_SUB ): {
397 40 : uchar _result[ FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ ];
398 : /* Compute sub */
399 40 : if( FD_LIKELY( fd_bls12_381_g2_sub_syscall( _result, inputL, inputR, big_endian )==0 ) ) {
400 34 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_point_addr, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ );
401 34 : memcpy( result, _result, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ );
402 34 : ret = 0UL; /* success */
403 34 : }
404 40 : break;
405 40 : }
406 :
407 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/syscalls/src/lib.rs#L1632 */
408 40 : case MATCH_ID_OP( BLS_G2_BE, FD_VM_SYSCALL_SOL_CURVE_MUL ):
409 45 : case MATCH_ID_OP( BLS_G2_LE, FD_VM_SYSCALL_SOL_CURVE_MUL ): {
410 45 : uchar _result[ FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ ];
411 : /* Compute mul */
412 45 : if( FD_LIKELY( fd_bls12_381_g2_mul_syscall( _result, inputL, inputR, big_endian )==0 ) ) {
413 33 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_point_addr, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ );
414 30 : memcpy( result, _result, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ );
415 30 : ret = 0UL; /* success */
416 30 : }
417 42 : break;
418 45 : }
419 :
420 42 : default:
421 : /* COV: this can never happen because of the previous switch */
422 0 : return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
423 1853 : }
424 :
425 574 : soft_error:
426 574 : vm->reg[0] = ret;
427 574 : return FD_VM_SUCCESS;
428 0 : #undef MATCH_ID_OP
429 0 : #undef EDWARDS
430 0 : #undef RISTRETTO
431 0 : #undef BLS_G1_BE
432 0 : #undef BLS_G1_LE
433 0 : #undef BLS_G2_BE
434 0 : #undef BLS_G2_LE
435 :
436 9 : invalid_error:
437 : /* https://github.com/anza-xyz/agave/blob/5b3390b99a6e7665439c623062c1a1dda2803524/programs/bpf_loader/src/syscalls/mod.rs#L1135-L1156 */
438 9 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
439 9 : return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
440 1853 : }
441 :
442 : /* multi_scalar_mul_edwards computes a MSM on curve25519.
443 :
444 : This function is equivalent to
445 : zk-token-sdk::edwards::multi_scalar_mul_edwards
446 :
447 : https://github.com/solana-labs/solana/blob/v1.17.7/zk-token-sdk/src/curve25519/edwards.rs#L116
448 :
449 : Specifically it takes as input byte arrays and takes care of scalars
450 : validation and points decompression. It then invokes ballet MSM
451 : function fd_ed25519_multi_scalar_mul. To avoid dynamic allocation,
452 : the full MSM is done in batches of FD_BALLET_CURVE25519_MSM_BATCH_SZ. */
453 :
454 : static fd_ed25519_point_t *
455 : multi_scalar_mul_edwards( fd_ed25519_point_t * r,
456 : uchar const * scalars,
457 : uchar const * points,
458 202 : ulong cnt ) {
459 : /* Validate all scalars first (fast) */
460 868 : for( ulong i=0UL; i<cnt; i++ ) {
461 713 : if( FD_UNLIKELY( !fd_curve25519_scalar_validate ( scalars + i*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ ) ) ) {
462 47 : return NULL;
463 47 : }
464 713 : }
465 :
466 : /* Static allocation of a batch of decompressed points */
467 155 : fd_ed25519_point_t tmp[1];
468 155 : fd_ed25519_point_t A[ FD_BALLET_CURVE25519_MSM_BATCH_SZ ];
469 :
470 155 : fd_ed25519_point_set_zero( r );
471 308 : for( ulong i=0UL; i<cnt; i+=FD_BALLET_CURVE25519_MSM_BATCH_SZ ) {
472 154 : ulong batch_cnt = fd_ulong_min( cnt-i, FD_BALLET_CURVE25519_MSM_BATCH_SZ );
473 :
474 : /* Decompress (and validate) points */
475 821 : for( ulong j=0UL; j<batch_cnt; j++ ) {
476 : //TODO: use fd_ed25519_point_frombytes_2x
477 668 : if( FD_UNLIKELY( !fd_ed25519_point_frombytes( &A[j], points + j*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ ) ) ) {
478 1 : return NULL;
479 1 : }
480 668 : }
481 :
482 153 : fd_ed25519_multi_scalar_mul( tmp, scalars, A, batch_cnt );
483 153 : fd_ed25519_point_add( r, r, tmp );
484 153 : points += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ *batch_cnt;
485 153 : scalars += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ*batch_cnt;
486 153 : }
487 :
488 154 : return r;
489 155 : }
490 :
491 : /* multi_scalar_mul_ristretto computes a MSM on ristretto255.
492 : See multi_scalar_mul_edwards for details. */
493 :
494 : static fd_ristretto255_point_t *
495 : multi_scalar_mul_ristretto( fd_ristretto255_point_t * r,
496 : uchar const * scalars,
497 : uchar const * points,
498 215 : ulong cnt ) {
499 : /* Validate all scalars first (fast) */
500 884 : for( ulong i=0UL; i<cnt; i++ ) {
501 718 : if( FD_UNLIKELY( !fd_curve25519_scalar_validate ( scalars + i*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ ) ) ) {
502 49 : return NULL;
503 49 : }
504 718 : }
505 :
506 : /* Static allocation of a batch of decompressed points */
507 166 : fd_ristretto255_point_t tmp[1];
508 166 : fd_ristretto255_point_t A[ FD_BALLET_CURVE25519_MSM_BATCH_SZ ];
509 :
510 166 : fd_ristretto255_point_set_zero( r );
511 328 : for( ulong i=0UL; i<cnt; i+=FD_BALLET_CURVE25519_MSM_BATCH_SZ ) {
512 166 : ulong batch_cnt = fd_ulong_min( cnt-i, FD_BALLET_CURVE25519_MSM_BATCH_SZ );
513 :
514 : /* Decompress (and validate) points */
515 824 : for( ulong j=0UL; j<batch_cnt; j++ ) {
516 : //TODO: use fd_ristretto255_point_frombytes_2x
517 662 : if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( &A[j], points + j*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ ) ) ) {
518 4 : return NULL;
519 4 : }
520 662 : }
521 :
522 162 : fd_ristretto255_multi_scalar_mul( tmp, scalars, A, batch_cnt );
523 162 : fd_ristretto255_point_add( r, r, tmp );
524 162 : points += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ *batch_cnt;
525 162 : scalars += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ*batch_cnt;
526 162 : }
527 :
528 162 : return r;
529 166 : }
530 :
531 : #undef BATCH_MAX
532 :
533 : int
534 : fd_vm_syscall_sol_curve_multiscalar_mul( void * _vm,
535 : ulong curve_id,
536 : ulong scalars_addr,
537 : ulong points_addr,
538 : ulong points_len,
539 462 : ulong result_point_addr ) {
540 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1129 */
541 462 : fd_vm_t * vm = (fd_vm_t *)_vm;
542 462 : ulong ret = 1UL; /* by default return Ok(1) == error */
543 :
544 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1143-L1151 */
545 462 : if( FD_UNLIKELY( points_len > 512 ) ) {
546 2 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );
547 2 : return FD_VM_SYSCALL_ERR_INVALID_LENGTH; /* SyscallError::InvalidLength */
548 2 : }
549 :
550 : /* Note: we don't strictly follow the Rust implementation, but instead combine
551 : common code across switch cases. Similar to fd_vm_syscall_sol_alt_bn128_group_op. */
552 :
553 460 : ulong base_cost = 0UL;
554 460 : ulong incremental_cost = 0UL;
555 460 : switch( curve_id ) {
556 223 : case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS:
557 223 : base_cost = FD_VM_CURVE_EDWARDS_MSM_BASE_COST;
558 223 : incremental_cost = FD_VM_CURVE_EDWARDS_MSM_INCREMENTAL_COST;
559 223 : break;
560 :
561 235 : case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO:
562 235 : base_cost = FD_VM_CURVE_RISTRETTO_MSM_BASE_COST;
563 235 : incremental_cost = FD_VM_CURVE_RISTRETTO_MSM_INCREMENTAL_COST;
564 235 : break;
565 :
566 2 : default:
567 : /* https://github.com/anza-xyz/agave/blob/5b3390b99a6e7665439c623062c1a1dda2803524/programs/bpf_loader/src/syscalls/mod.rs#L1262-L1271 */
568 2 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
569 2 : return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
570 460 : }
571 :
572 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1155-L1164 */
573 458 : ulong cost = fd_ulong_sat_add(
574 458 : base_cost,
575 458 : fd_ulong_sat_mul(
576 458 : incremental_cost,
577 458 : fd_ulong_sat_sub( points_len, 1 )
578 458 : )
579 458 : );
580 458 : FD_VM_CU_UPDATE( vm, cost );
581 :
582 : /* Edge case points_len==0.
583 : Agave computes the MSM, that returns the point at infinity, and stores the result.
584 : This means that we have to mem map result, and then set the point at infinity,
585 : that is 0x0100..00 for Edwards and 0x00..00 for Ristretto. */
586 454 : if ( FD_UNLIKELY( points_len==0 ) ) {
587 33 : uchar * result = FD_VM_MEM_HADDR_ST( vm, result_point_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
588 0 : memset( result, 0, 32 );
589 30 : result[0] = curve_id==FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS ? 1 : 0;
590 30 : vm->reg[0] = 0UL;
591 30 : return FD_VM_SUCCESS;
592 33 : }
593 :
594 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1166-L1178 */
595 1259 : uchar const * scalars = FD_VM_MEM_HADDR_LD( vm, scalars_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, points_len*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ );
596 1253 : uchar const * points = FD_VM_MEM_HADDR_LD( vm, points_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, points_len*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
597 :
598 0 : switch( curve_id ) {
599 :
600 202 : case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS: {
601 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1180-L1189 */
602 202 : fd_ed25519_point_t _r[1];
603 202 : fd_ed25519_point_t * r = multi_scalar_mul_edwards( _r, scalars, points, points_len );
604 :
605 202 : if( FD_LIKELY( r ) ) {
606 155 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_point_addr, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
607 141 : fd_ed25519_point_tobytes( result, r );
608 141 : ret = 0UL;
609 141 : }
610 188 : break;
611 202 : }
612 :
613 215 : case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO: {
614 215 : fd_ristretto255_point_t _r[1];
615 215 : fd_ristretto255_point_t * r = multi_scalar_mul_ristretto( _r, scalars, points, points_len );
616 :
617 215 : if( FD_LIKELY( r ) ) {
618 162 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_point_addr, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
619 154 : fd_ristretto255_point_tobytes( result, r );
620 154 : ret = 0UL;
621 154 : }
622 207 : break;
623 215 : }
624 :
625 207 : default:
626 : /* COV: this can never happen because of the previous switch */
627 0 : return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
628 1253 : }
629 :
630 396 : vm->reg[0] = ret;
631 396 : return FD_VM_SUCCESS;
632 1253 : }
633 :
634 : int
635 : fd_vm_syscall_sol_curve_decompress( /**/ void * _vm,
636 : /**/ ulong curve_id,
637 : /**/ ulong point_addr,
638 : /**/ ulong result_addr,
639 : FD_PARAM_UNUSED ulong r4,
640 159 : FD_PARAM_UNUSED ulong r5 ) {
641 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/syscalls/src/lib.rs#L1118 */
642 159 : fd_vm_t * vm = (fd_vm_t *)_vm;
643 159 : ulong ret = 1UL; /* by default return Ok(1) == error */
644 :
645 159 : int big_endian = ( curve_id & 0x80 ) ? 1 : 0;
646 :
647 159 : uchar const * point = NULL;
648 159 : switch( curve_id ) {
649 :
650 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/syscalls/src/lib.rs#L1137 */
651 30 : case FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_BE:
652 72 : case FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_LE: {
653 72 : FD_VM_CU_UPDATE( vm, FD_VM_CURVE_BLS12_381_G1_DECOMPRESS_COST );
654 216 : point = FD_VM_MEM_HADDR_LD( vm, point_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_COMPRESSED_SZ );
655 0 : uchar _result[ FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ ];
656 216 : if( FD_LIKELY( fd_bls12_381_g1_decompress_syscall( _result, point, big_endian )==0 ) ) {
657 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/syscalls/src/lib.rs#L1160 */
658 66 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_addr, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ );
659 63 : memcpy( result, _result, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ );
660 63 : ret = 0UL; /* success */
661 63 : }
662 216 : } break;
663 :
664 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/syscalls/src/lib.rs#L1171 */
665 69 : case FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_BE:
666 86 : case FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_LE: {
667 86 : FD_VM_CU_UPDATE( vm, FD_VM_CURVE_BLS12_381_G2_DECOMPRESS_COST );
668 258 : point = FD_VM_MEM_HADDR_LD( vm, point_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_COMPRESSED_SZ );
669 0 : uchar _result[ FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ ];
670 258 : if( FD_LIKELY( fd_bls12_381_g2_decompress_syscall( _result, point, big_endian )==0 ) ) {
671 74 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_addr, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ );
672 67 : memcpy( result, _result, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ );
673 67 : ret = 0UL; /* success */
674 67 : }
675 258 : } break;
676 :
677 79 : default:
678 1 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
679 1 : return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
680 159 : }
681 :
682 148 : vm->reg[0] = ret;
683 148 : return FD_VM_SUCCESS;
684 159 : }
685 :
686 : int
687 : fd_vm_syscall_sol_curve_pairing_map( /**/ void * _vm,
688 : /**/ ulong curve_id,
689 : /**/ ulong num_pairs,
690 : /**/ ulong g1_points_addr,
691 : /**/ ulong g2_points_addr,
692 139 : /**/ ulong result_addr ) {
693 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/syscalls/src/lib.rs#L1804 */
694 139 : fd_vm_t * vm = (fd_vm_t *)_vm;
695 139 : ulong ret = 1UL; /* by default return Ok(1) == error */
696 :
697 139 : int big_endian = ( curve_id & 0x80 ) ? 1 : 0;
698 :
699 139 : switch( curve_id ) {
700 :
701 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/syscalls/src/lib.rs#L1823 */
702 65 : case FD_VM_SYSCALL_SOL_CURVE_BLS12_381_BE:
703 138 : case FD_VM_SYSCALL_SOL_CURVE_BLS12_381_LE: {
704 :
705 138 : ulong cost = fd_ulong_sat_add( FD_VM_CURVE_BLS12_381_PAIRING_BASE_COST,
706 138 : fd_ulong_sat_mul( FD_VM_CURVE_BLS12_381_PAIRING_INCR_COST,
707 138 : fd_ulong_sat_sub( num_pairs, 1 ) ) );
708 138 : FD_VM_CU_UPDATE( vm, cost );
709 :
710 137 : ulong total_g1_sz = fd_ulong_sat_mul( FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G1_POINT_SZ, num_pairs );
711 274 : uchar const * g1_points = FD_VM_MEM_SLICE_HADDR_LD( vm, g1_points_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, total_g1_sz );
712 :
713 137 : ulong total_g2_sz = fd_ulong_sat_mul( FD_VM_SYSCALL_SOL_CURVE_BLS12_381_G2_POINT_SZ, num_pairs );
714 274 : uchar const * g2_points = FD_VM_MEM_SLICE_HADDR_LD( vm, g2_points_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, total_g2_sz );
715 :
716 0 : uchar _result[ FD_VM_SYSCALL_SOL_CURVE_BLS12_381_GT_ELE_SZ ];
717 271 : if( FD_LIKELY( fd_bls12_381_pairing_syscall( _result, g1_points, g2_points, num_pairs, big_endian )==0 ) ) {
718 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/syscalls/src/lib.rs#L1860 */
719 112 : uchar * result = FD_VM_HADDR_QUERY_U8_ARRAY( vm, result_addr, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_GT_ELE_SZ );
720 90 : memcpy( result, _result, FD_VM_SYSCALL_SOL_CURVE_BLS12_381_GT_ELE_SZ );
721 90 : ret = 0UL; /* success */
722 90 : }
723 271 : } break;
724 :
725 112 : default:
726 1 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
727 1 : return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
728 139 : }
729 :
730 112 : vm->reg[0] = ret;
731 112 : return FD_VM_SUCCESS;
732 139 : }
|