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