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