1#![cfg(feature = "full")]
6
7use {
8 crate::{
9 address_lookup_table, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
10 compute_budget, config, ed25519_program, feature, loader_v4, native_loader, pubkey::Pubkey,
11 secp256k1_program, stake, system_program, sysvar, vote,
12 },
13 lazy_static::lazy_static,
14 solana_feature_set::{self as feature_set, FeatureSet},
15 solana_secp256r1_program as secp256r1_program,
16 std::collections::{HashMap, HashSet},
17};
18
19mod zk_token_proof_program {
21 solana_sdk::declare_id!("ZkTokenProof1111111111111111111111111111111");
22}
23
24mod zk_elgamal_proof_program {
26 solana_sdk::declare_id!("ZkE1Gama1Proof11111111111111111111111111111");
27}
28
29#[cfg(feature = "frozen-abi")]
32impl ::solana_frozen_abi::abi_example::AbiExample for ReservedAccountKeys {
33 fn example() -> Self {
34 ReservedAccountKeys::default()
36 }
37}
38
39#[derive(Debug, Clone, PartialEq)]
43pub struct ReservedAccountKeys {
44 pub active: HashSet<Pubkey>,
46 inactive: HashMap<Pubkey, Pubkey>,
49}
50
51impl Default for ReservedAccountKeys {
52 fn default() -> Self {
53 Self::new(&RESERVED_ACCOUNTS)
54 }
55}
56
57impl ReservedAccountKeys {
58 fn new(reserved_accounts: &[ReservedAccount]) -> Self {
64 Self {
65 active: reserved_accounts
66 .iter()
67 .filter(|reserved| reserved.feature_id.is_none())
68 .map(|reserved| reserved.key)
69 .collect(),
70 inactive: reserved_accounts
71 .iter()
72 .filter_map(|ReservedAccount { key, feature_id }| {
73 feature_id.as_ref().map(|feature_id| (*key, *feature_id))
74 })
75 .collect(),
76 }
77 }
78
79 pub fn new_all_activated() -> Self {
83 Self {
84 active: Self::all_keys_iter().copied().collect(),
85 inactive: HashMap::default(),
86 }
87 }
88
89 pub fn is_reserved(&self, key: &Pubkey) -> bool {
91 self.active.contains(key)
92 }
93
94 pub fn update_active_set(&mut self, feature_set: &FeatureSet) {
97 self.inactive.retain(|reserved_key, feature_id| {
98 if feature_set.is_active(feature_id) {
99 self.active.insert(*reserved_key);
100 false
101 } else {
102 true
103 }
104 });
105 }
106
107 pub fn all_keys_iter() -> impl Iterator<Item = &'static Pubkey> {
111 RESERVED_ACCOUNTS
112 .iter()
113 .map(|reserved_key| &reserved_key.key)
114 }
115
116 pub fn empty_key_set() -> HashSet<Pubkey> {
119 HashSet::default()
120 }
121}
122
123#[derive(Debug, Clone, Copy, Eq, PartialEq)]
127struct ReservedAccount {
128 key: Pubkey,
129 feature_id: Option<Pubkey>,
130}
131
132impl ReservedAccount {
133 fn new_pending(key: Pubkey, feature_id: Pubkey) -> Self {
134 Self {
135 key,
136 feature_id: Some(feature_id),
137 }
138 }
139
140 fn new_active(key: Pubkey) -> Self {
141 Self {
142 key,
143 feature_id: None,
144 }
145 }
146}
147
148lazy_static! {
152 static ref RESERVED_ACCOUNTS: Vec<ReservedAccount> = [
153 ReservedAccount::new_pending(address_lookup_table::program::id(), feature_set::add_new_reserved_account_keys::id()),
155 ReservedAccount::new_active(bpf_loader::id()),
156 ReservedAccount::new_active(bpf_loader_deprecated::id()),
157 ReservedAccount::new_active(bpf_loader_upgradeable::id()),
158 ReservedAccount::new_pending(compute_budget::id(), feature_set::add_new_reserved_account_keys::id()),
159 ReservedAccount::new_active(config::program::id()),
160 ReservedAccount::new_pending(ed25519_program::id(), feature_set::add_new_reserved_account_keys::id()),
161 ReservedAccount::new_active(feature::id()),
162 ReservedAccount::new_pending(loader_v4::id(), feature_set::add_new_reserved_account_keys::id()),
163 ReservedAccount::new_pending(secp256k1_program::id(), feature_set::add_new_reserved_account_keys::id()),
164 ReservedAccount::new_pending(secp256r1_program::id(), feature_set::enable_secp256r1_precompile::id()),
165 #[allow(deprecated)]
166 ReservedAccount::new_active(stake::config::id()),
167 ReservedAccount::new_active(stake::program::id()),
168 ReservedAccount::new_active(system_program::id()),
169 ReservedAccount::new_active(vote::program::id()),
170 ReservedAccount::new_pending(zk_elgamal_proof_program::id(), feature_set::add_new_reserved_account_keys::id()),
171 ReservedAccount::new_pending(zk_token_proof_program::id(), feature_set::add_new_reserved_account_keys::id()),
172
173 ReservedAccount::new_active(sysvar::clock::id()),
175 ReservedAccount::new_pending(sysvar::epoch_rewards::id(), feature_set::add_new_reserved_account_keys::id()),
176 ReservedAccount::new_active(sysvar::epoch_schedule::id()),
177 #[allow(deprecated)]
178 ReservedAccount::new_active(sysvar::fees::id()),
179 ReservedAccount::new_active(sysvar::instructions::id()),
180 ReservedAccount::new_pending(sysvar::last_restart_slot::id(), feature_set::add_new_reserved_account_keys::id()),
181 #[allow(deprecated)]
182 ReservedAccount::new_active(sysvar::recent_blockhashes::id()),
183 ReservedAccount::new_active(sysvar::rent::id()),
184 ReservedAccount::new_active(sysvar::rewards::id()),
185 ReservedAccount::new_active(sysvar::slot_hashes::id()),
186 ReservedAccount::new_active(sysvar::slot_history::id()),
187 ReservedAccount::new_active(sysvar::stake_history::id()),
188
189 ReservedAccount::new_active(native_loader::id()),
191 ReservedAccount::new_pending(sysvar::id(), feature_set::add_new_reserved_account_keys::id()),
192 ].to_vec();
193}
194
195#[cfg(test)]
196mod tests {
197 #![allow(deprecated)]
198 use {
199 super::*,
200 solana_program::{message::legacy::BUILTIN_PROGRAMS_KEYS, sysvar::ALL_IDS},
201 };
202
203 #[test]
204 fn test_is_reserved() {
205 let feature_id = Pubkey::new_unique();
206 let active_reserved_account = ReservedAccount::new_active(Pubkey::new_unique());
207 let pending_reserved_account =
208 ReservedAccount::new_pending(Pubkey::new_unique(), feature_id);
209 let reserved_account_keys =
210 ReservedAccountKeys::new(&[active_reserved_account, pending_reserved_account]);
211
212 assert!(
213 reserved_account_keys.is_reserved(&active_reserved_account.key),
214 "active reserved accounts should be inserted into the active set"
215 );
216 assert!(
217 !reserved_account_keys.is_reserved(&pending_reserved_account.key),
218 "pending reserved accounts should NOT be inserted into the active set"
219 );
220 }
221
222 #[test]
223 fn test_update_active_set() {
224 let feature_ids = [Pubkey::new_unique(), Pubkey::new_unique()];
225 let active_reserved_key = Pubkey::new_unique();
226 let pending_reserved_keys = [Pubkey::new_unique(), Pubkey::new_unique()];
227 let reserved_accounts = vec![
228 ReservedAccount::new_active(active_reserved_key),
229 ReservedAccount::new_pending(pending_reserved_keys[0], feature_ids[0]),
230 ReservedAccount::new_pending(pending_reserved_keys[1], feature_ids[1]),
231 ];
232
233 let mut reserved_account_keys = ReservedAccountKeys::new(&reserved_accounts);
234 assert!(reserved_account_keys.is_reserved(&active_reserved_key));
235 assert!(!reserved_account_keys.is_reserved(&pending_reserved_keys[0]));
236 assert!(!reserved_account_keys.is_reserved(&pending_reserved_keys[1]));
237
238 let previous_reserved_account_keys = reserved_account_keys.clone();
240 let mut feature_set = FeatureSet::default();
241 reserved_account_keys.update_active_set(&feature_set);
242 assert_eq!(reserved_account_keys, previous_reserved_account_keys);
243
244 feature_set.active.insert(feature_ids[0], 0);
247 reserved_account_keys.update_active_set(&feature_set);
248
249 assert!(reserved_account_keys.is_reserved(&active_reserved_key));
250 assert!(reserved_account_keys.is_reserved(&pending_reserved_keys[0]));
251 assert!(!reserved_account_keys.is_reserved(&pending_reserved_keys[1]));
252
253 feature_set.active.insert(feature_ids[1], 0);
256 reserved_account_keys.update_active_set(&feature_set);
257
258 assert!(reserved_account_keys.is_reserved(&active_reserved_key));
259 assert!(reserved_account_keys.is_reserved(&pending_reserved_keys[0]));
260 assert!(reserved_account_keys.is_reserved(&pending_reserved_keys[1]));
261 }
262
263 #[test]
264 fn test_static_list_compat() {
265 let mut static_set = HashSet::new();
266 static_set.extend(ALL_IDS.iter().cloned());
267 static_set.extend(BUILTIN_PROGRAMS_KEYS.iter().cloned());
268
269 let initial_active_set = ReservedAccountKeys::default().active;
270
271 assert_eq!(initial_active_set, static_set);
272 }
273}