Line data Source code
1 : #include "fd_vm_syscall.h"
2 :
3 : #include "../../../ballet/bn254/fd_bn254.h"
4 : #include "../../../ballet/bn254/fd_poseidon.h"
5 : #include "../../../ballet/secp256k1/fd_secp256k1.h"
6 :
7 : int
8 : fd_vm_syscall_sol_alt_bn128_group_op( void * _vm,
9 : ulong group_op,
10 : ulong input_addr,
11 : ulong input_sz,
12 : ulong result_addr,
13 : FD_PARAM_UNUSED ulong r5,
14 6873 : ulong * _ret ) {
15 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1509 */
16 6873 : fd_vm_t * vm = (fd_vm_t *)_vm;
17 6873 : ulong ret = 1UL; /* by default return Ok(1) == error */
18 :
19 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1520-L1549 */
20 6873 : ulong cost = 0UL;
21 6873 : ulong output_sz = 0UL;
22 6873 : switch( group_op ) {
23 :
24 54 : case FD_VM_SYSCALL_SOL_ALT_BN128_ADD:
25 54 : output_sz = FD_VM_SYSCALL_SOL_ALT_BN128_G1_SZ;
26 54 : cost = FD_VM_ALT_BN128_ADDITION_COST;
27 54 : break;
28 :
29 5376 : case FD_VM_SYSCALL_SOL_ALT_BN128_MUL:
30 5376 : output_sz = FD_VM_SYSCALL_SOL_ALT_BN128_G1_SZ;
31 5376 : cost = FD_VM_ALT_BN128_MULTIPLICATION_COST;
32 5376 : break;
33 :
34 1437 : case FD_VM_SYSCALL_SOL_ALT_BN128_PAIRING:
35 1437 : output_sz = FD_VM_SYSCALL_SOL_ALT_BN128_PAIRING_OUTPUT_SZ;
36 1437 : ulong elements_len = input_sz / FD_VM_SYSCALL_SOL_ALT_BN128_PAIRING_INPUT_EL_SZ;
37 1437 : cost = FD_VM_ALT_BN128_PAIRING_ONE_PAIR_COST_FIRST
38 1437 : + FD_VM_SHA256_BASE_COST
39 1437 : + FD_VM_SYSCALL_SOL_ALT_BN128_PAIRING_OUTPUT_SZ;
40 1437 : cost = fd_ulong_sat_add( cost,
41 1437 : fd_ulong_sat_mul( FD_VM_ALT_BN128_PAIRING_ONE_PAIR_COST_OTHER,
42 1437 : fd_ulong_sat_sub( elements_len, 1 ) ) );
43 1437 : cost = fd_ulong_sat_add( cost, input_sz );
44 1437 : break;
45 :
46 6 : default:
47 6 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_INVALID_ATTRIBUTE );
48 6 : return FD_VM_ERR_INVAL; /* SyscallError::InvalidAttribute */
49 6873 : }
50 :
51 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1551 */
52 :
53 13725 : FD_VM_CU_UPDATE( vm, cost );
54 :
55 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1553-L1565 */
56 :
57 13707 : uchar const * input = FD_VM_MEM_SLICE_HADDR_LD( vm, input_addr, FD_VM_ALIGN_RUST_U8, input_sz );
58 13689 : uchar * call_result = FD_VM_MEM_SLICE_HADDR_ST( vm, result_addr, FD_VM_ALIGN_RUST_U8, output_sz );
59 :
60 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1567-L1598
61 : Note: this implementation is post SIMD-0129, we only support the simplified error codes. */
62 0 : switch( group_op ) {
63 :
64 45 : case FD_VM_SYSCALL_SOL_ALT_BN128_ADD:
65 : /* Compute add */
66 45 : if( FD_LIKELY( fd_bn254_g1_add_syscall( call_result, input, input_sz )==0 ) ) {
67 36 : ret = 0UL; /* success */
68 36 : }
69 45 : break;
70 :
71 5367 : case FD_VM_SYSCALL_SOL_ALT_BN128_MUL:
72 : /* Compute scalar mul */
73 5367 : if( FD_LIKELY( fd_bn254_g1_scalar_mul_syscall( call_result, input, input_sz )==0 ) ) {
74 1392 : ret = 0UL; /* success */
75 1392 : }
76 5367 : break;
77 :
78 1428 : case FD_VM_SYSCALL_SOL_ALT_BN128_PAIRING:
79 : /* Compute pairing */
80 1428 : if( FD_LIKELY( fd_bn254_pairing_is_one_syscall( call_result, input, input_sz )==0 ) ) {
81 264 : ret = 0UL; /* success */
82 264 : }
83 1428 : break;
84 13689 : }
85 :
86 6840 : *_ret = ret;
87 6840 : return FD_VM_SUCCESS; /* Ok(SUCCESS) or Ok(ERROR) */
88 13689 : }
89 :
90 : int
91 : fd_vm_syscall_sol_alt_bn128_compression( void * _vm,
92 : ulong op,
93 : ulong input_addr,
94 : ulong input_sz,
95 : ulong result_addr,
96 : FD_PARAM_UNUSED ulong r5,
97 2811 : ulong * _ret ) {
98 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1776 */
99 2811 : fd_vm_t * vm = (fd_vm_t *)_vm;
100 2811 : ulong ret = 1UL; /* by default return Ok(1) == error */
101 :
102 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1791-L1811 */
103 2811 : ulong cost = 0UL;
104 2811 : ulong output_sz = 0UL;
105 2811 : switch( op ) {
106 :
107 606 : case FD_VM_SYSCALL_SOL_ALT_BN128_G1_COMPRESS:
108 606 : output_sz = FD_VM_SYSCALL_SOL_ALT_BN128_G1_COMPRESSED_SZ;
109 606 : cost = FD_VM_ALT_BN128_G1_COMPRESS;
110 606 : break;
111 :
112 606 : case FD_VM_SYSCALL_SOL_ALT_BN128_G1_DECOMPRESS:
113 606 : output_sz = FD_VM_SYSCALL_SOL_ALT_BN128_G1_SZ;
114 606 : cost = FD_VM_ALT_BN128_G1_DECOMPRESS;
115 606 : break;
116 :
117 798 : case FD_VM_SYSCALL_SOL_ALT_BN128_G2_COMPRESS:
118 798 : output_sz = FD_VM_SYSCALL_SOL_ALT_BN128_G2_COMPRESSED_SZ;
119 798 : cost = FD_VM_ALT_BN128_G2_COMPRESS;
120 798 : break;
121 :
122 798 : case FD_VM_SYSCALL_SOL_ALT_BN128_G2_DECOMPRESS:
123 798 : output_sz = FD_VM_SYSCALL_SOL_ALT_BN128_G2_SZ;
124 798 : cost = FD_VM_ALT_BN128_G2_DECOMPRESS;
125 798 : break;
126 :
127 3 : default:
128 3 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_INVALID_ATTRIBUTE );
129 3 : return FD_VM_ERR_INVAL; /* SyscallError::InvalidAttribute */
130 2811 : }
131 2808 : cost = fd_ulong_sat_add( cost, FD_VM_SYSCALL_BASE_COST );
132 :
133 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1813 */
134 :
135 2808 : FD_VM_CU_UPDATE( vm, cost );
136 :
137 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1815-L1827 */
138 :
139 5580 : void const * input = FD_VM_MEM_SLICE_HADDR_LD( vm, input_addr, FD_VM_ALIGN_RUST_U8, input_sz );
140 5556 : void * call_result = FD_VM_MEM_SLICE_HADDR_ST( vm, result_addr, FD_VM_ALIGN_RUST_U8, output_sz );
141 :
142 : /* input and call_result may alias. Therefore, buffer via out_buf */
143 0 : uchar out_buf[128];
144 :
145 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1829-L1891
146 : Note: this implementation is post SIMD-0129, we only support the simplified error codes. */
147 5556 : switch( op ) {
148 :
149 597 : case FD_VM_SYSCALL_SOL_ALT_BN128_G1_COMPRESS:
150 597 : if( FD_UNLIKELY( input_sz!=FD_VM_SYSCALL_SOL_ALT_BN128_G1_SZ ) ) {
151 3 : goto soft_error;
152 3 : }
153 594 : if( FD_LIKELY( fd_bn254_g1_compress( out_buf, fd_type_pun_const(input) ) ) ) {
154 186 : fd_memcpy( call_result, out_buf, FD_VM_SYSCALL_SOL_ALT_BN128_G1_COMPRESSED_SZ );
155 186 : ret = 0UL; /* success */
156 186 : }
157 594 : break;
158 :
159 597 : case FD_VM_SYSCALL_SOL_ALT_BN128_G1_DECOMPRESS:
160 597 : if( FD_UNLIKELY( input_sz!=FD_VM_SYSCALL_SOL_ALT_BN128_G1_COMPRESSED_SZ ) ) {
161 3 : goto soft_error;
162 3 : }
163 594 : if( FD_LIKELY( fd_bn254_g1_decompress( out_buf, fd_type_pun_const(input) ) ) ) {
164 414 : fd_memcpy( call_result, out_buf, FD_VM_SYSCALL_SOL_ALT_BN128_G1_SZ );
165 414 : ret = 0UL; /* success */
166 414 : }
167 594 : break;
168 :
169 789 : case FD_VM_SYSCALL_SOL_ALT_BN128_G2_COMPRESS:
170 789 : if( FD_UNLIKELY( input_sz!=FD_VM_SYSCALL_SOL_ALT_BN128_G2_SZ ) ) {
171 3 : goto soft_error;
172 3 : }
173 786 : if( FD_LIKELY( fd_bn254_g2_compress( out_buf, fd_type_pun_const(input) ) ) ) {
174 114 : fd_memcpy( call_result, out_buf, FD_VM_SYSCALL_SOL_ALT_BN128_G2_COMPRESSED_SZ );
175 114 : ret = 0UL; /* success */
176 114 : }
177 786 : break;
178 :
179 789 : case FD_VM_SYSCALL_SOL_ALT_BN128_G2_DECOMPRESS:
180 789 : if( FD_UNLIKELY( input_sz!=FD_VM_SYSCALL_SOL_ALT_BN128_G2_COMPRESSED_SZ ) ) {
181 3 : goto soft_error;
182 3 : }
183 786 : if( FD_LIKELY( fd_bn254_g2_decompress( out_buf, fd_type_pun_const(input) ) ) ) {
184 402 : fd_memcpy( call_result, out_buf, FD_VM_SYSCALL_SOL_ALT_BN128_G2_SZ );
185 402 : ret = 0UL; /* success */
186 402 : }
187 786 : break;
188 5556 : }
189 :
190 2772 : soft_error:
191 2772 : *_ret = ret;
192 2772 : return FD_VM_SUCCESS; /* Ok(SUCCESS) or Ok(ERROR) */
193 5556 : }
194 :
195 : int
196 : fd_vm_syscall_sol_poseidon( void * _vm,
197 : ulong params,
198 : ulong endianness,
199 : ulong vals_addr,
200 : ulong vals_len,
201 : ulong result_addr,
202 126 : ulong * _ret ) {
203 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1678 */
204 126 : fd_vm_t * vm = (fd_vm_t *)_vm;
205 126 : ulong ret = 1UL; /* by default return Ok(1) == error */
206 :
207 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1688 */
208 :
209 126 : if( FD_UNLIKELY( params!=0UL ) ) {
210 3 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_POSEIDON_INVALID_PARAMS );
211 3 : return FD_VM_ERR_INVAL; /* PoseidonSyscallError::InvalidParameters */
212 3 : }
213 :
214 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1689 */
215 :
216 123 : if( FD_UNLIKELY(
217 123 : endianness!=0UL /* Big endian */
218 123 : && endianness!=1UL /* Little endian */
219 123 : ) ) {
220 3 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_POSEIDON_INVALID_ENDIANNESS );
221 3 : return FD_VM_ERR_INVAL; /* PoseidonSyscallError::InvalidEndianness */
222 3 : }
223 :
224 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1691-L1698 */
225 :
226 120 : if( FD_UNLIKELY( vals_len > FD_VM_SYSCALL_SOL_POSEIDON_MAX_VALS ) ) {
227 : /* Max msg_sz = 47 - 3 + 20 = 64 < 127 => we can use printf */
228 6 : fd_log_collector_printf_dangerous_max_127( vm->instr_ctx,
229 6 : "Poseidon hashing %lu sequences is not supported", vals_len );
230 6 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_INVALID_LENGTH );
231 6 : return FD_VM_ERR_INVAL; /* SyscallError::InvalidLength */
232 6 : }
233 :
234 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1700-L1707
235 : poseidon_cost(): https://github.com/solana-labs/solana/blob/v1.18.12/program-runtime/src/compute_budget.rs#L211 */
236 :
237 : /* vals_len^2 * A + C */
238 114 : ulong cost = fd_ulong_sat_add(
239 114 : fd_ulong_sat_mul(
240 114 : fd_ulong_sat_mul( vals_len, vals_len ),
241 114 : FD_VM_POSEIDON_COST_COEFFICIENT_A
242 114 : ),
243 114 : FD_VM_POSEIDON_COST_COEFFICIENT_C
244 114 : );
245 :
246 : /* The following can never happen, left as comment for completeness.
247 : if( FD_UNLIKELY( cost == ULONG_MAX ) ) {
248 : fd_vm_log_append_printf( vm, "Overflow while calculating the compute cost" );
249 : return FD_VM_ERR_INVAL; // SyscallError::ArithmeticOverflow
250 : }
251 : */
252 :
253 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1708 */
254 :
255 114 : FD_VM_CU_UPDATE( vm, cost );
256 :
257 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1710-L1715 */
258 :
259 279 : void * hash_result = FD_VM_MEM_HADDR_ST( vm, result_addr, FD_VM_ALIGN_RUST_U8, 32UL );
260 :
261 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1716-L1732 */
262 :
263 : /* Agave allocates a vector of translated slices (that can return a fatal
264 : error), and then computes Poseidon, returning a soft error in case of
265 : issues (e.g. invalid input).
266 :
267 : We must be careful in returning the correct fatal vs soft error.
268 :
269 : The special case of vals_len==0 returns Ok(1), so for simplicity
270 : we capture it explicitly. */
271 :
272 90 : if( FD_UNLIKELY( !vals_len ) ) {
273 3 : goto soft_error;
274 3 : }
275 :
276 : /* First loop to memory map. This can return a fatal error. */
277 249 : fd_vm_vec_t const * input_vec_haddr = (fd_vm_vec_t const *)FD_VM_MEM_HADDR_LD( vm, vals_addr, FD_VM_VEC_ALIGN, vals_len*sizeof(fd_vm_vec_t) );
278 0 : void const * inputs_haddr[ FD_VM_SYSCALL_SOL_POSEIDON_MAX_VALS ];
279 393 : for( ulong i=0UL; i<vals_len; i++ ) {
280 630 : inputs_haddr[i] = FD_VM_MEM_SLICE_HADDR_LD( vm, input_vec_haddr[i].addr, FD_VM_ALIGN_RUST_U8, input_vec_haddr[i].len );
281 630 : }
282 :
283 : /* https://github.com/anza-xyz/agave/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1734-L1750
284 : Note: this implementation is post SIMD-0129, we only support the simplified error codes. */
285 :
286 : /* Second loop to computed Poseidon. This can return a soft error. */
287 75 : int big_endian = endianness==0;
288 75 : fd_poseidon_t pos[1];
289 75 : fd_poseidon_init( pos, big_endian );
290 :
291 363 : for( ulong i=0UL; i<vals_len; i++ ) {
292 300 : if( FD_UNLIKELY( fd_poseidon_append( pos, inputs_haddr[ i ], input_vec_haddr[i].len )==NULL ) ) {
293 12 : goto soft_error;
294 12 : }
295 300 : }
296 :
297 63 : ret = !fd_poseidon_fini( pos, hash_result );
298 :
299 78 : soft_error:
300 78 : *_ret = ret;
301 78 : return FD_VM_SUCCESS; /* Ok(1) == error */
302 63 : }
303 :
304 : int
305 : fd_vm_syscall_sol_secp256k1_recover( /**/ void * _vm,
306 : /**/ ulong hash_vaddr,
307 : /**/ ulong recovery_id_val,
308 : /**/ ulong signature_vaddr,
309 : /**/ ulong result_vaddr,
310 : FD_PARAM_UNUSED ulong r5,
311 51 : /**/ ulong * _ret ) {
312 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L810 */
313 51 : fd_vm_t * vm = (fd_vm_t *)_vm;
314 :
315 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L820-L821 */
316 :
317 51 : FD_VM_CU_UPDATE( vm, FD_VM_SECP256K1_RECOVER_COST );
318 :
319 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L823-L840 */
320 :
321 126 : uchar const * hash = FD_VM_MEM_HADDR_LD( vm, hash_vaddr, FD_VM_ALIGN_RUST_U8, 32UL );
322 99 : uchar const * sig = FD_VM_MEM_HADDR_LD( vm, signature_vaddr, FD_VM_ALIGN_RUST_U8, 64UL );
323 72 : uchar * pubkey_result = FD_VM_MEM_HADDR_ST( vm, result_vaddr, FD_VM_ALIGN_RUST_U8, 64UL );
324 :
325 : /* CRITICAL */
326 :
327 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L842-L853 */
328 :
329 : /* Secp256k1RecoverError::InvalidHash
330 : This can never happen, as `libsecp256k1::Message::parse_slice(hash)`
331 : only checks that hash is 32-byte long, and that's by construction.
332 : https://github.com/paritytech/libsecp256k1/blob/v0.6.0/src/lib.rs#L657-L665
333 :
334 : if( FD_UNLIKELY( 0 ) ) {
335 : *_ret = 1UL; // Secp256k1RecoverError::InvalidHash
336 : return FD_VM_SUCCESS;
337 : }
338 : */
339 :
340 : /* Secp256k1RecoverError::InvalidRecoveryId
341 : Agave code has 2 checks: the first is a cast from u64 to u8.
342 : The second is `libsecp256k1::RecoveryId::parse(adjusted_recover_id_val)` that
343 : checks if `adjusted_recover_id_val < 4`.
344 : https://github.com/paritytech/libsecp256k1/blob/v0.6.0/src/lib.rs#L674-L680
345 : */
346 :
347 21 : if( FD_UNLIKELY( recovery_id_val >= 4UL ) ) {
348 6 : *_ret = 2UL; /* Secp256k1RecoverError::InvalidRecoveryId */
349 6 : return FD_VM_SUCCESS;
350 6 : }
351 :
352 : /* Secp256k1RecoverError::InvalidSignature
353 : We omit this check, as this is done as part of fd_secp256k1_recover() below,
354 : and the return code is the same.
355 :
356 : In more details, this checks that the signature is valid, i.e. if the
357 : signature is represented as two scalars (r, s), it checks that both r
358 : and s are canonical scalars.
359 :
360 : Note the `?` at the end of this line:
361 : https://github.com/paritytech/libsecp256k1/blob/v0.6.0/src/lib.rs#L535
362 : And following the code, `scalar::check_overflow` is checks that the scalar is valid:
363 : https://github.com/paritytech/libsecp256k1/blob/master/core/src/scalar.rs#L70-L87 */
364 :
365 : /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L855-L860 */
366 :
367 15 : uchar secp256k1_pubkey[64];
368 15 : if( FD_UNLIKELY( !fd_secp256k1_recover( secp256k1_pubkey, hash, sig, (int)recovery_id_val ) ) ) {
369 6 : *_ret = 3UL; /* Secp256k1RecoverError::InvalidSignature */
370 6 : return FD_VM_SUCCESS;
371 6 : }
372 :
373 9 : memcpy( pubkey_result, secp256k1_pubkey, 64UL );
374 :
375 9 : *_ret = 0UL;
376 9 : return FD_VM_SUCCESS;
377 15 : }
|