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