Line data Source code
1 : #include "fd_sysvar_instructions.h" 2 : #include "../fd_borrowed_account.h" 3 : #include "../fd_system_ids.h" 4 : 5 : static ulong 6 : instructions_serialized_size( fd_instr_info_t const * instrs, 7 0 : ushort instrs_cnt ) { 8 0 : ulong serialized_size = 0; 9 : 10 0 : serialized_size += sizeof(ushort) // num_instructions 11 0 : + (sizeof(ushort) * instrs_cnt); // instruction offsets 12 : 13 0 : for ( ushort i = 0; i < instrs_cnt; ++i ) { 14 0 : fd_instr_info_t const * instr = &instrs[i]; 15 : 16 0 : serialized_size += sizeof(ushort); // num_accounts; 17 : 18 0 : serialized_size += instr->acct_cnt * ( 19 0 : sizeof(uchar) // flags (is_signer, is_writeable) 20 0 : + sizeof(fd_pubkey_t) // pubkey 21 0 : ); 22 : 23 0 : serialized_size += sizeof(fd_pubkey_t) // program_id pubkey 24 0 : + sizeof(ushort) // instr_data_len; 25 0 : + instr->data_sz; // instr_data; 26 : 27 0 : } 28 : 29 0 : serialized_size += sizeof(ushort); // current_instr_idx 30 : 31 0 : return serialized_size; 32 0 : } 33 : 34 : /* https://github.com/anza-xyz/agave/blob/v2.1.1/svm/src/account_loader.rs#L547-L576 */ 35 : void 36 : fd_sysvar_instructions_serialize_account( fd_exec_txn_ctx_t * txn_ctx, 37 : fd_instr_info_t const * instrs, 38 0 : ushort instrs_cnt ) { 39 0 : ulong serialized_sz = instructions_serialized_size( instrs, instrs_cnt ); 40 : 41 0 : fd_txn_account_t * rec = NULL; 42 0 : int err = fd_exec_txn_ctx_get_account_with_key( txn_ctx, 43 0 : &fd_sysvar_instructions_id, 44 0 : &rec, 45 0 : fd_txn_account_check_exists ); 46 0 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS && rec==NULL ) ) { 47 : /* The way we use this, this should NEVER hit since the borrowed accounts should be set up 48 : before this is called, and this is only called if the sysvar instructions account is in 49 : the borrowed accounts list. */ 50 0 : FD_LOG_ERR(( "Failed to view sysvar instructions borrowed account. It may not be included in the txn account keys." )); 51 0 : } 52 : 53 : /* This stays within the FD spad allocation bounds because... 54 : 1. Case 1: rec->meta!=NULL 55 : - rec->meta was set up in `fd_executor_setup_accounts_for_txn()` and data was allocated from the spad 56 : - No need to allocate meta and data here 57 : 2. Case 2: rec->meta==NULL 58 : - `fd_executor_setup_accounts_for_txn()` did not make an spad allocation for this account 59 : - spad memory is sized out for allocations for 128 (max number) accounts 60 : - sizeof(fd_account_meta_t) + serialized_sz will always be less than FD_ACC_TOT_SZ_MAX 61 : - at most 127 accounts could be using spad memory right now, so this allocation is safe */ 62 0 : if( !rec->vt->is_mutable( rec ) ) { 63 0 : fd_txn_account_setup_meta_mutable( rec, txn_ctx->spad, serialized_sz ); 64 0 : } 65 : 66 : /* Agave sets up the borrowed account for the instructions sysvar to contain 67 : default values except for the data which is serialized into the account. */ 68 : 69 0 : rec->vt->set_owner( rec, &fd_sysvar_owner_id ); 70 0 : rec->vt->set_lamports( rec, 0UL ); 71 0 : rec->vt->set_executable( rec, 0 ); 72 0 : rec->vt->set_rent_epoch( rec, 0UL ); 73 0 : rec->vt->set_data_len( rec, serialized_sz ); 74 0 : rec->starting_lamports = 0UL; 75 : 76 0 : uchar * serialized_instructions = rec->vt->get_data_mut( rec ); 77 0 : ulong offset = 0; 78 : 79 : // TODO: do we needs bounds checking? 80 : // num_instructions 81 0 : FD_STORE( ushort, serialized_instructions + offset, instrs_cnt); 82 0 : offset += sizeof(ushort); 83 : 84 : // instruction offsets 85 0 : uchar * serialized_instruction_offsets = serialized_instructions + offset; 86 0 : offset += (ushort)(sizeof(ushort) * instrs_cnt); 87 : 88 : // serialize instructions 89 0 : for( ushort i = 0; i < instrs_cnt; ++i ) { 90 : // set the instruction offset 91 0 : FD_STORE( ushort, serialized_instruction_offsets, (ushort) offset ); 92 0 : serialized_instruction_offsets += sizeof(ushort); 93 : 94 0 : fd_instr_info_t const * instr = &instrs[i]; 95 : 96 : // num_accounts 97 0 : FD_STORE( ushort, serialized_instructions + offset, instr->acct_cnt ); 98 0 : offset += sizeof(ushort); 99 : 100 0 : for ( ushort j = 0; j < instr->acct_cnt; j++ ) { 101 : // flags 102 0 : FD_STORE( uchar, serialized_instructions + offset, fd_instr_get_acc_flags( instr, j ) ); 103 0 : offset += sizeof(uchar); 104 : 105 : // pubkey 106 0 : ushort idx_in_txn = instr->accounts[j].index_in_transaction; 107 0 : FD_STORE( fd_pubkey_t, serialized_instructions + offset, txn_ctx->account_keys[ idx_in_txn ] ); 108 0 : offset += sizeof(fd_pubkey_t); 109 0 : } 110 : 111 : // program_id_pubkey 112 0 : FD_STORE( fd_pubkey_t, serialized_instructions + offset, txn_ctx->account_keys[ instr->program_id ] ); 113 0 : offset += sizeof(fd_pubkey_t); 114 : 115 : // instr_data_len 116 0 : FD_STORE( ushort, serialized_instructions + offset, instr->data_sz ); 117 0 : offset += sizeof(ushort); 118 : 119 : // instr_data 120 0 : fd_memcpy( serialized_instructions + offset, instr->data, instr->data_sz ); 121 0 : offset += instr->data_sz; 122 0 : } 123 : 124 : // 125 0 : FD_STORE( ushort, serialized_instructions + offset, 0 ); 126 0 : offset += sizeof(ushort); 127 0 : } 128 : 129 : /* Stores the current instruction index in the instructions sysvar account. 130 : https://github.com/anza-xyz/solana-sdk/blob/instructions-sysvar%40v2.2.1/instructions-sysvar/src/lib.rs#L164-L167 */ 131 : void 132 : fd_sysvar_instructions_update_current_instr_idx( fd_txn_account_t * rec, 133 0 : ushort current_instr_idx ) { 134 : /* Extra safety checks */ 135 0 : if( FD_UNLIKELY( rec->vt->get_data_len( rec )<sizeof(ushort) ) ) { 136 0 : return; 137 0 : } 138 : 139 0 : uchar * serialized_current_instr_idx = rec->vt->get_data_mut( rec ) + (rec->vt->get_data_len( rec ) - sizeof(ushort)); 140 0 : FD_STORE( ushort, serialized_current_instr_idx, current_instr_idx ); 141 0 : }