Skip to content

Commit

Permalink
Accounting migrating builtin programs default Compute Unit Limit with…
Browse files Browse the repository at this point in the history
… feature status (#3975)

* Accounting migrating builtin programs default Compute Unit Limit with its feature gate status

* Declare Non/migrating buiiltins in const array, eleminates heap allocation (Vec<>) per transaction

* updates for review commients

add explicit positional information to migrating builtin feature obj

update developer notes, added static_assertion to validate no new items are added, add enum type

* use enum to separately define migrating and not-migrating builtins

* rename for clarity

(cherry picked from commit 9379fbc)

# Conflicts:
#	builtins-default-costs/src/lib.rs
#	compute-budget-instruction/Cargo.toml
#	compute-budget-instruction/src/builtin_programs_filter.rs
#	runtime-transaction/src/compute_budget_instruction_details.rs
  • Loading branch information
tao-stones authored and mergify[bot] committed Dec 12, 2024
1 parent 0125110 commit e75594a
Show file tree
Hide file tree
Showing 6 changed files with 790 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions builtins-default-costs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ name = "solana_builtins_default_costs"

[dev-dependencies]
rand = "0.8.5"
static_assertions = { workspace = true }

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
Expand All @@ -42,6 +43,7 @@ frozen-abi = [
"dep:solana-frozen-abi",
"solana-vote-program/frozen-abi",
]
dev-context-only-utils = []

[lints]
workspace = true
349 changes: 349 additions & 0 deletions builtins-default-costs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,74 @@ use {
},
};

<<<<<<< HEAD
// Number of compute units for each built-in programs
=======
#[derive(Clone)]
pub struct MigratingBuiltinCost {
native_cost: u64,
core_bpf_migration_feature: Pubkey,
// encoding positional information explicitly for migration feature item,
// its value must be correctly corresponding to this object's position
// in MIGRATING_BUILTINS_COSTS, otherwise a const validation
// `validate_position(MIGRATING_BUILTINS_COSTS)` will fail at compile time.
position: usize,
}

#[derive(Clone)]
pub struct NotMigratingBuiltinCost {
native_cost: u64,
}

/// DEVELOPER: when a builtin is migrated to sbpf, please add its corresponding
/// migration feature ID to BUILTIN_INSTRUCTION_COSTS, and move it from
/// NON_MIGRATING_BUILTINS_COSTS to MIGRATING_BUILTINS_COSTS, so the builtin's
/// default cost can be determined properly based on feature status.
/// When migration completed, eg the feature gate is enabled everywhere, please
/// remove that builtin entry from MIGRATING_BUILTINS_COSTS.
#[derive(Clone)]
pub enum BuiltinCost {
Migrating(MigratingBuiltinCost),
NotMigrating(NotMigratingBuiltinCost),
}

impl BuiltinCost {
pub fn native_cost(&self) -> u64 {
match self {
BuiltinCost::Migrating(MigratingBuiltinCost { native_cost, .. }) => *native_cost,
BuiltinCost::NotMigrating(NotMigratingBuiltinCost { native_cost }) => *native_cost,
}
}

pub fn core_bpf_migration_feature(&self) -> Option<&Pubkey> {
match self {
BuiltinCost::Migrating(MigratingBuiltinCost {
core_bpf_migration_feature,
..
}) => Some(core_bpf_migration_feature),
BuiltinCost::NotMigrating(_) => None,
}
}

pub fn position(&self) -> Option<usize> {
match self {
BuiltinCost::Migrating(MigratingBuiltinCost { position, .. }) => Some(*position),
BuiltinCost::NotMigrating(_) => None,
}
}

fn has_migrated(&self, feature_set: &FeatureSet) -> bool {
match self {
BuiltinCost::Migrating(MigratingBuiltinCost {
core_bpf_migration_feature,
..
}) => feature_set.is_active(core_bpf_migration_feature),
BuiltinCost::NotMigrating(_) => false,
}
}
}

