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

Generated by: LCOV version 1.14