solana_program_runtime/execution_budget.rs
1use {
2 solana_fee_structure::FeeDetails, solana_program_entrypoint::HEAP_LENGTH,
3 solana_transaction_context::MAX_INSTRUCTION_TRACE_LENGTH, std::num::NonZeroU32,
4};
5
6/// Max instruction stack depth. This is the maximum nesting of instructions that can happen during
7/// a transaction.
8pub const MAX_INSTRUCTION_STACK_DEPTH: usize = 5;
9/// Max instruction stack depth with SIMD-0268 enabled. Allows 8 nested CPIs.
10pub const MAX_INSTRUCTION_STACK_DEPTH_SIMD_0268: usize = 9;
11
12fn get_max_instruction_stack_depth(simd_0268_active: bool) -> usize {
13 if simd_0268_active {
14 MAX_INSTRUCTION_STACK_DEPTH_SIMD_0268
15 } else {
16 MAX_INSTRUCTION_STACK_DEPTH
17 }
18}
19
20//Default CPI invocation cost
21pub const DEFAULT_INVOCATION_COST: u64 = 1000;
22//CPI Invocation cost with SIMD-0339 active
23pub const INVOKE_UNITS_COST_SIMD_0339: u64 = 946;
24
25fn get_invoke_unit_cost(simd_0339_active: bool) -> u64 {
26 if simd_0339_active {
27 INVOKE_UNITS_COST_SIMD_0339
28 } else {
29 DEFAULT_INVOCATION_COST
30 }
31}
32
33/// Max call depth. This is the maximum nesting of SBF to SBF call that can happen within a program.
34pub const MAX_CALL_DEPTH: usize = 64;
35
36/// The size of one SBF stack frame.
37pub const STACK_FRAME_SIZE: usize = 4096;
38
39pub const MAX_COMPUTE_UNIT_LIMIT: u32 = 1_400_000;
40
41/// Roughly 0.5us/page, where page is 32K; given roughly 15CU/us, the
42/// default heap page cost = 0.5 * 15 ~= 8CU/page
43pub const DEFAULT_HEAP_COST: u64 = 8;
44pub const DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT: u32 = 200_000;
45// SIMD-170 defines max CUs to be allocated for any builtin program instructions, that
46// have not been migrated to sBPF programs.
47pub const MAX_BUILTIN_ALLOCATION_COMPUTE_UNIT_LIMIT: u32 = 3_000;
48pub const MAX_HEAP_FRAME_BYTES: u32 = 256 * 1024;
49pub const MIN_HEAP_FRAME_BYTES: u32 = HEAP_LENGTH as u32;
50
51/// The total accounts data a transaction can load is limited to 64MiB to not break
52/// anyone in Mainnet-beta today. It can be set by set_loaded_accounts_data_size_limit instruction
53pub const MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES: NonZeroU32 =
54 NonZeroU32::new(64 * 1024 * 1024).unwrap();
55
56#[derive(Clone, Copy, Debug, PartialEq, Eq)]
57pub struct SVMTransactionExecutionBudget {
58 /// Number of compute units that a transaction or individual instruction is
59 /// allowed to consume. Compute units are consumed by program execution,
60 /// resources they use, etc...
61 pub compute_unit_limit: u64,
62 /// Maximum program instruction invocation stack depth. Invocation stack
63 /// depth starts at 1 for transaction instructions and the stack depth is
64 /// incremented each time a program invokes an instruction and decremented
65 /// when a program returns.
66 pub max_instruction_stack_depth: usize,
67 /// Maximum cross-program invocation and instructions per transaction
68 pub max_instruction_trace_length: usize,
69 /// Maximum number of slices hashed per syscall
70 pub sha256_max_slices: u64,
71 /// Maximum SBF to BPF call depth
72 pub max_call_depth: usize,
73 /// Size of a stack frame in bytes, must match the size specified in the LLVM SBF backend
74 pub stack_frame_size: usize,
75 /// program heap region size, default: solana_program_entrypoint::HEAP_LENGTH
76 pub heap_size: u32,
77}
78
79#[cfg(feature = "dev-context-only-utils")]
80impl Default for SVMTransactionExecutionBudget {
81 fn default() -> Self {
82 Self::new_with_defaults(/* simd_0268_active */ false)
83 }
84}
85
86impl SVMTransactionExecutionBudget {
87 pub fn new_with_defaults(simd_0268_active: bool) -> Self {
88 SVMTransactionExecutionBudget {
89 compute_unit_limit: u64::from(MAX_COMPUTE_UNIT_LIMIT),
90 max_instruction_stack_depth: get_max_instruction_stack_depth(simd_0268_active),
91 max_instruction_trace_length: MAX_INSTRUCTION_TRACE_LENGTH,
92 sha256_max_slices: 20_000,
93 max_call_depth: MAX_CALL_DEPTH,
94 stack_frame_size: STACK_FRAME_SIZE,
95 heap_size: u32::try_from(solana_program_entrypoint::HEAP_LENGTH).unwrap(),
96 }
97 }
98}
99
100#[derive(Clone, Copy, Debug, PartialEq, Eq)]
101pub struct SVMTransactionExecutionCost {
102 /// Number of compute units consumed by a log_u64 call
103 pub log_64_units: u64,
104 /// Number of compute units consumed by a create_program_address call
105 pub create_program_address_units: u64,
106 /// Number of compute units consumed by an invoke call (not including the cost incurred by
107 /// the called program)
108 pub invoke_units: u64,
109 /// Base number of compute units consumed to call SHA256
110 pub sha256_base_cost: u64,
111 /// Incremental number of units consumed by SHA256 (based on bytes)
112 pub sha256_byte_cost: u64,
113 /// Number of compute units consumed by logging a `Pubkey`
114 pub log_pubkey_units: u64,
115 /// Number of account data bytes per compute unit charged during a cross-program invocation
116 pub cpi_bytes_per_unit: u64,
117 /// Base number of compute units consumed to get a sysvar
118 pub sysvar_base_cost: u64,
119 /// Number of compute units consumed to call secp256k1_recover
120 pub secp256k1_recover_cost: u64,
121 /// Number of compute units consumed to do a syscall without any work
122 pub syscall_base_cost: u64,
123 /// Number of compute units consumed to validate a curve25519 edwards point
124 pub curve25519_edwards_validate_point_cost: u64,
125 /// Number of compute units consumed to add two curve25519 edwards points
126 pub curve25519_edwards_add_cost: u64,
127 /// Number of compute units consumed to subtract two curve25519 edwards points
128 pub curve25519_edwards_subtract_cost: u64,
129 /// Number of compute units consumed to multiply a curve25519 edwards point
130 pub curve25519_edwards_multiply_cost: u64,
131 /// Number of compute units consumed for a multiscalar multiplication (msm) of edwards points.
132 /// The total cost is calculated as `msm_base_cost + (length - 1) * msm_incremental_cost`.
133 pub curve25519_edwards_msm_base_cost: u64,
134 /// Number of compute units consumed for a multiscalar multiplication (msm) of edwards points.
135 /// The total cost is calculated as `msm_base_cost + (length - 1) * msm_incremental_cost`.
136 pub curve25519_edwards_msm_incremental_cost: u64,
137 /// Number of compute units consumed to validate a curve25519 ristretto point
138 pub curve25519_ristretto_validate_point_cost: u64,
139 /// Number of compute units consumed to add two curve25519 ristretto points
140 pub curve25519_ristretto_add_cost: u64,
141 /// Number of compute units consumed to subtract two curve25519 ristretto points
142 pub curve25519_ristretto_subtract_cost: u64,
143 /// Number of compute units consumed to multiply a curve25519 ristretto point
144 pub curve25519_ristretto_multiply_cost: u64,
145 /// Number of compute units consumed for a multiscalar multiplication (msm) of ristretto points.
146 /// The total cost is calculated as `msm_base_cost + (length - 1) * msm_incremental_cost`.
147 pub curve25519_ristretto_msm_base_cost: u64,
148 /// Number of compute units consumed for a multiscalar multiplication (msm) of ristretto points.
149 /// The total cost is calculated as `msm_base_cost + (length - 1) * msm_incremental_cost`.
150 pub curve25519_ristretto_msm_incremental_cost: u64,
151 /// Number of compute units per additional 32k heap above the default (~.5
152 /// us per 32k at 15 units/us rounded up)
153 pub heap_cost: u64,
154 /// Memory operation syscall base cost
155 pub mem_op_base_cost: u64,
156 /// Number of compute units consumed to call alt_bn128_addition
157 pub alt_bn128_addition_cost: u64,
158 /// Number of compute units consumed to call alt_bn128_multiplication.
159 pub alt_bn128_multiplication_cost: u64,
160 /// Total cost will be alt_bn128_pairing_one_pair_cost_first
161 /// + alt_bn128_pairing_one_pair_cost_other * (num_elems - 1)
162 pub alt_bn128_pairing_one_pair_cost_first: u64,
163 pub alt_bn128_pairing_one_pair_cost_other: u64,
164 /// Big integer modular exponentiation base cost
165 pub big_modular_exponentiation_base_cost: u64,
166 /// Big integer moduler exponentiation cost divisor
167 /// The modular exponentiation cost is computed as
168 /// `input_length`/`big_modular_exponentiation_cost_divisor` + `big_modular_exponentiation_base_cost`
169 pub big_modular_exponentiation_cost_divisor: u64,
170 /// Coefficient `a` of the quadratic function which determines the number
171 /// of compute units consumed to call poseidon syscall for a given number
172 /// of inputs.
173 pub poseidon_cost_coefficient_a: u64,
174 /// Coefficient `c` of the quadratic function which determines the number
175 /// of compute units consumed to call poseidon syscall for a given number
176 /// of inputs.
177 pub poseidon_cost_coefficient_c: u64,
178 /// Number of compute units consumed for accessing the remaining compute units.
179 pub get_remaining_compute_units_cost: u64,
180 /// Number of compute units consumed to call alt_bn128_g1_compress.
181 pub alt_bn128_g1_compress: u64,
182 /// Number of compute units consumed to call alt_bn128_g1_decompress.
183 pub alt_bn128_g1_decompress: u64,
184 /// Number of compute units consumed to call alt_bn128_g2_compress.
185 pub alt_bn128_g2_compress: u64,
186 /// Number of compute units consumed to call alt_bn128_g2_decompress.
187 pub alt_bn128_g2_decompress: u64,
188}
189
190impl Default for SVMTransactionExecutionCost {
191 fn default() -> Self {
192 Self::new_with_defaults(/* simd_0339_active */ false)
193 }
194}
195
196impl SVMTransactionExecutionCost {
197 pub fn new_with_defaults(simd_0339_active: bool) -> Self {
198 SVMTransactionExecutionCost {
199 log_64_units: 100,
200 create_program_address_units: 1500,
201 invoke_units: get_invoke_unit_cost(simd_0339_active),
202 sha256_base_cost: 85,
203 sha256_byte_cost: 1,
204 log_pubkey_units: 100,
205 cpi_bytes_per_unit: 250, // ~50MB at 200,000 units
206 sysvar_base_cost: 100,
207 secp256k1_recover_cost: 25_000,
208 syscall_base_cost: 100,
209 curve25519_edwards_validate_point_cost: 159,
210 curve25519_edwards_add_cost: 473,
211 curve25519_edwards_subtract_cost: 475,
212 curve25519_edwards_multiply_cost: 2_177,
213 curve25519_edwards_msm_base_cost: 2_273,
214 curve25519_edwards_msm_incremental_cost: 758,
215 curve25519_ristretto_validate_point_cost: 169,
216 curve25519_ristretto_add_cost: 521,
217 curve25519_ristretto_subtract_cost: 519,
218 curve25519_ristretto_multiply_cost: 2_208,
219 curve25519_ristretto_msm_base_cost: 2303,
220 curve25519_ristretto_msm_incremental_cost: 788,
221 heap_cost: DEFAULT_HEAP_COST,
222 mem_op_base_cost: 10,
223 alt_bn128_addition_cost: 334,
224 alt_bn128_multiplication_cost: 3_840,
225 alt_bn128_pairing_one_pair_cost_first: 36_364,
226 alt_bn128_pairing_one_pair_cost_other: 12_121,
227 big_modular_exponentiation_base_cost: 190,
228 big_modular_exponentiation_cost_divisor: 2,
229 poseidon_cost_coefficient_a: 61,
230 poseidon_cost_coefficient_c: 542,
231 get_remaining_compute_units_cost: 100,
232 alt_bn128_g1_compress: 30,
233 alt_bn128_g1_decompress: 398,
234 alt_bn128_g2_compress: 86,
235 alt_bn128_g2_decompress: 13610,
236 }
237 }
238
239 /// Returns cost of the Poseidon hash function for the given number of
240 /// inputs is determined by the following quadratic function:
241 ///
242 /// 61*n^2 + 542
243 ///
244 /// Which aproximates the results of benchmarks of light-posiedon
245 /// library[0]. These results assume 1 CU per 33 ns. Examples:
246 ///
247 /// * 1 input
248 /// * light-poseidon benchmark: `18,303 / 33 ≈ 555`
249 /// * function: `61*1^2 + 542 = 603`
250 /// * 2 inputs
251 /// * light-poseidon benchmark: `25,866 / 33 ≈ 784`
252 /// * function: `61*2^2 + 542 = 786`
253 /// * 3 inputs
254 /// * light-poseidon benchmark: `37,549 / 33 ≈ 1,138`
255 /// * function; `61*3^2 + 542 = 1091`
256 ///
257 /// [0] https://github.com/Lightprotocol/light-poseidon#performance
258 pub fn poseidon_cost(&self, nr_inputs: u64) -> Option<u64> {
259 let squared_inputs = nr_inputs.checked_pow(2)?;
260 let mul_result = self
261 .poseidon_cost_coefficient_a
262 .checked_mul(squared_inputs)?;
263 let final_result = mul_result.checked_add(self.poseidon_cost_coefficient_c)?;
264
265 Some(final_result)
266 }
267}
268
269#[derive(Clone, Copy, Debug, PartialEq, Eq)]
270pub struct SVMTransactionExecutionAndFeeBudgetLimits {
271 pub budget: SVMTransactionExecutionBudget,
272 pub loaded_accounts_data_size_limit: NonZeroU32,
273 pub fee_details: FeeDetails,
274}
275
276#[cfg(feature = "dev-context-only-utils")]
277impl Default for SVMTransactionExecutionAndFeeBudgetLimits {
278 fn default() -> Self {
279 Self {
280 budget: SVMTransactionExecutionBudget::default(),
281 loaded_accounts_data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
282 fee_details: FeeDetails::default(),
283 }
284 }
285}
286
287#[cfg(feature = "dev-context-only-utils")]
288impl SVMTransactionExecutionAndFeeBudgetLimits {
289 pub fn with_fee(fee_details: FeeDetails) -> Self {
290 Self {
291 fee_details,
292 ..SVMTransactionExecutionAndFeeBudgetLimits::default()
293 }
294 }
295}