Line data Source code
1 : #include "fd_vm_syscall.h"
2 : #include "../../runtime/program/fd_vote_program.h"
3 : #include "../../runtime/sysvar/fd_sysvar.h"
4 : #include "../../runtime/sysvar/fd_sysvar_clock.h"
5 : #include "../../runtime/sysvar/fd_sysvar_epoch_rewards.h"
6 : #include "../../runtime/sysvar/fd_sysvar_epoch_schedule.h"
7 : #include "../../runtime/sysvar/fd_sysvar_rent.h"
8 : #include "../../runtime/sysvar/fd_sysvar_last_restart_slot.h"
9 : #include "../../runtime/context/fd_exec_txn_ctx.h"
10 : #include "../../runtime/context/fd_exec_instr_ctx.h"
11 : #include "../../runtime/fd_system_ids.h"
12 : #include "fd_vm_syscall_macros.h"
13 :
14 : int
15 : fd_vm_syscall_sol_get_clock_sysvar( /**/ void * _vm,
16 : /**/ ulong out_vaddr,
17 : FD_PARAM_UNUSED ulong r2,
18 : FD_PARAM_UNUSED ulong r3,
19 : FD_PARAM_UNUSED ulong r4,
20 : FD_PARAM_UNUSED ulong r5,
21 0 : /**/ ulong * _ret ) {
22 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
23 :
24 : /* FIXME: In the original version of this code, there was an FD_TEST
25 : to check if the VM was attached to an instruction context (that
26 : would have crashed anyway because of pointer chasing). If the VM
27 : is being run outside the Solana runtime, it should never invoke
28 : this syscall in the first place. So we treat this as a SIGCALL in
29 : a non-crashing way. */
30 :
31 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
32 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
33 :
34 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, sizeof(fd_sol_sysvar_clock_t) ) );
35 :
36 0 : fd_vm_haddr_query_t var_query = {
37 0 : .vaddr = out_vaddr,
38 0 : .align = FD_VM_ALIGN_RUST_SYSVAR_CLOCK,
39 0 : .sz = sizeof(fd_sol_sysvar_clock_t),
40 0 : .is_slice = 0,
41 0 : };
42 :
43 0 : fd_vm_haddr_query_t * queries[] = { &var_query };
44 0 : FD_VM_TRANSLATE_MUT( vm, queries );
45 :
46 0 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_clock_read( instr_ctx->txn_ctx->funk,
47 0 : instr_ctx->txn_ctx->funk_txn,
48 0 : instr_ctx->txn_ctx->spad );
49 0 : if( FD_UNLIKELY( !clock ) ) {
50 0 : FD_LOG_ERR(( "failed to read sysvar clock" ));
51 0 : }
52 :
53 0 : memcpy( var_query.haddr, clock, sizeof(fd_sol_sysvar_clock_t) );
54 :
55 0 : *_ret = 0UL;
56 0 : return FD_VM_SUCCESS;
57 0 : }
58 :
59 : int
60 : fd_vm_syscall_sol_get_epoch_schedule_sysvar( /**/ void * _vm,
61 : /**/ ulong out_vaddr,
62 : FD_PARAM_UNUSED ulong r2,
63 : FD_PARAM_UNUSED ulong r3,
64 : FD_PARAM_UNUSED ulong r4,
65 : FD_PARAM_UNUSED ulong r5,
66 0 : /**/ ulong * _ret ) {
67 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
68 :
69 : /* FIXME: In the original version of this code, there was an FD_TEST
70 : to check if the VM was attached to an instruction context (that
71 : would have crashed anyway because of pointer chasing). If the VM
72 : is being run outside the Solana runtime, it should never invoke
73 : this syscall in the first place. So we treat this as a SIGCALL in
74 : a non-crashing way for the time being. */
75 :
76 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
77 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
78 :
79 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, sizeof(fd_epoch_schedule_t) ) );
80 :
81 0 : fd_vm_haddr_query_t var_query = {
82 0 : .vaddr = out_vaddr,
83 0 : .align = FD_VM_ALIGN_RUST_SYSVAR_EPOCH_SCHEDULE,
84 0 : .sz = sizeof(fd_epoch_schedule_t),
85 0 : .is_slice = 0,
86 0 : };
87 :
88 0 : fd_vm_haddr_query_t * queries[] = { &var_query };
89 0 : FD_VM_TRANSLATE_MUT( vm, queries );
90 :
91 0 : fd_epoch_schedule_t * schedule = fd_sysvar_epoch_schedule_read( instr_ctx->txn_ctx->funk,
92 0 : instr_ctx->txn_ctx->funk_txn,
93 0 : instr_ctx->txn_ctx->spad );
94 0 : if( FD_UNLIKELY( schedule == NULL ) ) {
95 0 : FD_LOG_ERR(( "failed to read sysvar epoch schedule" ));
96 0 : }
97 :
98 0 : memcpy( var_query.haddr, schedule, sizeof(fd_epoch_schedule_t) );
99 :
100 0 : *_ret = 0UL;
101 0 : return FD_VM_SUCCESS;
102 0 : }
103 :
104 : int
105 : fd_vm_syscall_sol_get_rent_sysvar( /**/ void * _vm,
106 : /**/ ulong out_vaddr,
107 : FD_PARAM_UNUSED ulong r2,
108 : FD_PARAM_UNUSED ulong r3,
109 : FD_PARAM_UNUSED ulong r4,
110 : FD_PARAM_UNUSED ulong r5,
111 0 : /**/ ulong * _ret ) {
112 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
113 :
114 : /* FIXME: In the original version of this code, there was an FD_TEST
115 : to check if the VM was attached to an instruction context (that
116 : would have crashed anyway because of pointer chasing). If the VM
117 : is being run outside the Solana runtime, it should never invoke
118 : this syscall in the first place. So we treat this as a SIGCALL in
119 : a non-crashing way for the time being. */
120 :
121 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
122 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
123 :
124 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, sizeof(fd_rent_t) ) );
125 :
126 0 : fd_vm_haddr_query_t var_query = {
127 0 : .vaddr = out_vaddr,
128 0 : .align = FD_VM_ALIGN_RUST_SYSVAR_RENT,
129 0 : .sz = sizeof(fd_rent_t),
130 0 : .is_slice = 0,
131 0 : };
132 :
133 0 : fd_vm_haddr_query_t * queries[] = { &var_query };
134 0 : FD_VM_TRANSLATE_MUT( vm, queries );
135 :
136 0 : fd_rent_t const * rent = fd_sysvar_rent_read( instr_ctx->txn_ctx->funk,
137 0 : instr_ctx->txn_ctx->funk_txn,
138 0 : instr_ctx->txn_ctx->spad );
139 0 : if( FD_UNLIKELY( !rent ) ) {
140 0 : FD_LOG_ERR(( "failed to read sysvar rent" ));
141 0 : }
142 :
143 0 : memcpy( var_query.haddr, rent, sizeof(fd_rent_t) );
144 :
145 0 : *_ret = 0UL;
146 0 : return FD_VM_SUCCESS;
147 0 : }
148 :
149 : /* https://github.com/anza-xyz/agave/blob/36323b6dcd3e29e4d6fe6d73d716a3f33927148b/programs/bpf_loader/src/syscalls/sysvar.rs#L144 */
150 : int
151 : fd_vm_syscall_sol_get_last_restart_slot_sysvar( /**/ void * _vm,
152 : /**/ ulong out_vaddr,
153 : FD_PARAM_UNUSED ulong r2,
154 : FD_PARAM_UNUSED ulong r3,
155 : FD_PARAM_UNUSED ulong r4,
156 : FD_PARAM_UNUSED ulong r5,
157 0 : /**/ ulong * _ret ) {
158 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
159 :
160 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, sizeof(fd_sol_sysvar_last_restart_slot_t) ) );
161 :
162 0 : fd_vm_haddr_query_t var_query = {
163 0 : .vaddr = out_vaddr,
164 0 : .align = FD_VM_ALIGN_RUST_SYSVAR_LAST_RESTART_SLOT,
165 0 : .sz = sizeof(fd_sol_sysvar_last_restart_slot_t),
166 0 : .is_slice = 0,
167 0 : };
168 :
169 0 : fd_vm_haddr_query_t * queries[] = { &var_query };
170 0 : FD_VM_TRANSLATE_MUT( vm, queries );
171 :
172 0 : fd_sol_sysvar_last_restart_slot_t * last_restart_slot = fd_sysvar_last_restart_slot_read( vm->instr_ctx->txn_ctx->funk,
173 0 : vm->instr_ctx->txn_ctx->funk_txn,
174 0 : vm->instr_ctx->txn_ctx->spad );
175 0 : if( FD_UNLIKELY( !last_restart_slot ) ) {
176 0 : FD_LOG_ERR(( "failed to read sysvar last restart slot" ));
177 0 : }
178 :
179 0 : memcpy( var_query.haddr, last_restart_slot, sizeof(fd_sol_sysvar_last_restart_slot_t) );
180 :
181 0 : *_ret = 0UL;
182 0 : return FD_VM_SUCCESS;
183 0 : }
184 :
185 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L167-L232 */
186 : int
187 : fd_vm_syscall_sol_get_sysvar( /**/ void * _vm,
188 : /**/ ulong sysvar_id_vaddr,
189 : /**/ ulong out_vaddr,
190 : /**/ ulong offset,
191 : /**/ ulong sz,
192 : FD_PARAM_UNUSED ulong r5,
193 0 : /**/ ulong * _ret ) {
194 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
195 :
196 : /* sysvar_id_cost seems to just always be 32 / 250 = 0...
197 : https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L190-L197 */
198 0 : ulong sysvar_buf_cost = sz / FD_VM_CPI_BYTES_PER_UNIT;
199 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, fd_ulong_max( sysvar_buf_cost, FD_VM_MEM_OP_BASE_COST ) ) );
200 :
201 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/sysvar.rs#L207-L211 */
202 0 : fd_vm_haddr_query_t var_query = {
203 0 : .vaddr = out_vaddr,
204 0 : .align = FD_VM_ALIGN_RUST_U8,
205 0 : .sz = sz,
206 0 : .is_slice = 1,
207 0 : };
208 :
209 0 : fd_vm_haddr_query_t * queries[] = { &var_query };
210 0 : FD_VM_TRANSLATE_MUT( vm, queries );
211 :
212 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L199-L200 */
213 0 : const fd_pubkey_t * sysvar_id = FD_VM_MEM_HADDR_LD( vm, sysvar_id_vaddr, FD_VM_ALIGN_RUST_PUBKEY, FD_PUBKEY_FOOTPRINT );
214 :
215 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L205-L208 */
216 0 : ulong offset_length;
217 0 : int err = fd_int_if( __builtin_uaddl_overflow( offset, sz, &offset_length ), FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW, FD_EXECUTOR_INSTR_SUCCESS );
218 0 : if( FD_UNLIKELY( err ) ) {
219 0 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
220 0 : return FD_VM_SYSCALL_ERR_ABORT;
221 0 : }
222 :
223 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L210-L213
224 : We don't need this, we already checked we can store in out_vaddr with requested sz. */
225 :
226 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L215-L221 */
227 0 : if( FD_UNLIKELY( memcmp( sysvar_id->uc, fd_sysvar_clock_id.uc, FD_PUBKEY_FOOTPRINT ) &&
228 0 : memcmp( sysvar_id->uc, fd_sysvar_epoch_schedule_id.uc, FD_PUBKEY_FOOTPRINT ) &&
229 0 : memcmp( sysvar_id->uc, fd_sysvar_epoch_rewards_id.uc, FD_PUBKEY_FOOTPRINT ) &&
230 0 : memcmp( sysvar_id->uc, fd_sysvar_rent_id.uc, FD_PUBKEY_FOOTPRINT ) &&
231 0 : memcmp( sysvar_id->uc, fd_sysvar_slot_hashes_id.uc, FD_PUBKEY_FOOTPRINT ) &&
232 0 : memcmp( sysvar_id->uc, fd_sysvar_stake_history_id.uc, FD_PUBKEY_FOOTPRINT ) &&
233 0 : memcmp( sysvar_id->uc, fd_sysvar_last_restart_slot_id.uc, FD_PUBKEY_FOOTPRINT ) ) ) {
234 0 : *_ret = 2UL;
235 0 : return FD_VM_SUCCESS;
236 0 : }
237 :
238 : /* we know that the account data won't be changed for the lifetime of this view, because sysvars don't change inter-block */
239 0 : FD_TXN_ACCOUNT_DECL( sysvar_account );
240 0 : err = fd_txn_account_init_from_funk_readonly( sysvar_account, sysvar_id, vm->instr_ctx->txn_ctx->funk, vm->instr_ctx->txn_ctx->funk_txn );
241 0 : if( FD_UNLIKELY( err ) ) {
242 0 : *_ret = 2UL;
243 0 : return FD_VM_SUCCESS;
244 0 : }
245 :
246 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L223-L228
247 : Note the length check is at the very end to fail after performing sufficient checks. */
248 0 : const uchar * sysvar_buf = sysvar_account->vt->get_data( sysvar_account );
249 0 : ulong sysvar_buf_len = sysvar_account->vt->get_data_len( sysvar_account );
250 :
251 0 : if( FD_UNLIKELY( offset_length>sysvar_buf_len ) ) {
252 0 : *_ret = 1UL;
253 0 : return FD_VM_SUCCESS;
254 0 : }
255 :
256 0 : if( FD_UNLIKELY( sz==0UL ) ) {
257 0 : *_ret = 0UL;
258 0 : return FD_VM_SUCCESS;
259 0 : }
260 :
261 0 : fd_memcpy( var_query.haddr, sysvar_buf + offset, sz );
262 0 : *_ret = 0;
263 0 : return FD_VM_SUCCESS;
264 0 : }
265 :
266 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2043-L2118
267 :
268 : This syscall is meant to return the latest frozen stakes at an epoch
269 : boundary. So for instance, when we are executing in epoch 7, this
270 : should return the stakes at the end of epoch 6. Note that this is
271 : also the stakes that determined the leader schedule for the upcoming
272 : epoch, namely epoch 8. */
273 : int
274 : fd_vm_syscall_sol_get_epoch_stake( /**/ void * _vm,
275 : /**/ ulong var_addr,
276 : FD_PARAM_UNUSED ulong r2,
277 : FD_PARAM_UNUSED ulong r3,
278 : FD_PARAM_UNUSED ulong r4,
279 : FD_PARAM_UNUSED ulong r5,
280 0 : /**/ ulong * _ret ) {
281 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
282 :
283 : /* Var addr of 0 returns the total active stake on the cluster.
284 :
285 : https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2057-L2075 */
286 0 : if( FD_UNLIKELY( var_addr==0UL ) ) {
287 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2065-L2066 */
288 0 : FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST );
289 :
290 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2074 */
291 0 : *_ret = fd_bank_total_epoch_stake_get( vm->instr_ctx->txn_ctx->bank );
292 0 : return FD_VM_SUCCESS;
293 0 : }
294 :
295 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2083-L2091 */
296 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_MEM_OP_BASE_COST,
297 0 : fd_ulong_sat_add( FD_VM_SYSCALL_BASE_COST, FD_PUBKEY_FOOTPRINT / FD_VM_CPI_BYTES_PER_UNIT ) ) );
298 :
299 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2103-L2104 */
300 0 : const fd_pubkey_t * vote_address = FD_VM_MEM_HADDR_LD( vm, var_addr, FD_VM_ALIGN_RUST_PUBKEY, FD_PUBKEY_FOOTPRINT );
301 :
302 : /* https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank.rs#L6954 */
303 0 : fd_vote_accounts_global_t const * next_epoch_stakes = fd_bank_next_epoch_stakes_locking_query( vm->instr_ctx->txn_ctx->bank );
304 0 : *_ret = fd_query_pubkey_stake( vote_address, next_epoch_stakes );
305 0 : fd_bank_next_epoch_stakes_end_locking_query( vm->instr_ctx->txn_ctx->bank );
306 :
307 0 : return FD_VM_SUCCESS;
308 0 : }
309 :
310 : int
311 : fd_vm_syscall_sol_get_stack_height( /**/ void * _vm,
312 : FD_PARAM_UNUSED ulong r1,
313 : FD_PARAM_UNUSED ulong r2,
314 : FD_PARAM_UNUSED ulong r3,
315 : FD_PARAM_UNUSED ulong r4,
316 : FD_PARAM_UNUSED ulong r5,
317 0 : /**/ ulong * _ret ) {
318 : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L1547 */
319 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
320 :
321 0 : FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST );
322 :
323 0 : *_ret = vm->instr_ctx->txn_ctx->instr_stack_sz;
324 0 : return FD_VM_SUCCESS;
325 0 : }
326 :
327 : int
328 : fd_vm_syscall_sol_get_return_data( /**/ void * _vm,
329 : /**/ ulong return_data_vaddr,
330 : /**/ ulong sz,
331 : /**/ ulong program_id_vaddr,
332 : FD_PARAM_UNUSED ulong r4,
333 : FD_PARAM_UNUSED ulong r5,
334 0 : /**/ ulong * _ret ) {
335 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
336 :
337 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1465 */
338 0 : FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST );
339 :
340 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1467 */
341 0 : fd_txn_return_data_t const * return_data = &vm->instr_ctx->txn_ctx->return_data;
342 :
343 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1468 */
344 0 : ulong length = fd_ulong_min( return_data->len, sz );
345 :
346 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1469-L1492 */
347 0 : if( FD_LIKELY( length ) ) {
348 :
349 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1470-L1474 */
350 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( length, sizeof(fd_pubkey_t) ) / FD_VM_CPI_BYTES_PER_UNIT );
351 :
352 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1476-L1481 */
353 0 : fd_vm_haddr_query_t return_data_query = {
354 0 : .vaddr = return_data_vaddr,
355 0 : .align = FD_VM_ALIGN_RUST_U8,
356 0 : .sz = length,
357 0 : .is_slice = 1
358 0 : };
359 :
360 0 : fd_vm_haddr_query_t program_id_query = {
361 0 : .vaddr = program_id_vaddr,
362 0 : .align = FD_VM_ALIGN_RUST_PUBKEY,
363 0 : .sz = sizeof(fd_pubkey_t),
364 0 : .is_slice = 0
365 0 : };
366 :
367 0 : fd_vm_haddr_query_t * queries[] = { &return_data_query, &program_id_query };
368 0 : FD_VM_TRANSLATE_MUT( vm, queries );
369 :
370 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1490-L1491 */
371 0 : memcpy( return_data_query.haddr, return_data->data, length );
372 0 : memcpy( program_id_query.haddr, &return_data->program_id, sizeof(fd_pubkey_t) );
373 0 : }
374 :
375 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1495 */
376 0 : *_ret = return_data->len;
377 0 : return FD_VM_SUCCESS;
378 0 : }
379 :
380 : int
381 : fd_vm_syscall_sol_set_return_data( /**/ void * _vm,
382 : /**/ ulong src_vaddr,
383 : /**/ ulong src_sz,
384 : FD_PARAM_UNUSED ulong r3,
385 : FD_PARAM_UNUSED ulong r4,
386 : FD_PARAM_UNUSED ulong r5,
387 0 : /**/ ulong * _ret ) {
388 : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L1297 */
389 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
390 :
391 : /* FIXME: In the original version of this code, there was an FD_TEST
392 : to check if the VM was attached to an instruction context (that
393 : would have crashed anyway because of pointer chasing). If the VM
394 : is being run outside the Solana runtime, it should never invoke
395 : this syscall in the first place. So we treat this as a SIGCALL in
396 : a non-crashing way for the time being. */
397 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
398 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
399 :
400 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSCALL_BASE_COST, src_sz / FD_VM_CPI_BYTES_PER_UNIT ) );
401 :
402 : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L1316 */
403 0 : if( FD_UNLIKELY( src_sz>FD_VM_RETURN_DATA_MAX ) ) {
404 : /* TODO: this is a bit annoying, we may want to unify return codes...
405 : - FD_VM_SYSCALL_ERR_RETURN_DATA_TOO_LARGE is Agave's return code,
406 : also used for logging */
407 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_RETURN_DATA_TOO_LARGE );
408 0 : return FD_VM_SYSCALL_ERR_RETURN_DATA_TOO_LARGE;
409 0 : }
410 :
411 : /* src_sz == 0 is ok */
412 0 : void const * src = FD_VM_MEM_SLICE_HADDR_LD( vm, src_vaddr, FD_VM_ALIGN_RUST_U8, src_sz );
413 :
414 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/programs/bpf_loader/src/syscalls/mod.rs#L1480-L1484 */
415 0 : fd_pubkey_t const * program_id = NULL;
416 0 : int err = fd_exec_instr_ctx_get_last_program_key( vm->instr_ctx, &program_id );
417 0 : if( FD_UNLIKELY( err ) ) {
418 0 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
419 0 : return err;
420 0 : }
421 :
422 0 : fd_txn_return_data_t * return_data = &instr_ctx->txn_ctx->return_data;
423 :
424 0 : return_data->len = src_sz;
425 0 : if( FD_LIKELY( src_sz!=0UL ) ) {
426 0 : fd_memcpy( return_data->data, src, src_sz );
427 0 : }
428 0 : return_data->program_id = *program_id;
429 :
430 0 : *_ret = 0;
431 0 : return FD_VM_SUCCESS;
432 0 : }
433 :
434 : /* Used to query and convey information about the sibling instruction
435 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/sdk/program/src/instruction.rs#L676
436 :
437 : */
438 : struct fd_vm_syscall_processed_sibling_instruction {
439 : /* Length of the instruction data */
440 : ulong data_len;
441 : /* Number of accounts */
442 : ulong accounts_len;
443 : };
444 : typedef struct fd_vm_syscall_processed_sibling_instruction fd_vm_syscall_processed_sibling_instruction_t;
445 :
446 0 : #define FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_SIZE (16UL)
447 0 : #define FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_ALIGN (8UL )
448 :
449 : /*
450 : sol_get_last_processed_sibling_instruction returns the last element from a reverse-ordered
451 : list of successfully processed sibling instructions: the "processed sibling instruction list".
452 :
453 : For example, given the call flow:
454 : A
455 : B -> C -> D
456 : B -> E
457 : B -> F (current execution point)
458 :
459 : B's processed sibling instruction list is [A]
460 : F's processed sibling instruction list is [E, C]
461 :
462 : This allows the current instruction to know what the last processed sibling instruction was.
463 : This is useful to check that critical preceeding instructions have actually executed: for example
464 : ensuring that an assert instruction has successfully executed.
465 :
466 : Parameters:
467 : - index:
468 : - result_meta_vaddr: virtual address of the object where metadata about the last processed sibling instruction will be stored upon successful execution (the length of the arrays in the result).
469 : Has the type solana_program::instruction::ProcessedSiblingInstruction
470 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/sdk/program/src/instruction.rs#L672-L681
471 : - result_program_id_vaddr: virtual address where the pubkey of the program ID of the last processed sibling instruction will be stored upon successful execution
472 : - result_data_vaddr: virtual address where the instruction data of the last processed sibling instruction will be stored upon successful execution. The length of the data will be stored in ProcessedSiblingInstruction.data_len
473 : - result_accounts_vaddr: virtual address where an array of account meta structures will be stored into upon successful execution. The length of the data will be stored in ProcessedSiblingInstruction.accounts_len
474 : Each account meta has the type solana_program::instruction::AccountMeta
475 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/sdk/program/src/instruction.rs#L525-L548
476 :
477 : Result:
478 : If a processed sibling instruction is found then 1 will be written into r0, and the result_* data structures
479 : above will be populated with the last processed sibling instruction.
480 : If there is no processed sibling instruction, 0 will be written into r0.
481 :
482 : Syscall entrypoint: https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/programs/bpf_loader/src/syscalls/mod.rs#L1402
483 : */
484 : int
485 : fd_vm_syscall_sol_get_processed_sibling_instruction(
486 : void * _vm,
487 : ulong index,
488 : ulong result_meta_vaddr,
489 : ulong result_program_id_vaddr,
490 : ulong result_data_vaddr,
491 : ulong result_accounts_vaddr,
492 : ulong * ret
493 0 : ) {
494 :
495 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
496 :
497 : /* Consume base compute cost
498 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1513 */
499 0 : FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST );
500 :
501 : /*
502 : Get the current instruction stack height. This value is 1-indexed (top level instrution has a stack height
503 : of 1).
504 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1517 */
505 0 : ulong stack_height = vm->instr_ctx->txn_ctx->instr_stack_sz;
506 :
507 : /* Reverse iterate through the instruction trace, ignoring anything except instructions on the same level.
508 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1518-L1522 */
509 0 : ulong instruction_trace_length = vm->instr_ctx->txn_ctx->instr_trace_length;
510 0 : ulong reverse_index_at_stack_height = 0UL;
511 0 : fd_exec_instr_trace_entry_t * found_instruction_context = NULL;
512 0 : for( ulong index_in_trace=instruction_trace_length; index_in_trace>0UL; index_in_trace-- ) {
513 :
514 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1524-L1526
515 : This error can never happen */
516 :
517 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1527-L1529 */
518 0 : fd_exec_instr_trace_entry_t * instruction_context = &vm->instr_ctx->txn_ctx->instr_trace[ index_in_trace-1UL ];
519 0 : if( FD_LIKELY( instruction_context->stack_height<stack_height ) ) {
520 0 : break;
521 0 : }
522 :
523 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1530-L1536 */
524 0 : if( FD_UNLIKELY( instruction_context->stack_height==stack_height ) ) {
525 0 : if( FD_UNLIKELY( fd_ulong_sat_add( index, 1UL )==reverse_index_at_stack_height ) ) {
526 0 : found_instruction_context = instruction_context;
527 0 : break;
528 0 : }
529 0 : reverse_index_at_stack_height = fd_ulong_sat_add( reverse_index_at_stack_height, 1UL );
530 0 : }
531 0 : }
532 :
533 : /* If we have found an entry, then copy the instruction into the result addresses
534 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/programs/bpf_loader/src/syscalls/mod.rs#L1440-L1533
535 : */
536 0 : if( FD_LIKELY( found_instruction_context != NULL ) ) {
537 0 : fd_instr_info_t * instr_info = found_instruction_context->instr_info;
538 :
539 0 : fd_vm_haddr_query_t result_header_query = {
540 0 : .vaddr = result_meta_vaddr,
541 0 : .align = FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_ALIGN,
542 0 : .sz = FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_SIZE,
543 0 : .is_slice = 0,
544 0 : };
545 :
546 0 : fd_vm_haddr_query_t * queries[] = { &result_header_query };
547 0 : FD_VM_TRANSLATE_MUT( vm, queries );
548 :
549 0 : fd_vm_syscall_processed_sibling_instruction_t * result_header = result_header_query.haddr;
550 :
551 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1546-L1583 */
552 0 : if( result_header->data_len==instr_info->data_sz && result_header->accounts_len==instr_info->acct_cnt ) {
553 0 : fd_vm_haddr_query_t program_id_query = {
554 0 : .vaddr = result_program_id_vaddr,
555 0 : .align = FD_VM_ALIGN_RUST_PUBKEY,
556 0 : .sz = sizeof(fd_pubkey_t),
557 0 : .is_slice = 0,
558 0 : };
559 :
560 0 : fd_vm_haddr_query_t data_query = {
561 0 : .vaddr = result_data_vaddr,
562 0 : .align = FD_VM_ALIGN_RUST_U8,
563 0 : .sz = result_header->data_len,
564 0 : .is_slice = 1,
565 0 : };
566 :
567 0 : fd_vm_haddr_query_t accounts_query = {
568 0 : .vaddr = result_accounts_vaddr,
569 0 : .align = FD_VM_RUST_ACCOUNT_META_ALIGN,
570 0 : .sz = fd_ulong_sat_mul( result_header->accounts_len, FD_VM_RUST_ACCOUNT_META_SIZE ),
571 0 : .is_slice = 1,
572 0 : };
573 :
574 0 : fd_vm_haddr_query_t * queries[] = { &program_id_query, &data_query, &accounts_query, &result_header_query };
575 0 : FD_VM_TRANSLATE_MUT( vm, queries );
576 :
577 0 : fd_pubkey_t * program_id = program_id_query.haddr;
578 0 : uchar * data = data_query.haddr;
579 0 : fd_vm_rust_account_meta_t * accounts = accounts_query.haddr;
580 :
581 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1561-L1562 */
582 0 : fd_pubkey_t const * instr_ctx_program_id = NULL;
583 0 : int err = fd_exec_txn_ctx_get_key_of_account_at_index( vm->instr_ctx->txn_ctx,
584 0 : instr_info->program_id,
585 0 : &instr_ctx_program_id );
586 0 : if( FD_UNLIKELY( err ) ) {
587 0 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
588 0 : return err;
589 0 : }
590 0 : fd_memcpy( program_id, instr_ctx_program_id, sizeof(fd_pubkey_t) );
591 :
592 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1563 */
593 0 : fd_memcpy( data, instr_info->data, instr_info->data_sz );
594 :
595 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1564-L1581 */
596 0 : for( ushort i=0; i<instr_info->acct_cnt; i++ ) {
597 0 : fd_pubkey_t const * account_key;
598 0 : ushort txn_idx = instr_info->accounts[ i ].index_in_transaction;
599 0 : err = fd_exec_txn_ctx_get_key_of_account_at_index( vm->instr_ctx->txn_ctx, txn_idx, &account_key );
600 0 : if( FD_UNLIKELY( err ) ) {
601 0 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
602 0 : return err;
603 0 : }
604 :
605 0 : fd_memcpy( accounts[ i ].pubkey, account_key, sizeof(fd_pubkey_t) );
606 0 : accounts[ i ].is_signer = !!(instr_info->accounts[ i ].is_signer );
607 0 : accounts[ i ].is_writable = !!(instr_info->accounts[ i ].is_writable );
608 0 : }
609 0 : } else {
610 : /* Copy the actual metadata into the result meta struct
611 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1584-L1586 */
612 0 : result_header->data_len = instr_info->data_sz;
613 0 : result_header->accounts_len = instr_info->acct_cnt;
614 0 : }
615 :
616 : /* Return true as we found a sibling instruction
617 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1588 */
618 0 : *ret = 1UL;
619 0 : return FD_VM_SUCCESS;
620 0 : }
621 :
622 : /* Return false if we didn't find a sibling instruction
623 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1590 */
624 0 : *ret = 0UL;
625 0 : return FD_VM_SUCCESS;
626 0 : }
627 :
628 : // https://github.com/anza-xyz/agave/blob/master/programs/bpf_loader/src/syscalls/sysvar.rs#L75
629 : int
630 : fd_vm_syscall_sol_get_epoch_rewards_sysvar( /**/ void * _vm,
631 : /**/ ulong out_vaddr,
632 : FD_PARAM_UNUSED ulong r2,
633 : FD_PARAM_UNUSED ulong r3,
634 : FD_PARAM_UNUSED ulong r4,
635 : FD_PARAM_UNUSED ulong r5,
636 0 : /**/ ulong * _ret ) {
637 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
638 :
639 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
640 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
641 :
642 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, sizeof(fd_sysvar_epoch_rewards_t) ) );
643 :
644 0 : void * out = FD_VM_MEM_HADDR_ST( vm, out_vaddr, FD_VM_ALIGN_RUST_SYSVAR_EPOCH_REWARDS, sizeof(fd_sysvar_epoch_rewards_t) );
645 :
646 0 : fd_sysvar_epoch_rewards_t const * epoch_rewards = fd_sysvar_epoch_rewards_read( instr_ctx->txn_ctx->funk,
647 0 : instr_ctx->txn_ctx->funk_txn,
648 0 : instr_ctx->txn_ctx->spad );
649 0 : if( FD_UNLIKELY( !epoch_rewards ) ) {
650 0 : FD_LOG_ERR(( "failed to read sysvar epoch rewards" ));
651 0 : }
652 :
653 0 : memcpy( out, epoch_rewards, sizeof(fd_sysvar_epoch_rewards_t) );
654 :
655 0 : *_ret = 0UL;
656 0 : return FD_VM_SUCCESS;
657 0 : }
|