Line data Source code
1 : #include "fd_vm_syscall.h"
2 :
3 : #include "../../../ballet/ed25519/fd_curve25519.h"
4 : #include "../../../ballet/ed25519/fd_ristretto255.h"
5 :
6 : int
7 : fd_vm_syscall_sol_curve_validate_point( /**/ void * _vm,
8 : /**/ ulong curve_id,
9 : /**/ ulong point_addr,
10 : FD_PARAM_UNUSED ulong r3,
11 : FD_PARAM_UNUSED ulong r4,
12 : FD_PARAM_UNUSED ulong r5,
13 0 : /**/ ulong * _ret ) {
14 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L871 */
15 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
16 0 : ulong ret = 1UL; /* by default return Ok(1) == error */
17 :
18 0 : uchar const * point = NULL;
19 0 : switch( curve_id ) {
20 :
21 0 : case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS:
22 :
23 0 : FD_VM_CU_UPDATE( vm, FD_VM_CURVE25519_EDWARDS_VALIDATE_POINT_COST );
24 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 );
25 0 : ret = (ulong)!fd_ed25519_point_validate( point ); /* 0 if valid point, 1 if not */
26 0 : break;
27 :
28 0 : case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO:
29 :
30 0 : FD_VM_CU_UPDATE( vm, FD_VM_CURVE25519_RISTRETTO_VALIDATE_POINT_COST );
31 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 );
32 0 : ret = (ulong)!fd_ristretto255_point_validate( point ); /* 0 if valid point, 1 if not */
33 0 : break;
34 :
35 0 : default:
36 : /* https://github.com/anza-xyz/agave/blob/5b3390b99a6e7665439c623062c1a1dda2803524/programs/bpf_loader/src/syscalls/mod.rs#L919-L928 */
37 0 : if( FD_FEATURE_ACTIVE_BANK( vm->instr_ctx->txn_ctx->bank, abort_on_invalid_curve ) ) {
38 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
39 0 : return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
40 0 : }
41 0 : }
42 :
43 0 : *_ret = ret;
44 0 : return FD_VM_SUCCESS;
45 0 : }
46 :
47 : int
48 : fd_vm_syscall_sol_curve_group_op( void * _vm,
49 : ulong curve_id,
50 : ulong group_op,
51 : ulong left_input_addr,
52 : ulong right_input_addr,
53 : ulong result_point_addr,
54 12 : ulong * _ret ) {
55 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L928 */
56 12 : fd_vm_t * vm = (fd_vm_t *)_vm;
57 12 : ulong ret = 1UL; /* by default return Ok(1) == error */
58 :
59 : /* Note: we don't strictly follow the Rust implementation, but instead combine
60 : common code across switch cases. Similar to fd_vm_syscall_sol_alt_bn128_group_op. */
61 :
62 : /* MATCH_ID_OP allows us to unify 2 switch/case into 1.
63 : For better readability, we also temp define EDWARDS, RISTRETTO.
64 :
65 : The first time we check that both curve_id and group_op are valid
66 : with 2 nested switch/case. Using MATCH_ID_OP leads to undesidered
67 : edge cases. The second time, when we know that curve_id and group_op
68 : are correct, then we can use MATCH_ID_OP and a single switch/case. */
69 24 : #define MATCH_ID_OP(crv_id,grp_op) ((crv_id << 4) | grp_op)
70 12 : #define EDWARDS FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS
71 12 : #define RISTRETTO FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO
72 :
73 12 : ulong cost = 0UL;
74 12 : switch( curve_id ) {
75 :
76 0 : case EDWARDS:
77 0 : switch( group_op ) {
78 :
79 0 : case FD_VM_SYSCALL_SOL_CURVE_ADD:
80 0 : cost = FD_VM_CURVE25519_EDWARDS_ADD_COST;
81 0 : break;
82 :
83 0 : case FD_VM_SYSCALL_SOL_CURVE_SUB:
84 0 : cost = FD_VM_CURVE25519_EDWARDS_SUBTRACT_COST;
85 0 : break;
86 :
87 0 : case FD_VM_SYSCALL_SOL_CURVE_MUL:
88 0 : cost = FD_VM_CURVE25519_EDWARDS_MULTIPLY_COST;
89 0 : break;
90 :
91 0 : default:
92 0 : goto invalid_error;
93 0 : }
94 0 : break;
95 :
96 12 : case RISTRETTO:
97 12 : switch( group_op ) {
98 :
99 6 : case FD_VM_SYSCALL_SOL_CURVE_ADD:
100 6 : cost = FD_VM_CURVE25519_RISTRETTO_ADD_COST;
101 6 : break;
102 :
103 3 : case FD_VM_SYSCALL_SOL_CURVE_SUB:
104 3 : cost = FD_VM_CURVE25519_RISTRETTO_SUBTRACT_COST;
105 3 : break;
106 :
107 3 : case FD_VM_SYSCALL_SOL_CURVE_MUL:
108 3 : cost = FD_VM_CURVE25519_RISTRETTO_MULTIPLY_COST;
109 3 : break;
110 :
111 0 : default:
112 0 : goto invalid_error;
113 12 : }
114 12 : break;
115 :
116 12 : default:
117 0 : goto invalid_error;
118 12 : }
119 :
120 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L944-L947 */
121 24 : FD_VM_CU_UPDATE( vm, cost );
122 :
123 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L949-L958 */
124 :
125 : /* Note: left_input_addr is a point for add, sub, BUT it's a scalar for mul.
126 : However, from a memory mapping perspective it's always 32 bytes, so we unify the code. */
127 36 : uchar const * inputL = FD_VM_MEM_HADDR_LD( vm, left_input_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, 32UL );
128 36 : uchar const * inputR = FD_VM_MEM_HADDR_LD( vm, right_input_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
129 :
130 12 : switch( MATCH_ID_OP( curve_id, group_op ) ) {
131 :
132 0 : case MATCH_ID_OP( EDWARDS, FD_VM_SYSCALL_SOL_CURVE_ADD ): {
133 0 : fd_ed25519_point_t p0[1], p1[1], r[1];
134 0 : if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p0, inputL ) ) ) {
135 0 : goto soft_error;
136 0 : }
137 0 : if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p1, inputR ) ) ) {
138 0 : goto soft_error;
139 0 : }
140 :
141 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1098-L1102 */
142 0 : fd_vm_haddr_query_t result_query = {
143 0 : .vaddr = result_point_addr,
144 0 : .align = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
145 0 : .sz = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
146 0 : .is_slice = 0,
147 0 : };
148 :
149 0 : fd_vm_haddr_query_t * queries[] = { &result_query };
150 0 : FD_VM_TRANSLATE_MUT( vm, queries );
151 :
152 0 : fd_ed25519_point_add( r, p0, p1 );
153 0 : fd_ed25519_point_tobytes( result_query.haddr, r );
154 0 : ret = 0UL;
155 0 : break;
156 0 : }
157 :
158 0 : case MATCH_ID_OP( EDWARDS, FD_VM_SYSCALL_SOL_CURVE_SUB ): {
159 0 : fd_ed25519_point_t p0[1], p1[1], r[1];
160 0 : if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p0, inputL ) ) ) {
161 0 : goto soft_error;
162 0 : }
163 0 : if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p1, inputR ) ) ) {
164 0 : goto soft_error;
165 0 : }
166 :
167 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1127-L1131 */
168 0 : fd_vm_haddr_query_t result_query = {
169 0 : .vaddr = result_point_addr,
170 0 : .align = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
171 0 : .sz = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
172 0 : .is_slice = 0,
173 0 : };
174 :
175 0 : fd_vm_haddr_query_t * queries[] = { &result_query };
176 0 : FD_VM_TRANSLATE_MUT( vm, queries );
177 :
178 0 : fd_ed25519_point_sub( r, p0, p1 );
179 0 : fd_ed25519_point_tobytes( result_query.haddr, r );
180 0 : ret = 0UL;
181 0 : break;
182 0 : }
183 :
184 0 : case MATCH_ID_OP( EDWARDS, FD_VM_SYSCALL_SOL_CURVE_MUL ): {
185 0 : fd_ed25519_point_t p[1], r[1];
186 0 : if( FD_UNLIKELY( !fd_curve25519_scalar_validate( inputL ) ) ) {
187 0 : goto soft_error;
188 0 : }
189 0 : if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p, inputR ) ) ) {
190 0 : goto soft_error;
191 0 : }
192 :
193 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1156-L1160 */
194 0 : fd_vm_haddr_query_t result_query = {
195 0 : .vaddr = result_point_addr,
196 0 : .align = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
197 0 : .sz = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
198 0 : .is_slice = 0,
199 0 : };
200 :
201 0 : fd_vm_haddr_query_t * queries[] = { &result_query };
202 0 : FD_VM_TRANSLATE_MUT( vm, queries );
203 :
204 0 : fd_ed25519_scalar_mul( r, inputL, p );
205 0 : fd_ed25519_point_tobytes( result_query.haddr, r );
206 0 : ret = 0UL;
207 0 : break;
208 0 : }
209 :
210 6 : case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_ADD ): {
211 6 : fd_ristretto255_point_t p0[1], p1[1], r[1];
212 6 : if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p0, inputL ) ) ) {
213 0 : goto soft_error;
214 0 : }
215 6 : if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p1, inputR ) ) ) {
216 0 : goto soft_error;
217 0 : }
218 :
219 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1195-L1199 */
220 6 : fd_vm_haddr_query_t result_query = {
221 6 : .vaddr = result_point_addr,
222 6 : .align = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
223 6 : .sz = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
224 6 : .is_slice = 0,
225 6 : };
226 :
227 6 : fd_vm_haddr_query_t * queries[] = { &result_query };
228 6 : FD_VM_TRANSLATE_MUT( vm, queries );
229 :
230 6 : fd_ristretto255_point_add( r, p0, p1 );
231 6 : fd_ristretto255_point_tobytes( result_query.haddr, r );
232 6 : ret = 0UL;
233 6 : break;
234 6 : }
235 :
236 3 : case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_SUB ): {
237 3 : fd_ristretto255_point_t p0[1], p1[1], r[1];
238 3 : if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p0, inputL ) ) ) {
239 0 : goto soft_error;
240 0 : }
241 3 : if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p1, inputR ) ) ) {
242 0 : goto soft_error;
243 0 : }
244 :
245 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1226-L1230 */
246 3 : fd_vm_haddr_query_t result_query = {
247 3 : .vaddr = result_point_addr,
248 3 : .align = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
249 3 : .sz = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
250 3 : .is_slice = 0,
251 3 : };
252 :
253 3 : fd_vm_haddr_query_t * queries[] = { &result_query };
254 3 : FD_VM_TRANSLATE_MUT( vm, queries );
255 :
256 3 : fd_ristretto255_point_sub( r, p0, p1 );
257 3 : fd_ristretto255_point_tobytes( result_query.haddr, r );
258 3 : ret = 0UL;
259 3 : break;
260 3 : }
261 :
262 3 : case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_MUL ): {
263 3 : fd_ristretto255_point_t p[1], r[1];
264 3 : if( FD_UNLIKELY( !fd_curve25519_scalar_validate( inputL ) ) ) {
265 0 : goto soft_error;
266 0 : }
267 3 : if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p, inputR ) ) ) {
268 0 : goto soft_error;
269 0 : }
270 :
271 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1255-L1259 */
272 3 : fd_vm_haddr_query_t result_query = {
273 3 : .vaddr = result_point_addr,
274 3 : .align = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
275 3 : .sz = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
276 3 : .is_slice = 0,
277 3 : };
278 :
279 3 : fd_vm_haddr_query_t * queries[] = { &result_query };
280 3 : FD_VM_TRANSLATE_MUT( vm, queries );
281 :
282 3 : fd_ristretto255_scalar_mul( r, inputL, p );
283 3 : fd_ristretto255_point_tobytes( result_query.haddr, r );
284 3 : ret = 0UL;
285 3 : break;
286 3 : }
287 :
288 0 : default:
289 : /* COV: this can never happen because of the previous switch */
290 0 : return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
291 36 : }
292 :
293 12 : soft_error:
294 12 : *_ret = ret;
295 12 : return FD_VM_SUCCESS;
296 0 : #undef MATCH_ID_OP
297 0 : #undef EDWARDS
298 0 : #undef RISTRETTO
299 :
300 0 : invalid_error:
301 : /* https://github.com/anza-xyz/agave/blob/5b3390b99a6e7665439c623062c1a1dda2803524/programs/bpf_loader/src/syscalls/mod.rs#L1135-L1156 */
302 0 : if( FD_FEATURE_ACTIVE_BANK( vm->instr_ctx->txn_ctx->bank, abort_on_invalid_curve ) ) {
303 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
304 0 : return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
305 0 : }
306 0 : *_ret = 1UL;
307 0 : return FD_VM_SUCCESS;
308 0 : }
309 :
310 : /* multi_scalar_mul_edwards computes a MSM on curve25519.
311 :
312 : This function is equivalent to
313 : zk-token-sdk::edwards::multi_scalar_mul_edwards
314 :
315 : https://github.com/solana-labs/solana/blob/v1.17.7/zk-token-sdk/src/curve25519/edwards.rs#L116
316 :
317 : Specifically it takes as input byte arrays and takes care of scalars
318 : validation and points decompression. It then invokes ballet MSM
319 : function fd_ed25519_multi_scalar_mul. To avoid dynamic allocation,
320 : the full MSM is done in batches of FD_BALLET_CURVE25519_MSM_BATCH_SZ. */
321 :
322 : static fd_ed25519_point_t *
323 : multi_scalar_mul_edwards( fd_ed25519_point_t * r,
324 : uchar const * scalars,
325 : uchar const * points,
326 3 : ulong cnt ) {
327 : /* Validate all scalars first (fast) */
328 9 : for( ulong i=0UL; i<cnt; i++ ) {
329 6 : if( FD_UNLIKELY( !fd_curve25519_scalar_validate ( scalars + i*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ ) ) ) {
330 0 : return NULL;
331 0 : }
332 6 : }
333 :
334 : /* Static allocation of a batch of decompressed points */
335 3 : fd_ed25519_point_t tmp[1];
336 3 : fd_ed25519_point_t A[ FD_BALLET_CURVE25519_MSM_BATCH_SZ ];
337 :
338 3 : fd_ed25519_point_set_zero( r );
339 6 : for( ulong i=0UL; i<cnt; i+=FD_BALLET_CURVE25519_MSM_BATCH_SZ ) {
340 3 : ulong batch_cnt = fd_ulong_min( cnt-i, FD_BALLET_CURVE25519_MSM_BATCH_SZ );
341 :
342 : /* Decompress (and validate) points */
343 9 : for( ulong j=0UL; j<batch_cnt; j++ ) {
344 : //TODO: use fd_ed25519_point_frombytes_2x
345 6 : if( FD_UNLIKELY( !fd_ed25519_point_frombytes( &A[j], points + j*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ ) ) ) {
346 0 : return NULL;
347 0 : }
348 6 : }
349 :
350 3 : fd_ed25519_multi_scalar_mul( tmp, scalars, A, batch_cnt );
351 3 : fd_ed25519_point_add( r, r, tmp );
352 3 : points += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ *batch_cnt;
353 3 : scalars += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ*batch_cnt;
354 3 : }
355 :
356 3 : return r;
357 3 : }
358 :
359 : /* multi_scalar_mul_ristretto computes a MSM on ristretto255.
360 : See multi_scalar_mul_edwards for details. */
361 :
362 : static fd_ed25519_point_t *
363 : multi_scalar_mul_ristretto( fd_ristretto255_point_t * r,
364 : uchar const * scalars,
365 : uchar const * points,
366 3 : ulong cnt ) {
367 : /* Validate all scalars first (fast) */
368 9 : for( ulong i=0UL; i<cnt; i++ ) {
369 6 : if( FD_UNLIKELY( !fd_curve25519_scalar_validate ( scalars + i*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ ) ) ) {
370 0 : return NULL;
371 0 : }
372 6 : }
373 :
374 : /* Static allocation of a batch of decompressed points */
375 3 : fd_ristretto255_point_t tmp[1];
376 3 : fd_ristretto255_point_t A[ FD_BALLET_CURVE25519_MSM_BATCH_SZ ];
377 :
378 3 : fd_ristretto255_point_set_zero( r );
379 6 : for( ulong i=0UL; i<cnt; i+=FD_BALLET_CURVE25519_MSM_BATCH_SZ ) {
380 3 : ulong batch_cnt = fd_ulong_min( cnt-i, FD_BALLET_CURVE25519_MSM_BATCH_SZ );
381 :
382 : /* Decompress (and validate) points */
383 9 : for( ulong j=0UL; j<batch_cnt; j++ ) {
384 : //TODO: use fd_ristretto255_point_frombytes_2x
385 6 : if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( &A[j], points + j*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ ) ) ) {
386 0 : return NULL;
387 0 : }
388 6 : }
389 :
390 3 : fd_ristretto255_multi_scalar_mul( tmp, scalars, A, batch_cnt );
391 3 : fd_ristretto255_point_add( r, r, tmp );
392 3 : points += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ *batch_cnt;
393 3 : scalars += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ*batch_cnt;
394 3 : }
395 :
396 3 : return r;
397 3 : }
398 :
399 : #undef BATCH_MAX
400 :
401 : int
402 : fd_vm_syscall_sol_curve_multiscalar_mul( void * _vm,
403 : ulong curve_id,
404 : ulong scalars_addr,
405 : ulong points_addr,
406 : ulong points_len,
407 : ulong result_point_addr,
408 15 : ulong * _ret ) {
409 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1129 */
410 15 : fd_vm_t * vm = (fd_vm_t *)_vm;
411 15 : ulong ret = 1UL; /* by default return Ok(1) == error */
412 :
413 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1143-L1151 */
414 15 : if( FD_UNLIKELY( points_len > 512 ) ) {
415 3 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );
416 3 : return FD_VM_SYSCALL_ERR_INVALID_LENGTH; /* SyscallError::InvalidLength */
417 3 : }
418 :
419 : /* Note: we don't strictly follow the Rust implementation, but instead combine
420 : common code across switch cases. Similar to fd_vm_syscall_sol_alt_bn128_group_op. */
421 :
422 12 : ulong base_cost = 0UL;
423 12 : ulong incremental_cost = 0UL;
424 12 : switch( curve_id ) {
425 6 : case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS:
426 6 : base_cost = FD_VM_CURVE25519_EDWARDS_MSM_BASE_COST;
427 6 : incremental_cost = FD_VM_CURVE25519_EDWARDS_MSM_INCREMENTAL_COST;
428 6 : break;
429 :
430 3 : case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO:
431 3 : base_cost = FD_VM_CURVE25519_RISTRETTO_MSM_BASE_COST;
432 3 : incremental_cost = FD_VM_CURVE25519_RISTRETTO_MSM_INCREMENTAL_COST;
433 3 : break;
434 :
435 3 : default:
436 : /* https://github.com/anza-xyz/agave/blob/5b3390b99a6e7665439c623062c1a1dda2803524/programs/bpf_loader/src/syscalls/mod.rs#L1262-L1271 */
437 3 : if( FD_FEATURE_ACTIVE( vm->instr_ctx->txn_ctx->slot, &vm->instr_ctx->txn_ctx->features, abort_on_invalid_curve ) ) {
438 3 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
439 3 : return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
440 3 : }
441 0 : goto soft_error;
442 12 : }
443 :
444 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1155-L1164 */
445 9 : ulong cost = fd_ulong_sat_add(
446 9 : base_cost,
447 9 : fd_ulong_sat_mul(
448 9 : incremental_cost,
449 9 : fd_ulong_sat_sub( points_len, 1 )
450 9 : )
451 9 : );
452 9 : FD_VM_CU_UPDATE( vm, cost );
453 :
454 : /* Edge case points_len==0.
455 : Agave computes the MSM, that returns the point at infinity, and stores the result.
456 : This means that we have to mem map result, and then set the point at infinity,
457 : that is 0x0100..00 for Edwards and 0x00..00 for Ristretto. */
458 9 : if ( FD_UNLIKELY( points_len==0 ) ) {
459 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 );
460 0 : memset( result, 0, 32 );
461 0 : result[0] = curve_id==FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS ? 1 : 0;
462 0 : *_ret = 0;
463 0 : return FD_VM_SUCCESS;
464 3 : }
465 :
466 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1166-L1178 */
467 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 );
468 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 );
469 :
470 0 : switch( curve_id ) {
471 :
472 3 : case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS: {
473 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1180-L1189 */
474 3 : fd_ed25519_point_t _r[1];
475 3 : fd_ed25519_point_t * r = multi_scalar_mul_edwards( _r, scalars, points, points_len );
476 :
477 3 : if( FD_LIKELY( r ) ) {
478 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1339-L1343 */
479 3 : fd_vm_haddr_query_t result_query = {
480 3 : .vaddr = result_point_addr,
481 3 : .align = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
482 3 : .sz = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
483 3 : .is_slice = 0,
484 3 : };
485 :
486 3 : fd_vm_haddr_query_t * queries[] = { &result_query };
487 3 : FD_VM_TRANSLATE_MUT( vm, queries );
488 :
489 3 : fd_ed25519_point_tobytes( result_query.haddr, r );
490 3 : ret = 0UL;
491 3 : }
492 3 : break;
493 3 : }
494 :
495 3 : case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO: {
496 3 : fd_ristretto255_point_t _r[1];
497 3 : fd_ristretto255_point_t * r = multi_scalar_mul_ristretto( _r, scalars, points, points_len );
498 :
499 3 : if( FD_LIKELY( r ) ) {
500 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1380-L1384 */
501 3 : fd_vm_haddr_query_t result_query = {
502 3 : .vaddr = result_point_addr,
503 3 : .align = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
504 3 : .sz = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
505 3 : .is_slice = 0,
506 3 : };
507 :
508 3 : fd_vm_haddr_query_t * queries[] = { &result_query };
509 3 : FD_VM_TRANSLATE_MUT( vm, queries );
510 :
511 3 : fd_ristretto255_point_tobytes( result_query.haddr, r );
512 3 : ret = 0UL;
513 3 : }
514 3 : break;
515 3 : }
516 :
517 3 : default:
518 : /* COV: this can never happen because of the previous switch */
519 0 : return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
520 18 : }
521 :
522 6 : soft_error:
523 6 : *_ret = ret;
524 6 : return FD_VM_SUCCESS;
525 18 : }
|