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