1#![allow(clippy::arithmetic_side_effects)]
2use {
3 agave_feature_set::{
4 alpenglow, increase_cpi_account_info_limit, raise_cpi_nesting_limit_to_8, FeatureSet,
5 FEATURE_NAMES,
6 },
7 agave_snapshots::{
8 paths::BANK_SNAPSHOTS_DIR, snapshot_config::SnapshotConfig, SnapshotInterval,
9 },
10 base64::{prelude::BASE64_STANDARD, Engine},
11 crossbeam_channel::Receiver,
12 log::*,
13 solana_account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
14 solana_accounts_db::{
15 accounts_db::AccountsDbConfig, accounts_index::AccountsIndexConfig,
16 utils::create_accounts_run_and_snapshot_dirs,
17 },
18 solana_cli_output::CliAccount,
19 solana_clock::{Slot, DEFAULT_MS_PER_SLOT},
20 solana_commitment_config::CommitmentConfig,
21 solana_compute_budget::compute_budget::ComputeBudget,
22 solana_core::{
23 admin_rpc_post_init::AdminRpcRequestMetadataPostInit,
24 consensus::tower_storage::TowerStorage,
25 validator::{Validator, ValidatorConfig, ValidatorStartProgress, ValidatorTpuConfig},
26 },
27 solana_epoch_schedule::EpochSchedule,
28 solana_fee_calculator::FeeRateGovernor,
29 solana_genesis_utils::MAX_GENESIS_ARCHIVE_UNPACKED_SIZE,
30 solana_geyser_plugin_manager::{
31 geyser_plugin_manager::GeyserPluginManager, GeyserPluginManagerRequest,
32 },
33 solana_gossip::{
34 cluster_info::{ClusterInfo, NodeConfig},
35 contact_info::Protocol,
36 node::Node,
37 },
38 solana_inflation::Inflation,
39 solana_instruction::{AccountMeta, Instruction},
40 solana_keypair::{read_keypair_file, write_keypair_file, Keypair},
41 solana_ledger::{
42 blockstore::create_new_ledger, blockstore_options::LedgerColumnOptions,
43 create_new_tmp_ledger,
44 },
45 solana_loader_v3_interface::state::UpgradeableLoaderState,
46 solana_message::Message,
47 solana_native_token::LAMPORTS_PER_SOL,
48 solana_net_utils::{find_available_ports_in_range, multihomed_sockets::BindIpAddrs, PortRange},
49 solana_pubkey::Pubkey,
50 solana_rent::Rent,
51 solana_rpc::{rpc::JsonRpcConfig, rpc_pubsub_service::PubSubConfig},
52 solana_rpc_client::{nonblocking, rpc_client::RpcClient},
53 solana_rpc_client_api::request::MAX_MULTIPLE_ACCOUNTS,
54 solana_runtime::{
55 bank_forks::BankForks,
56 genesis_utils::{self, create_genesis_config_with_leader_ex_no_features},
57 runtime_config::RuntimeConfig,
58 },
59 solana_sdk_ids::address_lookup_table,
60 solana_signer::Signer,
61 solana_streamer::{quic::DEFAULT_QUIC_ENDPOINTS, socket::SocketAddrSpace},
62 solana_tpu_client::tpu_client::DEFAULT_TPU_ENABLE_UDP,
63 solana_transaction::Transaction,
64 solana_validator_exit::Exit,
65 std::{
66 collections::{HashMap, HashSet},
67 ffi::OsStr,
68 fmt::Display,
69 fs::{self, remove_dir_all, File},
70 io::Read,
71 net::{IpAddr, Ipv4Addr, SocketAddr},
72 num::{NonZero, NonZeroU64},
73 path::{Path, PathBuf},
74 str::FromStr,
75 sync::{Arc, RwLock},
76 time::Duration,
77 },
78 tokio::time::sleep,
79};
80
81#[derive(Clone)]
82pub struct AccountInfo<'a> {
83 pub address: Option<Pubkey>,
84 pub filename: &'a str,
85}
86
87#[derive(Clone)]
88pub struct UpgradeableProgramInfo {
89 pub program_id: Pubkey,
90 pub loader: Pubkey,
91 pub upgrade_authority: Pubkey,
92 pub program_path: PathBuf,
93}
94
95#[derive(Debug)]
96pub struct TestValidatorNodeConfig {
97 gossip_addr: SocketAddr,
98 port_range: PortRange,
99 bind_ip_addr: IpAddr,
100}
101
102impl Default for TestValidatorNodeConfig {
103 fn default() -> Self {
104 let bind_ip_addr = IpAddr::V4(Ipv4Addr::LOCALHOST);
105 #[cfg(not(debug_assertions))]
106 let port_range = solana_net_utils::VALIDATOR_PORT_RANGE;
107 #[cfg(debug_assertions)]
108 let port_range = solana_net_utils::sockets::localhost_port_range_for_tests();
109 Self {
110 gossip_addr: SocketAddr::new(bind_ip_addr, port_range.0),
111 port_range,
112 bind_ip_addr,
113 }
114 }
115}
116
117pub struct TestValidatorGenesis {
118 fee_rate_governor: FeeRateGovernor,
119 ledger_path: Option<PathBuf>,
120 tower_storage: Option<Arc<dyn TowerStorage>>,
121 pub rent: Rent,
122 rpc_config: JsonRpcConfig,
123 pubsub_config: PubSubConfig,
124 rpc_ports: Option<(u16, u16)>, warp_slot: Option<Slot>,
126 accounts: HashMap<Pubkey, AccountSharedData>,
127 upgradeable_programs: Vec<UpgradeableProgramInfo>,
128 ticks_per_slot: Option<u64>,
129 epoch_schedule: Option<EpochSchedule>,
130 inflation: Option<Inflation>,
131 node_config: TestValidatorNodeConfig,
132 pub validator_exit: Arc<RwLock<Exit>>,
133 pub start_progress: Arc<RwLock<ValidatorStartProgress>>,
134 pub authorized_voter_keypairs: Arc<RwLock<Vec<Arc<Keypair>>>>,
135 pub staked_nodes_overrides: Arc<RwLock<HashMap<Pubkey, u64>>>,
136 pub max_ledger_shreds: Option<u64>,
137 pub max_genesis_archive_unpacked_size: Option<u64>,
138 pub geyser_plugin_config_files: Option<Vec<PathBuf>>,
139 deactivate_feature_set: HashSet<Pubkey>,
140 compute_unit_limit: Option<u64>,
141 pub log_messages_bytes_limit: Option<usize>,
142 pub transaction_account_lock_limit: Option<usize>,
143 pub tpu_enable_udp: bool,
144 pub geyser_plugin_manager: Arc<RwLock<GeyserPluginManager>>,
145 admin_rpc_service_post_init: Arc<RwLock<Option<AdminRpcRequestMetadataPostInit>>>,
146}
147
148impl Default for TestValidatorGenesis {
149 fn default() -> Self {
150 let deactivate_feature_set = [alpenglow::id()].into_iter().collect();
152 Self {
153 fee_rate_governor: FeeRateGovernor::default(),
154 ledger_path: Option::<PathBuf>::default(),
155 tower_storage: Option::<Arc<dyn TowerStorage>>::default(),
156 rent: Rent::default(),
157 rpc_config: JsonRpcConfig::default_for_test(),
158 pubsub_config: PubSubConfig::default(),
159 rpc_ports: Option::<(u16, u16)>::default(),
160 warp_slot: Option::<Slot>::default(),
161 accounts: HashMap::<Pubkey, AccountSharedData>::default(),
162 upgradeable_programs: Vec::<UpgradeableProgramInfo>::default(),
163 ticks_per_slot: Option::<u64>::default(),
164 epoch_schedule: Option::<EpochSchedule>::default(),
165 inflation: Option::<Inflation>::default(),
166 node_config: TestValidatorNodeConfig::default(),
167 validator_exit: Arc::<RwLock<Exit>>::default(),
168 start_progress: Arc::<RwLock<ValidatorStartProgress>>::default(),
169 authorized_voter_keypairs: Arc::<RwLock<Vec<Arc<Keypair>>>>::default(),
170 staked_nodes_overrides: Arc::new(RwLock::new(HashMap::new())),
171 max_ledger_shreds: Option::<u64>::default(),
172 max_genesis_archive_unpacked_size: Option::<u64>::default(),
173 geyser_plugin_config_files: Option::<Vec<PathBuf>>::default(),
174 deactivate_feature_set,
175 compute_unit_limit: Option::<u64>::default(),
176 log_messages_bytes_limit: Option::<usize>::default(),
177 transaction_account_lock_limit: Option::<usize>::default(),
178 tpu_enable_udp: DEFAULT_TPU_ENABLE_UDP,
179 geyser_plugin_manager: Arc::new(RwLock::new(GeyserPluginManager::default())),
180 admin_rpc_service_post_init:
181 Arc::<RwLock<Option<AdminRpcRequestMetadataPostInit>>>::default(),
182 }
183 }
184}
185
186fn try_transform_program_data(
187 address: &Pubkey,
188 account: &mut AccountSharedData,
189) -> Result<(), String> {
190 if account.owner() == &solana_sdk_ids::bpf_loader_upgradeable::id() {
191 let programdata_offset = UpgradeableLoaderState::size_of_programdata_metadata();
192 let programdata_meta = account.data().get(0..programdata_offset).ok_or(format!(
193 "Failed to get upgradeable programdata data from {address}"
194 ))?;
195 if let Ok(UpgradeableLoaderState::ProgramData {
198 upgrade_authority_address,
199 ..
200 }) = bincode::deserialize::<UpgradeableLoaderState>(programdata_meta)
201 {
202 bincode::serialize_into(
205 account.data_as_mut_slice(),
206 &UpgradeableLoaderState::ProgramData {
207 slot: 0,
208 upgrade_authority_address,
209 },
210 )
211 .map_err(|_| format!("Failed to write to upgradeable programdata account {address}"))
212 } else {
213 Err(format!(
214 "Failed to read upgradeable programdata account {address}"
215 ))
216 }
217 } else {
218 Err(format!("Account {address} not owned by upgradeable loader"))
219 }
220}
221
222impl TestValidatorGenesis {
223 pub fn deactivate_features(&mut self, deactivate_list: &[Pubkey]) -> &mut Self {
227 self.deactivate_feature_set.extend(deactivate_list);
228 self
229 }
230 pub fn ledger_path<P: Into<PathBuf>>(&mut self, ledger_path: P) -> &mut Self {
231 self.ledger_path = Some(ledger_path.into());
232 self
233 }
234
235 pub fn tower_storage(&mut self, tower_storage: Arc<dyn TowerStorage>) -> &mut Self {
236 self.tower_storage = Some(tower_storage);
237 self
238 }
239
240 pub fn ledger_exists(ledger_path: &Path) -> bool {
242 ledger_path.join("vote-account-keypair.json").exists()
243 }
244
245 pub fn tpu_enable_udp(&mut self, tpu_enable_udp: bool) -> &mut Self {
246 self.tpu_enable_udp = tpu_enable_udp;
247 self
248 }
249
250 pub fn fee_rate_governor(&mut self, fee_rate_governor: FeeRateGovernor) -> &mut Self {
251 self.fee_rate_governor = fee_rate_governor;
252 self
253 }
254
255 pub fn ticks_per_slot(&mut self, ticks_per_slot: u64) -> &mut Self {
256 self.ticks_per_slot = Some(ticks_per_slot);
257 self
258 }
259
260 pub fn epoch_schedule(&mut self, epoch_schedule: EpochSchedule) -> &mut Self {
261 self.epoch_schedule = Some(epoch_schedule);
262 self
263 }
264
265 pub fn inflation(&mut self, inflation: Inflation) -> &mut Self {
266 self.inflation = Some(inflation);
267 self
268 }
269
270 pub fn rent(&mut self, rent: Rent) -> &mut Self {
271 self.rent = rent;
272 self
273 }
274
275 pub fn rpc_config(&mut self, rpc_config: JsonRpcConfig) -> &mut Self {
276 self.rpc_config = rpc_config;
277 self
278 }
279
280 pub fn pubsub_config(&mut self, pubsub_config: PubSubConfig) -> &mut Self {
281 self.pubsub_config = pubsub_config;
282 self
283 }
284
285 pub fn rpc_port(&mut self, rpc_port: u16) -> &mut Self {
286 self.rpc_ports = Some((rpc_port, rpc_port + 1));
287 self
288 }
289
290 pub fn faucet_addr(&mut self, faucet_addr: Option<SocketAddr>) -> &mut Self {
291 self.rpc_config.faucet_addr = faucet_addr;
292 self
293 }
294
295 pub fn warp_slot(&mut self, warp_slot: Slot) -> &mut Self {
296 self.warp_slot = Some(warp_slot);
297 self
298 }
299
300 pub fn gossip_host(&mut self, gossip_host: IpAddr) -> &mut Self {
301 self.node_config.gossip_addr.set_ip(gossip_host);
302 self
303 }
304
305 pub fn gossip_port(&mut self, gossip_port: u16) -> &mut Self {
306 self.node_config.gossip_addr.set_port(gossip_port);
307 self
308 }
309
310 pub fn port_range(&mut self, port_range: PortRange) -> &mut Self {
311 self.node_config.port_range = port_range;
312 self
313 }
314
315 pub fn bind_ip_addr(&mut self, bind_ip_addr: IpAddr) -> &mut Self {
316 self.node_config.bind_ip_addr = bind_ip_addr;
317 self
318 }
319
320 pub fn compute_unit_limit(&mut self, compute_unit_limit: u64) -> &mut Self {
321 self.compute_unit_limit = Some(compute_unit_limit);
322 self
323 }
324
325 pub fn add_account(&mut self, address: Pubkey, account: AccountSharedData) -> &mut Self {
327 self.accounts.insert(address, account);
328 self
329 }
330
331 pub fn add_accounts<T>(&mut self, accounts: T) -> &mut Self
332 where
333 T: IntoIterator<Item = (Pubkey, AccountSharedData)>,
334 {
335 for (address, account) in accounts {
336 self.add_account(address, account);
337 }
338 self
339 }
340
341 fn clone_accounts_and_transform<T, F>(
342 &mut self,
343 addresses: T,
344 rpc_client: &RpcClient,
345 skip_missing: bool,
346 transform: F,
347 ) -> Result<&mut Self, String>
348 where
349 T: IntoIterator<Item = Pubkey>,
350 F: Fn(&Pubkey, Account) -> Result<AccountSharedData, String>,
351 {
352 let addresses: Vec<Pubkey> = addresses.into_iter().collect();
353 for chunk in addresses.chunks(MAX_MULTIPLE_ACCOUNTS) {
354 info!("Fetching {chunk:?} over RPC...");
355 let responses = rpc_client
356 .get_multiple_accounts(chunk)
357 .map_err(|err| format!("Failed to fetch: {err}"))?;
358 for (address, res) in chunk.iter().zip(responses) {
359 if let Some(account) = res {
360 self.add_account(*address, transform(address, account)?);
361 } else if skip_missing {
362 warn!("Could not find {address}, skipping.");
363 } else {
364 return Err(format!("Failed to fetch {address}"));
365 }
366 }
367 }
368 Ok(self)
369 }
370
371 pub fn clone_accounts<T>(
372 &mut self,
373 addresses: T,
374 rpc_client: &RpcClient,
375 skip_missing: bool,
376 ) -> Result<&mut Self, String>
377 where
378 T: IntoIterator<Item = Pubkey>,
379 {
380 self.clone_accounts_and_transform(
381 addresses,
382 rpc_client,
383 skip_missing,
384 |address, account| {
385 let mut account_shared_data = AccountSharedData::from(account);
386 try_transform_program_data(address, &mut account_shared_data).ok();
388 Ok(account_shared_data)
389 },
390 )
391 }
392
393 pub fn deep_clone_address_lookup_table_accounts<T>(
394 &mut self,
395 addresses: T,
396 rpc_client: &RpcClient,
397 ) -> Result<&mut Self, String>
398 where
399 T: IntoIterator<Item = Pubkey>,
400 {
401 const LOOKUP_TABLE_META_SIZE: usize = 56;
402 let addresses: Vec<Pubkey> = addresses.into_iter().collect();
403 let mut alt_entries: Vec<Pubkey> = Vec::new();
404
405 for chunk in addresses.chunks(MAX_MULTIPLE_ACCOUNTS) {
406 info!("Fetching {chunk:?} over RPC...");
407 let responses = rpc_client
408 .get_multiple_accounts(chunk)
409 .map_err(|err| format!("Failed to fetch: {err}"))?;
410 for (address, res) in chunk.iter().zip(responses) {
411 if let Some(account) = res {
412 if address_lookup_table::check_id(account.owner()) {
413 let raw_addresses_data = account
414 .data()
415 .get(LOOKUP_TABLE_META_SIZE..)
416 .ok_or(format!("Failed to get addresses data from {address}"))?;
417
418 if raw_addresses_data.len() % std::mem::size_of::<Pubkey>() != 0 {
419 return Err(format!("Invalid alt account data length for {address}"));
420 }
421
422 for address_slice in
423 raw_addresses_data.chunks_exact(std::mem::size_of::<Pubkey>())
424 {
425 let address = Pubkey::try_from(address_slice).unwrap();
427 alt_entries.push(address);
428 }
429 self.add_account(*address, AccountSharedData::from(account));
430 } else {
431 return Err(format!("Account {address} is not an address lookup table"));
432 }
433 } else {
434 return Err(format!("Failed to fetch {address}"));
435 }
436 }
437 }
438
439 self.clone_accounts(alt_entries, rpc_client, true)
440 }
441
442 pub fn clone_programdata_accounts<T>(
443 &mut self,
444 addresses: T,
445 rpc_client: &RpcClient,
446 skip_missing: bool,
447 ) -> Result<&mut Self, String>
448 where
449 T: IntoIterator<Item = Pubkey>,
450 {
451 self.clone_accounts_and_transform(
452 addresses,
453 rpc_client,
454 skip_missing,
455 |address, account| {
456 let mut account_shared_data = AccountSharedData::from(account);
457 try_transform_program_data(address, &mut account_shared_data)?;
458 Ok(account_shared_data)
459 },
460 )
461 }
462
463 pub fn clone_upgradeable_programs<T>(
464 &mut self,
465 addresses: T,
466 rpc_client: &RpcClient,
467 ) -> Result<&mut Self, String>
468 where
469 T: IntoIterator<Item = Pubkey>,
470 {
471 let addresses: Vec<Pubkey> = addresses.into_iter().collect();
472 self.clone_accounts(addresses.clone(), rpc_client, false)?;
473
474 let mut programdata_addresses: HashSet<Pubkey> = HashSet::new();
475 for address in addresses {
476 let account = self.accounts.get(&address).unwrap();
477
478 if let Ok(UpgradeableLoaderState::Program {
479 programdata_address,
480 }) = account.deserialize_data()
481 {
482 programdata_addresses.insert(programdata_address);
483 } else {
484 return Err(format!(
485 "Failed to read upgradeable program account {address}",
486 ));
487 }
488 }
489
490 self.clone_programdata_accounts(programdata_addresses, rpc_client, false)?;
491
492 Ok(self)
493 }
494
495 pub fn clone_feature_set(&mut self, rpc_client: &RpcClient) -> Result<&mut Self, String> {
496 for feature_ids in FEATURE_NAMES
497 .keys()
498 .cloned()
499 .collect::<Vec<Pubkey>>()
500 .chunks(MAX_MULTIPLE_ACCOUNTS)
501 {
502 rpc_client
503 .get_multiple_accounts(feature_ids)
504 .map_err(|err| format!("Failed to fetch: {err}"))?
505 .into_iter()
506 .zip(feature_ids)
507 .for_each(|(maybe_account, feature_id)| {
508 if maybe_account
509 .as_ref()
510 .and_then(solana_feature_gate_interface::from_account)
511 .and_then(|feature| feature.activated_at)
512 .is_none()
513 {
514 self.deactivate_feature_set.insert(*feature_id);
515 }
516 });
517 }
518 Ok(self)
519 }
520
521 pub fn add_accounts_from_json_files(
522 &mut self,
523 accounts: &[AccountInfo],
524 ) -> Result<&mut Self, String> {
525 for account in accounts {
526 let Some(account_path) = solana_program_test::find_file(account.filename) else {
527 return Err(format!("Unable to locate {}", account.filename));
528 };
529 let mut file = File::open(&account_path).unwrap();
530 let mut account_info_raw = String::new();
531 file.read_to_string(&mut account_info_raw).unwrap();
532
533 let result: serde_json::Result<CliAccount> = serde_json::from_str(&account_info_raw);
534 let account_info = match result {
535 Err(err) => {
536 return Err(format!(
537 "Unable to deserialize {}: {}",
538 account_path.to_str().unwrap(),
539 err
540 ));
541 }
542 Ok(deserialized) => deserialized,
543 };
544
545 let address = account.address.unwrap_or_else(|| {
546 Pubkey::from_str(account_info.keyed_account.pubkey.as_str()).unwrap()
547 });
548 let account = account_info
549 .keyed_account
550 .account
551 .decode::<AccountSharedData>()
552 .unwrap();
553
554 self.add_account(address, account);
555 }
556 Ok(self)
557 }
558
559 pub fn add_accounts_from_directories<T, P>(&mut self, dirs: T) -> Result<&mut Self, String>
560 where
561 T: IntoIterator<Item = P>,
562 P: AsRef<Path> + Display,
563 {
564 let mut json_files: HashSet<String> = HashSet::new();
565 for dir in dirs {
566 let matched_files = match fs::read_dir(&dir) {
567 Ok(dir) => dir,
568 Err(e) => return Err(format!("Cannot read directory {}: {}", &dir, e)),
569 }
570 .flatten()
571 .map(|entry| entry.path())
572 .filter(|path| path.is_file() && path.extension() == Some(OsStr::new("json")))
573 .map(|path| String::from(path.to_string_lossy()));
574
575 json_files.extend(matched_files);
576 }
577
578 debug!("account files found: {json_files:?}");
579
580 let accounts: Vec<_> = json_files
581 .iter()
582 .map(|filename| AccountInfo {
583 address: None,
584 filename,
585 })
586 .collect();
587
588 self.add_accounts_from_json_files(&accounts)?;
589
590 Ok(self)
591 }
592
593 pub fn add_account_with_file_data(
595 &mut self,
596 address: Pubkey,
597 lamports: u64,
598 owner: Pubkey,
599 filename: &str,
600 ) -> &mut Self {
601 self.add_account(
602 address,
603 AccountSharedData::from(Account {
604 lamports,
605 data: solana_program_test::read_file(
606 solana_program_test::find_file(filename).unwrap_or_else(|| {
607 panic!("Unable to locate {filename}");
608 }),
609 ),
610 owner,
611 executable: false,
612 rent_epoch: 0,
613 }),
614 )
615 }
616
617 pub fn add_account_with_base64_data(
620 &mut self,
621 address: Pubkey,
622 lamports: u64,
623 owner: Pubkey,
624 data_base64: &str,
625 ) -> &mut Self {
626 self.add_account(
627 address,
628 AccountSharedData::from(Account {
629 lamports,
630 data: BASE64_STANDARD
631 .decode(data_base64)
632 .unwrap_or_else(|err| panic!("Failed to base64 decode: {err}")),
633 owner,
634 executable: false,
635 rent_epoch: 0,
636 }),
637 )
638 }
639
640 pub fn add_program(&mut self, program_name: &str, program_id: Pubkey) -> &mut Self {
645 let program_path = solana_program_test::find_file(&format!("{program_name}.so"))
646 .unwrap_or_else(|| panic!("Unable to locate program {program_name}"));
647
648 self.upgradeable_programs.push(UpgradeableProgramInfo {
649 program_id,
650 loader: solana_sdk_ids::bpf_loader_upgradeable::id(),
651 upgrade_authority: Pubkey::default(),
652 program_path,
653 });
654 self
655 }
656
657 pub fn add_upgradeable_programs_with_path(
659 &mut self,
660 programs: &[UpgradeableProgramInfo],
661 ) -> &mut Self {
662 for program in programs {
663 self.upgradeable_programs.push(program.clone());
664 }
665 self
666 }
667
668 pub fn start_with_mint_address(
672 &self,
673 mint_address: Pubkey,
674 socket_addr_space: SocketAddrSpace,
675 ) -> Result<TestValidator, Box<dyn std::error::Error>> {
676 self.start_with_mint_address_and_geyser_plugin_rpc(mint_address, socket_addr_space, None)
677 }
678
679 pub fn start_with_mint_address_and_geyser_plugin_rpc(
684 &self,
685 mint_address: Pubkey,
686 socket_addr_space: SocketAddrSpace,
687 rpc_to_plugin_manager_receiver: Option<Receiver<GeyserPluginManagerRequest>>,
688 ) -> Result<TestValidator, Box<dyn std::error::Error>> {
689 TestValidator::start(
690 mint_address,
691 self,
692 socket_addr_space,
693 rpc_to_plugin_manager_receiver,
694 )
695 .inspect(|test_validator| {
696 let runtime = tokio::runtime::Builder::new_current_thread()
697 .enable_io()
698 .enable_time()
699 .build()
700 .unwrap();
701 runtime.block_on(test_validator.wait_for_nonzero_fees());
702 })
703 }
704
705 pub fn start(&self) -> (TestValidator, Keypair) {
712 self.start_with_socket_addr_space(SocketAddrSpace::new(true))
713 }
714
715 pub fn start_with_socket_addr_space(
722 &self,
723 socket_addr_space: SocketAddrSpace,
724 ) -> (TestValidator, Keypair) {
725 let mint_keypair = Keypair::new();
726 self.start_with_mint_address(mint_keypair.pubkey(), socket_addr_space)
727 .inspect(|test_validator| {
728 let runtime = tokio::runtime::Builder::new_current_thread()
729 .enable_io()
730 .enable_time()
731 .build()
732 .unwrap();
733 let upgradeable_program_ids: Vec<&Pubkey> = self
734 .upgradeable_programs
735 .iter()
736 .map(|p| &p.program_id)
737 .collect();
738 runtime.block_on(test_validator.wait_for_upgradeable_programs_deployed(
739 &upgradeable_program_ids,
740 &mint_keypair,
741 ));
742 })
743 .map(|test_validator| (test_validator, mint_keypair))
744 .unwrap_or_else(|err| panic!("Test validator failed to start: {err}"))
745 }
746
747 pub async fn start_async(&self) -> (TestValidator, Keypair) {
748 self.start_async_with_socket_addr_space(SocketAddrSpace::new(
749 true,
750 ))
751 .await
752 }
753
754 pub async fn start_async_with_socket_addr_space(
755 &self,
756 socket_addr_space: SocketAddrSpace,
757 ) -> (TestValidator, Keypair) {
758 let mint_keypair = Keypair::new();
759 match TestValidator::start(mint_keypair.pubkey(), self, socket_addr_space, None) {
760 Ok(test_validator) => {
761 test_validator.wait_for_nonzero_fees().await;
762 let upgradeable_program_ids: Vec<&Pubkey> = self
763 .upgradeable_programs
764 .iter()
765 .map(|p| &p.program_id)
766 .collect();
767 test_validator
768 .wait_for_upgradeable_programs_deployed(&upgradeable_program_ids, &mint_keypair)
769 .await;
770 (test_validator, mint_keypair)
771 }
772 Err(err) => panic!("Test validator failed to start: {err}"),
773 }
774 }
775}
776
777pub struct TestValidator {
778 ledger_path: PathBuf,
779 preserve_ledger: bool,
780 rpc_pubsub_url: String,
781 rpc_url: String,
782 tpu: SocketAddr,
783 gossip: SocketAddr,
784 validator: Option<Validator>,
785 vote_account_address: Pubkey,
786}
787
788impl TestValidator {
789 pub fn with_no_fees(
794 mint_address: Pubkey,
795 faucet_addr: Option<SocketAddr>,
796 socket_addr_space: SocketAddrSpace,
797 ) -> Self {
798 TestValidatorGenesis::default()
799 .fee_rate_governor(FeeRateGovernor::new(0, 0))
800 .rent(Rent {
801 lamports_per_byte_year: 1,
802 exemption_threshold: 1.0,
803 ..Rent::default()
804 })
805 .faucet_addr(faucet_addr)
806 .start_with_mint_address(mint_address, socket_addr_space)
807 .expect("validator start failed")
808 }
809
810 pub fn with_no_fees_udp(
812 mint_address: Pubkey,
813 faucet_addr: Option<SocketAddr>,
814 socket_addr_space: SocketAddrSpace,
815 ) -> Self {
816 TestValidatorGenesis::default()
817 .tpu_enable_udp(true)
818 .fee_rate_governor(FeeRateGovernor::new(0, 0))
819 .rent(Rent {
820 lamports_per_byte_year: 1,
821 exemption_threshold: 1.0,
822 ..Rent::default()
823 })
824 .faucet_addr(faucet_addr)
825 .start_with_mint_address(mint_address, socket_addr_space)
826 .expect("validator start failed")
827 }
828
829 pub fn with_custom_fees(
834 mint_address: Pubkey,
835 target_lamports_per_signature: u64,
836 faucet_addr: Option<SocketAddr>,
837 socket_addr_space: SocketAddrSpace,
838 ) -> Self {
839 TestValidatorGenesis::default()
840 .fee_rate_governor(FeeRateGovernor::new(target_lamports_per_signature, 0))
841 .rent(Rent {
842 lamports_per_byte_year: 1,
843 exemption_threshold: 1.0,
844 ..Rent::default()
845 })
846 .faucet_addr(faucet_addr)
847 .start_with_mint_address(mint_address, socket_addr_space)
848 .expect("validator start failed")
849 }
850
851 fn initialize_ledger(
858 mint_address: Pubkey,
859 config: &TestValidatorGenesis,
860 ) -> Result<PathBuf, Box<dyn std::error::Error>> {
861 let validator_identity = Keypair::new();
862 let validator_vote_account = Keypair::new();
863 let validator_stake_account = Keypair::new();
864 let validator_identity_lamports = 500 * LAMPORTS_PER_SOL;
865 let validator_stake_lamports = 1_000_000 * LAMPORTS_PER_SOL;
866 let mint_lamports = 500_000_000 * LAMPORTS_PER_SOL;
867
868 let mut feature_set = FeatureSet::default().inactive().clone();
870 for feature in &config.deactivate_feature_set {
871 if feature_set.remove(feature) {
872 info!("Feature for {feature:?} deactivated")
873 } else {
874 warn!("Feature {feature:?} set for deactivation is not a known Feature public key",)
875 }
876 }
877
878 let mut accounts = config.accounts.clone();
879 for (address, account) in solana_program_binaries::spl_programs(&config.rent) {
880 accounts.entry(address).or_insert(account);
881 }
882 for (address, account) in
883 solana_program_binaries::core_bpf_programs(&config.rent, |feature_id| {
884 feature_set.contains(feature_id)
885 })
886 {
887 accounts.entry(address).or_insert(account);
888 }
889 for upgradeable_program in &config.upgradeable_programs {
890 let data = solana_program_test::read_file(&upgradeable_program.program_path);
891 let (programdata_address, _) = Pubkey::find_program_address(
892 &[upgradeable_program.program_id.as_ref()],
893 &upgradeable_program.loader,
894 );
895 let mut program_data = bincode::serialize(&UpgradeableLoaderState::ProgramData {
896 slot: 0,
897 upgrade_authority_address: Some(upgradeable_program.upgrade_authority),
898 })
899 .unwrap();
900 program_data.extend_from_slice(&data);
901 accounts.insert(
902 programdata_address,
903 AccountSharedData::from(Account {
904 lamports: Rent::default().minimum_balance(program_data.len()).max(1),
905 data: program_data,
906 owner: upgradeable_program.loader,
907 executable: false,
908 rent_epoch: 0,
909 }),
910 );
911
912 let data = bincode::serialize(&UpgradeableLoaderState::Program {
913 programdata_address,
914 })
915 .unwrap();
916 accounts.insert(
917 upgradeable_program.program_id,
918 AccountSharedData::from(Account {
919 lamports: Rent::default().minimum_balance(data.len()).max(1),
920 data,
921 owner: upgradeable_program.loader,
922 executable: true,
923 rent_epoch: 0,
924 }),
925 );
926 }
927
928 let mut genesis_config = create_genesis_config_with_leader_ex_no_features(
929 mint_lamports,
930 &mint_address,
931 &validator_identity.pubkey(),
932 &validator_vote_account.pubkey(),
933 &validator_stake_account.pubkey(),
934 None,
935 validator_stake_lamports,
936 validator_identity_lamports,
937 config.fee_rate_governor.clone(),
938 config.rent.clone(),
939 solana_cluster_type::ClusterType::Development,
940 accounts.into_iter().collect(),
941 );
942 genesis_config.epoch_schedule = config
943 .epoch_schedule
944 .as_ref()
945 .cloned()
946 .unwrap_or_else(EpochSchedule::without_warmup);
947
948 if let Some(ticks_per_slot) = config.ticks_per_slot {
949 genesis_config.ticks_per_slot = ticks_per_slot;
950 }
951
952 if let Some(inflation) = config.inflation {
953 genesis_config.inflation = inflation;
954 }
955
956 for feature in feature_set {
957 genesis_utils::activate_feature(&mut genesis_config, feature);
958 }
959
960 let ledger_path = match &config.ledger_path {
961 None => create_new_tmp_ledger!(&genesis_config).0,
962 Some(ledger_path) => {
963 if TestValidatorGenesis::ledger_exists(ledger_path) {
964 return Ok(ledger_path.to_path_buf());
965 }
966
967 let _ = create_new_ledger(
968 ledger_path,
969 &genesis_config,
970 config
971 .max_genesis_archive_unpacked_size
972 .unwrap_or(MAX_GENESIS_ARCHIVE_UNPACKED_SIZE),
973 LedgerColumnOptions::default(),
974 )
975 .map_err(|err| {
976 format!(
977 "Failed to create ledger at {}: {}",
978 ledger_path.display(),
979 err
980 )
981 })?;
982 ledger_path.to_path_buf()
983 }
984 };
985
986 write_keypair_file(
987 &validator_identity,
988 ledger_path.join("validator-keypair.json").to_str().unwrap(),
989 )?;
990
991 write_keypair_file(
992 &validator_stake_account,
993 ledger_path
994 .join("stake-account-keypair.json")
995 .to_str()
996 .unwrap(),
997 )?;
998
999 assert!(!TestValidatorGenesis::ledger_exists(&ledger_path));
1001
1002 write_keypair_file(
1003 &validator_vote_account,
1004 ledger_path
1005 .join("vote-account-keypair.json")
1006 .to_str()
1007 .unwrap(),
1008 )?;
1009
1010 Ok(ledger_path)
1011 }
1012
1013 fn start(
1015 mint_address: Pubkey,
1016 config: &TestValidatorGenesis,
1017 socket_addr_space: SocketAddrSpace,
1018 rpc_to_plugin_manager_receiver: Option<Receiver<GeyserPluginManagerRequest>>,
1019 ) -> Result<Self, Box<dyn std::error::Error>> {
1020 let preserve_ledger = config.ledger_path.is_some();
1021 let ledger_path = TestValidator::initialize_ledger(mint_address, config)?;
1022
1023 let validator_identity =
1024 read_keypair_file(ledger_path.join("validator-keypair.json").to_str().unwrap())?;
1025 let validator_vote_account = read_keypair_file(
1026 ledger_path
1027 .join("vote-account-keypair.json")
1028 .to_str()
1029 .unwrap(),
1030 )?;
1031 let node = {
1032 let bind_ip_addr = config.node_config.bind_ip_addr;
1033 let validator_node_config = NodeConfig {
1034 bind_ip_addrs: BindIpAddrs::new(vec![bind_ip_addr])?,
1035 gossip_port: config.node_config.gossip_addr.port(),
1036 port_range: config.node_config.port_range,
1037 advertised_ip: bind_ip_addr,
1038 public_tpu_addr: None,
1039 public_tpu_forwards_addr: None,
1040 num_tvu_receive_sockets: NonZero::new(1).unwrap(),
1041 num_tvu_retransmit_sockets: NonZero::new(1).unwrap(),
1042 num_quic_endpoints: NonZero::new(DEFAULT_QUIC_ENDPOINTS)
1043 .expect("Number of QUIC endpoints can not be zero"),
1044 vortexor_receiver_addr: None,
1045 };
1046 let mut node =
1047 Node::new_with_external_ip(&validator_identity.pubkey(), validator_node_config);
1048 let (rpc, rpc_pubsub) = config.rpc_ports.unwrap_or_else(|| {
1049 let rpc_ports: [u16; 2] =
1050 find_available_ports_in_range(bind_ip_addr, config.node_config.port_range)
1051 .unwrap();
1052 (rpc_ports[0], rpc_ports[1])
1053 });
1054 node.info.set_rpc((bind_ip_addr, rpc)).unwrap();
1055 node.info
1056 .set_rpc_pubsub((bind_ip_addr, rpc_pubsub))
1057 .unwrap();
1058 node
1059 };
1060
1061 let vote_account_address = validator_vote_account.pubkey();
1062 let rpc_url = format!("http://{}", node.info.rpc().unwrap());
1063 let rpc_pubsub_url = format!("ws://{}/", node.info.rpc_pubsub().unwrap());
1064 let tpu = node.info.tpu(Protocol::UDP).unwrap();
1065 let gossip = node.info.gossip().unwrap();
1066
1067 {
1068 let mut authorized_voter_keypairs = config.authorized_voter_keypairs.write().unwrap();
1069 if !authorized_voter_keypairs
1070 .iter()
1071 .any(|x| x.pubkey() == vote_account_address)
1072 {
1073 authorized_voter_keypairs.push(Arc::new(validator_vote_account))
1074 }
1075 }
1076
1077 let accounts_db_config = AccountsDbConfig {
1078 index: Some(AccountsIndexConfig::default()),
1079 account_indexes: Some(config.rpc_config.account_indexes.clone()),
1080 ..AccountsDbConfig::default()
1081 };
1082
1083 let runtime_config = RuntimeConfig {
1084 compute_budget: config
1085 .compute_unit_limit
1086 .map(|compute_unit_limit| ComputeBudget {
1087 compute_unit_limit,
1088 ..ComputeBudget::new_with_defaults(
1089 !config
1090 .deactivate_feature_set
1091 .contains(&raise_cpi_nesting_limit_to_8::id()),
1092 !config
1093 .deactivate_feature_set
1094 .contains(&increase_cpi_account_info_limit::id()),
1095 )
1096 }),
1097 log_messages_bytes_limit: config.log_messages_bytes_limit,
1098 transaction_account_lock_limit: config.transaction_account_lock_limit,
1099 };
1100
1101 let mut validator_config = ValidatorConfig {
1102 on_start_geyser_plugin_config_files: config.geyser_plugin_config_files.clone(),
1103 rpc_addrs: Some((
1104 SocketAddr::new(
1105 IpAddr::V4(Ipv4Addr::UNSPECIFIED),
1106 node.info.rpc().unwrap().port(),
1107 ),
1108 SocketAddr::new(
1109 IpAddr::V4(Ipv4Addr::UNSPECIFIED),
1110 node.info.rpc_pubsub().unwrap().port(),
1111 ),
1112 )),
1113 rpc_config: config.rpc_config.clone(),
1114 pubsub_config: config.pubsub_config.clone(),
1115 account_paths: vec![
1116 create_accounts_run_and_snapshot_dirs(ledger_path.join("accounts"))
1117 .unwrap()
1118 .0,
1119 ],
1120 run_verification: false, snapshot_config: SnapshotConfig {
1122 full_snapshot_archive_interval: SnapshotInterval::Slots(
1123 NonZeroU64::new(100).unwrap(),
1124 ),
1125 incremental_snapshot_archive_interval: SnapshotInterval::Disabled,
1126 bank_snapshots_dir: ledger_path.join(BANK_SNAPSHOTS_DIR),
1127 full_snapshot_archives_dir: ledger_path.to_path_buf(),
1128 incremental_snapshot_archives_dir: ledger_path.to_path_buf(),
1129 ..SnapshotConfig::default()
1130 },
1131 warp_slot: config.warp_slot,
1132 validator_exit: config.validator_exit.clone(),
1133 max_ledger_shreds: config.max_ledger_shreds,
1134 no_wait_for_vote_to_start_leader: true,
1135 staked_nodes_overrides: config.staked_nodes_overrides.clone(),
1136 accounts_db_config,
1137 runtime_config,
1138 ..ValidatorConfig::default_for_test()
1139 };
1140 if let Some(ref tower_storage) = config.tower_storage {
1141 validator_config.tower_storage = tower_storage.clone();
1142 }
1143
1144 let validator = Some(Validator::new(
1145 node,
1146 Arc::new(validator_identity),
1147 &ledger_path,
1148 &vote_account_address,
1149 config.authorized_voter_keypairs.clone(),
1150 vec![],
1151 &validator_config,
1152 true, rpc_to_plugin_manager_receiver,
1154 config.start_progress.clone(),
1155 socket_addr_space,
1156 ValidatorTpuConfig::new_for_tests(config.tpu_enable_udp),
1157 config.admin_rpc_service_post_init.clone(),
1158 )?);
1159
1160 let test_validator = TestValidator {
1161 ledger_path,
1162 preserve_ledger,
1163 rpc_pubsub_url,
1164 rpc_url,
1165 tpu,
1166 gossip,
1167 validator,
1168 vote_account_address,
1169 };
1170 Ok(test_validator)
1171 }
1172
1173 async fn wait_for_nonzero_fees(&self) {
1177 let rpc_client = nonblocking::rpc_client::RpcClient::new_with_commitment(
1178 self.rpc_url.clone(),
1179 CommitmentConfig::processed(),
1180 );
1181 let mut message = Message::new(
1182 &[Instruction::new_with_bytes(
1183 Pubkey::new_unique(),
1184 &[],
1185 vec![AccountMeta::new(Pubkey::new_unique(), true)],
1186 )],
1187 None,
1188 );
1189 const MAX_TRIES: u64 = 10;
1190 let mut num_tries = 0;
1191 loop {
1192 num_tries += 1;
1193 if num_tries > MAX_TRIES {
1194 break;
1195 }
1196 println!("Waiting for fees to stabilize {num_tries:?}...");
1197 match rpc_client.get_latest_blockhash().await {
1198 Ok(blockhash) => {
1199 message.recent_blockhash = blockhash;
1200 match rpc_client.get_fee_for_message(&message).await {
1201 Ok(fee) => {
1202 if fee != 0 {
1203 break;
1204 }
1205 }
1206 Err(err) => {
1207 warn!("get_fee_for_message() failed: {err:?}");
1208 break;
1209 }
1210 }
1211 }
1212 Err(err) => {
1213 warn!("get_latest_blockhash() failed: {err:?}");
1214 break;
1215 }
1216 }
1217 sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT)).await;
1218 }
1219 }
1220
1221 async fn wait_for_upgradeable_programs_deployed(
1224 &self,
1225 upgradeable_programs: &[&Pubkey],
1226 payer: &Keypair,
1227 ) {
1228 let rpc_client = nonblocking::rpc_client::RpcClient::new_with_commitment(
1229 self.rpc_url.clone(),
1230 CommitmentConfig::processed(),
1231 );
1232
1233 let mut deployed = vec![false; upgradeable_programs.len()];
1234 const MAX_ATTEMPTS: u64 = 10;
1235
1236 for attempt in 1..=MAX_ATTEMPTS {
1237 let blockhash = rpc_client.get_latest_blockhash().await.unwrap();
1238 for (program_id, is_deployed) in upgradeable_programs.iter().zip(deployed.iter_mut()) {
1239 if *is_deployed {
1240 continue;
1241 }
1242
1243 let transaction = Transaction::new_signed_with_payer(
1244 &[Instruction {
1245 program_id: **program_id,
1246 accounts: vec![],
1247 data: vec![],
1248 }],
1249 Some(&payer.pubkey()),
1250 &[&payer],
1251 blockhash,
1252 );
1253 match rpc_client.send_transaction(&transaction).await {
1254 Ok(_) => *is_deployed = true,
1255 Err(e) => {
1256 if format!("{e:?}").contains("Program is not deployed") {
1257 debug!("{program_id:?} - not deployed");
1258 } else {
1259 *is_deployed = true;
1262 debug!("{program_id:?} - Unexpected error: {e:?}");
1263 }
1264 }
1265 }
1266 }
1267 if deployed.iter().all(|&deployed| deployed) {
1268 return;
1269 }
1270
1271 println!("Waiting for programs to be fully deployed {attempt} ...");
1272 sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT)).await;
1273 }
1274 panic!("Timeout waiting for program to become usable");
1275 }
1276
1277 pub fn tpu(&self) -> &SocketAddr {
1279 &self.tpu
1280 }
1281
1282 pub fn gossip(&self) -> &SocketAddr {
1284 &self.gossip
1285 }
1286
1287 pub fn rpc_url(&self) -> String {
1289 self.rpc_url.clone()
1290 }
1291
1292 pub fn rpc_pubsub_url(&self) -> String {
1294 self.rpc_pubsub_url.clone()
1295 }
1296
1297 pub fn vote_account_address(&self) -> Pubkey {
1299 self.vote_account_address
1300 }
1301
1302 pub fn get_rpc_client(&self) -> RpcClient {
1304 RpcClient::new_with_commitment(self.rpc_url.clone(), CommitmentConfig::processed())
1305 }
1306
1307 pub fn get_async_rpc_client(&self) -> nonblocking::rpc_client::RpcClient {
1309 nonblocking::rpc_client::RpcClient::new_with_commitment(
1310 self.rpc_url.clone(),
1311 CommitmentConfig::processed(),
1312 )
1313 }
1314
1315 pub fn join(mut self) {
1316 if let Some(validator) = self.validator.take() {
1317 validator.join();
1318 }
1319 }
1320
1321 pub fn cluster_info(&self) -> Arc<ClusterInfo> {
1322 self.validator.as_ref().unwrap().cluster_info.clone()
1323 }
1324
1325 pub fn bank_forks(&self) -> Arc<RwLock<BankForks>> {
1326 self.validator.as_ref().unwrap().bank_forks.clone()
1327 }
1328
1329 pub fn repair_whitelist(&self) -> Arc<RwLock<HashSet<Pubkey>>> {
1330 Arc::new(RwLock::new(HashSet::default()))
1331 }
1332}
1333
1334impl Drop for TestValidator {
1335 fn drop(&mut self) {
1336 if let Some(validator) = self.validator.take() {
1337 validator.close();
1338 }
1339 if !self.preserve_ledger {
1340 remove_dir_all(&self.ledger_path).unwrap_or_else(|err| {
1341 panic!(
1342 "Failed to remove ledger directory {}: {}",
1343 self.ledger_path.display(),
1344 err
1345 )
1346 });
1347 }
1348 }
1349}
1350
1351#[cfg(test)]
1352mod test {
1353 use {super::*, solana_feature_gate_interface::Feature};
1354
1355 #[test]
1356 fn get_health() {
1357 let (test_validator, _payer) = TestValidatorGenesis::default().start();
1358 let rpc_client = test_validator.get_rpc_client();
1359 rpc_client.get_health().expect("health");
1360 }
1361
1362 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1363 async fn nonblocking_get_health() {
1364 let (test_validator, _payer) = TestValidatorGenesis::default().start_async().await;
1365 let rpc_client = test_validator.get_async_rpc_client();
1366 rpc_client.get_health().await.expect("health");
1367 }
1368
1369 #[test]
1370 fn test_upgradeable_program_deploayment() {
1371 let program_id = Pubkey::new_unique();
1372 let (test_validator, payer) = TestValidatorGenesis::default()
1373 .add_program("../programs/bpf-loader-tests/noop", program_id)
1374 .start();
1375 let rpc_client = test_validator.get_rpc_client();
1376
1377 let blockhash = rpc_client.get_latest_blockhash().unwrap();
1378 let transaction = Transaction::new_signed_with_payer(
1379 &[Instruction {
1380 program_id,
1381 accounts: vec![],
1382 data: vec![],
1383 }],
1384 Some(&payer.pubkey()),
1385 &[&payer],
1386 blockhash,
1387 );
1388
1389 assert!(rpc_client
1390 .send_and_confirm_transaction(&transaction)
1391 .is_ok());
1392 }
1393
1394 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1395 async fn test_nonblocking_upgradeable_program_deploayment() {
1396 let program_id = Pubkey::new_unique();
1397 let (test_validator, payer) = TestValidatorGenesis::default()
1398 .add_program("../programs/bpf-loader-tests/noop", program_id)
1399 .start_async()
1400 .await;
1401 let rpc_client = test_validator.get_async_rpc_client();
1402
1403 let blockhash = rpc_client.get_latest_blockhash().await.unwrap();
1404 let transaction = Transaction::new_signed_with_payer(
1405 &[Instruction {
1406 program_id,
1407 accounts: vec![],
1408 data: vec![],
1409 }],
1410 Some(&payer.pubkey()),
1411 &[&payer],
1412 blockhash,
1413 );
1414
1415 assert!(rpc_client
1416 .send_and_confirm_transaction(&transaction)
1417 .await
1418 .is_ok());
1419 }
1420
1421 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1422 #[should_panic]
1423 async fn document_tokio_panic() {
1424 let (_test_validator, _payer) = TestValidatorGenesis::default().start();
1426 }
1427
1428 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1429 async fn test_deactivate_features() {
1430 let mut control = FeatureSet::default().inactive().clone();
1431 let mut deactivate_features = Vec::new();
1432 [
1433 agave_feature_set::deprecate_rewards_sysvar::id(),
1434 agave_feature_set::disable_fees_sysvar::id(),
1435 alpenglow::id(),
1436 ]
1437 .into_iter()
1438 .for_each(|feature| {
1439 control.remove(&feature);
1440 deactivate_features.push(feature);
1441 });
1442
1443 let control: Vec<Pubkey> = control.into_iter().collect();
1445
1446 let (test_validator, _payer) = TestValidatorGenesis::default()
1447 .deactivate_features(&deactivate_features)
1448 .start_async()
1449 .await;
1450
1451 let rpc_client = test_validator.get_async_rpc_client();
1452
1453 let inactive_feature_accounts = rpc_client
1455 .get_multiple_accounts(&deactivate_features)
1456 .await
1457 .unwrap();
1458 for f in inactive_feature_accounts {
1459 assert!(f.is_none());
1460 }
1461
1462 for chunk in control.chunks(100) {
1464 let active_feature_accounts = rpc_client.get_multiple_accounts(chunk).await.unwrap();
1465 for f in active_feature_accounts {
1466 let account = f.unwrap(); let feature_state: Feature = bincode::deserialize(account.data()).unwrap();
1468 assert!(feature_state.activated_at.is_some());
1469 }
1470 }
1471 }
1472
1473 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1474 async fn test_override_feature_account() {
1475 let with_deactivate_flag = agave_feature_set::deprecate_rewards_sysvar::id();
1476 let without_deactivate_flag = agave_feature_set::disable_fees_sysvar::id();
1477
1478 let owner = Pubkey::new_unique();
1479 let account = || AccountSharedData::new(100_000, 0, &owner);
1480
1481 let (test_validator, _payer) = TestValidatorGenesis::default()
1482 .deactivate_features(&[with_deactivate_flag]) .add_accounts([
1484 (with_deactivate_flag, account()), (without_deactivate_flag, account()),
1486 ])
1487 .start_async()
1488 .await;
1489
1490 let rpc_client = test_validator.get_async_rpc_client();
1491
1492 let our_accounts = rpc_client
1493 .get_multiple_accounts(&[with_deactivate_flag, without_deactivate_flag])
1494 .await
1495 .unwrap();
1496
1497 let overriden_account = our_accounts[0].as_ref().unwrap();
1500 assert_eq!(overriden_account.lamports, 100_000);
1501 assert_eq!(overriden_account.data.len(), 0);
1502 assert_eq!(overriden_account.owner, owner);
1503
1504 let feature_account = our_accounts[1].as_ref().unwrap();
1506 assert_eq!(feature_account.owner, solana_sdk_ids::feature::id());
1507 let feature_state: Feature = bincode::deserialize(feature_account.data()).unwrap();
1508 assert!(feature_state.activated_at.is_some());
1509 }
1510
1511 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1512 async fn test_core_bpf_programs() {
1513 let (test_validator, _payer) = TestValidatorGenesis::default().start_async().await;
1514
1515 let rpc_client = test_validator.get_async_rpc_client();
1516
1517 let fetched_programs = rpc_client
1518 .get_multiple_accounts(&[
1519 solana_sdk_ids::address_lookup_table::id(),
1520 solana_sdk_ids::config::id(),
1521 solana_sdk_ids::feature::id(),
1522 solana_sdk_ids::stake::id(),
1523 ])
1524 .await
1525 .unwrap();
1526
1527 let account = fetched_programs[0].as_ref().unwrap();
1529 assert_eq!(account.owner, solana_sdk_ids::bpf_loader_upgradeable::id());
1530 assert!(account.executable);
1531
1532 let account = fetched_programs[1].as_ref().unwrap();
1534 assert_eq!(account.owner, solana_sdk_ids::bpf_loader_upgradeable::id());
1535 assert!(account.executable);
1536
1537 let account = fetched_programs[2].as_ref().unwrap();
1539 assert_eq!(account.owner, solana_sdk_ids::bpf_loader_upgradeable::id());
1540 assert!(account.executable);
1541
1542 let account = fetched_programs[3].as_ref().unwrap();
1544 assert_eq!(account.owner, solana_sdk_ids::bpf_loader_upgradeable::id());
1545 assert!(account.executable);
1546 }
1547}