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