solana_program_runtime/
execution_budget.rs

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