clone_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 clone_agave_feature_set::{self as feature_set, FeatureSet},
8 clone_solana_pubkey::Pubkey,
9 clone_solana_sdk_ids::{
10 address_lookup_table, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
11 compute_budget, config, ed25519_program, loader_v4, secp256k1_program, stake,
12 system_program, vote,
13 },
14 lazy_static::lazy_static,
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: clone_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: clone_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:
141 clone_solana_address_lookup_table_program::processor::DEFAULT_COMPUTE_UNITS,
142 core_bpf_migration_feature:
143 feature_set::migrate_address_lookup_table_program_to_core_bpf::id(),
144 position: 2,
145 }),
146 ),
147];
148
149const NON_MIGRATING_BUILTINS_COSTS: &[(Pubkey, BuiltinCost)] = &[
150 (
151 vote::id(),
152 BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
153 native_cost: clone_solana_vote_program::vote_processor::DEFAULT_COMPUTE_UNITS,
154 }),
155 ),
156 (
157 system_program::id(),
158 BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
159 native_cost: clone_solana_system_program::system_processor::DEFAULT_COMPUTE_UNITS,
160 }),
161 ),
162 (
163 compute_budget::id(),
164 BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
165 native_cost: clone_solana_compute_budget_program::DEFAULT_COMPUTE_UNITS,
166 }),
167 ),
168 (
169 bpf_loader_upgradeable::id(),
170 BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
171 native_cost: clone_solana_bpf_loader_program::UPGRADEABLE_LOADER_COMPUTE_UNITS,
172 }),
173 ),
174 (
175 bpf_loader_deprecated::id(),
176 BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
177 native_cost: clone_solana_bpf_loader_program::DEPRECATED_LOADER_COMPUTE_UNITS,
178 }),
179 ),
180 (
181 bpf_loader::id(),
182 BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
183 native_cost: clone_solana_bpf_loader_program::DEFAULT_LOADER_COMPUTE_UNITS,
184 }),
185 ),
186 (
187 loader_v4::id(),
188 BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
189 native_cost: clone_solana_loader_v4_program::DEFAULT_COMPUTE_UNITS,
190 }),
191 ),
192 (
194 secp256k1_program::id(),
195 BuiltinCost::NotMigrating(NotMigratingBuiltinCost { native_cost: 0 }),
196 ),
197 (
198 ed25519_program::id(),
199 BuiltinCost::NotMigrating(NotMigratingBuiltinCost { native_cost: 0 }),
200 ),
201];
202
203lazy_static! {
204 pub static ref MAYBE_BUILTIN_KEY: [bool; 256] = {
209 let mut temp_table: [bool; 256] = [false; 256];
210 BUILTIN_INSTRUCTION_COSTS
211 .keys()
212 .for_each(|key| temp_table[key.as_ref()[0] as usize] = true);
213 temp_table
214 };
215}
216
217pub fn get_builtin_instruction_cost<'a>(
218 program_id: &'a Pubkey,
219 feature_set: &'a FeatureSet,
220) -> Option<u64> {
221 BUILTIN_INSTRUCTION_COSTS
222 .get(program_id)
223 .filter(|builtin_cost| !builtin_cost.has_migrated(feature_set))
224 .map(|builtin_cost| builtin_cost.native_cost())
225}
226
227#[cfg(feature = "svm-internal")]
228#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
229enum BuiltinMigrationFeatureIndex {
230 NotBuiltin,
231 BuiltinNoMigrationFeature,
232 BuiltinWithMigrationFeature(usize),
233}
234
235#[cfg(feature = "svm-internal")]
236#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
237fn get_builtin_migration_feature_index(program_id: &Pubkey) -> BuiltinMigrationFeatureIndex {
238 BUILTIN_INSTRUCTION_COSTS.get(program_id).map_or(
239 BuiltinMigrationFeatureIndex::NotBuiltin,
240 |builtin_cost| {
241 builtin_cost.position().map_or(
242 BuiltinMigrationFeatureIndex::BuiltinNoMigrationFeature,
243 BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature,
244 )
245 },
246 )
247}
248
249#[allow(dead_code)]
251const fn validate_position(migrating_builtins: &[(Pubkey, BuiltinCost)]) {
252 let mut index = 0;
253 while index < migrating_builtins.len() {
254 match migrating_builtins[index].1 {
255 BuiltinCost::Migrating(MigratingBuiltinCost { position, .. }) => assert!(
256 position == index,
257 "migration feture must exist and at correct position"
258 ),
259 BuiltinCost::NotMigrating(_) => {
260 panic!("migration feture must exist and at correct position")
261 }
262 }
263 index += 1;
264 }
265}
266const _: () = validate_position(MIGRATING_BUILTINS_COSTS);
267
268#[cfg(feature = "svm-internal")]
271#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
272pub(crate) fn get_migration_feature_id(index: usize) -> &'static Pubkey {
273 MIGRATING_BUILTINS_COSTS
274 .get(index)
275 .expect("valid index of MIGRATING_BUILTINS_COSTS")
276 .1
277 .core_bpf_migration_feature()
278 .expect("migrating builtin")
279}
280
281#[cfg(feature = "dev-context-only-utils")]
282pub fn get_migration_feature_position(feature_id: &Pubkey) -> usize {
283 MIGRATING_BUILTINS_COSTS
284 .iter()
285 .position(|(_, c)| c.core_bpf_migration_feature().expect("migrating builtin") == feature_id)
286 .unwrap()
287}
288
289#[cfg(test)]
290mod test {
291 use super::*;
292
293 #[test]
294 fn test_const_builtin_cost_arrays() {
295 assert!(MIGRATING_BUILTINS_COSTS
297 .iter()
298 .enumerate()
299 .all(|(index, (_, c))| {
300 c.core_bpf_migration_feature().is_some() && c.position() == Some(index)
301 }));
302 assert!(NON_MIGRATING_BUILTINS_COSTS
303 .iter()
304 .all(|(_, c)| c.core_bpf_migration_feature().is_none()));
305 }
306
307 #[test]
308 fn test_get_builtin_instruction_cost() {
309 assert_eq!(
311 Some(clone_solana_compute_budget_program::DEFAULT_COMPUTE_UNITS),
312 get_builtin_instruction_cost(&compute_budget::id(), &FeatureSet::all_enabled())
313 );
314
315 assert_eq!(
317 Some(clone_solana_stake_program::stake_instruction::DEFAULT_COMPUTE_UNITS),
318 get_builtin_instruction_cost(&stake::id(), &FeatureSet::default())
319 );
320
321 assert!(get_builtin_instruction_cost(&stake::id(), &FeatureSet::all_enabled()).is_none());
323
324 assert!(
326 get_builtin_instruction_cost(&Pubkey::new_unique(), &FeatureSet::default()).is_none()
327 );
328 assert!(
329 get_builtin_instruction_cost(&Pubkey::new_unique(), &FeatureSet::all_enabled())
330 .is_none()
331 );
332 }
333
334 #[test]
335 fn test_get_builtin_migration_feature_index() {
336 assert!(matches!(
337 get_builtin_migration_feature_index(&Pubkey::new_unique()),
338 BuiltinMigrationFeatureIndex::NotBuiltin
339 ));
340 assert!(matches!(
341 get_builtin_migration_feature_index(&compute_budget::id()),
342 BuiltinMigrationFeatureIndex::BuiltinNoMigrationFeature,
343 ));
344 let feature_index = get_builtin_migration_feature_index(&stake::id());
345 assert!(matches!(
346 feature_index,
347 BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(_)
348 ));
349 let BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(feature_index) =
350 feature_index
351 else {
352 panic!("expect migrating builtin")
353 };
354 assert_eq!(
355 get_migration_feature_id(feature_index),
356 &feature_set::migrate_stake_program_to_core_bpf::id()
357 );
358 let feature_index = get_builtin_migration_feature_index(&config::id());
359 assert!(matches!(
360 feature_index,
361 BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(_)
362 ));
363 let BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(feature_index) =
364 feature_index
365 else {
366 panic!("expect migrating builtin")
367 };
368 assert_eq!(
369 get_migration_feature_id(feature_index),
370 &feature_set::migrate_config_program_to_core_bpf::id()
371 );
372 let feature_index = get_builtin_migration_feature_index(&address_lookup_table::id());
373 assert!(matches!(
374 feature_index,
375 BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(_)
376 ));
377 let BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(feature_index) =
378 feature_index
379 else {
380 panic!("expect migrating builtin")
381 };
382 assert_eq!(
383 get_migration_feature_id(feature_index),
384 &feature_set::migrate_address_lookup_table_program_to_core_bpf::id()
385 );
386 }
387
388 #[test]
389 #[should_panic(expected = "valid index of MIGRATING_BUILTINS_COSTS")]
390 fn test_get_migration_feature_id_invalid_index() {
391 let _ = get_migration_feature_id(MIGRATING_BUILTINS_COSTS.len() + 1);
392 }
393}