>>>>>>> 9379fbcba4 (Accounting migrating builtin programs default Compute Unit Limit with feature status (#3975))
lazy_static! {
/// Number of compute units for each built-in programs
///
Expand All @@ -20,6 +87,7 @@ lazy_static! {
/// calculate the cost of a transaction which is used in replay to enforce
/// block cost limits as of
/// https://github.com/solana-labs/solana/issues/29595.
<<<<<<< HEAD
pub static ref BUILTIN_INSTRUCTION_COSTS: AHashMap<Pubkey, u64> = [
(solana_stake_program::id(), solana_stake_program::stake_instruction::DEFAULT_COMPUTE_UNITS),
(solana_config_program::id(), solana_config_program::config_processor::DEFAULT_COMPUTE_UNITS),
Expand All @@ -40,6 +108,112 @@ lazy_static! {
.cloned()
.collect();
}
=======
static ref BUILTIN_INSTRUCTION_COSTS: AHashMap<Pubkey, BuiltinCost> =
MIGRATING_BUILTINS_COSTS
.iter()
.chain(NON_MIGRATING_BUILTINS_COSTS.iter())
.cloned()
.collect();
// DO NOT ADD MORE ENTRIES TO THIS MAP
}

/// DEVELOPER WARNING: please do not add new entry into MIGRATING_BUILTINS_COSTS or
/// NON_MIGRATING_BUILTINS_COSTS, do so will modify BUILTIN_INSTRUCTION_COSTS therefore
/// cause consensus failure. However, when a builtin started being migrated to core bpf,
/// it MUST be moved from NON_MIGRATING_BUILTINS_COSTS to MIGRATING_BUILTINS_COSTS, then
/// correctly furnishing `core_bpf_migration_feature`.
///
#[allow(dead_code)]
const TOTAL_COUNT_BUILTS: usize = 12;
#[cfg(test)]
static_assertions::const_assert_eq!(
MIGRATING_BUILTINS_COSTS.len() + NON_MIGRATING_BUILTINS_COSTS.len(),
TOTAL_COUNT_BUILTS
);

pub const MIGRATING_BUILTINS_COSTS: &[(Pubkey, BuiltinCost)] = &[
(
stake::id(),
BuiltinCost::Migrating(MigratingBuiltinCost {
native_cost: solana_stake_program::stake_instruction::DEFAULT_COMPUTE_UNITS,
core_bpf_migration_feature: feature_set::migrate_stake_program_to_core_bpf::id(),
position: 0,
}),
),
(
config::id(),
BuiltinCost::Migrating(MigratingBuiltinCost {
native_cost: solana_config_program::config_processor::DEFAULT_COMPUTE_UNITS,
core_bpf_migration_feature: feature_set::migrate_config_program_to_core_bpf::id(),
position: 1,
}),
),
(
address_lookup_table::id(),
BuiltinCost::Migrating(MigratingBuiltinCost {
native_cost: solana_address_lookup_table_program::processor::DEFAULT_COMPUTE_UNITS,
core_bpf_migration_feature:
feature_set::migrate_address_lookup_table_program_to_core_bpf::id(),
position: 2,
}),
),
];

pub const NON_MIGRATING_BUILTINS_COSTS: &[(Pubkey, BuiltinCost)] = &[
(
vote::id(),
BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
native_cost: solana_vote_program::vote_processor::DEFAULT_COMPUTE_UNITS,
}),
),
(
system_program::id(),
BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
native_cost: solana_system_program::system_processor::DEFAULT_COMPUTE_UNITS,
}),
),
(
compute_budget::id(),
BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
native_cost: solana_compute_budget_program::DEFAULT_COMPUTE_UNITS,
}),
),
(
bpf_loader_upgradeable::id(),
BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
native_cost: solana_bpf_loader_program::UPGRADEABLE_LOADER_COMPUTE_UNITS,
}),
),
(
bpf_loader_deprecated::id(),
BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
native_cost: solana_bpf_loader_program::DEPRECATED_LOADER_COMPUTE_UNITS,
}),
),
(
bpf_loader::id(),
BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
native_cost: solana_bpf_loader_program::DEFAULT_LOADER_COMPUTE_UNITS,
}),
),
(
loader_v4::id(),
BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
native_cost: solana_loader_v4_program::DEFAULT_COMPUTE_UNITS,
}),
),
// Note: These are precompile, run directly in bank during sanitizing;
(
secp256k1_program::id(),
BuiltinCost::NotMigrating(NotMigratingBuiltinCost { native_cost: 0 }),
),
(
ed25519_program::id(),
BuiltinCost::NotMigrating(NotMigratingBuiltinCost { native_cost: 0 }),
),
];
>>>>>>> 9379fbcba4 (Accounting migrating builtin programs default Compute Unit Limit with feature status (#3975))

lazy_static! {
/// A table of 256 booleans indicates whether the first `u8` of a Pubkey exists in
Expand All @@ -54,3 +228,178 @@ lazy_static! {
temp_table
};
}
<<<<<<< HEAD
=======

pub fn get_builtin_instruction_cost<'a>(
program_id: &'a Pubkey,
feature_set: &'a FeatureSet,
) -> Option<u64> {
BUILTIN_INSTRUCTION_COSTS
.get(program_id)
.filter(|builtin_cost| !builtin_cost.has_migrated(feature_set))
.map(|builtin_cost| builtin_cost.native_cost())
}

pub enum BuiltinMigrationFeatureIndex {
NotBuiltin,
BuiltinNoMigrationFeature,
BuiltinWithMigrationFeature(usize),
}

pub fn get_builtin_migration_feature_index(program_id: &Pubkey) -> BuiltinMigrationFeatureIndex {
BUILTIN_INSTRUCTION_COSTS.get(program_id).map_or(
BuiltinMigrationFeatureIndex::NotBuiltin,
|builtin_cost| {
builtin_cost.position().map_or(
BuiltinMigrationFeatureIndex::BuiltinNoMigrationFeature,
BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature,
)
},
)
}

/// const function validates `position` correctness at compile time.
#[allow(dead_code)]
const fn validate_position(migrating_builtins: &[(Pubkey, BuiltinCost)]) {
let mut index = 0;
while index < migrating_builtins.len() {
match migrating_builtins[index].1 {
BuiltinCost::Migrating(MigratingBuiltinCost { position, .. }) => assert!(
position == index,
"migration feture must exist and at correct position"
),
BuiltinCost::NotMigrating(_) => {
panic!("migration feture must exist and at correct position")
}
}
index += 1;
}
}
const _: () = validate_position(MIGRATING_BUILTINS_COSTS);

/// Helper function to return ref of migration feature Pubkey at position `index`
/// from MIGRATING_BUILTINS_COSTS
pub fn get_migration_feature_id(index: usize) -> &'static Pubkey {
MIGRATING_BUILTINS_COSTS
.get(index)
.expect("valid index of MIGRATING_BUILTINS_COSTS")
.1
.core_bpf_migration_feature()
.expect("migrating builtin")
}

