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