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