1#![allow(clippy::arithmetic_side_effects)]
2use {
3 agave_feature_set::{FEATURE_NAMES, FeatureSet, alpenglow, raise_cpi_nesting_limit_to_8},
4 agave_snapshots::{
5 SnapshotInterval, paths::BANK_SNAPSHOTS_DIR, snapshot_config::SnapshotConfig,
6 },
7 arc_swap::ArcSwap,
8 base64::{Engine, prelude::BASE64_STANDARD},
9 crossbeam_channel::Receiver,
10 log::*,
11 solana_account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
12 solana_accounts_db::{
13 accounts_db::{ACCOUNTS_DB_CONFIG_FOR_TESTING, AccountsDbConfig},
14 accounts_index::{AccountsIndexConfig, ScanFilter},
15 utils::create_accounts_run_and_snapshot_dirs,
16 },
17 solana_cli_output::CliAccount,
18 solana_clock::{DEFAULT_MS_PER_SLOT, Slot},
19 solana_commitment_config::CommitmentConfig,
20 solana_compute_budget::compute_budget::ComputeBudget,
21 solana_core::{
22 admin_rpc_post_init::AdminRpcRequestMetadataPostInit,
23 consensus::tower_storage::TowerStorage,
24 validator::{Validator, ValidatorConfig, ValidatorStartProgress, ValidatorTpuConfig},
25 },
26 solana_epoch_schedule::EpochSchedule,
27 solana_fee_calculator::FeeRateGovernor,
28 solana_genesis_utils::MAX_GENESIS_ARCHIVE_UNPACKED_SIZE,
29 solana_geyser_plugin_manager::{
30 GeyserPluginManagerRequest, geyser_plugin_manager::GeyserPluginManager,
31 },
32 solana_gossip::{
33 cluster_info::{ClusterInfo, NodeConfig},
34 contact_info::Protocol,
35 node::Node,
36 },
37 solana_inflation::Inflation,
38 solana_instruction::Instruction,
39 solana_keypair::{Keypair, read_keypair_file, write_keypair_file},
40 solana_ledger::{
41 blockstore::create_new_ledger, blockstore_options::LedgerColumnOptions,
42 create_new_tmp_ledger,
43 },
44 solana_loader_v3_interface::state::UpgradeableLoaderState,
45 solana_native_token::LAMPORTS_PER_SOL,
46 solana_net_utils::{
47 PortRange, SocketAddrSpace, find_available_ports_in_range, multihomed_sockets::BindIpAddrs,
48 },
49 solana_program_runtime::{
50 execution_budget::SVMTransactionExecutionBudget, invoke_context::InvokeContext,
51 },
52 solana_pubkey::Pubkey,
53 solana_rent::Rent,
54 solana_rpc::{rpc::JsonRpcConfig, rpc_pubsub_service::PubSubConfig},
55 solana_rpc_client::{nonblocking, rpc_client::RpcClient},
56 solana_rpc_client_api::{
57 client_error::Error as RpcClientError, request::MAX_MULTIPLE_ACCOUNTS,
58 },
59 solana_runtime::{
60 bank_forks::BankForks, genesis_utils::create_genesis_config_with_leader_ex,
61 runtime_config::RuntimeConfig,
62 },
63 solana_sbpf::{elf::Executable, verifier::RequisiteVerifier},
64 solana_sdk_ids::address_lookup_table,
65 solana_signer::Signer,
66 solana_streamer::quic::DEFAULT_QUIC_ENDPOINTS,
67 solana_syscalls::create_program_runtime_environment,
68 solana_transaction::{Transaction, TransactionError},
69 solana_validator_exit::Exit,
70 std::{
71 collections::{HashMap, HashSet},
72 ffi::OsStr,
73 fmt::Display,
74 fs::{self, File, remove_dir_all},
75 io::Read,
76 net::{IpAddr, Ipv4Addr, SocketAddr},
77 num::{NonZero, NonZeroU64},
78 path::{Path, PathBuf},
79 str::FromStr,
80 sync::{Arc, RwLock},
81 time::Duration,
82 },
83 tokio::time::sleep,
84};
85
86#[derive(Clone)]
87pub struct AccountInfo<'a> {
88 pub address: Option<Pubkey>,
89 pub filename: &'a str,
90}
91
92#[derive(Clone)]
93pub struct UpgradeableProgramInfo {
94 pub program_id: Pubkey,
95 pub loader: Pubkey,
96 pub upgrade_authority: Pubkey,
97 pub program_path: PathBuf,
98}
99
100#[derive(Debug)]
101pub struct TestValidatorNodeConfig {
102 gossip_addr: SocketAddr,
103 port_range: PortRange,
104 bind_ip_addr: IpAddr,
105}
106
107impl Default for TestValidatorNodeConfig {
108 fn default() -> Self {
109 let bind_ip_addr = IpAddr::V4(Ipv4Addr::LOCALHOST);
110 #[cfg(not(debug_assertions))]
111 let port_range = solana_net_utils::VALIDATOR_PORT_RANGE;
112 #[cfg(debug_assertions)]
113 let port_range = solana_net_utils::sockets::localhost_port_range_for_tests();
114 Self {
115 gossip_addr: SocketAddr::new(bind_ip_addr, port_range.0),
116 port_range,
117 bind_ip_addr,
118 }
119 }
120}
121
122pub struct TestValidatorGenesis {
123 fee_rate_governor: FeeRateGovernor,
124 ledger_path: Option<PathBuf>,
125 tower_storage: Option<Arc<dyn TowerStorage>>,
126 pub rent: Rent,
127 rpc_config: JsonRpcConfig,
128 pubsub_config: PubSubConfig,
129 rpc_ports: Option<(u16, u16)>, warp_slot: Option<Slot>,
131 accounts: HashMap<Pubkey, AccountSharedData>,
132 upgradeable_programs: Vec<UpgradeableProgramInfo>,
133 ticks_per_slot: Option<u64>,
134 epoch_schedule: Option<EpochSchedule>,
135 inflation: Option<Inflation>,
136 node_config: TestValidatorNodeConfig,
137 pub validator_exit: Arc<RwLock<Exit>>,
138 pub start_progress: Arc<RwLock<ValidatorStartProgress>>,
139 pub authorized_voter_keypairs: Arc<RwLock<Vec<Arc<Keypair>>>>,
140 pub staked_nodes_overrides: Arc<RwLock<HashMap<Pubkey, u64>>>,
141 pub max_ledger_shreds: Option<u64>,
142 pub max_genesis_archive_unpacked_size: Option<u64>,
143 pub geyser_plugin_config_files: Option<Vec<PathBuf>>,
144 pub enable_scheduler_bindings: bool,
145 deactivate_feature_set: HashSet<Pubkey>,
146 compute_unit_limit: Option<u64>,
147 pub log_messages_bytes_limit: Option<usize>,
148 pub transaction_account_lock_limit: Option<usize>,
149 pub geyser_plugin_manager: Arc<ArcSwap<GeyserPluginManager>>,
150 admin_rpc_service_post_init: Arc<RwLock<Option<AdminRpcRequestMetadataPostInit>>>,
151}
152
153impl Default for TestValidatorGenesis {
154 fn default() -> Self {
155 let deactivate_feature_set = [alpenglow::id()].into_iter().collect();
157 Self {
158 fee_rate_governor: FeeRateGovernor::default(),
159 ledger_path: Option::<PathBuf>::default(),
160 tower_storage: Option::<Arc<dyn TowerStorage>>::default(),
161 rent: Rent::default(),
162 rpc_config: JsonRpcConfig::default_for_test(),
163 pubsub_config: PubSubConfig::default_for_tests(),
164 rpc_ports: Option::<(u16, u16)>::default(),
165 warp_slot: Option::<Slot>::default(),
166 accounts: HashMap::<Pubkey, AccountSharedData>::default(),
167 upgradeable_programs: Vec::<UpgradeableProgramInfo>::default(),
168 ticks_per_slot: Option::<u64>::default(),
169 epoch_schedule: Option::<EpochSchedule>::default(),
170 inflation: Option::<Inflation>::default(),
171 node_config: TestValidatorNodeConfig::default(),
172 validator_exit: Arc::<RwLock<Exit>>::default(),
173 start_progress: Arc::<RwLock<ValidatorStartProgress>>::default(),
174 authorized_voter_keypairs: Arc::<RwLock<Vec<Arc<Keypair>>>>::default(),
175 staked_nodes_overrides: Arc::new(RwLock::new(HashMap::new())),
176 max_ledger_shreds: Option::<u64>::default(),
177 max_genesis_archive_unpacked_size: Option::<u64>::default(),
178 geyser_plugin_config_files: Option::<Vec<PathBuf>>::default(),
179 enable_scheduler_bindings: false,
180 deactivate_feature_set,
181 compute_unit_limit: Option::<u64>::default(),
182 log_messages_bytes_limit: Option::<usize>::default(),
183 transaction_account_lock_limit: Option::<usize>::default(),
184 geyser_plugin_manager: Arc::new(ArcSwap::new(Arc::new(GeyserPluginManager::default()))),
185 admin_rpc_service_post_init:
186 Arc::<RwLock<Option<AdminRpcRequestMetadataPostInit>>>::default(),
187 }
188 }
189}
190
191fn try_transform_program_data(
192 address: &Pubkey,
193 account: &mut AccountSharedData,
194) -> Result<(), String> {
195 if account.owner() == &solana_sdk_ids::bpf_loader_upgradeable::id() {
196 let programdata_offset = UpgradeableLoaderState::size_of_programdata_metadata();
197 let programdata_meta = account.data().get(0..programdata_offset).ok_or(format!(
198 "Failed to get upgradeable programdata data from {address}"
199 ))?;
200 if let Ok(UpgradeableLoaderState::ProgramData {
203 upgrade_authority_address,
204 ..
205 }) = bincode::deserialize::<UpgradeableLoaderState>(programdata_meta)
206 {
207 bincode::serialize_into(
210 account.data_as_mut_slice(),
211 &UpgradeableLoaderState::ProgramData {
212 slot: 0,
213 upgrade_authority_address,
214 },
215 )
216 .map_err(|_| format!("Failed to write to upgradeable programdata account {address}"))
217 } else {
218 Err(format!(
219 "Failed to read upgradeable programdata account {address}"
220 ))
221 }
222 } else {
223 Err(format!("Account {address} not owned by upgradeable loader"))
224 }
225}
226
227impl TestValidatorGenesis {
228 pub fn deactivate_features(&mut self, deactivate_list: &[Pubkey]) -> &mut Self {
232 self.deactivate_feature_set.extend(deactivate_list);
233 self
234 }
235 pub fn ledger_path<P: Into<PathBuf>>(&mut self, ledger_path: P) -> &mut Self {
236 self.ledger_path = Some(ledger_path.into());
237 self
238 }
239
240 pub fn tower_storage(&mut self, tower_storage: Arc<dyn TowerStorage>) -> &mut Self {
241 self.tower_storage = Some(tower_storage);
242 self
243 }
244
245 pub fn ledger_exists(ledger_path: &Path) -> bool {
247 ledger_path.join("vote-account-keypair.json").exists()
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 .to_account_shared_data()
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(
673 &self,
674 mint_address: Pubkey,
675 socket_addr_space: SocketAddrSpace,
676 ) -> Result<TestValidator, Box<dyn std::error::Error>> {
677 self.start_with_mint_address_and_geyser_plugin_rpc(mint_address, socket_addr_space, None)
678 }
679
680 pub fn start_with_mint_address_and_geyser_plugin_rpc(
686 &self,
687 mint_address: Pubkey,
688 socket_addr_space: SocketAddrSpace,
689 rpc_to_plugin_manager_receiver: Option<Receiver<GeyserPluginManagerRequest>>,
690 ) -> Result<TestValidator, Box<dyn std::error::Error>> {
691 TestValidator::start(
692 mint_address,
693 self,
694 socket_addr_space,
695 rpc_to_plugin_manager_receiver,
696 )
697 .inspect(|test_validator| {
698 let runtime = tokio::runtime::Builder::new_current_thread()
699 .enable_io()
700 .enable_time()
701 .build()
702 .unwrap();
703 runtime.block_on(test_validator.wait_for_first_slot());
704 })
705 }
706
707 pub fn start(&self) -> (TestValidator, Keypair) {
714 self.start_with_socket_addr_space(SocketAddrSpace::new(true))
715 }
716
717 pub fn start_with_socket_addr_space(
725 &self,
726 socket_addr_space: SocketAddrSpace,
727 ) -> (TestValidator, Keypair) {
728 let mint_keypair = Keypair::new();
729 self.start_with_mint_address(mint_keypair.pubkey(), socket_addr_space)
730 .inspect(|test_validator| {
731 let runtime = tokio::runtime::Builder::new_current_thread()
732 .enable_io()
733 .enable_time()
734 .build()
735 .unwrap();
736 let upgradeable_program_ids: Vec<&Pubkey> = self
737 .upgradeable_programs
738 .iter()
739 .map(|p| &p.program_id)
740 .collect();
741 runtime
742 .block_on(test_validator.wait_for_upgradeable_programs_deployed(
743 &upgradeable_program_ids,
744 &mint_keypair,
745 ))
746 .unwrap_or_else(|err| {
747 panic!("Failed to wait for programs to be deployed: {err:?}")
748 });
749 })
750 .map(|test_validator| (test_validator, mint_keypair))
751 .unwrap_or_else(|err| panic!("Test validator failed to start: {err}"))
752 }
753
754 pub async fn start_async_with_mint_address(
757 &self,
758 mint_keypair: &Keypair,
759 socket_addr_space: SocketAddrSpace,
760 ) -> Result<TestValidator, Box<dyn std::error::Error>> {
761 let test_validator =
762 TestValidator::start(mint_keypair.pubkey(), self, socket_addr_space, None)?;
763 test_validator.wait_for_first_slot().await;
764 let upgradeable_program_ids: Vec<&Pubkey> = self
765 .upgradeable_programs
766 .iter()
767 .map(|p| &p.program_id)
768 .collect();
769 test_validator
770 .wait_for_upgradeable_programs_deployed(&upgradeable_program_ids, mint_keypair)
771 .await
772 .unwrap_or_else(|err| panic!("Failed to wait for programs to be deployed: {err:?}"));
773 Ok(test_validator)
774 }
775
776 pub async fn start_async(&self) -> (TestValidator, Keypair) {
777 self.start_async_with_socket_addr_space(SocketAddrSpace::new(
778 true,
779 ))
780 .await
781 }
782
783 pub async fn start_async_with_socket_addr_space(
784 &self,
785 socket_addr_space: SocketAddrSpace,
786 ) -> (TestValidator, Keypair) {
787 let mint_keypair = Keypair::new();
788 let test_validator = self
789 .start_async_with_mint_address(&mint_keypair, socket_addr_space)
790 .await
791 .unwrap_or_else(|err| panic!("Test validator failed to start: {err}"));
792 (test_validator, mint_keypair)
793 }
794}
795
796pub struct TestValidator {
797 ledger_path: PathBuf,
798 preserve_ledger: bool,
799 rpc_pubsub_url: String,
800 rpc_url: String,
801 tpu_quic: SocketAddr,
802 gossip: SocketAddr,
803 validator: Option<Validator>,
804 vote_account_address: Pubkey,
805}
806
807impl TestValidator {
808 pub fn start_with_config(
811 mint_address: Pubkey,
812 faucet_addr: Option<SocketAddr>,
813 socket_addr_space: SocketAddrSpace,
814 ) -> Self {
815 TestValidatorGenesis::default()
816 .rent(Rent {
817 lamports_per_byte: 1,
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 async fn async_start_with_config(
827 mint_keypair: &Keypair,
828 faucet_addr: Option<SocketAddr>,
829 socket_addr_space: SocketAddrSpace,
830 ) -> Self {
831 TestValidatorGenesis::default()
832 .rent(Rent {
833 lamports_per_byte: 1,
834 ..Rent::default()
835 })
836 .faucet_addr(faucet_addr)
837 .start_async_with_mint_address(mint_keypair, socket_addr_space)
838 .await
839 .expect("validator start failed")
840 }
841
842 fn initialize_ledger(
849 mint_address: Pubkey,
850 config: &TestValidatorGenesis,
851 ) -> Result<PathBuf, Box<dyn std::error::Error>> {
852 let validator_identity = Keypair::new();
853 let validator_vote_account = Keypair::new();
854 let validator_stake_account = Keypair::new();
855 let validator_identity_lamports = 500 * LAMPORTS_PER_SOL;
856 let validator_stake_lamports = 1_000_000 * LAMPORTS_PER_SOL;
857 let mint_lamports = 500_000_000 * LAMPORTS_PER_SOL;
858
859 let mut feature_set = FeatureSet::all_enabled();
861 for feature in &config.deactivate_feature_set {
862 if FEATURE_NAMES.contains_key(feature) {
863 feature_set.deactivate(feature);
864 info!("Feature for {feature:?} deactivated");
865 } else {
866 warn!("Feature {feature:?} set for deactivation is not a known Feature public key",)
867 }
868 }
869
870 let runtime_features = feature_set.runtime_features();
871 let program_runtime_environment = create_program_runtime_environment(
872 &runtime_features,
873 &SVMTransactionExecutionBudget::new_with_defaults(
874 runtime_features.raise_cpi_nesting_limit_to_8,
875 ),
876 true,
877 false,
878 )?;
879
880 let mut accounts = config.accounts.clone();
881 for (address, account) in solana_program_binaries::spl_programs(&config.rent) {
882 accounts.entry(address).or_insert(account);
883 }
884 for (address, account) in
885 solana_program_binaries::core_bpf_programs(&config.rent, |feature_id| {
886 feature_set.is_active(feature_id)
887 })
888 {
889 accounts.entry(address).or_insert(account);
890 }
891 for upgradeable_program in &config.upgradeable_programs {
892 let data = solana_program_test::read_file(&upgradeable_program.program_path);
893 let executable = Executable::<InvokeContext>::from_elf(
894 &data,
895 Arc::clone(&*program_runtime_environment),
896 )
897 .map_err(|err| format!("ELF error: {err}"))?;
898 executable
899 .verify::<RequisiteVerifier>()
900 .map_err(|err| format!("ELF error: {err}"))?;
901
902 let (programdata_address, _) = Pubkey::find_program_address(
903 &[upgradeable_program.program_id.as_ref()],
904 &upgradeable_program.loader,
905 );
906 let mut program_data = bincode::serialize(&UpgradeableLoaderState::ProgramData {
907 slot: 0,
908 upgrade_authority_address: Some(upgradeable_program.upgrade_authority),
909 })
910 .unwrap();
911 program_data.extend_from_slice(&data);
912 accounts.insert(
913 programdata_address,
914 AccountSharedData::from(Account {
915 lamports: Rent::default().minimum_balance(program_data.len()).max(1),
916 data: program_data,
917 owner: upgradeable_program.loader,
918 executable: false,
919 rent_epoch: 0,
920 }),
921 );
922
923 let data = bincode::serialize(&UpgradeableLoaderState::Program {
924 programdata_address,
925 })
926 .unwrap();
927 accounts.insert(
928 upgradeable_program.program_id,
929 AccountSharedData::from(Account {
930 lamports: Rent::default().minimum_balance(data.len()).max(1),
931 data,
932 owner: upgradeable_program.loader,
933 executable: true,
934 rent_epoch: 0,
935 }),
936 );
937 }
938
939 let mut genesis_config = create_genesis_config_with_leader_ex(
940 mint_lamports,
941 &mint_address,
942 &validator_identity.pubkey(),
943 &validator_vote_account.pubkey(),
944 &validator_stake_account.pubkey(),
945 None,
946 validator_stake_lamports,
947 validator_identity_lamports,
948 config.fee_rate_governor.clone(),
949 config.rent.clone(),
950 solana_cluster_type::ClusterType::Development,
951 &feature_set,
952 accounts.into_iter().collect(),
953 );
954 genesis_config.epoch_schedule = config
955 .epoch_schedule
956 .as_ref()
957 .cloned()
958 .unwrap_or_else(EpochSchedule::without_warmup);
959
960 if let Some(ticks_per_slot) = config.ticks_per_slot {
961 genesis_config.ticks_per_slot = ticks_per_slot;
962 }
963
964 if let Some(inflation) = config.inflation {
965 genesis_config.inflation = inflation;
966 }
967
968 let ledger_path = match &config.ledger_path {
969 None => create_new_tmp_ledger!(&genesis_config).0,
970 Some(ledger_path) => {
971 if TestValidatorGenesis::ledger_exists(ledger_path) {
972 return Ok(ledger_path.to_path_buf());
973 }
974
975 let _ = create_new_ledger(
976 ledger_path,
977 &genesis_config,
978 config
979 .max_genesis_archive_unpacked_size
980 .unwrap_or(MAX_GENESIS_ARCHIVE_UNPACKED_SIZE),
981 LedgerColumnOptions::default(),
982 )
983 .map_err(|err| {
984 format!(
985 "Failed to create ledger at {}: {}",
986 ledger_path.display(),
987 err
988 )
989 })?;
990 ledger_path.to_path_buf()
991 }
992 };
993
994 write_keypair_file(
995 &validator_identity,
996 ledger_path.join("validator-keypair.json").to_str().unwrap(),
997 )?;
998
999 write_keypair_file(
1000 &validator_stake_account,
1001 ledger_path
1002 .join("stake-account-keypair.json")
1003 .to_str()
1004 .unwrap(),
1005 )?;
1006
1007 assert!(!TestValidatorGenesis::ledger_exists(&ledger_path));
1009
1010 write_keypair_file(
1011 &validator_vote_account,
1012 ledger_path
1013 .join("vote-account-keypair.json")
1014 .to_str()
1015 .unwrap(),
1016 )?;
1017
1018 Ok(ledger_path)
1019 }
1020
1021 fn start(
1023 mint_address: Pubkey,
1024 config: &TestValidatorGenesis,
1025 socket_addr_space: SocketAddrSpace,
1026 rpc_to_plugin_manager_receiver: Option<Receiver<GeyserPluginManagerRequest>>,
1027 ) -> Result<Self, Box<dyn std::error::Error>> {
1028 let preserve_ledger = config.ledger_path.is_some();
1029 let ledger_path = TestValidator::initialize_ledger(mint_address, config)?;
1030
1031 let validator_identity =
1032 read_keypair_file(ledger_path.join("validator-keypair.json").to_str().unwrap())?;
1033 let validator_vote_account = read_keypair_file(
1034 ledger_path
1035 .join("vote-account-keypair.json")
1036 .to_str()
1037 .unwrap(),
1038 )?;
1039 let node = {
1040 let bind_ip_addr = config.node_config.bind_ip_addr;
1041 let validator_node_config = NodeConfig {
1042 bind_ip_addrs: BindIpAddrs::new(vec![bind_ip_addr])?,
1043 gossip_port: config.node_config.gossip_addr.port(),
1044 port_range: config.node_config.port_range,
1045 advertised_ip: bind_ip_addr,
1046 public_tvu_addr: None,
1047 public_tpu_addr: None,
1048 public_tpu_forwards_addr: None,
1049 num_tvu_receive_sockets: NonZero::new(1).unwrap(),
1050 num_tvu_retransmit_sockets: NonZero::new(1).unwrap(),
1051 num_quic_endpoints: NonZero::new(DEFAULT_QUIC_ENDPOINTS)
1052 .expect("Number of QUIC endpoints can not be zero"),
1053 };
1054 let mut node =
1055 Node::new_with_external_ip(&validator_identity.pubkey(), validator_node_config);
1056 let (rpc, rpc_pubsub) = config.rpc_ports.unwrap_or_else(|| {
1057 let rpc_ports: [u16; 2] =
1058 find_available_ports_in_range(bind_ip_addr, config.node_config.port_range)
1059 .unwrap();
1060 (rpc_ports[0], rpc_ports[1])
1061 });
1062 node.info.set_rpc((bind_ip_addr, rpc)).unwrap();
1063 node.info
1064 .set_rpc_pubsub((bind_ip_addr, rpc_pubsub))
1065 .unwrap();
1066 node
1067 };
1068
1069 let vote_account_address = validator_vote_account.pubkey();
1070 let rpc_url = format!("http://{}", node.info.rpc().unwrap());
1071 let rpc_pubsub_url = format!("ws://{}/", node.info.rpc_pubsub().unwrap());
1072 let tpu_quic = node.info.tpu(Protocol::QUIC).unwrap();
1073 let gossip = node.info.gossip().unwrap();
1074
1075 {
1076 let mut authorized_voter_keypairs: std::sync::RwLockWriteGuard<'_, Vec<Arc<Keypair>>> =
1077 config.authorized_voter_keypairs.write().unwrap();
1078 if !authorized_voter_keypairs
1079 .iter()
1080 .any(|x| x.pubkey() == vote_account_address)
1081 {
1082 authorized_voter_keypairs.push(Arc::new(validator_vote_account))
1083 }
1084 }
1085
1086 let accounts_db_config = AccountsDbConfig {
1087 index: Some(AccountsIndexConfig::default()),
1088 account_indexes: Some(config.rpc_config.account_indexes.clone()),
1089 scan_filter_for_shrinking: ScanFilter::All,
1090 ..ACCOUNTS_DB_CONFIG_FOR_TESTING
1091 };
1092
1093 let runtime_config = RuntimeConfig {
1094 compute_budget: config
1095 .compute_unit_limit
1096 .map(|compute_unit_limit| ComputeBudget {
1097 compute_unit_limit,
1098 ..ComputeBudget::new_with_defaults(
1099 !config
1100 .deactivate_feature_set
1101 .contains(&raise_cpi_nesting_limit_to_8::id()),
1102 )
1103 }),
1104 log_messages_bytes_limit: config.log_messages_bytes_limit,
1105 transaction_account_lock_limit: config.transaction_account_lock_limit,
1106 };
1107
1108 let mut validator_config = ValidatorConfig {
1109 on_start_geyser_plugin_config_files: config.geyser_plugin_config_files.clone(),
1110 rpc_addrs: Some((
1111 SocketAddr::new(
1112 IpAddr::V4(Ipv4Addr::UNSPECIFIED),
1113 node.info.rpc().unwrap().port(),
1114 ),
1115 SocketAddr::new(
1116 IpAddr::V4(Ipv4Addr::UNSPECIFIED),
1117 node.info.rpc_pubsub().unwrap().port(),
1118 ),
1119 )),
1120 rpc_config: config.rpc_config.clone(),
1121 pubsub_config: config.pubsub_config.clone(),
1122 account_paths: vec![
1123 create_accounts_run_and_snapshot_dirs(ledger_path.join("accounts"))
1124 .unwrap()
1125 .0,
1126 ],
1127 run_verification: false, snapshot_config: SnapshotConfig {
1129 full_snapshot_archive_interval: SnapshotInterval::Slots(
1130 NonZeroU64::new(100).unwrap(),
1131 ),
1132 incremental_snapshot_archive_interval: SnapshotInterval::Disabled,
1133 bank_snapshots_dir: ledger_path.join(BANK_SNAPSHOTS_DIR),
1134 full_snapshot_archives_dir: ledger_path.to_path_buf(),
1135 incremental_snapshot_archives_dir: ledger_path.to_path_buf(),
1136 use_registered_io_uring_buffers: false,
1137 use_direct_io: false,
1138 ..SnapshotConfig::default()
1139 },
1140 warp_slot: config.warp_slot,
1141 validator_exit: config.validator_exit.clone(),
1142 max_ledger_shreds: config.max_ledger_shreds,
1143 no_wait_for_vote_to_start_leader: true,
1144 staked_nodes_overrides: config.staked_nodes_overrides.clone(),
1145 accounts_db_config,
1146 runtime_config,
1147 enable_scheduler_bindings: config.enable_scheduler_bindings,
1148 ..ValidatorConfig::default_for_test()
1149 };
1150 if let Some(ref tower_storage) = config.tower_storage {
1151 validator_config.tower_storage = tower_storage.clone();
1152 }
1153
1154 let validator = Some(Validator::new(
1155 node,
1156 Arc::new(validator_identity),
1157 &ledger_path,
1158 &vote_account_address,
1159 config.authorized_voter_keypairs.clone(),
1160 vec![],
1161 &validator_config,
1162 rpc_to_plugin_manager_receiver,
1163 config.start_progress.clone(),
1164 socket_addr_space,
1165 ValidatorTpuConfig::new_for_tests(),
1166 config.admin_rpc_service_post_init.clone(),
1167 None,
1168 )?);
1169
1170 let test_validator = TestValidator {
1171 ledger_path,
1172 preserve_ledger,
1173 rpc_pubsub_url,
1174 rpc_url,
1175 tpu_quic,
1176 gossip,
1177 validator,
1178 vote_account_address,
1179 };
1180 Ok(test_validator)
1181 }
1182
1183 async fn wait_for_first_slot(&self) {
1185 let rpc_client = nonblocking::rpc_client::RpcClient::new_with_commitment(
1186 self.rpc_url.clone(),
1187 CommitmentConfig::processed(),
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 first slot {num_tries:?}...");
1197 match rpc_client.get_slot().await {
1198 Ok(slot) => {
1199 if slot > 0 {
1200 break;
1201 }
1202 }
1203 Err(err) => {
1204 warn!("get_slot() failed: {err:?}");
1205 break;
1206 }
1207 }
1208 sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT)).await;
1209 }
1210 }
1211
1212 async fn wait_for_upgradeable_programs_deployed(
1218 &self,
1219 upgradeable_programs: &[&Pubkey],
1220 payer: &Keypair,
1221 ) -> Result<(), RpcClientError> {
1222 let rpc_client = nonblocking::rpc_client::RpcClient::new_with_commitment(
1223 self.rpc_url.clone(),
1224 CommitmentConfig::processed(),
1225 );
1226
1227 let mut deployed = vec![false; upgradeable_programs.len()];
1228 const MAX_ATTEMPTS: u64 = 10;
1229
1230 for attempt in 1..=MAX_ATTEMPTS {
1231 let blockhash = rpc_client.get_latest_blockhash().await.unwrap();
1232 for (program_id, is_deployed) in upgradeable_programs.iter().zip(deployed.iter_mut()) {
1233 if *is_deployed {
1234 continue;
1235 }
1236
1237 let transaction = Transaction::new_signed_with_payer(
1238 &[Instruction {
1239 program_id: **program_id,
1240 accounts: vec![],
1241 data: vec![],
1242 }],
1243 Some(&payer.pubkey()),
1244 &[&payer],
1245 blockhash,
1246 );
1247 match rpc_client.simulate_transaction(&transaction).await {
1248 Ok(response) => {
1249 if let Some(e) = response.value.err {
1250 let err_string = format!("{e:?}");
1251 if err_string.contains("Program is not deployed") {
1252 debug!("{program_id:?} - not deployed");
1253 } else if err_string.contains("AccountNotFound") {
1254 return Err(RpcClientError::from(
1256 TransactionError::AccountNotFound,
1257 ));
1258 } else {
1259 *is_deployed = true;
1262 debug!("{program_id:?} - Unexpected error: {e:?}");
1263 }
1264 } else {
1265 *is_deployed = true;
1266 }
1267 }
1268 Err(e) => {
1269 warn!("Failed to simulate transaction: {e:?}");
1270 if attempt == MAX_ATTEMPTS {
1272 return Err(e);
1273 }
1274 }
1275 }
1276 }
1277 if deployed.iter().all(|&deployed| deployed) {
1278 return Ok(());
1279 }
1280
1281 println!("Waiting for programs to be fully deployed {attempt} ...");
1282 sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT)).await;
1283 }
1284 panic!("Timeout waiting for program to become usable");
1285 }
1286
1287 pub fn tpu_quic(&self) -> &SocketAddr {
1289 &self.tpu_quic
1290 }
1291
1292 pub fn gossip(&self) -> &SocketAddr {
1294 &self.gossip
1295 }
1296
1297 pub fn rpc_url(&self) -> String {
1299 self.rpc_url.clone()
1300 }
1301
1302 pub fn rpc_pubsub_url(&self) -> String {
1304 self.rpc_pubsub_url.clone()
1305 }
1306
1307 pub fn vote_account_address(&self) -> Pubkey {
1309 self.vote_account_address
1310 }
1311
1312 pub fn get_rpc_client(&self) -> RpcClient {
1314 RpcClient::new_with_commitment(self.rpc_url.clone(), CommitmentConfig::processed())
1315 }
1316
1317 pub fn get_async_rpc_client(&self) -> nonblocking::rpc_client::RpcClient {
1319 nonblocking::rpc_client::RpcClient::new_with_commitment(
1320 self.rpc_url.clone(),
1321 CommitmentConfig::processed(),
1322 )
1323 }
1324
1325 pub fn join(mut self) {
1326 if let Some(validator) = self.validator.take() {
1327 validator.join();
1328 }
1329 }
1330
1331 pub fn cluster_info(&self) -> Arc<ClusterInfo> {
1332 self.validator.as_ref().unwrap().cluster_info.clone()
1333 }
1334
1335 pub fn bank_forks(&self) -> Arc<RwLock<BankForks>> {
1336 self.validator.as_ref().unwrap().bank_forks.clone()
1337 }
1338
1339 pub fn repair_whitelist(&self) -> Arc<RwLock<HashSet<Pubkey>>> {
1340 Arc::new(RwLock::new(HashSet::default()))
1341 }
1342}
1343
1344impl Drop for TestValidator {
1345 fn drop(&mut self) {
1346 if let Some(validator) = self.validator.take() {
1347 validator.close();
1348 }
1349 if !self.preserve_ledger {
1350 remove_dir_all(&self.ledger_path).unwrap_or_else(|err| {
1351 panic!(
1352 "Failed to remove ledger directory {}: {}",
1353 self.ledger_path.display(),
1354 err
1355 )
1356 });
1357 }
1358 }
1359}
1360
1361#[cfg(test)]
1362mod test {
1363 use {super::*, solana_feature_gate_interface::Feature};
1364
1365 #[test]
1366 fn get_health() {
1367 let (test_validator, _payer) = TestValidatorGenesis::default().start();
1368 let rpc_client = test_validator.get_rpc_client();
1369 rpc_client.get_health().expect("health");
1370 }
1371
1372 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1373 async fn nonblocking_get_health() {
1374 let (test_validator, _payer) = TestValidatorGenesis::default().start_async().await;
1375 let rpc_client = test_validator.get_async_rpc_client();
1376 rpc_client.get_health().await.expect("health");
1377 }
1378
1379 #[test]
1380 fn test_upgradeable_program_deploayment() {
1381 let program_id = Pubkey::new_unique();
1382 let (test_validator, payer) = TestValidatorGenesis::default()
1383 .add_program("../programs/bpf-loader-tests/noop", program_id)
1384 .start();
1385 let rpc_client = test_validator.get_rpc_client();
1386
1387 let blockhash = rpc_client.get_latest_blockhash().unwrap();
1388 let transaction = Transaction::new_signed_with_payer(
1389 &[Instruction {
1390 program_id,
1391 accounts: vec![],
1392 data: vec![],
1393 }],
1394 Some(&payer.pubkey()),
1395 &[&payer],
1396 blockhash,
1397 );
1398
1399 assert!(
1400 rpc_client
1401 .send_and_confirm_transaction(&transaction)
1402 .is_ok()
1403 );
1404 }
1405
1406 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1407 async fn test_nonblocking_upgradeable_program_deploayment() {
1408 let program_id = Pubkey::new_unique();
1409 let (test_validator, payer) = TestValidatorGenesis::default()
1410 .add_program("../programs/bpf-loader-tests/noop", program_id)
1411 .start_async()
1412 .await;
1413 let rpc_client = test_validator.get_async_rpc_client();
1414
1415 let blockhash = rpc_client.get_latest_blockhash().await.unwrap();
1416 let transaction = Transaction::new_signed_with_payer(
1417 &[Instruction {
1418 program_id,
1419 accounts: vec![],
1420 data: vec![],
1421 }],
1422 Some(&payer.pubkey()),
1423 &[&payer],
1424 blockhash,
1425 );
1426
1427 assert!(
1428 rpc_client
1429 .send_and_confirm_transaction(&transaction)
1430 .await
1431 .is_ok()
1432 );
1433 }
1434
1435 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1436 #[should_panic]
1437 async fn document_tokio_panic() {
1438 let (_test_validator, _payer) = TestValidatorGenesis::default().start();
1440 }
1441
1442 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1443 async fn test_deactivate_features() {
1444 let mut control = FeatureSet::default().inactive().clone();
1445 let mut deactivate_features = Vec::new();
1446 [
1447 agave_feature_set::deprecate_rewards_sysvar::id(),
1448 agave_feature_set::disable_fees_sysvar::id(),
1449 alpenglow::id(),
1450 agave_feature_set::bls_pubkey_management_in_vote_account::id(),
1451 agave_feature_set::vote_account_initialize_v2::id(),
1452 agave_feature_set::validator_admission_ticket::id(),
1453 ]
1454 .into_iter()
1455 .for_each(|feature| {
1456 control.remove(&feature);
1457 deactivate_features.push(feature);
1458 });
1459
1460 let control: Vec<Pubkey> = control.into_iter().collect();
1462
1463 let (test_validator, _payer) = TestValidatorGenesis::default()
1464 .deactivate_features(&deactivate_features)
1465 .start_async()
1466 .await;
1467
1468 let rpc_client = test_validator.get_async_rpc_client();
1469
1470 let inactive_feature_accounts = rpc_client
1472 .get_multiple_accounts(&deactivate_features)
1473 .await
1474 .unwrap();
1475 for f in inactive_feature_accounts {
1476 assert!(f.is_none());
1477 }
1478
1479 for chunk in control.chunks(100) {
1481 let active_feature_accounts = rpc_client.get_multiple_accounts(chunk).await.unwrap();
1482 for f in active_feature_accounts {
1483 let account = f.unwrap(); let feature_state: Feature = bincode::deserialize(account.data()).unwrap();
1485 assert!(feature_state.activated_at.is_some());
1486 }
1487 }
1488 }
1489
1490 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1491 async fn test_override_feature_account() {
1492 let with_deactivate_flag = agave_feature_set::deprecate_rewards_sysvar::id();
1493 let without_deactivate_flag = agave_feature_set::disable_fees_sysvar::id();
1494
1495 let owner = Pubkey::new_unique();
1496 let account = || AccountSharedData::new(100_000, 0, &owner);
1497
1498 let (test_validator, _payer) = TestValidatorGenesis::default()
1499 .deactivate_features(&[with_deactivate_flag]) .add_accounts([
1501 (with_deactivate_flag, account()), (without_deactivate_flag, account()),
1503 ])
1504 .start_async()
1505 .await;
1506
1507 let rpc_client = test_validator.get_async_rpc_client();
1508
1509 let our_accounts = rpc_client
1510 .get_multiple_accounts(&[with_deactivate_flag, without_deactivate_flag])
1511 .await
1512 .unwrap();
1513
1514 let overridden_account = our_accounts[0].as_ref().unwrap();
1517 assert_eq!(overridden_account.lamports, 100_000);
1518 assert_eq!(overridden_account.data.len(), 0);
1519 assert_eq!(overridden_account.owner, owner);
1520
1521 let feature_account = our_accounts[1].as_ref().unwrap();
1523 assert_eq!(feature_account.owner, solana_sdk_ids::feature::id());
1524 let feature_state: Feature = bincode::deserialize(feature_account.data()).unwrap();
1525 assert!(feature_state.activated_at.is_some());
1526 }
1527
1528 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1529 async fn test_core_bpf_programs() {
1530 let (test_validator, _payer) = TestValidatorGenesis::default().start_async().await;
1531
1532 let rpc_client = test_validator.get_async_rpc_client();
1533
1534 let fetched_programs = rpc_client
1535 .get_multiple_accounts(&[
1536 solana_sdk_ids::address_lookup_table::id(),
1537 solana_sdk_ids::config::id(),
1538 solana_sdk_ids::feature::id(),
1539 solana_sdk_ids::stake::id(),
1540 ])
1541 .await
1542 .unwrap();
1543
1544 let account = fetched_programs[0].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[1].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[2].as_ref().unwrap();
1556 assert_eq!(account.owner, solana_sdk_ids::bpf_loader_upgradeable::id());
1557 assert!(account.executable);
1558
1559 let account = fetched_programs[3].as_ref().unwrap();
1561 assert_eq!(account.owner, solana_sdk_ids::bpf_loader_upgradeable::id());
1562 assert!(account.executable);
1563 }
1564
1565 #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
1566 async fn test_wait_for_program_with_unfunded_payer() {
1567 let program_id = Pubkey::new_unique();
1568 let (test_validator, _mint_keypair) = TestValidatorGenesis::default()
1569 .add_program("../programs/bpf-loader-tests/noop", program_id)
1570 .start_async()
1571 .await;
1572
1573 let unfunded_payer = Keypair::new();
1575
1576 let result = test_validator
1578 .wait_for_upgradeable_programs_deployed(&[&program_id], &unfunded_payer)
1579 .await;
1580
1581 let err = result.unwrap_err();
1583 assert!(matches!(
1584 *err.kind,
1585 solana_rpc_client_api::client_error::ErrorKind::TransactionError(
1586 TransactionError::AccountNotFound
1587 )
1588 ));
1589 }
1590}