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