Line data Source code
1 : #include "fd_config_program.h" 2 : #include "../fd_borrowed_account.h" 3 : #include "../fd_executor.h" 4 : #include "../fd_system_ids.h" 5 : #include "../context/fd_exec_instr_ctx.h" 6 : #include "../../log_collector/fd_log_collector.h" 7 : 8 : /* Useful links: 9 : 10 : https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs */ 11 : 12 : #define DEFAULT_COMPUTE_UNITS 450UL 13 : 14 : /* The bound on the number of keys that could get passed into the config 15 : program is bounded by the TXN_MTU of 1232 bytes. Assuming that the 16 : vector of config keys comprises the entire transaction, then we can 17 : have 1232(bytes)/(33 bytes/key) = 37 keys. So our bound is equal to 18 : sizeof(fd_config_keys_t) + 37*sizeof(fd_config_keys_pair_t) = 1237 bytes. */ 19 : 20 : #define CONFIG_INSTRUCTION_KEYS_FOOTPRINT (1237UL) 21 : 22 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L16 */ 23 : 24 : static int 25 0 : _process_config_instr( fd_exec_instr_ctx_t * ctx ) { 26 : 27 0 : # define ACC_IDX_CONFIG ((uchar)0) 28 : 29 0 : int err; 30 : 31 : /* Deserialize the Config Program instruction data, which consists only of the ConfigKeys 32 : https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L21 */ 33 0 : if( FD_UNLIKELY( ctx->instr->data_sz>FD_TXN_MTU ) ) { 34 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA; 35 0 : } 36 : 37 0 : uchar key_list_mem[ CONFIG_INSTRUCTION_KEYS_FOOTPRINT ] __attribute__((aligned(FD_CONFIG_KEYS_ALIGN))); 38 0 : fd_config_keys_t * key_list = fd_bincode_decode_static( 39 0 : config_keys, key_list_mem, 40 0 : ctx->instr->data, 41 0 : ctx->instr->data_sz ); 42 0 : if( FD_UNLIKELY( !key_list ) ) { 43 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA; 44 0 : } 45 : 46 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L22-L26 */ 47 : 48 0 : int is_config_account_signer = 0; 49 0 : fd_pubkey_t const * config_account_key = NULL; 50 : 51 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/config/src/config_processor.rs#L26 */ 52 0 : fd_guarded_borrowed_account_t config_acc_rec = {0}; 53 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_CONFIG, &config_acc_rec ); 54 : 55 0 : config_account_key = config_acc_rec.pubkey; 56 : 57 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L27 */ 58 : 59 0 : is_config_account_signer = fd_instr_acc_is_signer_idx( ctx->instr, ACC_IDX_CONFIG, NULL ); 60 : 61 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L29-L31 */ 62 : 63 0 : if( FD_UNLIKELY( 0!=memcmp( fd_borrowed_account_get_owner( &config_acc_rec ), fd_solana_config_program_id.key, sizeof(fd_pubkey_t) ) ) ) { 64 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER; 65 0 : } 66 : 67 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L33-L40 */ 68 : 69 0 : uchar current_data_mem[ CONFIG_INSTRUCTION_KEYS_FOOTPRINT ] __attribute__((aligned(FD_CONFIG_KEYS_ALIGN))); 70 0 : fd_config_keys_t * current_data = fd_bincode_decode_static( 71 0 : config_keys, current_data_mem, 72 0 : fd_borrowed_account_get_data( &config_acc_rec ), 73 0 : fd_borrowed_account_get_data_len( &config_acc_rec ) ); 74 0 : if( FD_UNLIKELY( !current_data ) ) { 75 : //TODO: full log, including err 76 0 : fd_log_collector_msg_literal( ctx, "Unable to deserialize config account" ); 77 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; 78 0 : } 79 : 80 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/config/src/config_processor.rs#L43 */ 81 : 82 0 : fd_borrowed_account_drop( &config_acc_rec ); 83 : 84 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L44-L49 */ 85 : 86 0 : fd_pubkey_t current_signer_keys[ 37UL ]; 87 0 : ulong current_signer_key_cnt = 0UL; 88 : 89 0 : for( ulong i=0UL; i < current_data->keys_len; i++ ) { 90 0 : if( current_data->keys[i].signer ) { 91 0 : current_signer_keys[ current_signer_key_cnt++ ] = current_data->keys[i].key; 92 0 : } 93 0 : } 94 : 95 : /* If we have no keys in the account, require the config account to have signed the transaction 96 : https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L50-L56 */ 97 : 98 0 : if( FD_UNLIKELY( current_signer_key_cnt==0UL && !is_config_account_signer ) ) { 99 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 100 0 : } 101 : 102 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L58 */ 103 : 104 0 : ulong counter = 0UL; 105 : /* Invariant: counter <= key_list.keys_len */ 106 : 107 0 : for( ulong i=0UL; i<key_list->keys_len; i++ ) { 108 0 : if( !key_list->keys[i].signer ) continue; 109 0 : fd_pubkey_t const * signer = &key_list->keys[i].key; 110 : 111 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L60 */ 112 : 113 0 : counter++; 114 : 115 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L61 */ 116 : 117 0 : if( 0!=memcmp( signer, config_account_key, sizeof(fd_pubkey_t) ) ) { 118 : 119 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L62-L71 */ 120 : 121 : /* Intentionally don't use the scoping macro here because Anza maps the 122 : error to missing required signature if the try borrow fails */ 123 0 : fd_borrowed_account_t signer_account; 124 0 : int borrow_err = fd_exec_instr_ctx_try_borrow_instr_account( ctx, (uchar)counter, &signer_account ); 125 0 : if( FD_UNLIKELY( borrow_err ) ) { 126 : /* Max msg_sz: 33 - 2 + 45 = 76 < 127 => we can use printf */ 127 0 : FD_BASE58_ENCODE_32_BYTES( signer->uc, signer_b58 ); 128 0 : fd_log_collector_printf_dangerous_max_127( ctx, 129 0 : "account %s is not in account list", signer_b58 ); 130 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 131 0 : } 132 : 133 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L72-L79 */ 134 : 135 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, (uchar)counter, NULL ) ) ) { 136 : /* Max msg_sz: 33 - 2 + 45 = 76 < 127 => we can use printf */ 137 0 : FD_BASE58_ENCODE_32_BYTES( signer->uc, signer_b58 ); 138 0 : fd_log_collector_printf_dangerous_max_127( ctx, 139 0 : "account %s signer_key().is_none()", signer_b58 ); 140 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 141 0 : } 142 : 143 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L80-L87 */ 144 : 145 0 : if( FD_UNLIKELY( 0!=memcmp( signer_account.pubkey, signer, sizeof(fd_pubkey_t) ) ) ) { 146 : /* Max msg_sz: 53 - 3 + 20 = 70 < 127 => we can use printf */ 147 0 : fd_log_collector_printf_dangerous_max_127( ctx, 148 0 : "account[%lu].signer_key() does not match Config data)", counter+1 ); 149 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 150 0 : } 151 : 152 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L89-L98 */ 153 : 154 0 : if( current_data->keys_len>0UL ) { 155 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L90 */ 156 0 : int is_signer = 0; 157 0 : for( ulong j=0UL; j<current_signer_key_cnt; j++ ) { 158 0 : if( 0==memcmp( ¤t_signer_keys[j], signer, sizeof(fd_pubkey_t) ) ) { 159 0 : is_signer = 1; 160 0 : break; 161 0 : } 162 0 : } 163 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L97 */ 164 0 : if( FD_UNLIKELY( !is_signer ) ) { 165 : /* Max msg_sz: 39 - 2 + 45 = 82 < 127 => we can use printf */ 166 0 : FD_BASE58_ENCODE_32_BYTES( signer->uc, signer_b58 ); 167 0 : fd_log_collector_printf_dangerous_max_127( ctx, 168 0 : "account %s is not in stored signer list", signer_b58 ); 169 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 170 0 : } 171 0 : } 172 : 173 : /* implicit drop of signer account */ 174 : 175 0 : } else if( !is_config_account_signer ) { 176 : 177 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L101 */ 178 0 : fd_log_collector_msg_literal( ctx, "account[0].signer_key().is_none()" ); 179 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 180 : 181 0 : } 182 0 : } 183 : 184 : /* Disallow duplicate keys 185 : https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L105-L115 186 : 187 : TODO: Agave uses a O(n log n) algorithm here */ 188 0 : for( ulong i = 0; i < key_list->keys_len; i++ ) { 189 0 : for( ulong j = 0; j < key_list->keys_len; j++ ) { 190 0 : if( i == j ) continue; 191 : 192 0 : if( FD_UNLIKELY( memcmp( &key_list->keys[i].key, &key_list->keys[j].key, sizeof(fd_pubkey_t) ) == 0 && 193 0 : key_list->keys[i].signer == key_list->keys[j].signer ) ) { 194 0 : fd_log_collector_msg_literal( ctx, "new config contains duplicate keys" ); 195 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG; 196 0 : } 197 0 : } 198 0 : } 199 : 200 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L118-L126 */ 201 : 202 0 : if( FD_UNLIKELY( current_signer_key_cnt>counter ) ) { 203 : /* Max msg_sz: 35 - 6 + 2*20 = 69 < 127 => we can use printf */ 204 0 : fd_log_collector_printf_dangerous_max_127( ctx, 205 0 : "too few signers: %lu; expected: %lu", counter, current_signer_key_cnt ); 206 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 207 0 : } 208 : 209 : /* Upgrade to writable handle 210 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/config/src/config_processor.rs#L125-L126 */ 211 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_CONFIG, &config_acc_rec ); 212 : 213 : /* Upgrade to writable handle 214 : https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L130-L133 */ 215 : 216 0 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &config_acc_rec )<ctx->instr->data_sz ) ) { 217 0 : fd_log_collector_msg_literal( ctx, "instruction data too large" ); 218 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA; 219 0 : } 220 : 221 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/config/src/config_processor.rs#L131 */ 222 0 : uchar * data = NULL; 223 0 : ulong dlen = 0UL; 224 0 : err = fd_borrowed_account_get_data_mut( &config_acc_rec, &data, &dlen ); 225 0 : if( FD_UNLIKELY( err ) ) { 226 0 : return err; 227 0 : } 228 : 229 : /* copy_from_slice */ 230 0 : fd_memcpy( data, ctx->instr->data, ctx->instr->data_sz ); 231 : 232 : /* Implicitly dropped */ 233 : 234 0 : return FD_EXECUTOR_INSTR_SUCCESS; 235 0 : # undef ACC_IDX_CONFIG 236 : 237 0 : } 238 : 239 : int 240 0 : fd_config_program_execute( fd_exec_instr_ctx_t * ctx ) { 241 : /* Prevent execution of migrated native programs */ 242 0 : if( FD_UNLIKELY( FD_FEATURE_ACTIVE_BANK( ctx->bank, migrate_config_program_to_core_bpf ) ) ) { 243 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID; 244 0 : } 245 : 246 : /* https://github.com/solana-labs/solana/blob/v1.17.27/programs/config/src/config_processor.rs#L14 247 : See DEFAULT_COMPUTE_UNITS */ 248 0 : FD_EXEC_CU_UPDATE( ctx, DEFAULT_COMPUTE_UNITS ); 249 : 250 0 : int ret = _process_config_instr( ctx ); 251 0 : return ret; 252 0 : }