solana_builtins_default_costs/
lib.rs1#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
2#![allow(clippy::arithmetic_side_effects)]
3use {
4 ahash::AHashMap,
5 lazy_static::lazy_static,
6 solana_sdk::{
7 address_lookup_table, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
8 compute_budget, ed25519_program,
9 feature_set::{self, FeatureSet},
10 loader_v4,
11 pubkey::Pubkey,
12 secp256k1_program,
13 },
14};
15
16#[derive(Clone)]
17pub struct MigratingBuiltinCost {
18 native_cost: u64,
19 core_bpf_migration_feature: Pubkey,
20 position: usize,
25}
26
27#[derive(Clone)]
28pub struct NotMigratingBuiltinCost {
29 native_cost: u64,
30}
31
32#[derive(Clone)]
39pub enum BuiltinCost {
40 Migrating(MigratingBuiltinCost),
41 NotMigrating(NotMigratingBuiltinCost),
42}
43
44impl BuiltinCost {
45 pub fn native_cost(&self) -> u64 {
46 match self {
47 BuiltinCost::Migrating(MigratingBuiltinCost { native_cost, .. }) => *native_cost,
48 BuiltinCost::NotMigrating(NotMigratingBuiltinCost { native_cost }) => *native_cost,
49 }
50 }
51
52 pub fn core_bpf_migration_feature(&self) -> Option<&Pubkey> {
53 match self {
54 BuiltinCost::Migrating(MigratingBuiltinCost {
55 core_bpf_migration_feature,
56 ..
57 }) => Some(core_bpf_migration_feature),
58 BuiltinCost::NotMigrating(_) => None,
59 }
60 }
61
62 pub fn position(&self) -> Option<usize> {
63 match self {
64 BuiltinCost::Migrating(MigratingBuiltinCost { position, .. }) => Some(*position),
65 BuiltinCost::NotMigrating(_) => None,
66 }
67 }
68
69 fn has_migrated(&self, feature_set: &FeatureSet) -> bool {
70 match self {
71 BuiltinCost::Migrating(MigratingBuiltinCost {
72 core_bpf_migration_feature,
73 ..
74 }) => feature_set.is_active(core_bpf_migration_feature),
75 BuiltinCost::NotMigrating(_) => false,
76 }
77 }
78}
79
80lazy_static! {
81 static ref BUILTIN_INSTRUCTION_COSTS: AHashMap<Pubkey, BuiltinCost> =
91 MIGRATING_BUILTINS_COSTS
92 .iter()
93 .chain(NON_MIGRATING_BUILTINS_COSTS.iter())
94 .cloned()
95 .collect();
96 }
98
99#[allow(dead_code)]
106const TOTAL_COUNT_BUILTS: usize = 12;
107#[cfg(test)]
108static_assertions::const_assert_eq!(
109 MIGRATING_BUILTINS_COSTS.len() + NON_MIGRATING_BUILTINS_COSTS.len(),
110 TOTAL_COUNT_BUILTS
111);
112
113pub const MIGRATING_BUILTINS_COSTS: &[(Pubkey, BuiltinCost)] = &[
114 (
115 solana_stake_program::id(),
116 BuiltinCost::Migrating(MigratingBuiltinCost {
117 native_cost: solana_stake_program::stake_instruction::DEFAULT_COMPUTE_UNITS,
118 core_bpf_migration_feature: feature_set::migrate_stake_program_to_core_bpf::id(),
119 position: 0,
120 }),
121 ),
122 (
123 solana_config_program::id(),
124 BuiltinCost::Migrating(MigratingBuiltinCost {
125 native_cost: solana_config_program::config_processor::DEFAULT_COMPUTE_UNITS,
126 core_bpf_migration_feature: feature_set::migrate_config_program_to_core_bpf::id(),
127 position: 1,
128 }),
129 ),
130 (
131 address_lookup_table::program::id(),
132 BuiltinCost::Migrating(MigratingBuiltinCost {
133 native_cost: solana_address_lookup_table_program::processor::DEFAULT_COMPUTE_UNITS,
134 core_bpf_migration_feature:
135 feature_set::migrate_address_lookup_table_program_to_core_bpf::id(),
136 position: 2,
137 }),
138 ),
139];
140
141pub const NON_MIGRATING_BUILTINS_COSTS: &[(Pubkey, BuiltinCost)] = &[
142 (
143 solana_vote_program::id(),
144 BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
145 native_cost: solana_vote_program::vote_processor::DEFAULT_COMPUTE_UNITS,
146 }),
147 ),
148 (
149 solana_system_program::id(),
150 BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
151 native_cost: solana_system_program::system_processor::DEFAULT_COMPUTE_UNITS,
152 }),
153 ),
154 (
155 compute_budget::id(),
156 BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
157 native_cost: solana_compute_budget_program::DEFAULT_COMPUTE_UNITS,
158 }),
159 ),
160 (
161 bpf_loader_upgradeable::id(),
162 BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
163 native_cost: solana_bpf_loader_program::UPGRADEABLE_LOADER_COMPUTE_UNITS,
164 }),
165 ),
166 (
167 bpf_loader_deprecated::id(),
168 BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
169 native_cost: solana_bpf_loader_program::DEPRECATED_LOADER_COMPUTE_UNITS,
170 }),
171 ),
172 (
173 bpf_loader::id(),
174 BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
175 native_cost: solana_bpf_loader_program::DEFAULT_LOADER_COMPUTE_UNITS,
176 }),
177 ),
178 (
179 loader_v4::id(),
180 BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
181 native_cost: solana_loader_v4_program::DEFAULT_COMPUTE_UNITS,
182 }),
183 ),
184 (
186 secp256k1_program::id(),
187 BuiltinCost::NotMigrating(NotMigratingBuiltinCost { native_cost: 0 }),
188 ),
189 (
190 ed25519_program::id(),
191 BuiltinCost::NotMigrating(NotMigratingBuiltinCost { native_cost: 0 }),
192 ),
193];
194
195lazy_static! {
196 pub static ref MAYBE_BUILTIN_KEY: [bool; 256] = {
201 let mut temp_table: [bool; 256] = [false; 256];
202 BUILTIN_INSTRUCTION_COSTS
203 .keys()
204 .for_each(|key| temp_table[key.as_ref()[0] as usize] = true);
205 temp_table
206 };
207}
208
209pub fn get_builtin_instruction_cost<'a>(
210 program_id: &'a Pubkey,
211 feature_set: &'a FeatureSet,
212) -> Option<u64> {
213 BUILTIN_INSTRUCTION_COSTS
214 .get(program_id)
215 .filter(|builtin_cost| !builtin_cost.has_migrated(feature_set))
216 .map(|builtin_cost| builtin_cost.native_cost())
217}
218
219pub enum BuiltinMigrationFeatureIndex {
220 NotBuiltin,
221 BuiltinNoMigrationFeature,
222 BuiltinWithMigrationFeature(usize),
223}
224
225pub fn get_builtin_migration_feature_index(program_id: &Pubkey) -> BuiltinMigrationFeatureIndex {
226 BUILTIN_INSTRUCTION_COSTS.get(program_id).map_or(
227 BuiltinMigrationFeatureIndex::NotBuiltin,
228 |builtin_cost| {
229 builtin_cost.position().map_or(
230 BuiltinMigrationFeatureIndex::BuiltinNoMigrationFeature,
231 BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature,
232 )
233 },
234 )
235}
236
237#[allow(dead_code)]
239const fn validate_position(migrating_builtins: &[(Pubkey, BuiltinCost)]) {
240 let mut index = 0;
241 while index < migrating_builtins.len() {
242 match migrating_builtins[index].1 {
243 BuiltinCost::Migrating(MigratingBuiltinCost { position, .. }) => assert!(
244 position == index,
245 "migration feture must exist and at correct position"
246 ),
247 BuiltinCost::NotMigrating(_) => {
248 panic!("migration feture must exist and at correct position")
249 }
250 }
251 index += 1;
252 }
253}
254const _: () = validate_position(MIGRATING_BUILTINS_COSTS);
255
256pub fn get_migration_feature_id(index: usize) -> &'static Pubkey {
259 MIGRATING_BUILTINS_COSTS
260 .get(index)
261 .expect("valid index of MIGRATING_BUILTINS_COSTS")
262 .1
263 .core_bpf_migration_feature()
264 .expect("migrating builtin")
265}
266
267#[cfg(feature = "dev-context-only-utils")]
268pub fn get_migration_feature_position(feature_id: &Pubkey) -> usize {
269 MIGRATING_BUILTINS_COSTS
270 .iter()
271 .position(|(_, c)| c.core_bpf_migration_feature().expect("migrating builtin") == feature_id)
272 .unwrap()
273}
274
275#[cfg(test)]
276mod test {
277 use super::*;
278
279 #[test]
280 fn test_const_builtin_cost_arrays() {
281 assert!(MIGRATING_BUILTINS_COSTS
283 .iter()
284 .enumerate()
285 .all(|(index, (_, c))| {
286 c.core_bpf_migration_feature().is_some() && c.position() == Some(index)
287 }));
288 assert!(NON_MIGRATING_BUILTINS_COSTS
289 .iter()
290 .all(|(_, c)| c.core_bpf_migration_feature().is_none()));
291 }
292
293 #[test]
294 fn test_get_builtin_instruction_cost() {
295 assert_eq!(
297 Some(solana_compute_budget_program::DEFAULT_COMPUTE_UNITS),
298 get_builtin_instruction_cost(&compute_budget::id(), &FeatureSet::all_enabled())
299 );
300
301 assert_eq!(
303 Some(solana_stake_program::stake_instruction::DEFAULT_COMPUTE_UNITS),
304 get_builtin_instruction_cost(&solana_stake_program::id(), &FeatureSet::default())
305 );
306
307 assert!(get_builtin_instruction_cost(
309 &solana_stake_program::id(),
310 &FeatureSet::all_enabled()
311 )
312 .is_none());
313
314 assert!(
316 get_builtin_instruction_cost(&Pubkey::new_unique(), &FeatureSet::default()).is_none()
317 );
318 assert!(
319 get_builtin_instruction_cost(&Pubkey::new_unique(), &FeatureSet::all_enabled())
320 .is_none()
321 );
322 }
323
324 #[test]
325 fn test_get_builtin_migration_feature_index() {
326 assert!(matches!(
327 get_builtin_migration_feature_index(&Pubkey::new_unique()),
328 BuiltinMigrationFeatureIndex::NotBuiltin
329 ));
330 assert!(matches!(
331 get_builtin_migration_feature_index(&compute_budget::id()),
332 BuiltinMigrationFeatureIndex::BuiltinNoMigrationFeature,
333 ));
334 let feature_index = get_builtin_migration_feature_index(&solana_stake_program::id());
335 assert!(matches!(
336 feature_index,
337 BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(_)
338 ));
339 let BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(feature_index) =
340 feature_index
341 else {
342 panic!("expect migrating builtin")
343 };
344 assert_eq!(
345 get_migration_feature_id(feature_index),
346 &feature_set::migrate_stake_program_to_core_bpf::id()
347 );
348 let feature_index = get_builtin_migration_feature_index(&solana_config_program::id());
349 assert!(matches!(
350 feature_index,
351 BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(_)
352 ));
353 let BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(feature_index) =
354 feature_index
355 else {
356 panic!("expect migrating builtin")
357 };
358 assert_eq!(
359 get_migration_feature_id(feature_index),
360 &feature_set::migrate_config_program_to_core_bpf::id()
361 );
362 let feature_index =
363 get_builtin_migration_feature_index(&address_lookup_table::program::id());
364 assert!(matches!(
365 feature_index,
366 BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(_)
367 ));
368 let BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(feature_index) =
369 feature_index
370 else {
371 panic!("expect migrating builtin")
372 };
373 assert_eq!(
374 get_migration_feature_id(feature_index),
375 &feature_set::migrate_address_lookup_table_program_to_core_bpf::id()
376 );
377 }
378
379 #[test]
380 #[should_panic(expected = "valid index of MIGRATING_BUILTINS_COSTS")]
381 fn test_get_migration_feature_id_invalid_index() {
382 let _ = get_migration_feature_id(MIGRATING_BUILTINS_COSTS.len() + 1);
383 }
384}