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