Line data Source code
1 : /* This file contains all the logic that is common to both the C and Rust
2 : CPI syscalls (sol_invoke_signed_{rust/c}). As such, all of the functions in
3 : here are templated and will be instantiated for both the C and Rust CPI ABIs.
4 :
5 : The only difference between the C and Rust CPI syscalls is the ABI data layout
6 : of the parameters to these calls - all the logic is identical. As such, we have
7 : defined a series of macros to abstract away the ABI differences from the CPI implementation.
8 :
9 : The entry-point for these syscalls is VM_SYSCALL_CPI_ENTRYPOINT.
10 :
11 : Note that the code for these syscalls could be simplified somewhat, but we have opted to keep
12 : it as close to the Solana code as possible to make it easier to audit that we execute equivalently.
13 : Most of the top-level functions in this file correspond directly to functions in the Solana codebase
14 : and links to the source have been provided.
15 : */
16 :
17 : /* fd_vm_syscall_cpi_instruction_to_instr_{c/rust} takes the translated
18 : CPI ABI structures (instruction and account meta list), and uses these
19 : to populate a fd_instr_info_t struct. This struct can then be given to the
20 : FD runtime for execution.
21 :
22 : Parameters:
23 : - vm: handle to the vm
24 : - cpi_instr: instruction to execute laid out in the CPI ABI format (Rust or C)
25 : - cpi_acc_metas: list of account metas, again in the CPI ABI format
26 : - signers: derived signers for this CPI call
27 : - signers_cnt: length of the signers list
28 : - cpi_instr_data: instruction data in host address space
29 :
30 : TODO: return codes/errors?
31 : */
32 264 : #define VM_SYSCALL_CPI_INSTRUCTION_TO_INSTR_FUNC FD_EXPAND_THEN_CONCAT2(fd_vm_syscall_cpi_instruction_to_instr_, VM_SYSCALL_CPI_ABI)
33 : static int
34 : VM_SYSCALL_CPI_INSTRUCTION_TO_INSTR_FUNC( fd_vm_t * vm,
35 : VM_SYSCALL_CPI_INSTR_T const * cpi_instr,
36 : VM_SYSCALL_CPI_ACC_META_T const * cpi_acct_metas,
37 : fd_pubkey_t const * program_id,
38 : uchar const * cpi_instr_data,
39 264 : fd_instr_info_t * out_instr ) {
40 : /* fd_vm_prepare_instruction will handle the case where pubkey is missing */
41 264 : out_instr->program_id_pubkey = *program_id;
42 264 : out_instr->program_id = UCHAR_MAX;
43 :
44 : /* Find the index of the CPI instruction's program account in the transaction */
45 264 : fd_pubkey_t * txn_accs = vm->instr_ctx->txn_ctx->accounts;
46 7488 : for( ulong i=0UL; i < vm->instr_ctx->txn_ctx->accounts_cnt; i++ ) {
47 7488 : if( !memcmp( program_id, &txn_accs[i], sizeof( fd_pubkey_t ) ) ) {
48 264 : out_instr->program_id = (uchar)i;
49 264 : break;
50 264 : }
51 7488 : }
52 :
53 : /* Calculate summary information for the account list */
54 264 : ulong starting_lamports_h = 0UL;
55 264 : ulong starting_lamports_l = 0UL;
56 264 : uchar acc_idx_seen[256] = {0};
57 1473 : for( ulong i=0UL; i<VM_SYSCALL_CPI_INSTR_ACCS_LEN( cpi_instr ); i++ ) {
58 1209 : VM_SYSCALL_CPI_ACC_META_T const * cpi_acct_meta = &cpi_acct_metas[i];
59 1209 : uchar const * pubkey = VM_SYSCALL_CPI_ACC_META_PUBKEY( vm, cpi_acct_meta );
60 :
61 0 : int account_found = 0;
62 20553 : for( ulong j=0UL; j<vm->instr_ctx->txn_ctx->accounts_cnt; j++ ) {
63 20553 : if( !memcmp( pubkey, &txn_accs[j], sizeof(fd_pubkey_t) ) ) {
64 1209 : account_found = 1;
65 : /* TODO: error if flags are wrong */
66 1209 : memcpy( out_instr->acct_pubkeys[i].uc, pubkey, sizeof( fd_pubkey_t ) );
67 1209 : out_instr->acct_txn_idxs[i] = (uchar)j;
68 1209 : out_instr->acct_flags[i] = 0;
69 1209 : out_instr->borrowed_accounts[i] = &vm->instr_ctx->txn_ctx->borrowed_accounts[j];
70 1209 : out_instr->is_duplicate[i] = acc_idx_seen[j];
71 :
72 1209 : if( FD_LIKELY( !acc_idx_seen[j] ) ) {
73 : /* This is the first time seeing this account */
74 1206 : acc_idx_seen[j] = 1;
75 1206 : if( out_instr->borrowed_accounts[i]->const_meta ) {
76 : /* TODO: what if this account is borrowed as writable? */
77 1206 : fd_uwide_inc(
78 1206 : &starting_lamports_h, &starting_lamports_l,
79 1206 : starting_lamports_h, starting_lamports_l,
80 1206 : out_instr->borrowed_accounts[i]->const_meta->info.lamports );
81 1206 : }
82 1206 : }
83 :
84 : /* The parent flag(s) for is writable/signer is checked in
85 : fd_vm_prepare_instruction. Signer privilege is allowed iff the account
86 : is a signer in the caller or if it is a derived signer. */
87 1209 : if( VM_SYSCALL_CPI_ACC_META_IS_WRITABLE( cpi_acct_meta ) ) {
88 777 : out_instr->acct_flags[i] |= FD_INSTR_ACCT_FLAGS_IS_WRITABLE;
89 777 : }
90 :
91 1209 : if( VM_SYSCALL_CPI_ACC_META_IS_SIGNER( cpi_acct_meta ) ) {
92 261 : out_instr->acct_flags[i] |= FD_INSTR_ACCT_FLAGS_IS_SIGNER;
93 261 : }
94 :
95 1209 : break;
96 1209 : }
97 20553 : }
98 1209 : if( FD_UNLIKELY( !account_found ) ) {
99 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
100 0 : }
101 162 : }
102 :
103 264 : out_instr->data_sz = (ushort)VM_SYSCALL_CPI_INSTR_DATA_LEN( cpi_instr );
104 264 : out_instr->data = (uchar *)cpi_instr_data;
105 264 : out_instr->acct_cnt = (ushort)VM_SYSCALL_CPI_INSTR_ACCS_LEN( cpi_instr );
106 264 : out_instr->starting_lamports_h = starting_lamports_h;
107 264 : out_instr->starting_lamports_l = starting_lamports_l;
108 :
109 264 : return FD_VM_SUCCESS;
110 264 : }
111 :
112 : /*
113 : fd_vm_syscall_cpi_update_callee_acc_{rust/c} corresponds to solana_bpf_loader_program::syscalls::cpi::update_callee_account:
114 : https://github.com/solana-labs/solana/blob/dbf06e258ae418097049e845035d7d5502fe1327/programs/bpf_loader/src/syscalls/cpi.rs#L1302
115 :
116 : (the copy of the account stored in the instruction context's
117 : borrowed accounts cache)
118 :
119 : This function should be called before the CPI instruction is executed. Its purpose is to
120 : update the callee account's view of the given account with any changes the caller may made
121 : to the account before the CPI instruction is executed.
122 :
123 : The callee's view of the account is the borrowed accounts cache, so to update the
124 : callee account we look up the account in the borrowed accounts cache and update it.
125 :
126 : Paramaters:
127 : - vm: pointer to the virtual machine handle
128 : - account_info: account info object
129 : - callee_acc_pubkey: pubkey of the account. this is used to look up the account in the borrowed accounts cache
130 : (TODO: this seems redundant? we can probably remove this, as the account_info contains the pubkey)
131 : */
132 : #define VM_SYCALL_CPI_UPDATE_CALLEE_ACC_FUNC FD_EXPAND_THEN_CONCAT2(fd_vm_syscall_cpi_update_callee_acc_, VM_SYSCALL_CPI_ABI)
133 : static int
134 : VM_SYCALL_CPI_UPDATE_CALLEE_ACC_FUNC( fd_vm_t * vm,
135 : VM_SYSCALL_CPI_ACC_INFO_T const * account_info,
136 1098 : uchar instr_acc_idx ) {
137 : /* Consume compute units for the account data access */
138 :
139 : /* FIXME: do we also need to consume the compute units if the account is not known? */
140 :
141 3258 : VM_SYSCALL_CPI_ACC_INFO_METADATA( vm, account_info, caller_acc_data );
142 :
143 : // FIXME: should this be FD_VM_CU_MEM_UPDATE? Changing this changes the CU behaviour from main
144 3258 : FD_VM_CU_UPDATE( vm, caller_acc_data_len / FD_VM_CPI_BYTES_PER_UNIT );
145 :
146 0 : fd_borrowed_account_t * callee_acc = NULL;
147 1098 : int err = fd_instr_borrowed_account_modify_idx(vm->instr_ctx, instr_acc_idx, 0, &callee_acc);
148 1098 : if( FD_UNLIKELY( err ) ) {
149 : /* No need to do anything if the account is missing from the borrowed accounts cache */
150 0 : return FD_VM_SUCCESS;
151 0 : }
152 :
153 1098 : if( FD_UNLIKELY( !callee_acc->meta ) ) {
154 : /* If the account is not modifiable, we can't change it (and it can't have been changed by the callee) */
155 171 : return FD_VM_SUCCESS;
156 171 : }
157 :
158 : /* Update the lamports. TODO: This should technically be a load, but it
159 : doesn't matter in this case. */
160 6489 : VM_SYSCALL_CPI_ACC_INFO_LAMPORTS( vm, account_info, caller_acc_lamports );
161 6489 : if( callee_acc->meta->info.lamports!=*caller_acc_lamports ) {
162 0 : err = fd_account_set_lamports( vm->instr_ctx, instr_acc_idx, *caller_acc_lamports );
163 0 : if( FD_UNLIKELY( err ) ) {
164 0 : return err;
165 0 : }
166 0 : }
167 :
168 927 : if( !FD_FEATURE_ACTIVE( vm->instr_ctx->slot_ctx, bpf_account_data_direct_mapping ) ) {
169 : /* Get the account data */
170 : /* Update the account data, if the account data can be changed */
171 : /* FIXME: double-check these permissions, especially the callee_acc_idx */
172 :
173 : /* Translate and get the account data */
174 2781 : uchar const * caller_acc_data = FD_VM_MEM_HADDR_LD( vm, caller_acc_data_vm_addr, sizeof(uchar), caller_acc_data_len );
175 :
176 927 : if( fd_account_can_data_be_resized( vm->instr_ctx, callee_acc->meta, caller_acc_data_len, &err ) &&
177 927 : fd_account_can_data_be_changed( vm->instr_ctx->instr, instr_acc_idx, &err ) ) {
178 : /* We must ignore the errors here, as they are informational and do not mean the result is invalid. */
179 : /* TODO: not pass informational errors like this? */
180 :
181 45 : err = fd_account_set_data_from_slice( vm->instr_ctx, instr_acc_idx, caller_acc_data, caller_acc_data_len );
182 45 : if( FD_UNLIKELY( err ) ) {
183 0 : return err;
184 0 : }
185 882 : } else if( FD_UNLIKELY( caller_acc_data_len!=callee_acc->const_meta->dlen ||
186 882 : memcmp( callee_acc->const_data, caller_acc_data, caller_acc_data_len ) ) ) {
187 0 : return err;
188 0 : }
189 :
190 2781 : uchar const * caller_acc_owner = FD_VM_MEM_HADDR_ST( vm, account_info->owner_addr, alignof(uchar), sizeof(fd_pubkey_t) );
191 927 : if( memcmp( callee_acc->meta->info.owner, caller_acc_owner, sizeof(fd_pubkey_t) ) ) {
192 0 : fd_memcpy( callee_acc->meta->info.owner, caller_acc_owner, sizeof(fd_pubkey_t) );
193 0 : }
194 :
195 2781 : } else { /* Direct mapping enabled */
196 0 : ulong region_idx = vm->acc_region_metas[ instr_acc_idx ].region_idx;
197 0 : uint original_len = vm->acc_region_metas[ instr_acc_idx ].has_data_region ?
198 0 : vm->input_mem_regions[ region_idx ].region_sz : 0U;
199 0 : ulong prev_len = callee_acc->const_meta->dlen;
200 0 : ulong post_len = caller_acc_data_len;
201 :
202 0 : int err;
203 0 : if( fd_account_can_data_be_resized( vm->instr_ctx, callee_acc->meta, post_len, &err ) &&
204 0 : fd_account_can_data_be_changed( vm->instr_ctx->instr, instr_acc_idx, &err ) ) {
205 :
206 0 : ulong realloc_bytes_used = fd_ulong_sat_sub( post_len, original_len );
207 :
208 0 : if( FD_UNLIKELY( vm->is_deprecated && realloc_bytes_used ) ) {
209 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC;
210 0 : }
211 :
212 0 : err = fd_account_set_data_length( vm->instr_ctx, instr_acc_idx, post_len );
213 0 : if( FD_UNLIKELY( err ) ) {
214 0 : return err;
215 0 : }
216 :
217 :
218 0 : if( realloc_bytes_used ) {
219 : /* We need to get the relevant data slice. However, we know that the
220 : current length currently exceeds the original length for the account
221 : data. This means that all of the additional bytes must exist in the
222 : account data resizing region. As an invariant, original_len must be
223 : equal to the length of the account data region. This means we can
224 : smartly look up the right region and don't need to worry about
225 : multiple region access.We just need to load in the bytes from
226 : (original len, post_len]. */
227 0 : uchar const * realloc_data = FD_VM_MEM_HADDR_LD( vm, caller_acc_data_vm_addr+original_len, alignof(uchar), realloc_bytes_used );
228 :
229 0 : uchar * data = NULL;
230 0 : ulong dlen = 0UL;
231 0 : err = fd_account_get_data_mut( vm->instr_ctx, instr_acc_idx, &data, &dlen );
232 0 : if( FD_UNLIKELY( err ) ) {
233 0 : return err;
234 0 : }
235 0 : fd_memcpy( data+original_len, realloc_data, realloc_bytes_used );
236 0 : }
237 :
238 0 : } else if( FD_UNLIKELY( prev_len!=post_len ) ) {
239 0 : return err;
240 0 : }
241 0 : }
242 :
243 2781 : uchar const * caller_acc_owner = FD_VM_MEM_HADDR_LD( vm, account_info->owner_addr, alignof(uchar), sizeof(fd_pubkey_t) );
244 927 : if( FD_UNLIKELY( memcmp( callee_acc->meta->info.owner, caller_acc_owner, sizeof(fd_pubkey_t) ) ) ) {
245 0 : err = fd_account_set_owner( vm->instr_ctx, instr_acc_idx, (fd_pubkey_t*)caller_acc_owner );
246 0 : if( FD_UNLIKELY( err ) ) {
247 0 : return err;
248 0 : }
249 0 : }
250 :
251 927 : return FD_VM_SUCCESS;
252 2781 : }
253 :
254 : /*
255 : fd_vm_syscall_cpi_translate_and_update_accounts_ mirrors the behaviour of
256 : solana_bpf_loader_program::syscalls::cpi::translate_and_update_accounts:
257 : https://github.com/solana-labs/solana/blob/dbf06e258ae418097049e845035d7d5502fe1327/programs/bpf_loader/src/syscalls/cpi.rs#L954-L1085
258 :
259 : It translates the caller accounts to the host address space, and then calls
260 : fd_vm_syscall_cpi_update_callee_acc to update the callee borrowed account with any changes
261 : the caller has made to the account during execution before this CPI call.
262 :
263 : It also populates the out_callee_indices and out_caller_indices arrays:
264 : - out_callee_indices: indices of the callee accounts in the transaction
265 : - out_caller_indices: indices of the caller accounts in the account_infos array
266 :
267 : Parameters:
268 : - vm: pointer to the virtual machine handle
269 : - instruction_accounts: array of instruction accounts
270 : - instruction_accounts_cnt: length of the instruction_accounts array
271 : - account_infos: array of account infos
272 : - account_infos_length: length of the account_infos array
273 :
274 : Populates the given out_callee_indices and out_caller_indices arrays:
275 : - out_callee_indices: indices of the callee accounts in the transaction
276 : - out_caller_indices: indices of the caller accounts in the account_infos array
277 : - out_len: length of the out_callee_indices and out_caller_indices arrays
278 : */
279 252 : #define VM_SYSCALL_CPI_TRANSLATE_AND_UPDATE_ACCOUNTS_FUNC FD_EXPAND_THEN_CONCAT2(fd_vm_syscall_cpi_translate_and_update_accounts_, VM_SYSCALL_CPI_ABI)
280 : static int
281 : VM_SYSCALL_CPI_TRANSLATE_AND_UPDATE_ACCOUNTS_FUNC(
282 : fd_vm_t * vm,
283 : fd_instruction_account_t const * instruction_accounts,
284 : ulong const instruction_accounts_cnt,
285 : VM_SYSCALL_CPI_ACC_INFO_T const * account_infos,
286 : ulong const account_infos_length,
287 : ulong * out_callee_indices,
288 : ulong * out_caller_indices,
289 252 : ulong * out_len ) {
290 :
291 1461 : for( ulong i=0UL; i<instruction_accounts_cnt; i++ ) {
292 1209 : if( i!=instruction_accounts[i].index_in_callee ) {
293 : /* Skip duplicate accounts */
294 3 : continue;
295 3 : }
296 :
297 1206 : fd_pubkey_t const * callee_account = &vm->instr_ctx->instr->acct_pubkeys[instruction_accounts[i].index_in_caller];
298 1206 : fd_pubkey_t const * account_key = &vm->instr_ctx->txn_ctx->accounts[instruction_accounts[i].index_in_transaction];
299 1206 : fd_borrowed_account_t * acc_rec = NULL;
300 : /* TODO: replace with more efficient idx lookup */
301 1206 : int err = fd_instr_borrowed_account_view( vm->instr_ctx, callee_account, &acc_rec );
302 1206 : if( FD_UNLIKELY( err && ( err != FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) ) ) {
303 : /* TODO: magic number */
304 0 : return 1000;
305 0 : }
306 :
307 : /* const_meta is NULL if the account is a new account (it doesn't exist in Funk at the time of the transaction) */
308 1206 : fd_account_meta_t const * acc_meta = acc_rec->const_meta;
309 1206 : uchar known_account = !!acc_meta;
310 :
311 : /* If the account is known and executable, we only need to consume the compute units.
312 : Executable accounts can't be modified, so we don't need to update the callee account. */
313 1206 : if( known_account && fd_account_is_executable( acc_meta ) ) {
314 : // FIXME: should this be FD_VM_CU_MEM_UPDATE? Changing this changes the CU behaviour from main (because of the base cost)
315 108 : FD_VM_CU_UPDATE( vm, acc_meta->dlen / FD_VM_CPI_BYTES_PER_UNIT );
316 0 : continue;
317 108 : }
318 :
319 : /* Find the indicies of the account in the caller and callee instructions */
320 1098 : uint found = 0;
321 6744 : for( ulong j=0; (j < account_infos_length) && !found; j++ ) {
322 :
323 : /* Look up the pubkey to see if it is the account we're looking for */
324 5646 : fd_pubkey_t const * acct_addr = FD_VM_MEM_HADDR_LD_UNCHECKED(
325 5646 : vm, account_infos[j].pubkey_addr, alignof(uchar), sizeof(fd_pubkey_t) );
326 : /* If the address does not point to a valid address, then we should skip over this account, not error out. */
327 5646 : if ( acct_addr == 0UL ) {
328 0 : continue;
329 0 : }
330 :
331 5646 : if( memcmp( account_key->uc, acct_addr->uc, sizeof(fd_pubkey_t) ) != 0 ) {
332 4548 : continue;
333 4548 : }
334 :
335 : /* Record the indicies of this account */
336 1098 : if (instruction_accounts[i].is_writable) {
337 768 : out_callee_indices[*out_len] = instruction_accounts[i].index_in_caller;
338 768 : out_caller_indices[*out_len] = j;
339 768 : (*out_len)++;
340 768 : }
341 1098 : found = 1;
342 :
343 : /* Update the callee account to reflect any changes the caller has made */
344 1098 : if( FD_UNLIKELY( acc_meta && VM_SYCALL_CPI_UPDATE_CALLEE_ACC_FUNC(vm, &account_infos[j], (uchar)instruction_accounts[i].index_in_caller ) ) ) {
345 0 : return 1001;
346 0 : }
347 1098 : }
348 :
349 1098 : if( !found ) {
350 : /* TODO: magic number */
351 0 : return 1002;
352 0 : }
353 1098 : }
354 :
355 252 : return FD_VM_SUCCESS;
356 252 : }
357 :
358 : /* fd_vm_cpi_update_caller_acc_{rust/c} mirrors the behaviour of
359 : solana_bpf_loader_program::syscalls::cpi::update_caller_account:
360 : https://github.com/solana-labs/solana/blob/2afde1b028ed4593da5b6c735729d8994c4bfac6/programs/bpf_loader/src/syscalls/cpi.rs#L1291
361 :
362 : This method should be called after a CPI instruction execution has
363 : returned. It updates the given caller account info with any changes the callee
364 : has made to this account during execution, so that those changes are
365 : reflected in the rest of the caller's execution.
366 :
367 : Those changes will be in the instructions borrowed accounts cache.
368 :
369 : Paramaters:
370 : - vm: handle to the vm
371 : - caller_acc_info: caller account info object, which should be updated
372 : - pubkey: pubkey of the account
373 :
374 : TODO: error codes
375 : */
376 768 : #define VM_SYSCALL_CPI_UPDATE_CALLER_ACC_FUNC FD_EXPAND_THEN_CONCAT2(fd_vm_cpi_update_caller_acc_, VM_SYSCALL_CPI_ABI)
377 : static int
378 : VM_SYSCALL_CPI_UPDATE_CALLER_ACC_FUNC( fd_vm_t * vm,
379 : VM_SYSCALL_CPI_ACC_INFO_T * caller_acc_info,
380 : uchar instr_acc_idx,
381 768 : fd_pubkey_t const * pubkey ) {
382 :
383 768 : if( !FD_FEATURE_ACTIVE( vm->instr_ctx->slot_ctx, bpf_account_data_direct_mapping ) ) {
384 : /* Look up the borrowed account from the instruction context, which will contain
385 : the callee's changes. */
386 768 : fd_borrowed_account_t * callee_acc_rec = NULL;
387 768 : int err = fd_instr_borrowed_account_view( vm->instr_ctx, pubkey, &callee_acc_rec );
388 768 : if( FD_UNLIKELY( err && ( err != FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) ) ) {
389 0 : return 1;
390 0 : }
391 :
392 : /* Update the caller account lamports with the value from the callee */
393 5376 : VM_SYSCALL_CPI_ACC_INFO_LAMPORTS( vm, caller_acc_info, caller_acc_lamports );
394 5376 : *caller_acc_lamports = callee_acc_rec->const_meta->info.lamports;
395 :
396 : /* Update the caller account owner with the value from the callee */
397 5376 : uchar const * updated_owner = callee_acc_rec->const_meta->info.owner;
398 5376 : uchar * caller_acc_owner = FD_VM_MEM_HADDR_ST( vm, caller_acc_info->owner_addr, alignof(uchar), sizeof(fd_pubkey_t) );
399 768 : if( updated_owner ) fd_memcpy( caller_acc_owner, updated_owner, sizeof(fd_pubkey_t) );
400 0 : else fd_memset( caller_acc_owner, 0, sizeof(fd_pubkey_t) );
401 :
402 : /* Update the caller account data with the value from the callee */
403 4608 : VM_SYSCALL_CPI_ACC_INFO_DATA( vm, caller_acc_info, caller_acc_data );
404 :
405 4608 : ulong const updated_data_len = callee_acc_rec->const_meta->dlen;
406 4608 : if( !updated_data_len ) fd_memset( (void*)caller_acc_data, 0, caller_acc_data_len );
407 :
408 4608 : if( caller_acc_data_len != updated_data_len ) {
409 : /* FIXME: missing MAX_PERMITTED_DATA_INCREASE check from solana
410 : https://github.com/solana-labs/solana/blob/2afde1b028ed4593da5b6c735729d8994c4bfac6/programs/bpf_loader/src/syscalls/cpi.rs#L1342 */
411 :
412 : /* FIXME: do we need to zero the memory that was previously used, if the new data_len is smaller?
413 : https://github.com/solana-labs/solana/blob/2afde1b028ed4593da5b6c735729d8994c4bfac6/programs/bpf_loader/src/syscalls/cpi.rs#L1361
414 : I don't think we do but need to double-check. */
415 :
416 0 : VM_SYSCALL_CPI_SET_ACC_INFO_DATA_LEN( vm, caller_acc_info, caller_acc_data, updated_data_len );
417 :
418 : /* Update the serialized len field
419 : https://github.com/solana-labs/solana/blob/2afde1b028ed4593da5b6c735729d8994c4bfac6/programs/bpf_loader/src/syscalls/cpi.rs#L1437 */
420 0 : ulong * caller_len = FD_VM_MEM_HADDR_ST( vm, fd_ulong_sat_sub(caller_acc_data_vm_addr, sizeof(ulong)), alignof(ulong), sizeof(ulong) );
421 0 : *caller_len = updated_data_len;
422 :
423 : /* FIXME return instruction error account data size too small in the same scenarios solana does
424 : https://github.com/solana-labs/solana/blob/2afde1b028ed4593da5b6c735729d8994c4bfac6/programs/bpf_loader/src/syscalls/cpi.rs#L1534 */
425 0 : }
426 :
427 768 : fd_memcpy( (void*)caller_acc_data, callee_acc_rec->const_data, updated_data_len );
428 768 : } else { /* Direct mapping enabled */
429 :
430 : /* Look up the borrowed account from the instruction context, which will
431 : contain the callee's changes. */
432 0 : fd_borrowed_account_t * callee_acc_rec = NULL;
433 0 : int err = fd_instr_borrowed_account_view( vm->instr_ctx, pubkey, &callee_acc_rec );
434 0 : if( FD_UNLIKELY( err && err!=FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) ) {
435 0 : return 1;
436 0 : }
437 :
438 : /* Update the caller account lamports with the value from the callee */
439 0 : VM_SYSCALL_CPI_ACC_INFO_LAMPORTS( vm, caller_acc_info, caller_acc_lamports );
440 0 : *caller_acc_lamports = callee_acc_rec->const_meta->info.lamports;
441 :
442 : /* Update the caller account owner with the value from the callee */
443 0 : uchar const * updated_owner = callee_acc_rec->const_meta->info.owner;
444 0 : uchar * caller_acc_owner = (uchar*)FD_VM_MEM_HADDR_ST( vm, caller_acc_info->owner_addr, alignof(uchar), sizeof(fd_pubkey_t) );
445 0 : if( updated_owner ) {
446 0 : fd_memcpy( caller_acc_owner, updated_owner, sizeof(fd_pubkey_t) );
447 0 : } else {
448 0 : fd_memset( caller_acc_owner, 0, sizeof(fd_pubkey_t) );
449 0 : }
450 :
451 : /* Make sure that the capacity of the borrowed account is sized up in case
452 : it was shrunk in the CPI. It needs to be sized up in order to fit within
453 : the originally delinated regions when the account data was serialized.
454 : https://github.com/anza-xyz/agave/blob/36323b6dcd3e29e4d6fe6d73d716a3f33927148b/programs/bpf_loader/src/syscalls/cpi.rs#L1311 */
455 0 : VM_SYSCALL_CPI_ACC_INFO_METADATA( vm, caller_acc_info, caller_acc_data );
456 0 : ulong region_idx = vm->acc_region_metas[ instr_acc_idx ].region_idx;
457 0 : uint original_len = vm->acc_region_metas[ instr_acc_idx ].has_data_region ?
458 0 : vm->input_mem_regions[ region_idx ].region_sz : 0U;
459 :
460 0 : uchar zero_all_mapped_spare_capacity = 0;
461 : /* This case can only be triggered if the original length is more than 0 */
462 0 : if( callee_acc_rec->const_meta->dlen < original_len ) {
463 0 : ulong new_len = callee_acc_rec->const_meta->dlen;
464 : /* Allocate into the buffer to make sure that the original data len
465 : is still valid but don't change the dlen. Zero out the rest of the
466 : memory which is not used. */
467 0 : err = fd_instr_borrowed_account_modify( vm->instr_ctx, pubkey, original_len, &callee_acc_rec );
468 0 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
469 0 : return 1;
470 0 : }
471 0 : callee_acc_rec->meta->dlen = new_len;
472 0 : zero_all_mapped_spare_capacity = 1;
473 0 : }
474 :
475 : /* Update the account data region if an account data region exists. We
476 : know that one exists iff the original len was non-zero. */
477 0 : ulong acc_region_idx = vm->acc_region_metas[instr_acc_idx].region_idx;
478 0 : if( original_len && vm->input_mem_regions[ acc_region_idx ].haddr!=(ulong)callee_acc_rec->data ) {
479 0 : vm->input_mem_regions[ acc_region_idx ].haddr = (ulong)callee_acc_rec->data;
480 0 : zero_all_mapped_spare_capacity = 1;
481 0 : }
482 :
483 0 : ulong prev_len = caller_acc_data_len;
484 0 : ulong post_len = callee_acc_rec->const_meta->dlen;
485 :
486 : /* Do additional handling in the case where the data size has changed in
487 : the course of the callee's CPI. */
488 0 : if( prev_len!=post_len ) {
489 : /* There is an illegal data overflow if the post len is greater than the
490 : original data len + the max resizing limit (10KiB). Can't resize the
491 : account if the deprecated loader is being used */
492 0 : ulong max_increase = vm->is_deprecated ? 0UL : 10240UL;
493 0 : if( FD_UNLIKELY( post_len>fd_ulong_sat_add( (ulong)original_len, max_increase ) ) ) {
494 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC;
495 0 : }
496 : /* There is additonal handling in the case where the account is larger
497 : than it was previously, but it has still grown since it was initially
498 : serialized. To handle this, we need to just zero out the now unused
499 : space in the account resizing region. */
500 0 : if( post_len<prev_len && prev_len>original_len ) {
501 0 : ulong dirty_realloc_start = fd_ulong_max( post_len, original_len );
502 0 : ulong dirty_realloc_len = fd_ulong_sat_sub( prev_len, dirty_realloc_start );
503 : /* We don't have to worry about multiple region writes here since we
504 : know the amount to zero out is located in the account's data
505 : resizing region. We intentionally write to the pointer despite
506 : loading it in because we assume that the permissions were changed
507 : in the callee. */
508 0 : uchar * dirty_region = FD_VM_MEM_HADDR_ST_WRITE_UNCHECKED( vm, caller_acc_data_vm_addr + dirty_realloc_start,
509 0 : alignof(uchar), dirty_realloc_len );
510 0 : fd_memset( dirty_region, 0, dirty_realloc_len );
511 0 : }
512 :
513 : /* Because the account data length changed from before to after the
514 : CPI we must update the fields appropriately. */
515 0 : VM_SYSCALL_CPI_SET_ACC_INFO_DATA_LEN( vm, caller_acc_info, caller_acc_data, post_len );
516 0 : ulong * caller_len = FD_VM_MEM_HADDR_ST( vm, fd_ulong_sat_sub( caller_acc_data_vm_addr, sizeof(ulong) ), alignof(ulong), sizeof(ulong) );
517 0 : *caller_len = post_len;
518 0 : }
519 :
520 : /* We need to zero out the end of the account data buffer if the account
521 : shrunk in size. This is because the bytes are accessible from within
522 : the VM but should be equal to zero to prevent undefined behavior. If
523 : prev_len > post_len, then dlen should be equal to original_len. */
524 0 : ulong spare_len = fd_ulong_sat_sub( fd_ulong_if( zero_all_mapped_spare_capacity, original_len, prev_len ), post_len );
525 0 : if( FD_UNLIKELY( spare_len ) ) {
526 0 : if( callee_acc_rec->const_meta->dlen>spare_len ) {
527 0 : memset( callee_acc_rec->data+callee_acc_rec->const_meta->dlen-spare_len, 0, spare_len );
528 0 : }
529 0 : }
530 :
531 0 : ulong realloc_bytes_used = fd_ulong_sat_sub( post_len, original_len );
532 0 : if( realloc_bytes_used && !vm->is_deprecated ) {
533 : /* We intentionally do a load in the case where we are writing to because
534 : we want to ignore the write checks. We load from the first byte of the
535 : resizing region */
536 0 : ulong resizing_idx = vm->acc_region_metas[ instr_acc_idx ].region_idx;
537 0 : if( vm->acc_region_metas[ instr_acc_idx ].has_data_region ) {
538 0 : resizing_idx++;
539 0 : }
540 0 : uchar * to_slice = (uchar*)vm->input_mem_regions[ resizing_idx ].haddr;
541 0 : uchar * from_slice = callee_acc_rec->data + original_len;
542 :
543 0 : fd_memcpy( to_slice, from_slice, realloc_bytes_used );
544 0 : }
545 0 : }
546 :
547 768 : return FD_VM_SUCCESS;
548 768 : }
549 :
550 : /* fd_vm_syscall_cpi_{rust/c} is the entrypoint for the sol_invoke_signed_{rust/c} syscalls.
551 :
552 : The bulk of the high-level logic mirrors Solana's cpi_common entrypoint function at
553 : https://github.com/solana-labs/solana/blob/2afde1b028ed4593da5b6c735729d8994c4bfac6/programs/bpf_loader/src/syscalls/cpi.rs#L1060
554 : The only differences should be in the order of the error checks, which does not affect consensus.
555 :
556 : 100-foot flow:
557 : - Translate the CPI ABI structures to the FD runtime's instruction format
558 : - Update the callee accounts with any changes made by the caller prior to this CPI instruction
559 : - Dispatch the instruction to the FD runtime (actually making the CPI call)
560 : - Update the caller accounts with any changes made by the callee during CPI execution
561 :
562 : Paramaters:
563 : - vm: pointer to the virtual machine handle
564 : - instruction_va: vm address of the instruction to execute, which will be in the language-specific ABI format.
565 : - acct_infos_va: vm address of the account infos, which will be in the language-specific ABI format.
566 : - acct_info_cnt: number of account infos
567 : - signers_seeds_va: vm address of the signers seeds
568 : - signers_seeds_cnt: number of signers seeds
569 : - _ret: pointer to the return value
570 : */
571 : #define VM_SYSCALL_CPI_ENTRYPOINT FD_EXPAND_THEN_CONCAT2(fd_vm_syscall_cpi_, VM_SYSCALL_CPI_ABI)
572 : int
573 : VM_SYSCALL_CPI_ENTRYPOINT( void * _vm,
574 : ulong instruction_va,
575 : ulong acct_infos_va,
576 : ulong acct_info_cnt,
577 : ulong signers_seeds_va,
578 : ulong signers_seeds_cnt,
579 273 : ulong * _ret ) {
580 :
581 273 : fd_vm_t * vm = (fd_vm_t *)_vm;
582 :
583 273 : FD_VM_CU_UPDATE( vm, FD_VM_INVOKE_UNITS );
584 :
585 : /* Translate instruction ********************************************/
586 : /* translate_instruction is the first thing that agave does
587 : https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/programs/bpf_loader/src/syscalls/cpi.rs#L1089 */
588 270 : VM_SYSCALL_CPI_INSTR_T const * cpi_instruction =
589 804 : FD_VM_MEM_HADDR_LD( vm, instruction_va, VM_SYSCALL_CPI_INSTR_ALIGN, VM_SYSCALL_CPI_INSTR_SIZE );
590 : /* Agave consumes CU in translate_instruction
591 : https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/programs/bpf_loader/src/syscalls/cpi.rs#L445 */
592 267 : if( FD_FEATURE_ACTIVE( vm->instr_ctx->slot_ctx, loosen_cpi_size_restriction ) ) {
593 9 : FD_VM_CU_UPDATE( vm, VM_SYSCALL_CPI_INSTR_DATA_LEN( cpi_instruction ) / FD_VM_CPI_BYTES_PER_UNIT );
594 9 : }
595 :
596 : /* Derive PDA signers ************************************************/
597 267 : fd_pubkey_t signers[ FD_CPI_MAX_SIGNER_CNT ] = {0};
598 267 : fd_pubkey_t * caller_program_id = &vm->instr_ctx->txn_ctx->accounts[ vm->instr_ctx->instr->program_id ];
599 : /* This is the equivalent of translate_slice in translate_signers:
600 : https://github.com/solana-labs/solana/blob/dbf06e258ae418097049e845035d7d5502fe1327/programs/bpf_loader/src/syscalls/cpi.rs#L595 */
601 528 : fd_vm_vec_t const * signers_seeds = FD_VM_MEM_SLICE_HADDR_LD( vm, signers_seeds_va, FD_VM_VEC_ALIGN, fd_ulong_sat_mul( signers_seeds_cnt, FD_VM_VEC_SIZE ) );
602 : /* Right after translating, Agave checks against MAX_SIGNERS:
603 : https://github.com/solana-labs/solana/blob/dbf06e258ae418097049e845035d7d5502fe1327/programs/bpf_loader/src/syscalls/cpi.rs#L602 */
604 264 : if( FD_UNLIKELY( signers_seeds_cnt > FD_CPI_MAX_SIGNER_CNT ) ) {
605 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_TOO_MANY_SIGNERS );
606 0 : return FD_VM_ERR_SYSCALL_TOO_MANY_SIGNERS;
607 0 : }
608 387 : for( ulong i=0UL; i<signers_seeds_cnt; i++ ) {
609 123 : int err = fd_vm_derive_pda( vm, caller_program_id, 0UL, signers_seeds[i].addr, signers_seeds[i].len, NULL, &signers[i] );
610 123 : if( FD_UNLIKELY( err ) ) {
611 0 : return err;
612 0 : }
613 123 : }
614 :
615 : /* Translate CPI account metas *************************************************/
616 264 : VM_SYSCALL_CPI_ACC_META_T const * cpi_account_metas =
617 528 : FD_VM_MEM_SLICE_HADDR_LD( vm, VM_SYSCALL_CPI_INSTR_ACCS_ADDR( cpi_instruction ),
618 528 : VM_SYSCALL_CPI_ACC_META_ALIGN,
619 528 : fd_ulong_sat_mul( VM_SYSCALL_CPI_INSTR_ACCS_LEN( cpi_instruction ), VM_SYSCALL_CPI_ACC_META_SIZE ) );
620 :
621 : /* Translate instruction data *************************************************/
622 :
623 528 : uchar const * data = FD_VM_MEM_SLICE_HADDR_LD(
624 528 : vm, VM_SYSCALL_CPI_INSTR_DATA_ADDR( cpi_instruction ),
625 528 : FD_VM_ALIGN_RUST_U8,
626 528 : VM_SYSCALL_CPI_INSTR_DATA_LEN( cpi_instruction ));
627 :
628 : /* Authorized program check *************************************************/
629 :
630 264 : fd_pubkey_t const * program_id = (fd_pubkey_t *)VM_SYSCALL_CPI_INSTR_PROGRAM_ID( vm, cpi_instruction );
631 264 : if( FD_UNLIKELY( fd_vm_syscall_cpi_check_authorized_program( program_id, vm->instr_ctx->slot_ctx, data, VM_SYSCALL_CPI_INSTR_DATA_LEN( cpi_instruction ) ) ) ) {
632 0 : return FD_VM_ERR_PERM;
633 0 : }
634 :
635 : /* Instruction checks ***********************************************/
636 :
637 264 : int err = fd_vm_syscall_cpi_check_instruction( vm, VM_SYSCALL_CPI_INSTR_ACCS_LEN( cpi_instruction ), VM_SYSCALL_CPI_INSTR_DATA_LEN( cpi_instruction ) );
638 264 : if( FD_UNLIKELY( err ) ) {
639 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, err );
640 0 : return err;
641 0 : }
642 :
643 : /* Create the instruction to execute (in the input format the FD runtime expects) from
644 : the translated CPI ABI inputs. */
645 264 : fd_instr_info_t * instruction_to_execute = &vm->instr_ctx->txn_ctx->instr_infos[ vm->instr_ctx->txn_ctx->instr_info_cnt ];
646 :
647 264 : vm->instr_ctx->txn_ctx->instr_info_cnt++;
648 264 : if( FD_UNLIKELY( vm->instr_ctx->txn_ctx->instr_info_cnt>FD_MAX_INSTRUCTION_TRACE_LENGTH ) ) {
649 0 : return FD_EXECUTOR_INSTR_ERR_MAX_INSN_TRACE_LENS_EXCEEDED;;
650 0 : }
651 :
652 264 : err = VM_SYSCALL_CPI_INSTRUCTION_TO_INSTR_FUNC( vm, cpi_instruction, cpi_account_metas, program_id, data, instruction_to_execute );
653 264 : if( FD_UNLIKELY( err ) ) return err;
654 :
655 : /* Prepare the instruction for execution in the runtime. This is required by the runtime
656 : before we can pass an instruction to the executor. */
657 264 : fd_instruction_account_t instruction_accounts[256];
658 264 : ulong instruction_accounts_cnt;
659 264 : err = fd_vm_prepare_instruction( vm->instr_ctx->instr, instruction_to_execute, vm->instr_ctx, instruction_accounts, &instruction_accounts_cnt, signers, signers_seeds_cnt );
660 264 : if( FD_UNLIKELY( err ) ) return err;
661 :
662 : /* Translate account infos ******************************************/
663 : /* This is the equivalent of translate_slice in translate_account_infos:
664 : https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/programs/bpf_loader/src/syscalls/cpi.rs#L816 */
665 252 : VM_SYSCALL_CPI_ACC_INFO_T * acc_infos =
666 504 : FD_VM_MEM_SLICE_HADDR_ST( vm,
667 504 : acct_infos_va,
668 504 : VM_SYSCALL_CPI_ACC_INFO_ALIGN,
669 504 : fd_ulong_sat_mul( acct_info_cnt, VM_SYSCALL_CPI_ACC_INFO_SIZE ) );
670 : /* Right after translating, Agave checks the number of account infos:
671 : https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/programs/bpf_loader/src/syscalls/cpi.rs#L822 */
672 252 : if( FD_FEATURE_ACTIVE( vm->instr_ctx->slot_ctx, loosen_cpi_size_restriction ) ) {
673 0 : if( FD_UNLIKELY( acct_info_cnt > get_cpi_max_account_infos( vm->instr_ctx->slot_ctx ) ) ) {
674 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_MAX_INSTRUCTION_ACCOUNT_INFOS_EXCEEDED );
675 0 : return FD_VM_ERR_SYSCALL_MAX_INSTRUCTION_ACCOUNT_INFOS_EXCEEDED;
676 0 : }
677 252 : } else {
678 252 : ulong adjusted_len = fd_ulong_sat_mul( acct_info_cnt, sizeof( fd_pubkey_t ) );
679 252 : if ( FD_UNLIKELY( adjusted_len > FD_VM_MAX_CPI_INSTRUCTION_SIZE ) ) {
680 : /* "Cap the number of account_infos a caller can pass to approximate
681 : maximum that accounts that could be passed in an instruction" */
682 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_TOO_MANY_ACCOUNTS );
683 0 : return FD_VM_ERR_SYSCALL_TOO_MANY_ACCOUNTS;
684 0 : }
685 252 : }
686 :
687 : /* Update the callee accounts with any changes made by the caller prior to this CPI execution */
688 252 : ulong callee_account_keys[256];
689 252 : ulong caller_accounts_to_update[256];
690 252 : ulong caller_accounts_to_update_len = 0;
691 252 : err = VM_SYSCALL_CPI_TRANSLATE_AND_UPDATE_ACCOUNTS_FUNC( vm, instruction_accounts, instruction_accounts_cnt, acc_infos, acct_info_cnt, callee_account_keys, caller_accounts_to_update, &caller_accounts_to_update_len );
692 252 : if( FD_UNLIKELY( err ) ) return err;
693 :
694 : /* Check that the caller lamports haven't changed */
695 252 : ulong caller_lamports_h = 0UL;
696 252 : ulong caller_lamports_l = 0UL;
697 :
698 252 : err = fd_instr_info_sum_account_lamports( vm->instr_ctx->instr, &caller_lamports_h, &caller_lamports_l );
699 252 : if ( FD_UNLIKELY( err ) ) return FD_VM_ERR_INSTR_ERR;
700 :
701 252 : if( caller_lamports_h != vm->instr_ctx->instr->starting_lamports_h ||
702 252 : caller_lamports_l != vm->instr_ctx->instr->starting_lamports_l ) {
703 0 : return FD_VM_ERR_INSTR_ERR;
704 0 : }
705 :
706 : /* Set the transaction compute meter to be the same as the VM's compute meter,
707 : so that the callee cannot use compute units that the caller has already used. */
708 252 : vm->instr_ctx->txn_ctx->compute_meter = vm->cu;
709 :
710 : /* Execute the CPI instruction in the runtime */
711 252 : int err_exec = fd_execute_instr( vm->instr_ctx->txn_ctx, instruction_to_execute );
712 252 : ulong instr_exec_res = (ulong)err_exec;
713 :
714 : /* Set the CU meter to the instruction context's transaction context's compute meter,
715 : so that the caller can't use compute units that the callee has already used. */
716 252 : vm->cu = vm->instr_ctx->txn_ctx->compute_meter;
717 :
718 252 : *_ret = instr_exec_res;
719 :
720 252 : if( FD_UNLIKELY( err_exec ) ) return err_exec;
721 :
722 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/syscalls/cpi.rs#L1128-L1145 */
723 : /* Update all account permissions before updating the account data updates.
724 : We have inlined the anza function update_caller_account_perms here.
725 : TODO: consider factoring this out */
726 240 : if( FD_FEATURE_ACTIVE( vm->instr_ctx->slot_ctx, bpf_account_data_direct_mapping ) ) {
727 0 : for( ulong i=0UL; i<vm->instr_ctx->instr->acct_cnt; i++ ) {
728 : /* https://github.com/firedancer-io/solana/blob/508f325e19c0fd8e16683ea047d7c1a85f127e74/programs/bpf_loader/src/syscalls/cpi.rs#L939-L943 */
729 : /* Anza only even attemps to update the account permissions if it is a
730 : "caller account". Only writable accounts are caller accounts. */
731 0 : if( fd_instr_acc_is_writable_idx( vm->instr_ctx->instr, i ) ) {
732 :
733 0 : uint is_writable = (uint)fd_account_can_data_be_changed( vm->instr_ctx->instr, i, &err );
734 : /* Lookup memory regions for the account data and the realloc region. */
735 0 : ulong data_region_idx = vm->acc_region_metas[i].has_data_region ? vm->acc_region_metas[i].region_idx : 0;
736 0 : ulong realloc_region_idx = vm->acc_region_metas[i].has_resizing_region ? vm->acc_region_metas[i].region_idx : 0;
737 0 : if( data_region_idx && realloc_region_idx ) {
738 0 : realloc_region_idx++;
739 0 : }
740 :
741 0 : if( data_region_idx ) {
742 0 : vm->input_mem_regions[ data_region_idx ].is_writable = is_writable;
743 0 : }
744 0 : if( FD_LIKELY( realloc_region_idx ) ) { /* Unless is deprecated loader */
745 0 : vm->input_mem_regions[ realloc_region_idx ].is_writable = is_writable;
746 0 : }
747 0 : }
748 0 : }
749 0 : }
750 :
751 : /* Update the caller accounts with any changes made by the callee during CPI execution */
752 1008 : for( ulong i=0UL; i<caller_accounts_to_update_len; i++ ) {
753 : /* https://github.com/firedancer-io/solana/blob/508f325e19c0fd8e16683ea047d7c1a85f127e74/programs/bpf_loader/src/syscalls/cpi.rs#L939-L943 */
754 : /* We only want to update the writable accounts, because the non-writable
755 : caller accounts can't be changed during a CPI execution. */
756 768 : if( fd_instr_acc_is_writable_idx( vm->instr_ctx->instr, callee_account_keys[i] ) ) {
757 768 : fd_pubkey_t const * callee = &vm->instr_ctx->instr->acct_pubkeys[callee_account_keys[i]];
758 768 : err = VM_SYSCALL_CPI_UPDATE_CALLER_ACC_FUNC(vm, &acc_infos[caller_accounts_to_update[i]], (uchar)callee_account_keys[i], callee);
759 768 : if( FD_UNLIKELY( err ) ) return err;
760 768 : }
761 768 : }
762 :
763 240 : return FD_VM_SUCCESS;
764 240 : }
765 :
766 : #undef VM_SYSCALL_CPI_UPDATE_CALLER_ACC_FUNC
767 : #undef VM_SYSCALL_CPI_FROM_ACC_INFO_FUNC
768 : #undef VM_SYSCALL_CPI_TRANSLATE_AND_UPDATE_ACCOUNTS_FUNC
769 : #undef VM_SYSCALL_CPI_INSTRUCTION_TO_INSTR_FUNC
770 : #undef VM_SYSCALL_CPI_FUNC
|