#[cfg(feature = "dev-context-only-utils")]
pub fn get_migration_feature_position(feature_id: &Pubkey) -> usize {
MIGRATING_BUILTINS_COSTS
.iter()
.position(|(_, c)| c.core_bpf_migration_feature().expect("migrating builtin") == feature_id)
.unwrap()
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_const_builtin_cost_arrays() {
// sanity check to make sure built-ins are declared in the correct array
assert!(MIGRATING_BUILTINS_COSTS
.iter()
.enumerate()
.all(|(index, (_, c))| {
c.core_bpf_migration_feature().is_some() && c.position() == Some(index)
}));
assert!(NON_MIGRATING_BUILTINS_COSTS
.iter()
.all(|(_, c)| c.core_bpf_migration_feature().is_none()));
}

#[test]
fn test_get_builtin_instruction_cost() {
// use native cost if no migration planned
assert_eq!(
Some(solana_compute_budget_program::DEFAULT_COMPUTE_UNITS),
get_builtin_instruction_cost(&compute_budget::id(), &FeatureSet::all_enabled())
);

// use native cost if migration is planned but not activated
assert_eq!(
Some(solana_stake_program::stake_instruction::DEFAULT_COMPUTE_UNITS),
get_builtin_instruction_cost(&stake::id(), &FeatureSet::default())
);

// None if migration is planned and activated, in which case, it's no longer builtin
assert!(get_builtin_instruction_cost(&stake::id(), &FeatureSet::all_enabled()).is_none());

// None if not builtin
assert!(
get_builtin_instruction_cost(&Pubkey::new_unique(), &FeatureSet::default()).is_none()
);
assert!(
get_builtin_instruction_cost(&Pubkey::new_unique(), &FeatureSet::all_enabled())
.is_none()
);
}

#[test]
fn test_get_builtin_migration_feature_index() {
assert!(matches!(
get_builtin_migration_feature_index(&Pubkey::new_unique()),
BuiltinMigrationFeatureIndex::NotBuiltin
));
assert!(matches!(
get_builtin_migration_feature_index(&compute_budget::id()),
BuiltinMigrationFeatureIndex::BuiltinNoMigrationFeature,
));
let feature_index = get_builtin_migration_feature_index(&stake::id());
assert!(matches!(
feature_index,
BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(_)
));
let BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(feature_index) =
feature_index
else {
panic!("expect migrating builtin")
};
assert_eq!(
get_migration_feature_id(feature_index),
&feature_set::migrate_stake_program_to_core_bpf::id()
);
let feature_index = get_builtin_migration_feature_index(&config::id());
assert!(matches!(
feature_index,
BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(_)
));
let BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(feature_index) =
feature_index
else {
panic!("expect migrating builtin")
};
assert_eq!(
get_migration_feature_id(feature_index),
&feature_set::migrate_config_program_to_core_bpf::id()
);
let feature_index = get_builtin_migration_feature_index(&address_lookup_table::id());
assert!(matches!(
feature_index,
BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(_)
));
let BuiltinMigrationFeatureIndex::BuiltinWithMigrationFeature(feature_index) =
feature_index
else {
panic!("expect migrating builtin")
};
assert_eq!(
get_migration_feature_id(feature_index),
&feature_set::migrate_address_lookup_table_program_to_core_bpf::id()
);
}

#[test]
#[should_panic(expected = "valid index of MIGRATING_BUILTINS_COSTS")]
fn test_get_migration_feature_id_invalid_index() {
let _ = get_migration_feature_id(MIGRATING_BUILTINS_COSTS.len() + 1);
}
}
>>>>>>> 9379fbcba4 (Accounting migrating builtin programs default Compute Unit Limit with feature status (#3975))
Loading

0 comments on commit e75594a

Please sign in to comment.