LCOV - code coverage report
Current view: top level - flamenco/runtime/program - fd_config_program.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 131 135 97.0 %
Date: 2024-11-13 11:58:15 Functions: 2 2 100.0 %

          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( &current_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( &current_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 : }

Generated by: LCOV version 1.14