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: 116 122 95.1 %
Date: 2026-03-19 18:19:27 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        4493 : #define DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT    (200000UL)
      11             : #define DEFAULT_COMPUTE_UNITS                     (150UL)
      12             : 
      13             : /* https://github.com/anza-xyz/agave/blob/v2.1.13/compute-budget/src/compute_budget_limits.rs#L11-L13 */
      14        4493 : #define MAX_BUILTIN_ALLOCATION_COMPUTE_UNIT_LIMIT (3000UL)
      15             : 
      16             : FD_FN_PURE static inline uchar
      17             : get_program_kind( fd_bank_t const *      bank,
      18             :                   fd_txn_in_t const *    txn_in,
      19        7868 :                   fd_txn_instr_t const * instr ) {
      20        7868 :   fd_acct_addr_t const * txn_accs       = fd_txn_get_acct_addrs( TXN( txn_in->txn ), txn_in->txn->payload );
      21        7868 :   fd_pubkey_t const *    program_pubkey = fd_type_pun_const( &txn_accs[ instr->program_id ] );
      22             : 
      23             :   /* The program is a standard, non-migrating builtin (e.g. system program) */
      24        7868 :   if( fd_is_non_migrating_builtin_program( program_pubkey ) ) {
      25        4296 :     return FD_PROGRAM_KIND_BUILTIN;
      26        4296 :   }
      27             : 
      28        3572 :   uchar migrated_yet;
      29        3572 :   uchar is_migrating_program = fd_is_migrating_builtin_program( bank, program_pubkey, &migrated_yet );
      30             : 
      31             :   /* The program has a BPF migration config but has not been migrated yet, so it's still a builtin program */
      32        3572 :   if( is_migrating_program && !migrated_yet ) {
      33           0 :     return FD_PROGRAM_KIND_BUILTIN;
      34           0 :   }
      35             : 
      36             :   /* The program has a BPF migration config AND has been migrated */
      37        3572 :   if( is_migrating_program && migrated_yet ) {
      38           0 :     return FD_PROGRAM_KIND_MIGRATING_BUILTIN;
      39           0 :   }
      40             : 
      41             :   /* The program is some other program kind, i.e. not a builtin */
      42        3572 :   return FD_PROGRAM_KIND_NOT_BUILTIN;
      43        3572 : }
      44             : 
      45             : FD_FN_PURE static inline int
      46             : is_compute_budget_instruction( fd_txn_t const *       txn,
      47             :                                uchar const *          txn_payload,
      48        7869 :                                fd_txn_instr_t const * instr ) {
      49        7869 :   fd_acct_addr_t const * txn_accs       = fd_txn_get_acct_addrs( txn, txn_payload );
      50        7869 :   fd_acct_addr_t const * program_pubkey = &txn_accs[ instr->program_id ];
      51        7869 :   return !memcmp(program_pubkey, fd_solana_compute_budget_program_id.key, sizeof(fd_pubkey_t));
      52        7869 : }
      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( ulong num_builtin_instrs,
      61        4493 :                                       ulong num_non_builtin_instrs ) {
      62             :   /* https://github.com/anza-xyz/agave/blob/v2.1.13/runtime-transaction/src/compute_budget_instruction_details.rs#L227-L234 */
      63        4493 :   return fd_ulong_sat_add( fd_ulong_sat_mul( num_builtin_instrs, MAX_BUILTIN_ALLOCATION_COMPUTE_UNIT_LIMIT ),
      64        4493 :                            fd_ulong_sat_mul( num_non_builtin_instrs, DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT ) );
      65             : 
      66        4493 : }
      67             : 
      68             : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/compute-budget/src/compute_budget_processor.rs#L150-L153 */
      69             : FD_FN_PURE static inline int
      70         186 : sanitize_requested_heap_size( ulong bytes ) {
      71         186 :   return !(bytes>FD_MAX_HEAP_FRAME_BYTES || bytes<FD_MIN_HEAP_FRAME_BYTES || bytes%FD_HEAP_FRAME_BYTES_GRANULARITY);
      72         186 : }
      73             : 
      74             : int
      75        4714 : fd_sanitize_compute_unit_limits( fd_txn_out_t * txn_out ) {
      76        4714 :   fd_compute_budget_details_t * details = &txn_out->details.compute_budget;
      77             : 
      78             :   /* https://github.com/anza-xyz/agave/blob/v2.3.1/compute-budget-instruction/src/compute_budget_instruction_details.rs#L106-L119 */
      79        4714 :   if( details->has_requested_heap_size ) {
      80         186 :     if( FD_UNLIKELY( !sanitize_requested_heap_size( details->heap_size ) ) ) {
      81         163 :       FD_TXN_ERR_FOR_LOG_INSTR( txn_out, FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA, details->requested_heap_size_instr_index );
      82         163 :       return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
      83         163 :     }
      84         186 :   }
      85             : 
      86             :   /* https://github.com/anza-xyz/agave/blob/v2.3.1/compute-budget-instruction/src/compute_budget_instruction_details.rs#L122-L128 */
      87        4551 :   if( !details->has_compute_units_limit_update ) {
      88        4493 :     details->compute_unit_limit = calculate_default_compute_unit_limit( details->num_builtin_instrs,
      89        4493 :                                                                         details->num_non_builtin_instrs );
      90        4493 :   }
      91        4551 :   details->compute_unit_limit = fd_ulong_min( FD_MAX_COMPUTE_UNIT_LIMIT, details->compute_unit_limit );
      92        4551 :   details->compute_meter      = details->compute_unit_limit;
      93             : 
      94             :   /* https://github.com/anza-xyz/agave/blob/v2.3.1/compute-budget-instruction/src/compute_budget_instruction_details.rs#L136-L145 */
      95        4551 :   if( details->has_loaded_accounts_data_size_limit_update ) {
      96          57 :     if( FD_UNLIKELY( details->loaded_accounts_data_size_limit==0UL ) ) {
      97          17 :       return FD_RUNTIME_TXN_ERR_INVALID_LOADED_ACCOUNTS_DATA_SIZE_LIMIT;
      98          17 :     }
      99          40 :     details->loaded_accounts_data_size_limit = fd_ulong_min( FD_VM_LOADED_ACCOUNTS_DATA_SIZE_LIMIT,
     100          40 :                                                              details->loaded_accounts_data_size_limit );
     101          40 :   }
     102             : 
     103        4534 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     104        4551 : }
     105             : 
     106             : /* Like Agave, this function is called during transaction verification
     107             :    and is responsible for simply reading and decoding the compute budget
     108             :    instruction data. Throws an error if any compute budget instruction
     109             :    in the transaction has invalid instruction data, or if there are duplicate
     110             :    compute budget instructions.
     111             : 
     112             :    NOTE: At this point, the transaction context has NOT been fully
     113             :    initialized (namely, the accounts). The accounts are NOT safe to access.
     114             : 
     115             :    https://github.com/anza-xyz/agave/blob/v2.3.1/compute-budget-instruction/src/compute_budget_instruction_details.rs#L54-L99 */
     116             : int
     117             : fd_executor_compute_budget_program_execute_instructions( fd_bank_t const *   bank,
     118             :                                                          fd_txn_in_t const * txn_in,
     119        5537 :                                                          fd_txn_out_t *      txn_out ) {
     120        5537 :   fd_compute_budget_details_t * details = &txn_out->details.compute_budget;
     121             : 
     122       13356 :   for( ushort i=0; i<TXN( txn_in->txn )->instr_cnt; i++ ) {
     123        7867 :     fd_txn_instr_t const * instr = &TXN( txn_in->txn )->instr[i];
     124             : 
     125             :     /* Only `FD_PROGRAM_KIND_BUILTIN` gets charged as a builtin instruction */
     126        7867 :     uchar program_kind = get_program_kind( bank, txn_in, instr );
     127        7867 :     if( program_kind==FD_PROGRAM_KIND_BUILTIN ) {
     128        4296 :       details->num_builtin_instrs++;
     129        4296 :     } else {
     130        3571 :       details->num_non_builtin_instrs++;
     131        3571 :     }
     132             : 
     133        7867 :     if( !is_compute_budget_instruction( TXN( txn_in->txn ), txn_in->txn->payload, instr ) ) {
     134        7239 :       continue;
     135        7239 :     }
     136             : 
     137             :     /* Deserialize the ComputeBudgetInstruction enum */
     138         628 :     uchar * data = (uchar *)txn_in->txn->payload + instr->data_off;
     139             : 
     140         628 :     fd_compute_budget_program_instruction_t instruction[1];
     141         628 :     if( FD_UNLIKELY( !fd_bincode_decode_static(
     142         628 :         compute_budget_program_instruction,
     143         628 :         instruction,
     144         628 :         data,
     145         628 :         instr->data_sz ) ) ) {
     146          30 :       FD_TXN_ERR_FOR_LOG_INSTR( txn_out, FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA, i );
     147          30 :       return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
     148          30 :     }
     149             : 
     150         598 :     switch( instruction->discriminant ) {
     151         249 :       case fd_compute_budget_program_instruction_enum_request_heap_frame: {
     152         249 :         if( FD_UNLIKELY( details->has_requested_heap_size ) ) {
     153           2 :           return FD_RUNTIME_TXN_ERR_DUPLICATE_INSTRUCTION;
     154           2 :         }
     155             : 
     156         247 :         details->has_requested_heap_size         = 1;
     157         247 :         details->heap_size                       = instruction->inner.request_heap_frame;
     158         247 :         details->requested_heap_size_instr_index = i;
     159         247 :         break;
     160         249 :       }
     161         115 :       case fd_compute_budget_program_instruction_enum_set_compute_unit_limit: {
     162         115 :         if( FD_UNLIKELY( details->has_compute_units_limit_update ) ) {
     163           1 :           return FD_RUNTIME_TXN_ERR_DUPLICATE_INSTRUCTION;
     164           1 :         }
     165             : 
     166         114 :         details->has_compute_units_limit_update = 1;
     167         114 :         details->compute_unit_limit             = instruction->inner.set_compute_unit_limit;
     168         114 :         break;
     169         115 :       }
     170         120 :       case fd_compute_budget_program_instruction_enum_set_compute_unit_price: {
     171         120 :         if( FD_UNLIKELY( details->has_compute_units_price_update ) ) {
     172          14 :           return FD_RUNTIME_TXN_ERR_DUPLICATE_INSTRUCTION;
     173          14 :         }
     174             : 
     175         106 :         details->has_compute_units_price_update = 1;
     176         106 :         details->compute_unit_price             = instruction->inner.set_compute_unit_price;
     177         106 :         break;
     178         120 :       }
     179         113 :       case fd_compute_budget_program_instruction_enum_set_loaded_accounts_data_size_limit: {
     180         113 :           if( FD_UNLIKELY( details->has_loaded_accounts_data_size_limit_update ) ) {
     181           0 :             return FD_RUNTIME_TXN_ERR_DUPLICATE_INSTRUCTION;
     182           0 :           }
     183             : 
     184         113 :           details->has_loaded_accounts_data_size_limit_update = 1;
     185         113 :           details->loaded_accounts_data_size_limit            = instruction->inner.set_loaded_accounts_data_size_limit;
     186         113 :           break;
     187         113 :       }
     188           2 :       default: {
     189           2 :         FD_TXN_ERR_FOR_LOG_INSTR( txn_out, FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA, i );
     190           2 :         return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
     191         113 :       }
     192         598 :     }
     193         598 :   }
     194             : 
     195        5489 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     196        5537 : }
     197             : 
     198         238 : int fd_compute_budget_program_execute( fd_exec_instr_ctx_t * ctx ) {
     199         238 :   FD_EXEC_CU_UPDATE( ctx, DEFAULT_COMPUTE_UNITS );
     200         238 :   return FD_EXECUTOR_INSTR_SUCCESS;
     201         238 : }

Generated by: LCOV version 1.14