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