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