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 : }