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