#![cfg(feature = "full")]
use {
crate::{
address_lookup_table, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
compute_budget, config, ed25519_program, feature, loader_v4, native_loader, pubkey::Pubkey,
secp256k1_program, stake, system_program, sysvar, vote,
},
lazy_static::lazy_static,
solana_feature_set::{self as feature_set, FeatureSet},
solana_secp256r1_program as secp256r1_program,
std::collections::{HashMap, HashSet},
};
mod zk_token_proof_program {
solana_sdk::declare_id!("ZkTokenProof1111111111111111111111111111111");
}
mod zk_elgamal_proof_program {
solana_sdk::declare_id!("ZkE1Gama1Proof11111111111111111111111111111");
}
#[cfg(feature = "frozen-abi")]
impl ::solana_frozen_abi::abi_example::AbiExample for ReservedAccountKeys {
fn example() -> Self {
ReservedAccountKeys::default()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ReservedAccountKeys {
pub active: HashSet<Pubkey>,
inactive: HashMap<Pubkey, Pubkey>,
}
impl Default for ReservedAccountKeys {
fn default() -> Self {
Self::new(&RESERVED_ACCOUNTS)
}
}
impl ReservedAccountKeys {
fn new(reserved_accounts: &[ReservedAccount]) -> Self {
Self {
active: reserved_accounts
.iter()
.filter(|reserved| reserved.feature_id.is_none())
.map(|reserved| reserved.key)
.collect(),
inactive: reserved_accounts
.iter()
.filter_map(|ReservedAccount { key, feature_id }| {
feature_id.as_ref().map(|feature_id| (*key, *feature_id))
})
.collect(),
}
}
pub fn new_all_activated() -> Self {
Self {
active: Self::all_keys_iter().copied().collect(),
inactive: HashMap::default(),
}
}
pub fn is_reserved(&self, key: &Pubkey) -> bool {
self.active.contains(key)
}
pub fn update_active_set(&mut self, feature_set: &FeatureSet) {
self.inactive.retain(|reserved_key, feature_id| {
if feature_set.is_active(feature_id) {
self.active.insert(*reserved_key);
false
} else {
true
}
});
}
pub fn all_keys_iter() -> impl Iterator<Item = &'static Pubkey> {
RESERVED_ACCOUNTS
.iter()
.map(|reserved_key| &reserved_key.key)
}
pub fn empty_key_set() -> HashSet<Pubkey> {
HashSet::default()
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
struct ReservedAccount {
key: Pubkey,
feature_id: Option<Pubkey>,
}
impl ReservedAccount {
fn new_pending(key: Pubkey, feature_id: Pubkey) -> Self {
Self {
key,
feature_id: Some(feature_id),
}
}
fn new_active(key: Pubkey) -> Self {
Self {
key,
feature_id: None,
}
}
}
lazy_static! {
static ref RESERVED_ACCOUNTS: Vec<ReservedAccount> = [
ReservedAccount::new_pending(address_lookup_table::program::id(), feature_set::add_new_reserved_account_keys::id()),
ReservedAccount::new_active(bpf_loader::id()),
ReservedAccount::new_active(bpf_loader_deprecated::id()),
ReservedAccount::new_active(bpf_loader_upgradeable::id()),
ReservedAccount::new_pending(compute_budget::id(), feature_set::add_new_reserved_account_keys::id()),
ReservedAccount::new_active(config::program::id()),
ReservedAccount::new_pending(ed25519_program::id(), feature_set::add_new_reserved_account_keys::id()),
ReservedAccount::new_active(feature::id()),
ReservedAccount::new_pending(loader_v4::id(), feature_set::add_new_reserved_account_keys::id()),
ReservedAccount::new_pending(secp256k1_program::id(), feature_set::add_new_reserved_account_keys::id()),
ReservedAccount::new_pending(secp256r1_program::id(), feature_set::enable_secp256r1_precompile::id()),
#[allow(deprecated)]
ReservedAccount::new_active(stake::config::id()),
ReservedAccount::new_active(stake::program::id()),
ReservedAccount::new_active(system_program::id()),
ReservedAccount::new_active(vote::program::id()),
ReservedAccount::new_pending(zk_elgamal_proof_program::id(), feature_set::add_new_reserved_account_keys::id()),
ReservedAccount::new_pending(zk_token_proof_program::id(), feature_set::add_new_reserved_account_keys::id()),
ReservedAccount::new_active(sysvar::clock::id()),
ReservedAccount::new_pending(sysvar::epoch_rewards::id(), feature_set::add_new_reserved_account_keys::id()),
ReservedAccount::new_active(sysvar::epoch_schedule::id()),
#[allow(deprecated)]
ReservedAccount::new_active(sysvar::fees::id()),
ReservedAccount::new_active(sysvar::instructions::id()),
ReservedAccount::new_pending(sysvar::last_restart_slot::id(), feature_set::add_new_reserved_account_keys::id()),
#[allow(deprecated)]
ReservedAccount::new_active(sysvar::recent_blockhashes::id()),
ReservedAccount::new_active(sysvar::rent::id()),
ReservedAccount::new_active(sysvar::rewards::id()),
ReservedAccount::new_active(sysvar::slot_hashes::id()),
ReservedAccount::new_active(sysvar::slot_history::id()),
ReservedAccount::new_active(sysvar::stake_history::id()),
ReservedAccount::new_active(native_loader::id()),
ReservedAccount::new_pending(sysvar::id(), feature_set::add_new_reserved_account_keys::id()),
].to_vec();
}
#[cfg(test)]
mod tests {
#![allow(deprecated)]
use {
super::*,
solana_program::{message::legacy::BUILTIN_PROGRAMS_KEYS, sysvar::ALL_IDS},
};
#[test]
fn test_is_reserved() {
let feature_id = Pubkey::new_unique();
let active_reserved_account = ReservedAccount::new_active(Pubkey::new_unique());
let pending_reserved_account =
ReservedAccount::new_pending(Pubkey::new_unique(), feature_id);
let reserved_account_keys =
ReservedAccountKeys::new(&[active_reserved_account, pending_reserved_account]);
assert!(
reserved_account_keys.is_reserved(&active_reserved_account.key),
"active reserved accounts should be inserted into the active set"
);
assert!(
!reserved_account_keys.is_reserved(&pending_reserved_account.key),
"pending reserved accounts should NOT be inserted into the active set"
);
}
#[test]
fn test_update_active_set() {
let feature_ids = [Pubkey::new_unique(), Pubkey::new_unique()];
let active_reserved_key = Pubkey::new_unique();
let pending_reserved_keys = [Pubkey::new_unique(), Pubkey::new_unique()];
let reserved_accounts = vec![
ReservedAccount::new_active(active_reserved_key),
ReservedAccount::new_pending(pending_reserved_keys[0], feature_ids[0]),
ReservedAccount::new_pending(pending_reserved_keys[1], feature_ids[1]),
];
let mut reserved_account_keys = ReservedAccountKeys::new(&reserved_accounts);
assert!(reserved_account_keys.is_reserved(&active_reserved_key));
assert!(!reserved_account_keys.is_reserved(&pending_reserved_keys[0]));
assert!(!reserved_account_keys.is_reserved(&pending_reserved_keys[1]));
let previous_reserved_account_keys = reserved_account_keys.clone();
let mut feature_set = FeatureSet::default();
reserved_account_keys.update_active_set(&feature_set);
assert_eq!(reserved_account_keys, previous_reserved_account_keys);
feature_set.active.insert(feature_ids[0], 0);
reserved_account_keys.update_active_set(&feature_set);
assert!(reserved_account_keys.is_reserved(&active_reserved_key));
assert!(reserved_account_keys.is_reserved(&pending_reserved_keys[0]));
assert!(!reserved_account_keys.is_reserved(&pending_reserved_keys[1]));
feature_set.active.insert(feature_ids[1], 0);
reserved_account_keys.update_active_set(&feature_set);
assert!(reserved_account_keys.is_reserved(&active_reserved_key));
assert!(reserved_account_keys.is_reserved(&pending_reserved_keys[0]));
assert!(reserved_account_keys.is_reserved(&pending_reserved_keys[1]));
}
#[test]
fn test_static_list_compat() {
let mut static_set = HashSet::new();
static_set.extend(ALL_IDS.iter().cloned());
static_set.extend(BUILTIN_PROGRAMS_KEYS.iter().cloned());
let initial_active_set = ReservedAccountKeys::default().active;
assert_eq!(initial_active_set, static_set);
}
}