LCOV - code coverage report
Current view: top level - flamenco/runtime/program - fd_compute_budget_program.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 92 125 73.6 %
Date: 2026-06-09 08:01:34 Functions: 7 7 100.0 %

          Line data    Source code
       1             : #include "fd_compute_budget_program.h"
       2             : 
       3             : #include "../fd_runtime_err.h"
       4             : #include "../fd_runtime.h"
       5             : #include "../fd_system_ids.h"
       6             : #include "../fd_executor.h"
       7             : #include "fd_builtin_programs.h"
       8             : #include "../fd_compute_budget_details.h"
       9             : #include "../../vm/fd_vm_base.h"
      10             : 
      11             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/execution_budget.rs#L44 */
      12         195 : #define DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT (200000UL)
      13             : 
      14             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/programs/compute-budget/src/lib.rs#L4 */
      15             : #define DEFAULT_COMPUTE_UNITS (150UL)
      16             : 
      17             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/execution_budget.rs#L47 */
      18         195 : #define MAX_BUILTIN_ALLOCATION_COMPUTE_UNIT_LIMIT (3000UL)
      19             : 
      20             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/compute-budget-instruction/src/builtin_programs_filter.rs#L38 */
      21             : 
      22             : FD_FN_PURE static inline uchar
      23             : get_program_kind( fd_bank_t const *      bank,
      24             :                   fd_txn_in_t const *    txn_in,
      25         162 :                   fd_txn_instr_t const * instr ) {
      26         162 :   fd_acct_addr_t const * txn_accs       = fd_txn_get_acct_addrs( TXN( txn_in->txn ), txn_in->txn->payload );
      27         162 :   fd_pubkey_t const *    program_pubkey = fd_type_pun_const( &txn_accs[ instr->program_id ] );
      28             : 
      29             :   /* The program is a standard, non-migrating builtin (e.g. system program) */
      30         162 :   if( fd_is_non_migrating_builtin_program( program_pubkey ) ) {
      31             :     /* bls_pubkey_management_in_vote_account: the vote program is NOT
      32             :        being migrated to BPF, but is treated as migrated by the cost
      33             :        model.
      34             : 
      35             :        https://github.com/anza-xyz/agave/blob/v4.0.0-beta.6/builtins-default-costs/src/lib.rs#L95-L107 */
      36         147 :     if( FD_UNLIKELY( FD_FEATURE_ACTIVE_BANK( bank, bls_pubkey_management_in_vote_account ) &&
      37         147 :                      fd_pubkey_eq( program_pubkey, &fd_solana_vote_program_id ) ) ) {
      38           6 :       return FD_PROGRAM_KIND_MIGRATING_BUILTIN;
      39           6 :     }
      40         141 :     return FD_PROGRAM_KIND_BUILTIN;
      41         147 :   }
      42             : 
      43          15 :   uchar migrated_yet;
      44          15 :   uchar is_migrating_program = fd_is_migrating_builtin_program( bank, program_pubkey, &migrated_yet );
      45             : 
      46             :   /* The program has a BPF migration config but has not been migrated yet, so it's still a builtin program */
      47          15 :   if( is_migrating_program && !migrated_yet ) {
      48           0 :     return FD_PROGRAM_KIND_BUILTIN;
      49           0 :   }
      50             : 
      51             :   /* The program has a BPF migration config AND has been migrated */
      52          15 :   if( is_migrating_program && migrated_yet ) {
      53           0 :     return FD_PROGRAM_KIND_MIGRATING_BUILTIN;
      54           0 :   }
      55             : 
      56             :   /* The program is some other program kind, i.e. not a builtin */
      57          15 :   return FD_PROGRAM_KIND_NOT_BUILTIN;
      58          15 : }
      59             : 
      60             : FD_FN_PURE static inline int
      61             : is_compute_budget_instruction( fd_txn_t const *       txn,
      62             :                                uchar const *          txn_payload,
      63         162 :                                fd_txn_instr_t const * instr ) {
      64         162 :   fd_acct_addr_t const * txn_accs       = fd_txn_get_acct_addrs( txn, txn_payload );
      65         162 :   fd_acct_addr_t const * program_pubkey = &txn_accs[ instr->program_id ];
      66         162 :   return !memcmp(program_pubkey, fd_solana_compute_budget_program_id.key, sizeof(fd_pubkey_t));
      67         162 : }
      68             : 
      69             : /* In our implementation of this function, our parameters map to Agave's as follows:
      70             :   - `num_builtin_instrs` -> `num_non_migratable_builtin_instructions` + `num_not_migrated`
      71             :   - `num_non_builtin_instrs` -> `num_non_builtin_instructions` + `num_migrated`
      72             : 
      73             :    https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/compute-budget-instruction/src/compute_budget_instruction_details.rs#L196 */
      74             : 
      75             : FD_FN_CONST static inline ulong
      76             : calculate_default_compute_unit_limit( ulong num_builtin_instrs,
      77         195 :                                       ulong num_non_builtin_instrs ) {
      78             :   /* https://github.com/anza-xyz/agave/blob/v2.1.13/runtime-transaction/src/compute_budget_instruction_details.rs#L227-L234 */
      79         195 :   return fd_ulong_sat_add( fd_ulong_sat_mul( num_builtin_instrs, MAX_BUILTIN_ALLOCATION_COMPUTE_UNIT_LIMIT ),
      80         195 :                            fd_ulong_sat_mul( num_non_builtin_instrs, DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT ) );
      81             : 
      82         195 : }
      83             : 
      84             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/compute-budget-instruction/src/compute_budget_instruction_details.rs#L192 */
      85             : 
      86             : FD_FN_CONST static inline int
      87           6 : sanitize_requested_heap_size( ulong bytes ) {
      88           6 :   return
      89           6 :     bytes >= FD_MIN_HEAP_FRAME_BYTES &&
      90           6 :     bytes <= FD_MAX_HEAP_FRAME_BYTES &&
      91           6 :     ( bytes%FD_HEAP_FRAME_BYTES_GRANULARITY )==0UL;
      92           6 : }
      93             : 
      94             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/compute-budget-instruction/src/compute_budget_instruction_details.rs#L101-L153 */
      95             : 
      96             : int
      97         213 : fd_sanitize_compute_unit_limits( fd_txn_out_t * txn_out ) {
      98         213 :   fd_compute_budget_details_t * details = &txn_out->details.compute_budget;
      99             : 
     100             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/compute-budget-instruction/src/compute_budget_instruction_details.rs#L105-L119 */
     101         213 :   if( details->has_requested_heap_size ) {
     102           6 :     if( FD_UNLIKELY( !sanitize_requested_heap_size( details->heap_size ) ) ) {
     103           3 :       FD_TXN_ERR_FOR_LOG_INSTR( txn_out, FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA, details->requested_heap_size_instr_index );
     104           3 :       return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
     105           3 :     }
     106           6 :   }
     107             : 
     108             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/compute-budget-instruction/src/compute_budget_instruction_details.rs#L121-L128 */
     109         210 :   if( !details->has_compute_units_limit_update ) {
     110         195 :     details->compute_unit_limit = calculate_default_compute_unit_limit( details->num_builtin_instrs,
     111         195 :                                                                         details->num_non_builtin_instrs );
     112         195 :   }
     113         210 :   details->compute_unit_limit = fd_ulong_min( FD_MAX_COMPUTE_UNIT_LIMIT, details->compute_unit_limit );
     114         210 :   details->compute_meter      = details->compute_unit_limit;
     115             : 
     116             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/compute-budget-instruction/src/compute_budget_instruction_details.rs#L136-L145 */
     117         210 :   if( details->has_loaded_accounts_data_size_limit_update ) {
     118           9 :     if( FD_UNLIKELY( details->loaded_accounts_data_size_limit==0UL ) ) {
     119           0 :       return FD_RUNTIME_TXN_ERR_INVALID_LOADED_ACCOUNTS_DATA_SIZE_LIMIT;
     120           0 :     }
     121           9 :     details->loaded_accounts_data_size_limit = fd_ulong_min( FD_VM_LOADED_ACCOUNTS_DATA_SIZE_LIMIT,
     122           9 :                                                              details->loaded_accounts_data_size_limit );
     123           9 :   }
     124             : 
     125         210 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     126         210 : }
     127             : 
     128             : /* Like Agave, this function is called during transaction verification
     129             :    and is responsible for simply reading and decoding the compute budget
     130             :    instruction data. Throws an error if any compute budget instruction
     131             :    in the transaction has invalid instruction data, or if there are duplicate
     132             :    compute budget instructions.
     133             : 
     134             :    NOTE: At this point, the transaction context has NOT been fully
     135             :    initialized (namely, the accounts). The accounts are NOT safe to access.
     136             : 
     137             :    https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/compute-budget-instruction/src/compute_budget_instruction_details.rs#L54-L99 */
     138             : int
     139             : fd_executor_compute_budget_program_execute_instructions( fd_bank_t const *   bank,
     140             :                                                          fd_txn_in_t const * txn_in,
     141         207 :                                                          fd_txn_out_t *      txn_out ) {
     142         207 :   fd_compute_budget_details_t * details = &txn_out->details.compute_budget;
     143             : 
     144         369 :   for( ushort i=0; i<TXN( txn_in->txn )->instr_cnt; i++ ) {
     145         162 :     fd_txn_instr_t const * instr = &TXN( txn_in->txn )->instr[i];
     146             : 
     147             :     /* Only `FD_PROGRAM_KIND_BUILTIN` gets charged as a builtin instruction */
     148         162 :     uchar program_kind = get_program_kind( bank, txn_in, instr );
     149         162 :     if( program_kind==FD_PROGRAM_KIND_BUILTIN ) {
     150         141 :       details->num_builtin_instrs++;
     151         141 :     } else {
     152          21 :       details->num_non_builtin_instrs++;
     153          21 :     }
     154             : 
     155         162 :     if( !is_compute_budget_instruction( TXN( txn_in->txn ), txn_in->txn->payload, instr ) ) {
     156         138 :       continue;
     157         138 :     }
     158             : 
     159          24 :     uchar const * data = (uchar const *)txn_in->txn->payload + instr->data_off;
     160             : 
     161          24 :     fd_compute_budget_instr_t instruction[1];
     162          24 :     if( FD_UNLIKELY( fd_compute_budget_instr_decode( data, instr->data_sz, instruction ) ) ) {
     163           0 :       FD_TXN_ERR_FOR_LOG_INSTR( txn_out, FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA, i );
     164           0 :       return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
     165           0 :     }
     166             : 
     167          24 :     switch( instruction->discriminant ) {
     168           0 :       case FD_COMPUTE_BUDGET_INSTR_DISC_REQUEST_HEAP_FRAME: {
     169           0 :         if( FD_UNLIKELY( details->has_requested_heap_size ) ) {
     170           0 :           return FD_RUNTIME_TXN_ERR_DUPLICATE_INSTRUCTION;
     171           0 :         }
     172             : 
     173           0 :         details->has_requested_heap_size         = 1;
     174           0 :         details->heap_size                       = instruction->request_heap_frame;
     175           0 :         details->requested_heap_size_instr_index = i;
     176           0 :         break;
     177           0 :       }
     178          15 :       case FD_COMPUTE_BUDGET_INSTR_DISC_SET_COMPUTE_UNIT_LIMIT: {
     179          15 :         if( FD_UNLIKELY( details->has_compute_units_limit_update ) ) {
     180           0 :           return FD_RUNTIME_TXN_ERR_DUPLICATE_INSTRUCTION;
     181           0 :         }
     182             : 
     183          15 :         details->has_compute_units_limit_update = 1;
     184          15 :         details->compute_unit_limit             = instruction->set_compute_unit_limit;
     185          15 :         break;
     186          15 :       }
     187           0 :       case FD_COMPUTE_BUDGET_INSTR_DISC_SET_COMPUTE_UNIT_PRICE: {
     188           0 :         if( FD_UNLIKELY( details->has_compute_units_price_update ) ) {
     189           0 :           return FD_RUNTIME_TXN_ERR_DUPLICATE_INSTRUCTION;
     190           0 :         }
     191             : 
     192           0 :         details->has_compute_units_price_update = 1;
     193           0 :         details->compute_unit_price             = instruction->set_compute_unit_price;
     194           0 :         break;
     195           0 :       }
     196           9 :       case FD_COMPUTE_BUDGET_INSTR_DISC_SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT: {
     197           9 :         if( FD_UNLIKELY( details->has_loaded_accounts_data_size_limit_update ) ) {
     198           0 :           return FD_RUNTIME_TXN_ERR_DUPLICATE_INSTRUCTION;
     199           0 :         }
     200             : 
     201           9 :         details->has_loaded_accounts_data_size_limit_update = 1;
     202           9 :         details->loaded_accounts_data_size_limit            = instruction->set_loaded_accounts_data_size_limit;
     203           9 :         break;
     204           9 :       }
     205           0 :       default: {
     206           0 :         FD_TXN_ERR_FOR_LOG_INSTR( txn_out, FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA, i );
     207           0 :         return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
     208           9 :       }
     209          24 :     }
     210          24 :   }
     211             : 
     212         207 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     213         207 : }
     214             : 
     215             : int
     216          15 : fd_compute_budget_program_execute( fd_exec_instr_ctx_t * ctx ) {
     217          15 :   FD_EXEC_CU_UPDATE( ctx, DEFAULT_COMPUTE_UNITS );
     218          15 :   return FD_EXECUTOR_INSTR_SUCCESS;
     219          15 : }

Generated by: LCOV version 1.14