solana_compute_budget/
compute_budget.rs

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