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: 0 147 0.0 %
Date: 2025-07-01 05:00:49 Functions: 0 6 0.0 %

          Line data    Source code
       1             : #include "fd_compute_budget_program.h"
       2             : 
       3             : #include "../fd_runtime_err.h"
       4             : #include "../fd_system_ids.h"
       5             : #include "../fd_executor.h"
       6             : #include "../context/fd_exec_instr_ctx.h"
       7             : #include "../context/fd_exec_txn_ctx.h"
       8             : #include "../context/fd_exec_slot_ctx.h"
       9             : #include "../../vm/fd_vm.h"
      10             : #include "fd_builtin_programs.h"
      11             : 
      12           0 : #define DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT    (200000UL)
      13             : #define DEFAULT_COMPUTE_UNITS                     (150UL)
      14             : 
      15             : /* https://github.com/anza-xyz/agave/blob/v2.1.13/compute-budget/src/compute_budget_limits.rs#L11-L13 */
      16           0 : #define MAX_BUILTIN_ALLOCATION_COMPUTE_UNIT_LIMIT (3000UL)
      17             : 
      18             : FD_FN_PURE static inline uchar
      19             : get_program_kind( fd_exec_txn_ctx_t const * ctx,
      20           0 :                   fd_txn_instr_t const *    instr ) {
      21           0 :   fd_pubkey_t const * txn_accs       = ctx->account_keys;
      22           0 :   fd_pubkey_t const * program_pubkey = &txn_accs[ instr->program_id ];
      23             : 
      24             :   /* The program is a standard, non-migrating builtin (e.g. system program) */
      25           0 :   if( fd_is_non_migrating_builtin_program( program_pubkey ) ) {
      26           0 :     return FD_PROGRAM_KIND_BUILTIN;
      27           0 :   }
      28             : 
      29           0 :   uchar migrated_yet;
      30           0 :   uchar is_migrating_program = fd_is_migrating_builtin_program( ctx, program_pubkey, &migrated_yet );
      31             : 
      32             :   /* The program has a BPF migration config but has not been migrated yet, so it's still a builtin program */
      33           0 :   if( is_migrating_program && !migrated_yet ) {
      34           0 :     return FD_PROGRAM_KIND_BUILTIN;
      35           0 :   }
      36             : 
      37             :   /* The program has a BPF migration config AND has been migrated */
      38           0 :   if( is_migrating_program && migrated_yet ) {
      39           0 :     return FD_PROGRAM_KIND_MIGRATING_BUILTIN;
      40           0 :   }
      41             : 
      42             :   /* The program is some other program kind, i.e. not a builtin */
      43           0 :   return FD_PROGRAM_KIND_NOT_BUILTIN;
      44           0 : }
      45             : 
      46             : FD_FN_PURE static inline int
      47             : is_compute_budget_instruction( fd_exec_txn_ctx_t const * ctx,
      48           0 :                                fd_txn_instr_t    const * instr ) {
      49           0 :   fd_pubkey_t const * txn_accs       = ctx->account_keys;
      50           0 :   fd_pubkey_t const * program_pubkey = &txn_accs[ instr->program_id ];
      51           0 :   return !memcmp(program_pubkey, fd_solana_compute_budget_program_id.key, sizeof(fd_pubkey_t));
      52           0 : }
      53             : 
      54             : /* In our implementation of this function, our parameters map to Agave's as follows:
      55             :   - `num_builtin_instrs` -> `num_non_migratable_builtin_instructions` + `num_not_migrated`
      56             :   - `num_non_builtin_instrs` -> `num_non_builtin_instructions` + `num_migrated`
      57             : 
      58             :    https://github.com/anza-xyz/agave/blob/v2.1.13/runtime-transaction/src/compute_budget_instruction_details.rs#L211-L239 */
      59             : FD_FN_PURE static inline ulong
      60             : calculate_default_compute_unit_limit( fd_exec_txn_ctx_t const * ctx,
      61             :                                       ulong                     num_builtin_instrs,
      62             :                                       ulong                     num_non_builtin_instrs,
      63           0 :                                       ulong                     num_non_compute_budget_instrs ) {
      64           0 :   if( FD_FEATURE_ACTIVE_BANK( ctx->bank, reserve_minimal_cus_for_builtin_instructions ) ) {
      65             :     /* https://github.com/anza-xyz/agave/blob/v2.1.13/runtime-transaction/src/compute_budget_instruction_details.rs#L227-L234 */
      66           0 :     return fd_ulong_sat_add( fd_ulong_sat_mul( num_builtin_instrs, MAX_BUILTIN_ALLOCATION_COMPUTE_UNIT_LIMIT ),
      67           0 :                              fd_ulong_sat_mul( num_non_builtin_instrs, DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT ) );
      68           0 :   } else {
      69             :     /* https://github.com/anza-xyz/agave/blob/v2.1.13/runtime-transaction/src/compute_budget_instruction_details.rs#L236-L237 */
      70           0 :     return fd_ulong_sat_mul( num_non_compute_budget_instrs, DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT );
      71           0 :   }
      72           0 : }
      73             : 
      74             : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/compute-budget/src/compute_budget_processor.rs#L150-L153 */
      75             : FD_FN_PURE static inline int
      76           0 : sanitize_requested_heap_size( ulong bytes ) {
      77           0 :   return !(bytes>FD_MAX_HEAP_FRAME_BYTES || bytes<FD_MIN_HEAP_FRAME_BYTES || bytes%FD_HEAP_FRAME_BYTES_GRANULARITY);
      78           0 : }
      79             : 
      80             : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/compute-budget/src/compute_budget_processor.rs#L69-L148 */
      81             : int
      82           0 : fd_executor_compute_budget_program_execute_instructions( fd_exec_txn_ctx_t * ctx, fd_rawtxn_b_t const * txn_raw ) {
      83           0 :   uint   has_compute_units_limit_update              = 0UL;
      84           0 :   uint   has_compute_units_price_update              = 0UL;
      85           0 :   uint   has_requested_heap_size                     = 0UL;
      86           0 :   uint   has_loaded_accounts_data_size_limit_update  = 0UL;
      87             : 
      88           0 :   ushort requested_heap_size_instr_index             = 0;
      89             : 
      90             :   /* SIMD-170 introduces a conservative CU limit of 3,000 CUs per non-migrated native program,
      91             :      and 200,000 CUs for all other programs (including migrated builtins). */
      92           0 :   ulong  num_non_compute_budget_instrs               = 0UL;
      93           0 :   ulong  num_builtin_instrs                          = 0UL;
      94           0 :   ulong  num_non_builtin_instrs                      = 0UL;
      95             : 
      96           0 :   uint   updated_compute_unit_limit                  = 0U;
      97           0 :   uint   updated_requested_heap_size                 = 0U;
      98           0 :   uint   updated_loaded_accounts_data_size_limit     = 0U;
      99           0 :   ulong  updated_compute_unit_price                  = 0UL;
     100             : 
     101           0 :   uint prioritization_fee_type = FD_COMPUTE_BUDGET_PRIORITIZATION_FEE_TYPE_COMPUTE_UNIT_PRICE;
     102             : 
     103           0 :   for( ushort i=0; i<ctx->txn_descriptor->instr_cnt; i++ ) {
     104           0 :     fd_txn_instr_t const * instr = &ctx->txn_descriptor->instr[i];
     105             : 
     106             :     /* Track builtin vs non-builtin metrics only if SIMD-170 is active */
     107           0 :     if( FD_FEATURE_ACTIVE_BANK( ctx->bank, reserve_minimal_cus_for_builtin_instructions ) ) {
     108             :       /* Only `FD_PROGRAM_KIND_BUILTIN` gets charged as a builtin instruction */
     109           0 :       uchar program_kind = get_program_kind( ctx, instr );
     110           0 :       if( program_kind==FD_PROGRAM_KIND_BUILTIN ) {
     111           0 :         num_builtin_instrs++;
     112           0 :       } else {
     113           0 :         num_non_builtin_instrs++;
     114           0 :       }
     115           0 :     }
     116             : 
     117           0 :     if( !is_compute_budget_instruction( ctx, instr ) ) {
     118           0 :       num_non_compute_budget_instrs++;
     119           0 :       continue;
     120           0 :     }
     121             : 
     122             :     /* Deserialize the ComputeBudgetInstruction enum */
     123           0 :     uchar * data = (uchar *)txn_raw->raw + instr->data_off;
     124             : 
     125           0 :     int ret;
     126           0 :     fd_compute_budget_program_instruction_t * instruction =
     127           0 :       fd_bincode_decode_spad(
     128           0 :         compute_budget_program_instruction, ctx->spad,
     129           0 :         data, instr->data_sz, &ret );
     130           0 :     if( FD_UNLIKELY( ret ) ) {
     131           0 :       FD_TXN_ERR_FOR_LOG_INSTR( ctx, FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA, i );
     132           0 :       return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
     133           0 :     }
     134             : 
     135           0 :     switch( instruction->discriminant ) {
     136           0 :       case fd_compute_budget_program_instruction_enum_request_heap_frame: {
     137           0 :         if( FD_UNLIKELY( has_requested_heap_size ) ) {
     138           0 :           return FD_RUNTIME_TXN_ERR_DUPLICATE_INSTRUCTION;
     139           0 :         }
     140             : 
     141           0 :         has_requested_heap_size     = 1U;
     142           0 :         updated_requested_heap_size = instruction->inner.request_heap_frame;
     143           0 :         requested_heap_size_instr_index = i;
     144             : 
     145           0 :         break;
     146           0 :       }
     147           0 :       case fd_compute_budget_program_instruction_enum_set_compute_unit_limit: {
     148           0 :         if( FD_UNLIKELY( has_compute_units_limit_update ) ) {
     149           0 :           return FD_RUNTIME_TXN_ERR_DUPLICATE_INSTRUCTION;
     150           0 :         }
     151             : 
     152           0 :         has_compute_units_limit_update  = 1U;
     153           0 :         updated_compute_unit_limit      = instruction->inner.set_compute_unit_limit;
     154             : 
     155           0 :         break;
     156           0 :       }
     157           0 :       case fd_compute_budget_program_instruction_enum_set_compute_unit_price: {
     158           0 :         if( FD_UNLIKELY( has_compute_units_price_update ) ) {
     159           0 :           return FD_RUNTIME_TXN_ERR_DUPLICATE_INSTRUCTION;
     160           0 :         }
     161             : 
     162           0 :         has_compute_units_price_update = 1U;
     163           0 :         prioritization_fee_type        = FD_COMPUTE_BUDGET_PRIORITIZATION_FEE_TYPE_COMPUTE_UNIT_PRICE;
     164           0 :         updated_compute_unit_price     = instruction->inner.set_compute_unit_price;
     165             : 
     166           0 :         break;
     167           0 :       }
     168           0 :       case fd_compute_budget_program_instruction_enum_set_loaded_accounts_data_size_limit: {
     169           0 :           if( FD_UNLIKELY( has_loaded_accounts_data_size_limit_update ) ) {
     170           0 :             return FD_RUNTIME_TXN_ERR_DUPLICATE_INSTRUCTION;
     171           0 :           }
     172             : 
     173           0 :           has_loaded_accounts_data_size_limit_update = 1U;
     174           0 :           updated_loaded_accounts_data_size_limit    = instruction->inner.set_loaded_accounts_data_size_limit;
     175             : 
     176           0 :           break;
     177           0 :       }
     178           0 :       default: {
     179           0 :         FD_TXN_ERR_FOR_LOG_INSTR( ctx, FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA, i );
     180           0 :         return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
     181           0 :       }
     182           0 :     }
     183           0 :   }
     184             : 
     185             :   /* https://github.com/anza-xyz/agave/blob/v2.1/runtime-transaction/src/compute_budget_instruction_details.rs#L51-L64 */
     186           0 :   if( has_requested_heap_size ) {
     187           0 :     if( FD_UNLIKELY( !sanitize_requested_heap_size( updated_requested_heap_size ) ) ) {
     188           0 :       FD_TXN_ERR_FOR_LOG_INSTR( ctx, FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA, requested_heap_size_instr_index );
     189           0 :       return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
     190           0 :     }
     191           0 :     ctx->heap_size = updated_requested_heap_size;
     192           0 :   }
     193             : 
     194             :   /* https://github.com/anza-xyz/agave/blob/v2.1.13/runtime-transaction/src/compute_budget_instruction_details.rs#L137-L143 */
     195           0 :   if( has_compute_units_limit_update ) {
     196           0 :     ctx->compute_unit_limit = fd_ulong_min( FD_MAX_COMPUTE_UNIT_LIMIT, updated_compute_unit_limit );
     197           0 :   } else {
     198           0 :     ctx->compute_unit_limit = fd_ulong_min( calculate_default_compute_unit_limit(ctx,
     199           0 :                                                                                     num_builtin_instrs,
     200           0 :                                                                                     num_non_builtin_instrs,
     201           0 :                                                                                     num_non_compute_budget_instrs),
     202           0 :                                             FD_MAX_COMPUTE_UNIT_LIMIT );
     203           0 :   }
     204           0 :   ctx->compute_meter = ctx->compute_unit_limit;
     205             : 
     206           0 :   if( has_compute_units_price_update ) {
     207           0 :     ctx->prioritization_fee_type = prioritization_fee_type;
     208           0 :     ctx->compute_unit_price      = updated_compute_unit_price;
     209           0 :   }
     210             : 
     211             :   /* https://github.com/anza-xyz/agave/blob/v2.1/runtime-transaction/src/compute_budget_instruction_details.rs#L84-L93 */
     212           0 :   if( has_loaded_accounts_data_size_limit_update ) {
     213           0 :     if( FD_UNLIKELY( updated_loaded_accounts_data_size_limit==0UL ) ) {
     214           0 :       return FD_RUNTIME_TXN_ERR_INVALID_LOADED_ACCOUNTS_DATA_SIZE_LIMIT;
     215           0 :     }
     216           0 :     ctx->loaded_accounts_data_size_limit =
     217           0 :       fd_ulong_min( FD_VM_LOADED_ACCOUNTS_DATA_SIZE_LIMIT, updated_loaded_accounts_data_size_limit );
     218           0 :   }
     219             : 
     220           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     221           0 : }
     222             : 
     223             : 
     224           0 : int fd_compute_budget_program_execute( fd_exec_instr_ctx_t * ctx ) {
     225           0 :   FD_EXEC_CU_UPDATE( ctx, DEFAULT_COMPUTE_UNITS );
     226           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     227           0 : }

Generated by: LCOV version 1.14