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