LCOV - code coverage report
Current view: top level - flamenco/runtime/program - fd_config_program.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 129 0.0 %
Date: 2025-12-04 04:56:06 Functions: 0 2 0.0 %

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

Generated by: LCOV version 1.14