agave_reserved_account_keys/
lib.rs1#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
5#![cfg_attr(docsrs, feature(doc_auto_cfg))]
6use {
7 agave_feature_set::{self as feature_set, FeatureSet},
8 lazy_static::lazy_static,
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, feature, loader_v4, native_loader,
13 secp256k1_program, secp256r1_program, stake, system_program, sysvar, vote,
14 zk_elgamal_proof_program, zk_token_proof_program,
15 },
16 std::collections::{HashMap, HashSet},
17};
18
19#[cfg(feature = "frozen-abi")]
22impl ::solana_frozen_abi::abi_example::AbiExample for ReservedAccountKeys {
23 fn example() -> Self {
24 ReservedAccountKeys::default()
26 }
27}
28
29#[derive(Debug, Clone, PartialEq)]
33pub struct ReservedAccountKeys {
34 pub active: HashSet<Pubkey>,
36 inactive: HashMap<Pubkey, Pubkey>,
39}
40
41impl Default for ReservedAccountKeys {
42 fn default() -> Self {
43 Self::new(&RESERVED_ACCOUNTS)
44 }
45}
46
47impl ReservedAccountKeys {
48 fn new(reserved_accounts: &[ReservedAccount]) -> Self {
54 Self {
55 active: reserved_accounts
56 .iter()
57 .filter(|reserved| reserved.feature_id.is_none())
58 .map(|reserved| reserved.key)
59 .collect(),
60 inactive: reserved_accounts
61 .iter()
62 .filter_map(|ReservedAccount { key, feature_id }| {
63 feature_id.as_ref().map(|feature_id| (*key, *feature_id))
64 })
65 .collect(),
66 }
67 }
68
69 pub fn new_all_activated() -> Self {
73 Self {
74 active: Self::all_keys_iter().copied().collect(),
75 inactive: HashMap::default(),
76 }
77 }
78
79 pub fn is_reserved(&self, key: &Pubkey) -> bool {
81 self.active.contains(key)
82 }
83
84 pub fn update_active_set(&mut self, feature_set: &FeatureSet) {
87 self.inactive.retain(|reserved_key, feature_id| {
88 if feature_set.is_active(feature_id) {
89 self.active.insert(*reserved_key);
90 false
91 } else {
92 true
93 }
94 });
95 }
96
97 pub fn all_keys_iter() -> impl Iterator<Item = &'static Pubkey> {
101 RESERVED_ACCOUNTS
102 .iter()
103 .map(|reserved_key| &reserved_key.key)
104 }
105
106 pub fn empty_key_set() -> HashSet<Pubkey> {
109 HashSet::default()
110 }
111}
112
113#[derive(Debug, Clone, Copy, Eq, PartialEq)]
117struct ReservedAccount {
118 key: Pubkey,
119 feature_id: Option<Pubkey>,
120}
121
122impl ReservedAccount {
123 fn new_pending(key: Pubkey, feature_id: Pubkey) -> Self {
124 Self {
125 key,
126 feature_id: Some(feature_id),
127 }
128 }
129
130 fn new_active(key: Pubkey) -> Self {
131 Self {
132 key,
133 feature_id: None,
134 }
135 }
136}
137
138lazy_static! {
142 static ref RESERVED_ACCOUNTS: Vec<ReservedAccount> = [
143 ReservedAccount::new_pending(address_lookup_table::id(), feature_set::add_new_reserved_account_keys::id()),
145 ReservedAccount::new_active(bpf_loader::id()),
146 ReservedAccount::new_active(bpf_loader_deprecated::id()),
147 ReservedAccount::new_active(bpf_loader_upgradeable::id()),
148 ReservedAccount::new_pending(compute_budget::id(), feature_set::add_new_reserved_account_keys::id()),
149 ReservedAccount::new_active(config::id()),
150 ReservedAccount::new_pending(ed25519_program::id(), feature_set::add_new_reserved_account_keys::id()),
151 ReservedAccount::new_active(feature::id()),
152 ReservedAccount::new_pending(loader_v4::id(), feature_set::add_new_reserved_account_keys::id()),
153 ReservedAccount::new_pending(secp256k1_program::id(), feature_set::add_new_reserved_account_keys::id()),
154 ReservedAccount::new_pending(secp256r1_program::id(), feature_set::enable_secp256r1_precompile::id()),
155 #[allow(deprecated)]
156 ReservedAccount::new_active(stake::config::id()),
157 ReservedAccount::new_active(stake::id()),
158 ReservedAccount::new_active(system_program::id()),
159 ReservedAccount::new_active(vote::id()),
160 ReservedAccount::new_pending(zk_elgamal_proof_program::id(), feature_set::add_new_reserved_account_keys::id()),
161 ReservedAccount::new_pending(zk_token_proof_program::id(), feature_set::add_new_reserved_account_keys::id()),
162
163 ReservedAccount::new_active(sysvar::clock::id()),
165 ReservedAccount::new_pending(sysvar::epoch_rewards::id(), feature_set::add_new_reserved_account_keys::id()),
166 ReservedAccount::new_active(sysvar::epoch_schedule::id()),
167 #[allow(deprecated)]
168 ReservedAccount::new_active(sysvar::fees::id()),
169 ReservedAccount::new_active(sysvar::instructions::id()),
170 ReservedAccount::new_pending(sysvar::last_restart_slot::id(), feature_set::add_new_reserved_account_keys::id()),
171 #[allow(deprecated)]
172 ReservedAccount::new_active(sysvar::recent_blockhashes::id()),
173 ReservedAccount::new_active(sysvar::rent::id()),
174 ReservedAccount::new_active(sysvar::rewards::id()),
175 ReservedAccount::new_active(sysvar::slot_hashes::id()),
176 ReservedAccount::new_active(sysvar::slot_history::id()),
177 ReservedAccount::new_active(sysvar::stake_history::id()),
178
179 ReservedAccount::new_active(native_loader::id()),
181 ReservedAccount::new_pending(sysvar::id(), feature_set::add_new_reserved_account_keys::id()),
182 ].to_vec();
183}
184
185#[cfg(test)]
186mod tests {
187 #![allow(deprecated)]
188 use {super::*, solana_message::legacy::BUILTIN_PROGRAMS_KEYS, solana_sysvar::ALL_IDS};
189
190 #[test]
191 fn test_is_reserved() {
192 let feature_id = Pubkey::new_unique();
193 let active_reserved_account = ReservedAccount::new_active(Pubkey::new_unique());
194 let pending_reserved_account =
195 ReservedAccount::new_pending(Pubkey::new_unique(), feature_id);
196 let reserved_account_keys =
197 ReservedAccountKeys::new(&[active_reserved_account, pending_reserved_account]);
198
199 assert!(
200 reserved_account_keys.is_reserved(&active_reserved_account.key),
201 "active reserved accounts should be inserted into the active set"
202 );
203 assert!(
204 !reserved_account_keys.is_reserved(&pending_reserved_account.key),
205 "pending reserved accounts should NOT be inserted into the active set"
206 );
207 }
208
209 #[test]
210 fn test_update_active_set() {
211 let feature_ids = [Pubkey::new_unique(), Pubkey::new_unique()];
212 let active_reserved_key = Pubkey::new_unique();
213 let pending_reserved_keys = [Pubkey::new_unique(), Pubkey::new_unique()];
214 let reserved_accounts = vec![
215 ReservedAccount::new_active(active_reserved_key),
216 ReservedAccount::new_pending(pending_reserved_keys[0], feature_ids[0]),
217 ReservedAccount::new_pending(pending_reserved_keys[1], feature_ids[1]),
218 ];
219
220 let mut reserved_account_keys = ReservedAccountKeys::new(&reserved_accounts);
221 assert!(reserved_account_keys.is_reserved(&active_reserved_key));
222 assert!(!reserved_account_keys.is_reserved(&pending_reserved_keys[0]));
223 assert!(!reserved_account_keys.is_reserved(&pending_reserved_keys[1]));
224
225 let previous_reserved_account_keys = reserved_account_keys.clone();
227 let mut feature_set = FeatureSet::default();
228 reserved_account_keys.update_active_set(&feature_set);
229 assert_eq!(reserved_account_keys, previous_reserved_account_keys);
230
231 feature_set.active_mut().insert(feature_ids[0], 0);
234 reserved_account_keys.update_active_set(&feature_set);
235
236 assert!(reserved_account_keys.is_reserved(&active_reserved_key));
237 assert!(reserved_account_keys.is_reserved(&pending_reserved_keys[0]));
238 assert!(!reserved_account_keys.is_reserved(&pending_reserved_keys[1]));
239
240 feature_set.active_mut().insert(feature_ids[1], 0);
243 reserved_account_keys.update_active_set(&feature_set);
244
245 assert!(reserved_account_keys.is_reserved(&active_reserved_key));
246 assert!(reserved_account_keys.is_reserved(&pending_reserved_keys[0]));
247 assert!(reserved_account_keys.is_reserved(&pending_reserved_keys[1]));
248 }
249
250 #[test]
251 fn test_static_list_compat() {
252 let mut static_set = HashSet::new();
253 static_set.extend(ALL_IDS.iter().cloned());
254 static_set.extend(BUILTIN_PROGRAMS_KEYS.iter().cloned());
255
256 let initial_active_set = ReservedAccountKeys::default().active;
257
258 assert_eq!(initial_active_set, static_set);
259 }
260}