Fix builtin handling on epoch boundaries (#23256)
This commit is contained in:
@ -3175,6 +3175,11 @@ impl Bank {
|
|||||||
|
|
||||||
/// Add a precompiled program account
|
/// Add a precompiled program account
|
||||||
pub fn add_precompiled_account(&self, program_id: &Pubkey) {
|
pub fn add_precompiled_account(&self, program_id: &Pubkey) {
|
||||||
|
self.add_precompiled_account_with_owner(program_id, native_loader::id())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used by tests to simulate clusters with precompiles that aren't owned by the native loader
|
||||||
|
fn add_precompiled_account_with_owner(&self, program_id: &Pubkey, owner: Pubkey) {
|
||||||
if let Some(account) = self.get_account_with_fixed_root(program_id) {
|
if let Some(account) = self.get_account_with_fixed_root(program_id) {
|
||||||
if account.executable() {
|
if account.executable() {
|
||||||
// The account is already executable, that's all we need
|
// The account is already executable, that's all we need
|
||||||
@ -3196,7 +3201,7 @@ impl Bank {
|
|||||||
let (lamports, rent_epoch) = self.inherit_specially_retained_account_fields(&None);
|
let (lamports, rent_epoch) = self.inherit_specially_retained_account_fields(&None);
|
||||||
let account = AccountSharedData::from(Account {
|
let account = AccountSharedData::from(Account {
|
||||||
lamports,
|
lamports,
|
||||||
owner: native_loader::id(),
|
owner,
|
||||||
data: vec![],
|
data: vec![],
|
||||||
executable: true,
|
executable: true,
|
||||||
rent_epoch,
|
rent_epoch,
|
||||||
@ -6457,12 +6462,12 @@ impl Bank {
|
|||||||
|
|
||||||
fn apply_builtin_program_feature_transitions(
|
fn apply_builtin_program_feature_transitions(
|
||||||
&mut self,
|
&mut self,
|
||||||
apply_transitions_for_new_features: bool,
|
only_apply_transitions_for_new_features: bool,
|
||||||
new_feature_activations: &HashSet<Pubkey>,
|
new_feature_activations: &HashSet<Pubkey>,
|
||||||
) {
|
) {
|
||||||
let feature_set = self.feature_set.clone();
|
let feature_set = self.feature_set.clone();
|
||||||
let should_apply_action_for_feature = |feature_id: &Pubkey| -> bool {
|
let should_apply_action_for_feature_transition = |feature_id: &Pubkey| -> bool {
|
||||||
if apply_transitions_for_new_features {
|
if only_apply_transitions_for_new_features {
|
||||||
new_feature_activations.contains(feature_id)
|
new_feature_activations.contains(feature_id)
|
||||||
} else {
|
} else {
|
||||||
feature_set.is_active(feature_id)
|
feature_set.is_active(feature_id)
|
||||||
@ -6471,7 +6476,9 @@ impl Bank {
|
|||||||
|
|
||||||
let builtin_feature_transitions = self.builtin_feature_transitions.clone();
|
let builtin_feature_transitions = self.builtin_feature_transitions.clone();
|
||||||
for transition in builtin_feature_transitions.iter() {
|
for transition in builtin_feature_transitions.iter() {
|
||||||
if let Some(builtin_action) = transition.to_action(&should_apply_action_for_feature) {
|
if let Some(builtin_action) =
|
||||||
|
transition.to_action(&should_apply_action_for_feature_transition)
|
||||||
|
{
|
||||||
match builtin_action {
|
match builtin_action {
|
||||||
BuiltinAction::Add(builtin) => self.add_builtin(
|
BuiltinAction::Add(builtin) => self.add_builtin(
|
||||||
&builtin.name,
|
&builtin.name,
|
||||||
@ -12963,25 +12970,25 @@ pub(crate) mod tests {
|
|||||||
if bank.slot == 0 {
|
if bank.slot == 0 {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bank.hash().to_string(),
|
bank.hash().to_string(),
|
||||||
"HREoNvUAuqqGdJxYTgnFqjTxsuuBVUFFDNLGR2cdrtMf"
|
"9tLrxkBoNE7zEUZ2g72ZwE4fTfhUQnhC8A4Xt4EmYhP1"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if bank.slot == 32 {
|
if bank.slot == 32 {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bank.hash().to_string(),
|
bank.hash().to_string(),
|
||||||
"J8kjxLMMrpEVQUbX54zDALkXidjdXyFSL5wD2d7xUaWL"
|
"7qCbZN5WLT928VpsaLwLp6HfRDzZirmoU4JM4XBEyupu"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if bank.slot == 64 {
|
if bank.slot == 64 {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bank.hash().to_string(),
|
bank.hash().to_string(),
|
||||||
"yPCTEPtNi2DJb8KyqPKgBK7HCfiEpH2oS3Nn12LPBHm"
|
"D3ypfQFreDaQhJuuYN8rWG1TVy9ApvTCx5CAiQ5i9d7A"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if bank.slot == 128 {
|
if bank.slot == 128 {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bank.hash().to_string(),
|
bank.hash().to_string(),
|
||||||
"2oG1rmA59tmr457oK4oF6C6TcM2gyy2ZAKeJoUhMNb1L"
|
"67krqDMqjkkixdfypnCCgSyUm2FoqAE8KB1hgRAtCaBp"
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -13210,7 +13217,7 @@ pub(crate) mod tests {
|
|||||||
// No more slots should be shrunk
|
// No more slots should be shrunk
|
||||||
assert_eq!(bank2.shrink_candidate_slots(), 0);
|
assert_eq!(bank2.shrink_candidate_slots(), 0);
|
||||||
// alive_counts represents the count of alive accounts in the three slots 0,1,2
|
// alive_counts represents the count of alive accounts in the three slots 0,1,2
|
||||||
assert_eq!(alive_counts, vec![11, 1, 7]);
|
assert_eq!(alive_counts, vec![9, 1, 7]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -13258,7 +13265,7 @@ pub(crate) mod tests {
|
|||||||
.map(|_| bank.process_stale_slot_with_budget(0, force_to_return_alive_account))
|
.map(|_| bank.process_stale_slot_with_budget(0, force_to_return_alive_account))
|
||||||
.sum();
|
.sum();
|
||||||
// consumed_budgets represents the count of alive accounts in the three slots 0,1,2
|
// consumed_budgets represents the count of alive accounts in the three slots 0,1,2
|
||||||
assert_eq!(consumed_budgets, 12);
|
assert_eq!(consumed_budgets, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -5,6 +5,41 @@ mod tests {
|
|||||||
solana_sdk::{feature_set::FeatureSet, genesis_config::create_genesis_config},
|
solana_sdk::{feature_set::FeatureSet, genesis_config::create_genesis_config},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_apply_builtin_program_feature_transitions_for_new_epoch() {
|
||||||
|
let (genesis_config, _mint_keypair) = create_genesis_config(100_000);
|
||||||
|
|
||||||
|
let mut bank = Bank::new_for_tests(&genesis_config);
|
||||||
|
bank.feature_set = Arc::new(FeatureSet::all_enabled());
|
||||||
|
bank.finish_init(&genesis_config, None, false);
|
||||||
|
|
||||||
|
// Overwrite precompile accounts to simulate a cluster which already added precompiles.
|
||||||
|
for precompile in get_precompiles() {
|
||||||
|
bank.store_account(&precompile.program_id, &AccountSharedData::default());
|
||||||
|
// Simulate cluster which added ed25519 precompile with a system program owner
|
||||||
|
if precompile.program_id == ed25519_program::id() {
|
||||||
|
bank.add_precompiled_account_with_owner(
|
||||||
|
&precompile.program_id,
|
||||||
|
solana_sdk::system_program::id(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
bank.add_precompiled_account(&precompile.program_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normally feature transitions are applied to a bank that hasn't been
|
||||||
|
// frozen yet. Freeze the bank early to ensure that no account changes
|
||||||
|
// are made.
|
||||||
|
bank.freeze();
|
||||||
|
|
||||||
|
// Simulate crossing an epoch boundary for a new bank
|
||||||
|
let only_apply_transitions_for_new_features = true;
|
||||||
|
bank.apply_builtin_program_feature_transitions(
|
||||||
|
only_apply_transitions_for_new_features,
|
||||||
|
&HashSet::new(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_startup_from_snapshot_after_precompile_transition() {
|
fn test_startup_from_snapshot_after_precompile_transition() {
|
||||||
let (genesis_config, _mint_keypair) = create_genesis_config(100_000);
|
let (genesis_config, _mint_keypair) = create_genesis_config(100_000);
|
||||||
|
@ -117,7 +117,8 @@ pub enum BuiltinFeatureTransition {
|
|||||||
/// Remove a builtin program if a feature is activated or
|
/// Remove a builtin program if a feature is activated or
|
||||||
/// retain a previously added builtin.
|
/// retain a previously added builtin.
|
||||||
RemoveOrRetain {
|
RemoveOrRetain {
|
||||||
previous_builtin: Builtin,
|
previously_added_builtin: Builtin,
|
||||||
|
addition_feature_id: Pubkey,
|
||||||
removal_feature_id: Pubkey,
|
removal_feature_id: Pubkey,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -139,14 +140,17 @@ impl BuiltinFeatureTransition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::RemoveOrRetain {
|
Self::RemoveOrRetain {
|
||||||
previous_builtin,
|
previously_added_builtin,
|
||||||
|
addition_feature_id,
|
||||||
removal_feature_id,
|
removal_feature_id,
|
||||||
} => {
|
} => {
|
||||||
if should_apply_action_for_feature(removal_feature_id) {
|
if should_apply_action_for_feature(removal_feature_id) {
|
||||||
Some(BuiltinAction::Remove(previous_builtin.id))
|
Some(BuiltinAction::Remove(previously_added_builtin.id))
|
||||||
} else {
|
} else if should_apply_action_for_feature(addition_feature_id) {
|
||||||
// Retaining is no different from adding a new builtin.
|
// Retaining is no different from adding a new builtin.
|
||||||
Some(BuiltinAction::Add(previous_builtin.clone()))
|
Some(BuiltinAction::Add(previously_added_builtin.clone()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,19 +204,21 @@ fn builtin_feature_transitions() -> Vec<BuiltinFeatureTransition> {
|
|||||||
feature_id: feature_set::add_compute_budget_program::id(),
|
feature_id: feature_set::add_compute_budget_program::id(),
|
||||||
},
|
},
|
||||||
BuiltinFeatureTransition::RemoveOrRetain {
|
BuiltinFeatureTransition::RemoveOrRetain {
|
||||||
previous_builtin: Builtin::new(
|
previously_added_builtin: Builtin::new(
|
||||||
"secp256k1_program",
|
"secp256k1_program",
|
||||||
solana_sdk::secp256k1_program::id(),
|
solana_sdk::secp256k1_program::id(),
|
||||||
dummy_process_instruction,
|
dummy_process_instruction,
|
||||||
),
|
),
|
||||||
|
addition_feature_id: feature_set::secp256k1_program_enabled::id(),
|
||||||
removal_feature_id: feature_set::prevent_calling_precompiles_as_programs::id(),
|
removal_feature_id: feature_set::prevent_calling_precompiles_as_programs::id(),
|
||||||
},
|
},
|
||||||
BuiltinFeatureTransition::RemoveOrRetain {
|
BuiltinFeatureTransition::RemoveOrRetain {
|
||||||
previous_builtin: Builtin::new(
|
previously_added_builtin: Builtin::new(
|
||||||
"ed25519_program",
|
"ed25519_program",
|
||||||
solana_sdk::ed25519_program::id(),
|
solana_sdk::ed25519_program::id(),
|
||||||
dummy_process_instruction,
|
dummy_process_instruction,
|
||||||
),
|
),
|
||||||
|
addition_feature_id: feature_set::ed25519_program_enabled::id(),
|
||||||
removal_feature_id: feature_set::prevent_calling_precompiles_as_programs::id(),
|
removal_feature_id: feature_set::prevent_calling_precompiles_as_programs::id(),
|
||||||
},
|
},
|
||||||
BuiltinFeatureTransition::Add {
|
BuiltinFeatureTransition::Add {
|
||||||
|
@ -26,7 +26,7 @@ pub fn bootstrap_validator_stake_lamports() -> u64 {
|
|||||||
|
|
||||||
// Number of lamports automatically used for genesis accounts
|
// Number of lamports automatically used for genesis accounts
|
||||||
pub const fn genesis_sysvar_and_builtin_program_lamports() -> u64 {
|
pub const fn genesis_sysvar_and_builtin_program_lamports() -> u64 {
|
||||||
const NUM_BUILTIN_PROGRAMS: u64 = 6;
|
const NUM_BUILTIN_PROGRAMS: u64 = 4;
|
||||||
const FEES_SYSVAR_MIN_BALANCE: u64 = 946_560;
|
const FEES_SYSVAR_MIN_BALANCE: u64 = 946_560;
|
||||||
const STAKE_HISTORY_MIN_BALANCE: u64 = 114_979_200;
|
const STAKE_HISTORY_MIN_BALANCE: u64 = 114_979_200;
|
||||||
const CLOCK_SYSVAR_MIN_BALANCE: u64 = 1_169_280;
|
const CLOCK_SYSVAR_MIN_BALANCE: u64 = 1_169_280;
|
||||||
|
@ -53,6 +53,10 @@ pub mod full_inflation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod secp256k1_program_enabled {
|
||||||
|
solana_sdk::declare_id!("E3PHP7w8kB7np3CTQ1qQ2tW3KCtjRSXBQgW9vM2mWv2Y");
|
||||||
|
}
|
||||||
|
|
||||||
pub mod spl_token_v2_multisig_fix {
|
pub mod spl_token_v2_multisig_fix {
|
||||||
solana_sdk::declare_id!("E5JiFDQCwyC6QfT9REFyMpfK2mHcmv1GUDySU1Ue7TYv");
|
solana_sdk::declare_id!("E5JiFDQCwyC6QfT9REFyMpfK2mHcmv1GUDySU1Ue7TYv");
|
||||||
}
|
}
|
||||||
@ -314,6 +318,7 @@ pub mod record_instruction_in_transaction_context_push {
|
|||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Map of feature identifiers to user-visible description
|
/// Map of feature identifiers to user-visible description
|
||||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||||
|
(secp256k1_program_enabled::id(), "secp256k1 program"),
|
||||||
(deprecate_rewards_sysvar::id(), "deprecate unused rewards sysvar"),
|
(deprecate_rewards_sysvar::id(), "deprecate unused rewards sysvar"),
|
||||||
(pico_inflation::id(), "pico inflation"),
|
(pico_inflation::id(), "pico inflation"),
|
||||||
(full_inflation::devnet_and_testnet::id(), "full inflation on devnet and testnet"),
|
(full_inflation::devnet_and_testnet::id(), "full inflation on devnet and testnet"),
|
||||||
|
Reference in New Issue
Block a user