Line data Source code
1 : #include "fd_config_program.h" 2 : #include "../fd_account.h" 3 : #include "../fd_acc_mgr.h" 4 : #include "../fd_executor.h" 5 : #include "../fd_system_ids.h" 6 : #include "../context/fd_exec_epoch_ctx.h" 7 : #include "../context/fd_exec_slot_ctx.h" 8 : #include "../context/fd_exec_txn_ctx.h" 9 : #include "../context/fd_exec_instr_ctx.h" 10 : 11 : /* Useful links: 12 : 13 : https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs */ 14 : 15 : #define DEFAULT_COMPUTE_UNITS 450UL 16 : 17 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L16 */ 18 : 19 : static int 20 10377 : _process_config_instr( fd_exec_instr_ctx_t * ctx ) { 21 : 22 10377 : # define ACC_IDX_CONFIG ((uchar)0) 23 : 24 : /* Deserialize the Config Program instruction data, which consists only of the ConfigKeys 25 : https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L21 */ 26 10377 : if( FD_UNLIKELY( ctx->instr->data==NULL ) ) { 27 453 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA; 28 453 : } 29 : 30 9924 : fd_bincode_decode_ctx_t decode = 31 9924 : { .valloc = ctx->valloc, 32 9924 : .data = ctx->instr->data, 33 9924 : .dataend = ctx->instr->data + ctx->instr->data_sz }; 34 : 35 9924 : fd_config_keys_t key_list = {0}; 36 9924 : int decode_result = fd_config_keys_decode( &key_list, &decode ); 37 : /* Fail if the number of bytes consumed by deserialize exceeds 1232 38 : (hardcoded constant by Agave limited_deserialize) */ 39 9924 : if( FD_UNLIKELY( decode_result != FD_BINCODE_SUCCESS || 40 9924 : (ulong)ctx->instr->data + 1232UL < (ulong)decode.data ) ) { 41 3984 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA; 42 3984 : } 43 : 44 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L22-L26 */ 45 : 46 5940 : fd_config_keys_t current_data; 47 5940 : int is_config_account_signer = 0; 48 5940 : fd_pubkey_t const * config_account_key = NULL; 49 23283 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( ctx, ACC_IDX_CONFIG, config_acc_rec ) { 50 : 51 5781 : config_account_key = config_acc_rec->pubkey; 52 : 53 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L27 */ 54 : 55 5781 : is_config_account_signer = fd_instr_acc_is_signer_idx( ctx->instr, ACC_IDX_CONFIG ); 56 : 57 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L29-L31 */ 58 : 59 5781 : if( FD_UNLIKELY( 0!=memcmp( &config_acc_rec->const_meta->info.owner, fd_solana_config_program_id.key, sizeof(fd_pubkey_t) ) ) ) { 60 1698 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER; 61 1698 : } 62 : 63 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L33-L40 */ 64 : 65 4083 : fd_bincode_decode_ctx_t config_acc_state_decode_context = { 66 4083 : .valloc = ctx->valloc, 67 4083 : .data = config_acc_rec->const_data, 68 4083 : .dataend = config_acc_rec->const_data + config_acc_rec->const_meta->dlen, 69 4083 : }; 70 4083 : decode_result = fd_config_keys_decode( ¤t_data, &config_acc_state_decode_context ); 71 4083 : if( FD_UNLIKELY( decode_result!=FD_BINCODE_SUCCESS ) ) { 72 : //TODO: full log, including err 73 987 : fd_log_collector_msg_literal( ctx, "Unable to deserialize config account" ); 74 987 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; 75 987 : } 76 : 77 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L42 */ 78 : 79 5781 : } FD_BORROWED_ACCOUNT_DROP( config_acc_rec ); 80 : 81 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L44-L49 */ 82 : 83 3096 : fd_pubkey_t * current_signer_keys = fd_scratch_alloc( alignof(fd_pubkey_t), sizeof(fd_pubkey_t) * current_data.keys_len ); 84 3096 : ulong current_signer_key_cnt = 0UL; 85 : 86 56598 : for( ulong i=0UL; i < current_data.keys_len; i++ ) { 87 53502 : if( current_data.keys[i].signer ) { 88 26367 : current_signer_keys[ current_signer_key_cnt++ ] = current_data.keys[i].key; 89 26367 : } 90 53502 : } 91 : 92 : /* If we have no keys in the account, require the config account to have signed the transaction 93 : https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L50-L56 */ 94 : 95 3096 : if( FD_UNLIKELY( current_signer_key_cnt==0UL && !is_config_account_signer ) ) { 96 180 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 97 180 : } 98 : 99 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L58 */ 100 : 101 2916 : ulong counter = 0UL; 102 : /* Invariant: counter <= key_list.keys_len */ 103 : 104 12528 : for( ulong i=0UL; i<key_list.keys_len; i++ ) { 105 11475 : if( !key_list.keys[i].signer ) continue; 106 3294 : fd_pubkey_t const * signer = &key_list.keys[i].key; 107 : 108 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L60 */ 109 : 110 3294 : counter++; 111 : 112 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L61 */ 113 : 114 3294 : if( 0!=memcmp( signer, config_account_key, sizeof(fd_pubkey_t) ) ) { 115 : 116 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L62-L71 */ 117 : 118 : /* Intentionally don't use the scoping macro here because Anza maps the 119 : error to missing required signature if the try borrow fails */ 120 1938 : fd_borrowed_account_t * signer_account = NULL; 121 1938 : int borrow_err = fd_instr_borrowed_account_view_idx( ctx, (uchar)counter, &signer_account ); 122 1938 : if( FD_UNLIKELY( borrow_err!=FD_ACC_MGR_SUCCESS ) ) { 123 : /* Max msg_sz: 33 - 2 + 45 = 76 < 127 => we can use printf */ 124 18 : fd_log_collector_printf_dangerous_max_127( ctx, 125 18 : "account %s is not in account list", FD_BASE58_ENC_32_ALLOCA( signer ) ); 126 18 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 127 18 : } 128 1920 : if( FD_UNLIKELY( !fd_borrowed_account_acquire_read( signer_account ) ) ) { 129 : /* Max msg_sz: 33 - 2 + 45 = 76 < 127 => we can use printf */ 130 0 : fd_log_collector_printf_dangerous_max_127( ctx, 131 0 : "account %s is not in account list", FD_BASE58_ENC_32_ALLOCA( signer ) ); 132 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; /* seems to be deliberately not ACC_BORROW_FAILED? */ 133 0 : } 134 : 135 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L72-L79 */ 136 : 137 1920 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, (uchar)counter ) ) ) { 138 : /* Max msg_sz: 33 - 2 + 45 = 76 < 127 => we can use printf */ 139 585 : fd_log_collector_printf_dangerous_max_127( ctx, 140 585 : "account %s signer_key().is_none()", FD_BASE58_ENC_32_ALLOCA( signer ) ); 141 585 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 142 585 : } 143 : 144 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L80-L87 */ 145 : 146 1335 : if( FD_UNLIKELY( 0!=memcmp( signer_account->pubkey, signer, sizeof(fd_pubkey_t) ) ) ) { 147 : /* Max msg_sz: 53 - 3 + 20 = 70 < 127 => we can use printf */ 148 429 : fd_log_collector_printf_dangerous_max_127( ctx, 149 429 : "account[%lu].signer_key() does not match Config data)", counter+1 ); 150 429 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 151 429 : } 152 : 153 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L89-L98 */ 154 : 155 906 : if( current_data.keys_len>0UL ) { 156 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L90 */ 157 498 : int is_signer = 0; 158 6027 : for( ulong j=0UL; j<current_signer_key_cnt; j++ ) { 159 5667 : if( 0==memcmp( ¤t_signer_keys[j], signer, sizeof(fd_pubkey_t) ) ) { 160 138 : is_signer = 1; 161 138 : break; 162 138 : } 163 5667 : } 164 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L97 */ 165 498 : if( FD_UNLIKELY( !is_signer ) ) { 166 : /* Max msg_sz: 39 - 2 + 45 = 82 < 127 => we can use printf */ 167 360 : fd_log_collector_printf_dangerous_max_127( ctx, 168 360 : "account %s is not in stored signer list", FD_BASE58_ENC_32_ALLOCA( signer ) ); 169 360 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 170 360 : } 171 498 : } 172 : 173 546 : fd_borrowed_account_release_read( signer_account ); 174 : 175 1356 : } 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 471 : fd_log_collector_msg_literal( ctx, "account[0].signer_key().is_none()" ); 179 471 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 180 : 181 471 : } 182 3294 : } 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 6456 : for( ulong i = 0; i < key_list.keys_len; i++ ) { 189 89559 : for( ulong j = 0; j < key_list.keys_len; j++ ) { 190 84156 : if( i == j ) continue; 191 : 192 78621 : if( FD_UNLIKELY( memcmp( &key_list.keys[i].key, &key_list.keys[j].key, sizeof(fd_pubkey_t) ) == 0 && 193 78621 : key_list.keys[i].signer == key_list.keys[j].signer ) ) { 194 132 : fd_log_collector_msg_literal( ctx, "new config contains duplicate keys" ); 195 132 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG; 196 132 : } 197 78621 : } 198 5535 : } 199 : 200 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L118-L126 */ 201 : 202 921 : if( FD_UNLIKELY( current_signer_key_cnt>counter ) ) { 203 : /* Max msg_sz: 35 - 6 + 2*20 = 69 < 127 => we can use printf */ 204 369 : fd_log_collector_printf_dangerous_max_127( ctx, 205 369 : "too few signers: %lu; expected: %lu", counter, current_signer_key_cnt ); 206 369 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 207 369 : } 208 : 209 : /* Upgrade to writable handle 210 : https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L128-L129 */ 211 : 212 2208 : FD_BORROWED_ACCOUNT_TRY_BORROW_IDX( ctx, ACC_IDX_CONFIG, config_acc_rec ) { 213 : 214 : /* Upgrade to writable handle 215 : https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L130-L133 */ 216 : 217 552 : if( FD_UNLIKELY( config_acc_rec->const_meta->dlen<ctx->instr->data_sz ) ) { 218 219 : fd_log_collector_msg_literal( ctx, "instruction data too large" ); 219 219 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA; 220 219 : } 221 : 222 : /* Inlined solana_sdk::transaction_context::BorrowedAccount::get_data_mut */ 223 : 224 333 : do { 225 333 : int err; 226 333 : if( FD_UNLIKELY( !fd_account_can_data_be_changed( ctx->instr, 0, &err ) ) ) { 227 69 : return err; 228 69 : } 229 333 : } while(0); 230 : 231 264 : do { 232 264 : int err = fd_instr_borrowed_account_modify_idx( ctx, 0, config_acc_rec->const_meta->dlen, &config_acc_rec ); 233 264 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) )); 234 264 : } while(0); 235 : 236 : /* copy_from_slice */ 237 : 238 264 : fd_memcpy( config_acc_rec->data, ctx->instr->data, ctx->instr->data_sz ); 239 : 240 : /* Implicitly dropped in Anza */ 241 : 242 552 : } FD_BORROWED_ACCOUNT_DROP( config_acc_rec ); 243 : 244 264 : return FD_EXECUTOR_INSTR_SUCCESS; 245 552 : # undef ACC_IDX_CONFIG 246 : 247 552 : } 248 : 249 : int 250 13893 : fd_config_program_execute( fd_exec_instr_ctx_t * ctx ) { 251 : 252 : /* https://github.com/solana-labs/solana/blob/v1.17.27/programs/config/src/config_processor.rs#L14 253 : See DEFAULT_COMPUTE_UNITS */ 254 13893 : FD_EXEC_CU_UPDATE( ctx, DEFAULT_COMPUTE_UNITS ); 255 : 256 10377 : FD_SCRATCH_SCOPE_BEGIN { 257 : 258 10377 : int ret = _process_config_instr( ctx ); 259 10377 : return ret; 260 : 261 10377 : } FD_SCRATCH_SCOPE_END; 262 10377 : }