Line data Source code
1 : #include "fd_config_program.h" 2 : #include "../fd_borrowed_account.h" 3 : #include "../fd_acc_mgr.h" 4 : #include "../fd_executor.h" 5 : #include "../fd_system_ids.h" 6 : #include "../context/fd_exec_slot_ctx.h" 7 : #include "../context/fd_exec_txn_ctx.h" 8 : #include "../context/fd_exec_instr_ctx.h" 9 : 10 : /* Useful links: 11 : 12 : https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs */ 13 : 14 : #define DEFAULT_COMPUTE_UNITS 450UL 15 : 16 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L16 */ 17 : 18 : static int 19 0 : _process_config_instr( fd_exec_instr_ctx_t * ctx ) { 20 : 21 0 : # define ACC_IDX_CONFIG ((uchar)0) 22 : 23 0 : int err; 24 : 25 : /* Deserialize the Config Program instruction data, which consists only of the ConfigKeys 26 : https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L21 */ 27 0 : if( FD_UNLIKELY( ctx->instr->data==NULL ) ) { 28 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA; 29 0 : } 30 : 31 0 : int decode_result; 32 0 : ulong decoded_sz = 0UL; 33 0 : fd_config_keys_t * key_list = fd_bincode_decode1_spad( 34 0 : config_keys, ctx->txn_ctx->spad, 35 0 : ctx->instr->data, 36 0 : ctx->instr->data_sz, 37 0 : &decode_result, 38 0 : &decoded_sz ); 39 0 : if( FD_UNLIKELY( decode_result != FD_BINCODE_SUCCESS || 40 0 : decoded_sz > FD_TXN_MTU ) ) { 41 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA; 42 0 : } 43 : 44 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L22-L26 */ 45 : 46 0 : int is_config_account_signer = 0; 47 0 : fd_pubkey_t const * config_account_key = NULL; 48 : 49 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/config/src/config_processor.rs#L26 */ 50 0 : fd_guarded_borrowed_account_t config_acc_rec; 51 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_CONFIG, &config_acc_rec ); 52 : 53 0 : config_account_key = config_acc_rec.acct->pubkey; 54 : 55 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L27 */ 56 : 57 0 : is_config_account_signer = fd_instr_acc_is_signer_idx( ctx->instr, ACC_IDX_CONFIG ); 58 : 59 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L29-L31 */ 60 : 61 0 : if( FD_UNLIKELY( 0!=memcmp( fd_borrowed_account_get_owner( &config_acc_rec ), fd_solana_config_program_id.key, sizeof(fd_pubkey_t) ) ) ) { 62 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER; 63 0 : } 64 : 65 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L33-L40 */ 66 : 67 0 : fd_config_keys_t * current_data = fd_bincode_decode_spad( 68 0 : config_keys, ctx->txn_ctx->spad, 69 0 : fd_borrowed_account_get_data( &config_acc_rec ), 70 0 : fd_borrowed_account_get_data_len( &config_acc_rec ), 71 0 : &decode_result ); 72 0 : if( FD_UNLIKELY( decode_result!=FD_BINCODE_SUCCESS ) ) { 73 : //TODO: full log, including err 74 0 : fd_log_collector_msg_literal( ctx, "Unable to deserialize config account" ); 75 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; 76 0 : } 77 : 78 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/config/src/config_processor.rs#L43 */ 79 : 80 0 : fd_borrowed_account_drop( &config_acc_rec ); 81 : 82 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L44-L49 */ 83 : 84 0 : fd_pubkey_t * current_signer_keys = fd_spad_alloc( ctx->txn_ctx->spad, 85 0 : alignof(fd_pubkey_t), 86 0 : sizeof(fd_pubkey_t) * current_data->keys_len ); 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_log_collector_printf_dangerous_max_127( ctx, 128 0 : "account %s is not in account list", FD_BASE58_ENC_32_ALLOCA( signer ) ); 129 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 130 0 : } 131 : 132 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L72-L79 */ 133 : 134 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, (uchar)counter ) ) ) { 135 : /* Max msg_sz: 33 - 2 + 45 = 76 < 127 => we can use printf */ 136 0 : fd_log_collector_printf_dangerous_max_127( ctx, 137 0 : "account %s signer_key().is_none()", FD_BASE58_ENC_32_ALLOCA( signer ) ); 138 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 139 0 : } 140 : 141 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L80-L87 */ 142 : 143 0 : if( FD_UNLIKELY( 0!=memcmp( signer_account.acct->pubkey, signer, sizeof(fd_pubkey_t) ) ) ) { 144 : /* Max msg_sz: 53 - 3 + 20 = 70 < 127 => we can use printf */ 145 0 : fd_log_collector_printf_dangerous_max_127( ctx, 146 0 : "account[%lu].signer_key() does not match Config data)", counter+1 ); 147 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 148 0 : } 149 : 150 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L89-L98 */ 151 : 152 0 : if( current_data->keys_len>0UL ) { 153 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L90 */ 154 0 : int is_signer = 0; 155 0 : for( ulong j=0UL; j<current_signer_key_cnt; j++ ) { 156 0 : if( 0==memcmp( ¤t_signer_keys[j], signer, sizeof(fd_pubkey_t) ) ) { 157 0 : is_signer = 1; 158 0 : break; 159 0 : } 160 0 : } 161 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L97 */ 162 0 : if( FD_UNLIKELY( !is_signer ) ) { 163 : /* Max msg_sz: 39 - 2 + 45 = 82 < 127 => we can use printf */ 164 0 : fd_log_collector_printf_dangerous_max_127( ctx, 165 0 : "account %s is not in stored signer list", FD_BASE58_ENC_32_ALLOCA( signer ) ); 166 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 167 0 : } 168 0 : } 169 : 170 : /* implicit drop of signer account */ 171 : 172 0 : } else if( !is_config_account_signer ) { 173 : 174 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L101 */ 175 0 : fd_log_collector_msg_literal( ctx, "account[0].signer_key().is_none()" ); 176 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 177 : 178 0 : } 179 0 : } 180 : 181 : /* Disallow duplicate keys 182 : https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L105-L115 183 : 184 : TODO: Agave uses a O(n log n) algorithm here */ 185 0 : for( ulong i = 0; i < key_list->keys_len; i++ ) { 186 0 : for( ulong j = 0; j < key_list->keys_len; j++ ) { 187 0 : if( i == j ) continue; 188 : 189 0 : if( FD_UNLIKELY( memcmp( &key_list->keys[i].key, &key_list->keys[j].key, sizeof(fd_pubkey_t) ) == 0 && 190 0 : key_list->keys[i].signer == key_list->keys[j].signer ) ) { 191 0 : fd_log_collector_msg_literal( ctx, "new config contains duplicate keys" ); 192 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG; 193 0 : } 194 0 : } 195 0 : } 196 : 197 : /* https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L118-L126 */ 198 : 199 0 : if( FD_UNLIKELY( current_signer_key_cnt>counter ) ) { 200 : /* Max msg_sz: 35 - 6 + 2*20 = 69 < 127 => we can use printf */ 201 0 : fd_log_collector_printf_dangerous_max_127( ctx, 202 0 : "too few signers: %lu; expected: %lu", counter, current_signer_key_cnt ); 203 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE; 204 0 : } 205 : 206 : /* Upgrade to writable handle 207 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/config/src/config_processor.rs#L125-L126 */ 208 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_CONFIG, &config_acc_rec ); 209 : 210 : /* Upgrade to writable handle 211 : https://github.com/solana-labs/solana/blob/v1.17.17/programs/config/src/config_processor.rs#L130-L133 */ 212 : 213 0 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &config_acc_rec )<ctx->instr->data_sz ) ) { 214 0 : fd_log_collector_msg_literal( ctx, "instruction data too large" ); 215 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA; 216 0 : } 217 : 218 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/config/src/config_processor.rs#L131 */ 219 0 : uchar * data = NULL; 220 0 : ulong dlen = 0UL; 221 0 : err = fd_borrowed_account_get_data_mut( &config_acc_rec, &data, &dlen ); 222 0 : if( FD_UNLIKELY( err ) ) { 223 0 : return err; 224 0 : } 225 : 226 : /* copy_from_slice */ 227 0 : fd_memcpy( data, ctx->instr->data, ctx->instr->data_sz ); 228 : 229 : /* Implicitly dropped */ 230 : 231 0 : return FD_EXECUTOR_INSTR_SUCCESS; 232 0 : # undef ACC_IDX_CONFIG 233 : 234 0 : } 235 : 236 : int 237 0 : fd_config_program_execute( fd_exec_instr_ctx_t * ctx ) { 238 : /* Prevent execution of migrated native programs */ 239 0 : if( FD_UNLIKELY( FD_FEATURE_ACTIVE_BANK( ctx->txn_ctx->bank, migrate_config_program_to_core_bpf ) ) ) { 240 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID; 241 0 : } 242 : 243 : /* https://github.com/solana-labs/solana/blob/v1.17.27/programs/config/src/config_processor.rs#L14 244 : See DEFAULT_COMPUTE_UNITS */ 245 0 : FD_EXEC_CU_UPDATE( ctx, DEFAULT_COMPUTE_UNITS ); 246 : 247 0 : FD_SPAD_FRAME_BEGIN( ctx->txn_ctx->spad ) { 248 : 249 0 : int ret = _process_config_instr( ctx ); 250 0 : return ret; 251 : 252 0 : } FD_SPAD_FRAME_END; 253 0 : }