lib.rs 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258
  1. //! The solana-program-test provides a BanksClient-based test framework SBF programs
  2. #![allow(clippy::arithmetic_side_effects)]
  3. // Export tokio for test clients
  4. pub use tokio;
  5. use {
  6. agave_feature_set::{raise_cpi_nesting_limit_to_8, FEATURE_NAMES},
  7. async_trait::async_trait,
  8. base64::{prelude::BASE64_STANDARD, Engine},
  9. chrono_humanize::{Accuracy, HumanTime, Tense},
  10. log::*,
  11. solana_account::{create_account_shared_data_for_test, Account, AccountSharedData},
  12. solana_account_info::AccountInfo,
  13. solana_banks_client::start_client,
  14. solana_banks_server::banks_server::start_local_server,
  15. solana_clock::{Epoch, Slot},
  16. solana_cluster_type::ClusterType,
  17. solana_compute_budget::compute_budget::ComputeBudget,
  18. solana_fee_calculator::{FeeRateGovernor, DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE},
  19. solana_genesis_config::GenesisConfig,
  20. solana_hash::Hash,
  21. solana_instruction::{
  22. error::{InstructionError, UNSUPPORTED_SYSVAR},
  23. Instruction,
  24. },
  25. solana_keypair::Keypair,
  26. solana_native_token::LAMPORTS_PER_SOL,
  27. solana_poh_config::PohConfig,
  28. solana_program_entrypoint::{deserialize, SUCCESS},
  29. solana_program_error::{ProgramError, ProgramResult},
  30. solana_program_runtime::{
  31. invoke_context::BuiltinFunctionWithContext, loaded_programs::ProgramCacheEntry,
  32. serialization::serialize_parameters, stable_log,
  33. },
  34. solana_pubkey::Pubkey,
  35. solana_rent::Rent,
  36. solana_runtime::{
  37. bank::Bank,
  38. bank_forks::BankForks,
  39. commitment::BlockCommitmentCache,
  40. genesis_utils::{create_genesis_config_with_leader_ex, GenesisConfigInfo},
  41. runtime_config::RuntimeConfig,
  42. },
  43. solana_signer::Signer,
  44. solana_svm_log_collector::ic_msg,
  45. solana_svm_timings::ExecuteTimings,
  46. solana_sysvar::SysvarSerialize,
  47. solana_sysvar_id::SysvarId,
  48. solana_vote_program::vote_state::{self, VoteStateV3, VoteStateVersions},
  49. std::{
  50. cell::RefCell,
  51. collections::{HashMap, HashSet},
  52. convert::TryFrom,
  53. fs::File,
  54. io::{self, Read},
  55. mem::transmute,
  56. panic::AssertUnwindSafe,
  57. path::{Path, PathBuf},
  58. sync::{
  59. atomic::{AtomicBool, Ordering},
  60. Arc, RwLock,
  61. },
  62. time::{Duration, Instant},
  63. },
  64. thiserror::Error,
  65. tokio::task::JoinHandle,
  66. };
  67. // Export types so test clients can limit their solana crate dependencies
  68. pub use {
  69. solana_banks_client::{BanksClient, BanksClientError},
  70. solana_banks_interface::BanksTransactionResultWithMetadata,
  71. solana_program_runtime::invoke_context::InvokeContext,
  72. solana_sbpf::{
  73. error::EbpfError,
  74. vm::{get_runtime_environment_key, EbpfVm},
  75. },
  76. solana_transaction_context::IndexOfAccount,
  77. };
  78. pub mod programs;
  79. /// Errors from the program test environment
  80. #[derive(Error, Debug, PartialEq, Eq)]
  81. pub enum ProgramTestError {
  82. /// The chosen warp slot is not in the future, so warp is not performed
  83. #[error("Warp slot not in the future")]
  84. InvalidWarpSlot,
  85. }
  86. thread_local! {
  87. static INVOKE_CONTEXT: RefCell<Option<usize>> = const { RefCell::new(None) };
  88. }
  89. fn set_invoke_context(new: &mut InvokeContext) {
  90. INVOKE_CONTEXT.with(|invoke_context| unsafe {
  91. invoke_context.replace(Some(transmute::<&mut InvokeContext, usize>(new)))
  92. });
  93. }
  94. fn get_invoke_context<'a, 'b>() -> &'a mut InvokeContext<'b> {
  95. let ptr = INVOKE_CONTEXT.with(|invoke_context| match *invoke_context.borrow() {
  96. Some(val) => val,
  97. None => panic!("Invoke context not set!"),
  98. });
  99. unsafe { transmute::<usize, &mut InvokeContext>(ptr) }
  100. }
  101. pub fn invoke_builtin_function(
  102. builtin_function: solana_program_entrypoint::ProcessInstruction,
  103. invoke_context: &mut InvokeContext,
  104. ) -> Result<u64, Box<dyn std::error::Error>> {
  105. set_invoke_context(invoke_context);
  106. let transaction_context = &invoke_context.transaction_context;
  107. let instruction_context = transaction_context.get_current_instruction_context()?;
  108. let instruction_account_indices = 0..instruction_context.get_number_of_instruction_accounts();
  109. // mock builtin program must consume units
  110. invoke_context.consume_checked(1)?;
  111. let log_collector = invoke_context.get_log_collector();
  112. let program_id = instruction_context.get_program_key()?;
  113. stable_log::program_invoke(
  114. &log_collector,
  115. program_id,
  116. invoke_context.get_stack_height(),
  117. );
  118. // Copy indices_in_instruction into a HashSet to ensure there are no duplicates
  119. let deduplicated_indices: HashSet<IndexOfAccount> = instruction_account_indices.collect();
  120. // Serialize entrypoint parameters with SBF ABI
  121. let mask_out_rent_epoch_in_vm_serialization = invoke_context
  122. .get_feature_set()
  123. .mask_out_rent_epoch_in_vm_serialization;
  124. let (mut parameter_bytes, _regions, _account_lengths) = serialize_parameters(
  125. &instruction_context,
  126. false, // There is no VM so stricter_abi_and_runtime_constraints can not be implemented here
  127. false, // There is no VM so account_data_direct_mapping can not be implemented here
  128. mask_out_rent_epoch_in_vm_serialization,
  129. )?;
  130. // Deserialize data back into instruction params
  131. let (program_id, account_infos, input) =
  132. unsafe { deserialize(&mut parameter_bytes.as_slice_mut()[0] as *mut u8) };
  133. // Execute the program
  134. match std::panic::catch_unwind(AssertUnwindSafe(|| {
  135. builtin_function(program_id, &account_infos, input)
  136. })) {
  137. Ok(program_result) => {
  138. program_result.map_err(|program_error| {
  139. let err = InstructionError::from(u64::from(program_error));
  140. stable_log::program_failure(&log_collector, program_id, &err);
  141. let err: Box<dyn std::error::Error> = Box::new(err);
  142. err
  143. })?;
  144. }
  145. Err(_panic_error) => {
  146. let err = InstructionError::ProgramFailedToComplete;
  147. stable_log::program_failure(&log_collector, program_id, &err);
  148. let err: Box<dyn std::error::Error> = Box::new(err);
  149. Err(err)?;
  150. }
  151. };
  152. stable_log::program_success(&log_collector, program_id);
  153. // Lookup table for AccountInfo
  154. let account_info_map: HashMap<_, _> = account_infos.into_iter().map(|a| (a.key, a)).collect();
  155. // Re-fetch the instruction context. The previous reference may have been
  156. // invalidated due to the `set_invoke_context` in a CPI.
  157. let transaction_context = &invoke_context.transaction_context;
  158. let instruction_context = transaction_context.get_current_instruction_context()?;
  159. // Commit AccountInfo changes back into KeyedAccounts
  160. for i in deduplicated_indices.into_iter() {
  161. let mut borrowed_account = instruction_context.try_borrow_instruction_account(i)?;
  162. if borrowed_account.is_writable() {
  163. if let Some(account_info) = account_info_map.get(borrowed_account.get_key()) {
  164. if borrowed_account.get_lamports() != account_info.lamports() {
  165. borrowed_account.set_lamports(account_info.lamports())?;
  166. }
  167. if borrowed_account
  168. .can_data_be_resized(account_info.data_len())
  169. .is_ok()
  170. {
  171. borrowed_account.set_data_from_slice(&account_info.data.borrow())?;
  172. }
  173. if borrowed_account.get_owner() != account_info.owner {
  174. borrowed_account.set_owner(account_info.owner.as_ref())?;
  175. }
  176. }
  177. }
  178. }
  179. Ok(0)
  180. }
  181. /// Converts a `solana-program`-style entrypoint into the runtime's entrypoint style, for
  182. /// use with `ProgramTest::add_program`
  183. #[macro_export]
  184. macro_rules! processor {
  185. ($builtin_function:expr) => {
  186. Some(|vm, _arg0, _arg1, _arg2, _arg3, _arg4| {
  187. let vm = unsafe {
  188. &mut *((vm as *mut u64).offset(-($crate::get_runtime_environment_key() as isize))
  189. as *mut $crate::EbpfVm<$crate::InvokeContext>)
  190. };
  191. vm.program_result =
  192. $crate::invoke_builtin_function($builtin_function, vm.context_object_pointer)
  193. .map_err(|err| $crate::EbpfError::SyscallError(err))
  194. .into();
  195. })
  196. };
  197. }
  198. fn get_sysvar<T: Default + SysvarSerialize + Sized + serde::de::DeserializeOwned + Clone>(
  199. sysvar: Result<Arc<T>, InstructionError>,
  200. var_addr: *mut u8,
  201. ) -> u64 {
  202. let invoke_context = get_invoke_context();
  203. if invoke_context
  204. .consume_checked(invoke_context.get_execution_cost().sysvar_base_cost + T::size_of() as u64)
  205. .is_err()
  206. {
  207. panic!("Exceeded compute budget");
  208. }
  209. match sysvar {
  210. Ok(sysvar_data) => unsafe {
  211. *(var_addr as *mut _ as *mut T) = T::clone(&sysvar_data);
  212. SUCCESS
  213. },
  214. Err(_) => UNSUPPORTED_SYSVAR,
  215. }
  216. }
  217. struct SyscallStubs {}
  218. impl solana_sysvar::program_stubs::SyscallStubs for SyscallStubs {
  219. fn sol_log(&self, message: &str) {
  220. let invoke_context = get_invoke_context();
  221. ic_msg!(invoke_context, "Program log: {}", message);
  222. }
  223. fn sol_invoke_signed(
  224. &self,
  225. instruction: &Instruction,
  226. account_infos: &[AccountInfo],
  227. signers_seeds: &[&[&[u8]]],
  228. ) -> ProgramResult {
  229. let invoke_context = get_invoke_context();
  230. let log_collector = invoke_context.get_log_collector();
  231. let transaction_context = &invoke_context.transaction_context;
  232. let instruction_context = transaction_context
  233. .get_current_instruction_context()
  234. .unwrap();
  235. let caller = instruction_context.get_program_key().unwrap();
  236. stable_log::program_invoke(
  237. &log_collector,
  238. &instruction.program_id,
  239. invoke_context.get_stack_height(),
  240. );
  241. let signers = signers_seeds
  242. .iter()
  243. .map(|seeds| Pubkey::create_program_address(seeds, caller).unwrap())
  244. .collect::<Vec<_>>();
  245. invoke_context
  246. .prepare_next_instruction(instruction, &signers)
  247. .unwrap();
  248. // Copy caller's account_info modifications into invoke_context accounts
  249. let transaction_context = &invoke_context.transaction_context;
  250. let instruction_context = transaction_context
  251. .get_current_instruction_context()
  252. .unwrap();
  253. let next_instruction_context = transaction_context.get_next_instruction_context().unwrap();
  254. let next_instruction_accounts = next_instruction_context.instruction_accounts();
  255. let mut account_indices = Vec::with_capacity(next_instruction_accounts.len());
  256. for instruction_account in next_instruction_accounts.iter() {
  257. let account_key = transaction_context
  258. .get_key_of_account_at_index(instruction_account.index_in_transaction)
  259. .unwrap();
  260. let account_info_index = account_infos
  261. .iter()
  262. .position(|account_info| account_info.unsigned_key() == account_key)
  263. .ok_or(InstructionError::MissingAccount)
  264. .unwrap();
  265. let account_info = &account_infos[account_info_index];
  266. let index_in_caller = instruction_context
  267. .get_index_of_account_in_instruction(instruction_account.index_in_transaction)
  268. .unwrap();
  269. let mut borrowed_account = instruction_context
  270. .try_borrow_instruction_account(index_in_caller)
  271. .unwrap();
  272. if borrowed_account.get_lamports() != account_info.lamports() {
  273. borrowed_account
  274. .set_lamports(account_info.lamports())
  275. .unwrap();
  276. }
  277. let account_info_data = account_info.try_borrow_data().unwrap();
  278. // The redundant check helps to avoid the expensive data comparison if we can
  279. match borrowed_account.can_data_be_resized(account_info_data.len()) {
  280. Ok(()) => borrowed_account
  281. .set_data_from_slice(&account_info_data)
  282. .unwrap(),
  283. Err(err) if borrowed_account.get_data() != *account_info_data => {
  284. panic!("{err:?}");
  285. }
  286. _ => {}
  287. }
  288. // Change the owner at the end so that we are allowed to change the lamports and data before
  289. if borrowed_account.get_owner() != account_info.owner {
  290. borrowed_account
  291. .set_owner(account_info.owner.as_ref())
  292. .unwrap();
  293. }
  294. if instruction_account.is_writable() {
  295. account_indices
  296. .push((instruction_account.index_in_transaction, account_info_index));
  297. }
  298. }
  299. let mut compute_units_consumed = 0;
  300. invoke_context
  301. .process_instruction(&mut compute_units_consumed, &mut ExecuteTimings::default())
  302. .map_err(|err| ProgramError::try_from(err).unwrap_or_else(|err| panic!("{}", err)))?;
  303. // Copy invoke_context accounts modifications into caller's account_info
  304. let transaction_context = &invoke_context.transaction_context;
  305. let instruction_context = transaction_context
  306. .get_current_instruction_context()
  307. .unwrap();
  308. for (index_in_transaction, account_info_index) in account_indices.into_iter() {
  309. let index_in_caller = instruction_context
  310. .get_index_of_account_in_instruction(index_in_transaction)
  311. .unwrap();
  312. let borrowed_account = instruction_context
  313. .try_borrow_instruction_account(index_in_caller)
  314. .unwrap();
  315. let account_info = &account_infos[account_info_index];
  316. **account_info.try_borrow_mut_lamports().unwrap() = borrowed_account.get_lamports();
  317. if account_info.owner != borrowed_account.get_owner() {
  318. // TODO Figure out a better way to allow the System Program to set the account owner
  319. #[allow(clippy::transmute_ptr_to_ptr)]
  320. #[allow(mutable_transmutes)]
  321. let account_info_mut =
  322. unsafe { transmute::<&Pubkey, &mut Pubkey>(account_info.owner) };
  323. *account_info_mut = *borrowed_account.get_owner();
  324. }
  325. let new_data = borrowed_account.get_data();
  326. let new_len = new_data.len();
  327. // Resize account_info data
  328. if account_info.data_len() != new_len {
  329. account_info.resize(new_len)?;
  330. }
  331. // Clone the data
  332. let mut data = account_info.try_borrow_mut_data()?;
  333. data.clone_from_slice(new_data);
  334. }
  335. stable_log::program_success(&log_collector, &instruction.program_id);
  336. Ok(())
  337. }
  338. fn sol_get_clock_sysvar(&self, var_addr: *mut u8) -> u64 {
  339. get_sysvar(
  340. get_invoke_context().get_sysvar_cache().get_clock(),
  341. var_addr,
  342. )
  343. }
  344. fn sol_get_epoch_schedule_sysvar(&self, var_addr: *mut u8) -> u64 {
  345. get_sysvar(
  346. get_invoke_context().get_sysvar_cache().get_epoch_schedule(),
  347. var_addr,
  348. )
  349. }
  350. fn sol_get_epoch_rewards_sysvar(&self, var_addr: *mut u8) -> u64 {
  351. get_sysvar(
  352. get_invoke_context().get_sysvar_cache().get_epoch_rewards(),
  353. var_addr,
  354. )
  355. }
  356. #[allow(deprecated)]
  357. fn sol_get_fees_sysvar(&self, var_addr: *mut u8) -> u64 {
  358. get_sysvar(get_invoke_context().get_sysvar_cache().get_fees(), var_addr)
  359. }
  360. fn sol_get_rent_sysvar(&self, var_addr: *mut u8) -> u64 {
  361. get_sysvar(get_invoke_context().get_sysvar_cache().get_rent(), var_addr)
  362. }
  363. fn sol_get_last_restart_slot(&self, var_addr: *mut u8) -> u64 {
  364. get_sysvar(
  365. get_invoke_context()
  366. .get_sysvar_cache()
  367. .get_last_restart_slot(),
  368. var_addr,
  369. )
  370. }
  371. fn sol_get_return_data(&self) -> Option<(Pubkey, Vec<u8>)> {
  372. let (program_id, data) = get_invoke_context().transaction_context.get_return_data();
  373. Some((*program_id, data.to_vec()))
  374. }
  375. fn sol_set_return_data(&self, data: &[u8]) {
  376. let invoke_context = get_invoke_context();
  377. let transaction_context = &mut invoke_context.transaction_context;
  378. let instruction_context = transaction_context
  379. .get_current_instruction_context()
  380. .unwrap();
  381. let caller = *instruction_context.get_program_key().unwrap();
  382. transaction_context
  383. .set_return_data(caller, data.to_vec())
  384. .unwrap();
  385. }
  386. fn sol_get_stack_height(&self) -> u64 {
  387. let invoke_context = get_invoke_context();
  388. invoke_context.get_stack_height().try_into().unwrap()
  389. }
  390. }
  391. pub fn find_file(filename: &str) -> Option<PathBuf> {
  392. for dir in default_shared_object_dirs() {
  393. let candidate = dir.join(filename);
  394. if candidate.exists() {
  395. return Some(candidate);
  396. }
  397. }
  398. None
  399. }
  400. fn default_shared_object_dirs() -> Vec<PathBuf> {
  401. let mut search_path = vec![];
  402. if let Ok(bpf_out_dir) = std::env::var("BPF_OUT_DIR") {
  403. search_path.push(PathBuf::from(bpf_out_dir));
  404. } else if let Ok(bpf_out_dir) = std::env::var("SBF_OUT_DIR") {
  405. search_path.push(PathBuf::from(bpf_out_dir));
  406. }
  407. search_path.push(PathBuf::from("tests/fixtures"));
  408. if let Ok(dir) = std::env::current_dir() {
  409. search_path.push(dir);
  410. }
  411. trace!("SBF .so search path: {search_path:?}");
  412. search_path
  413. }
  414. pub fn read_file<P: AsRef<Path>>(path: P) -> Vec<u8> {
  415. let path = path.as_ref();
  416. let mut file = File::open(path)
  417. .unwrap_or_else(|err| panic!("Failed to open \"{}\": {}", path.display(), err));
  418. let mut file_data = Vec::new();
  419. file.read_to_end(&mut file_data)
  420. .unwrap_or_else(|err| panic!("Failed to read \"{}\": {}", path.display(), err));
  421. file_data
  422. }
  423. pub struct ProgramTest {
  424. accounts: Vec<(Pubkey, AccountSharedData)>,
  425. genesis_accounts: Vec<(Pubkey, AccountSharedData)>,
  426. builtin_programs: Vec<(Pubkey, &'static str, ProgramCacheEntry)>,
  427. compute_max_units: Option<u64>,
  428. prefer_bpf: bool,
  429. deactivate_feature_set: HashSet<Pubkey>,
  430. transaction_account_lock_limit: Option<usize>,
  431. }
  432. impl Default for ProgramTest {
  433. /// Initialize a new ProgramTest
  434. ///
  435. /// If the `BPF_OUT_DIR` environment variable is defined, BPF programs will be preferred over
  436. /// over a native instruction processor. The `ProgramTest::prefer_bpf()` method may be
  437. /// used to override this preference at runtime. `cargo test-bpf` will set `BPF_OUT_DIR`
  438. /// automatically.
  439. ///
  440. /// SBF program shared objects and account data files are searched for in
  441. /// * the value of the `BPF_OUT_DIR` environment variable
  442. /// * the `tests/fixtures` sub-directory
  443. /// * the current working directory
  444. ///
  445. fn default() -> Self {
  446. solana_logger::setup_with_default(
  447. "solana_sbpf::vm=debug,solana_runtime::message_processor=debug,\
  448. solana_runtime::system_instruction_processor=trace,solana_program_test=info",
  449. );
  450. let prefer_bpf =
  451. std::env::var("BPF_OUT_DIR").is_ok() || std::env::var("SBF_OUT_DIR").is_ok();
  452. Self {
  453. accounts: vec![],
  454. genesis_accounts: vec![],
  455. builtin_programs: vec![],
  456. compute_max_units: None,
  457. prefer_bpf,
  458. deactivate_feature_set: HashSet::default(),
  459. transaction_account_lock_limit: None,
  460. }
  461. }
  462. }
  463. impl ProgramTest {
  464. /// Create a `ProgramTest`.
  465. ///
  466. /// This is a wrapper around [`default`] and [`add_program`]. See their documentation for more
  467. /// details.
  468. ///
  469. /// [`default`]: #method.default
  470. /// [`add_program`]: #method.add_program
  471. pub fn new(
  472. program_name: &'static str,
  473. program_id: Pubkey,
  474. builtin_function: Option<BuiltinFunctionWithContext>,
  475. ) -> Self {
  476. let mut me = Self::default();
  477. me.add_program(program_name, program_id, builtin_function);
  478. me
  479. }
  480. /// Override default SBF program selection
  481. pub fn prefer_bpf(&mut self, prefer_bpf: bool) {
  482. self.prefer_bpf = prefer_bpf;
  483. }
  484. /// Override the default maximum compute units
  485. pub fn set_compute_max_units(&mut self, compute_max_units: u64) {
  486. debug_assert!(
  487. compute_max_units <= i64::MAX as u64,
  488. "Compute unit limit must fit in `i64::MAX`"
  489. );
  490. self.compute_max_units = Some(compute_max_units);
  491. }
  492. /// Override the default transaction account lock limit
  493. pub fn set_transaction_account_lock_limit(&mut self, transaction_account_lock_limit: usize) {
  494. self.transaction_account_lock_limit = Some(transaction_account_lock_limit);
  495. }
  496. /// Add an account to the test environment's genesis config.
  497. pub fn add_genesis_account(&mut self, address: Pubkey, account: Account) {
  498. self.genesis_accounts
  499. .push((address, AccountSharedData::from(account)));
  500. }
  501. /// Add an account to the test environment
  502. pub fn add_account(&mut self, address: Pubkey, account: Account) {
  503. self.accounts
  504. .push((address, AccountSharedData::from(account)));
  505. }
  506. /// Add an account to the test environment with the account data in the provided `filename`
  507. pub fn add_account_with_file_data(
  508. &mut self,
  509. address: Pubkey,
  510. lamports: u64,
  511. owner: Pubkey,
  512. filename: &str,
  513. ) {
  514. self.add_account(
  515. address,
  516. Account {
  517. lamports,
  518. data: read_file(find_file(filename).unwrap_or_else(|| {
  519. panic!("Unable to locate {filename}");
  520. })),
  521. owner,
  522. executable: false,
  523. rent_epoch: 0,
  524. },
  525. );
  526. }
  527. /// Add an account to the test environment with the account data in the provided as a base 64
  528. /// string
  529. pub fn add_account_with_base64_data(
  530. &mut self,
  531. address: Pubkey,
  532. lamports: u64,
  533. owner: Pubkey,
  534. data_base64: &str,
  535. ) {
  536. self.add_account(
  537. address,
  538. Account {
  539. lamports,
  540. data: BASE64_STANDARD
  541. .decode(data_base64)
  542. .unwrap_or_else(|err| panic!("Failed to base64 decode: {err}")),
  543. owner,
  544. executable: false,
  545. rent_epoch: 0,
  546. },
  547. );
  548. }
  549. pub fn add_sysvar_account<S: SysvarSerialize>(&mut self, address: Pubkey, sysvar: &S) {
  550. let account = create_account_shared_data_for_test(sysvar);
  551. self.add_account(address, account.into());
  552. }
  553. /// Add a BPF Upgradeable program to the test environment's genesis config.
  554. ///
  555. /// When testing BPF programs using the program ID of a runtime builtin
  556. /// program - such as Core BPF programs - the program accounts must be
  557. /// added to the genesis config in order to make them available to the new
  558. /// Bank as it's being initialized.
  559. ///
  560. /// The presence of these program accounts will cause Bank to skip adding
  561. /// the builtin version of the program, allowing the provided BPF program
  562. /// to be used at the designated program ID instead.
  563. ///
  564. /// See https://github.com/anza-xyz/agave/blob/c038908600b8a1b0080229dea015d7fc9939c418/runtime/src/bank.rs#L5109-L5126.
  565. pub fn add_upgradeable_program_to_genesis(
  566. &mut self,
  567. program_name: &'static str,
  568. program_id: &Pubkey,
  569. ) {
  570. let program_file = find_file(&format!("{program_name}.so"))
  571. .expect("Program file data not available for {program_name} ({program_id})");
  572. let elf = read_file(program_file);
  573. let program_accounts =
  574. programs::bpf_loader_upgradeable_program_accounts(program_id, &elf, &Rent::default());
  575. for (address, account) in program_accounts {
  576. self.add_genesis_account(address, account);
  577. }
  578. }
  579. /// Add a SBF program to the test environment.
  580. ///
  581. /// `program_name` will also be used to locate the SBF shared object in the current or fixtures
  582. /// directory.
  583. ///
  584. /// If `builtin_function` is provided, the natively built-program may be used instead of the
  585. /// SBF shared object depending on the `BPF_OUT_DIR` environment variable.
  586. pub fn add_program(
  587. &mut self,
  588. program_name: &'static str,
  589. program_id: Pubkey,
  590. builtin_function: Option<BuiltinFunctionWithContext>,
  591. ) {
  592. let add_bpf = |this: &mut ProgramTest, program_file: PathBuf| {
  593. let data = read_file(&program_file);
  594. info!(
  595. "\"{}\" SBF program from {}{}",
  596. program_name,
  597. program_file.display(),
  598. std::fs::metadata(&program_file)
  599. .map(|metadata| {
  600. metadata
  601. .modified()
  602. .map(|time| {
  603. format!(
  604. ", modified {}",
  605. HumanTime::from(time)
  606. .to_text_en(Accuracy::Precise, Tense::Past)
  607. )
  608. })
  609. .ok()
  610. })
  611. .ok()
  612. .flatten()
  613. .unwrap_or_default()
  614. );
  615. this.add_account(
  616. program_id,
  617. Account {
  618. lamports: Rent::default().minimum_balance(data.len()).max(1),
  619. data,
  620. owner: solana_sdk_ids::bpf_loader::id(),
  621. executable: true,
  622. rent_epoch: 0,
  623. },
  624. );
  625. };
  626. let warn_invalid_program_name = || {
  627. let valid_program_names = default_shared_object_dirs()
  628. .iter()
  629. .filter_map(|dir| dir.read_dir().ok())
  630. .flat_map(|read_dir| {
  631. read_dir.filter_map(|entry| {
  632. let path = entry.ok()?.path();
  633. if !path.is_file() {
  634. return None;
  635. }
  636. match path.extension()?.to_str()? {
  637. "so" => Some(path.file_stem()?.to_os_string()),
  638. _ => None,
  639. }
  640. })
  641. })
  642. .collect::<Vec<_>>();
  643. if valid_program_names.is_empty() {
  644. // This should be unreachable as `test-bpf` should guarantee at least one shared
  645. // object exists somewhere.
  646. warn!("No SBF shared objects found.");
  647. return;
  648. }
  649. warn!(
  650. "Possible bogus program name. Ensure the program name ({program_name}) matches \
  651. one of the following recognizable program names:",
  652. );
  653. for name in valid_program_names {
  654. warn!(" - {}", name.to_str().unwrap());
  655. }
  656. };
  657. let program_file = find_file(&format!("{program_name}.so"));
  658. match (self.prefer_bpf, program_file, builtin_function) {
  659. // If SBF is preferred (i.e., `test-sbf` is invoked) and a BPF shared object exists,
  660. // use that as the program data.
  661. (true, Some(file), _) => add_bpf(self, file),
  662. // If SBF is not required (i.e., we were invoked with `test`), use the provided
  663. // processor function as is.
  664. (false, _, Some(builtin_function)) => {
  665. self.add_builtin_program(program_name, program_id, builtin_function)
  666. }
  667. // Invalid: `test-sbf` invocation with no matching SBF shared object.
  668. (true, None, _) => {
  669. warn_invalid_program_name();
  670. panic!("Program file data not available for {program_name} ({program_id})");
  671. }
  672. // Invalid: regular `test` invocation without a processor.
  673. (false, _, None) => {
  674. panic!("Program processor not available for {program_name} ({program_id})");
  675. }
  676. }
  677. }
  678. /// Add a builtin program to the test environment.
  679. ///
  680. /// Note that builtin programs are responsible for their own `stable_log` output.
  681. pub fn add_builtin_program(
  682. &mut self,
  683. program_name: &'static str,
  684. program_id: Pubkey,
  685. builtin_function: BuiltinFunctionWithContext,
  686. ) {
  687. info!("\"{program_name}\" builtin program");
  688. self.builtin_programs.push((
  689. program_id,
  690. program_name,
  691. ProgramCacheEntry::new_builtin(0, program_name.len(), builtin_function),
  692. ));
  693. }
  694. /// Deactivate a runtime feature.
  695. ///
  696. /// Note that all features are activated by default.
  697. pub fn deactivate_feature(&mut self, feature_id: Pubkey) {
  698. self.deactivate_feature_set.insert(feature_id);
  699. }
  700. fn setup_bank(
  701. &mut self,
  702. ) -> (
  703. Arc<RwLock<BankForks>>,
  704. Arc<RwLock<BlockCommitmentCache>>,
  705. Hash,
  706. GenesisConfigInfo,
  707. ) {
  708. {
  709. use std::sync::Once;
  710. static ONCE: Once = Once::new();
  711. ONCE.call_once(|| {
  712. solana_sysvar::program_stubs::set_syscall_stubs(Box::new(SyscallStubs {}));
  713. });
  714. }
  715. let rent = Rent::default();
  716. let fee_rate_governor = FeeRateGovernor {
  717. // Initialize with a non-zero fee
  718. lamports_per_signature: DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE / 2,
  719. ..FeeRateGovernor::default()
  720. };
  721. let bootstrap_validator_pubkey = Pubkey::new_unique();
  722. let bootstrap_validator_stake_lamports =
  723. rent.minimum_balance(VoteStateV3::size_of()) + 1_000_000 * LAMPORTS_PER_SOL;
  724. let mint_keypair = Keypair::new();
  725. let voting_keypair = Keypair::new();
  726. let mut genesis_config = create_genesis_config_with_leader_ex(
  727. 1_000_000 * LAMPORTS_PER_SOL,
  728. &mint_keypair.pubkey(),
  729. &bootstrap_validator_pubkey,
  730. &voting_keypair.pubkey(),
  731. &Pubkey::new_unique(),
  732. bootstrap_validator_stake_lamports,
  733. 42,
  734. fee_rate_governor,
  735. rent.clone(),
  736. ClusterType::Development,
  737. std::mem::take(&mut self.genesis_accounts),
  738. );
  739. // Remove features tagged to deactivate
  740. for deactivate_feature_pk in &self.deactivate_feature_set {
  741. if FEATURE_NAMES.contains_key(deactivate_feature_pk) {
  742. match genesis_config.accounts.remove(deactivate_feature_pk) {
  743. Some(_) => debug!("Feature for {deactivate_feature_pk:?} deactivated"),
  744. None => warn!(
  745. "Feature {deactivate_feature_pk:?} set for deactivation not found in \
  746. genesis_config account list, ignored."
  747. ),
  748. }
  749. } else {
  750. warn!(
  751. "Feature {deactivate_feature_pk:?} set for deactivation is not a known \
  752. Feature public key"
  753. );
  754. }
  755. }
  756. let target_tick_duration = Duration::from_micros(100);
  757. genesis_config.poh_config = PohConfig::new_sleep(target_tick_duration);
  758. debug!("Payer address: {}", mint_keypair.pubkey());
  759. debug!("Genesis config: {genesis_config}");
  760. let bank = Bank::new_with_paths(
  761. &genesis_config,
  762. Arc::new(RuntimeConfig {
  763. compute_budget: self.compute_max_units.map(|max_units| ComputeBudget {
  764. compute_unit_limit: max_units,
  765. ..ComputeBudget::new_with_defaults(
  766. genesis_config
  767. .accounts
  768. .contains_key(&raise_cpi_nesting_limit_to_8::id()),
  769. )
  770. }),
  771. transaction_account_lock_limit: self.transaction_account_lock_limit,
  772. ..RuntimeConfig::default()
  773. }),
  774. Vec::default(),
  775. None,
  776. None,
  777. false,
  778. None,
  779. None,
  780. None,
  781. Arc::default(),
  782. None,
  783. None,
  784. );
  785. // Add commonly-used SPL programs as a convenience to the user
  786. for (program_id, account) in programs::spl_programs(&rent).iter() {
  787. bank.store_account(program_id, account);
  788. }
  789. // Add migrated Core BPF programs.
  790. for (program_id, account) in programs::core_bpf_programs(&rent, |feature_id| {
  791. genesis_config.accounts.contains_key(feature_id)
  792. })
  793. .iter()
  794. {
  795. bank.store_account(program_id, account);
  796. }
  797. // User-supplied additional builtins
  798. let mut builtin_programs = Vec::new();
  799. std::mem::swap(&mut self.builtin_programs, &mut builtin_programs);
  800. for (program_id, name, builtin) in builtin_programs.into_iter() {
  801. bank.add_builtin(program_id, name, builtin);
  802. }
  803. for (address, account) in self.accounts.iter() {
  804. if bank.get_account(address).is_some() {
  805. info!("Overriding account at {address}");
  806. }
  807. bank.store_account(address, account);
  808. }
  809. bank.set_capitalization_for_tests(bank.calculate_capitalization_for_tests());
  810. // Advance beyond slot 0 for a slightly more realistic test environment
  811. let bank = {
  812. let bank = Arc::new(bank);
  813. bank.fill_bank_with_ticks_for_tests();
  814. let bank = Bank::new_from_parent(bank.clone(), bank.collector_id(), bank.slot() + 1);
  815. debug!("Bank slot: {}", bank.slot());
  816. bank
  817. };
  818. let slot = bank.slot();
  819. let last_blockhash = bank.last_blockhash();
  820. let bank_forks = BankForks::new_rw_arc(bank);
  821. let block_commitment_cache = Arc::new(RwLock::new(
  822. BlockCommitmentCache::new_for_tests_with_slots(slot, slot),
  823. ));
  824. (
  825. bank_forks,
  826. block_commitment_cache,
  827. last_blockhash,
  828. GenesisConfigInfo {
  829. genesis_config,
  830. mint_keypair,
  831. voting_keypair,
  832. validator_pubkey: bootstrap_validator_pubkey,
  833. },
  834. )
  835. }
  836. pub async fn start(mut self) -> (BanksClient, Keypair, Hash) {
  837. let (bank_forks, block_commitment_cache, last_blockhash, gci) = self.setup_bank();
  838. let target_tick_duration = gci.genesis_config.poh_config.target_tick_duration;
  839. let target_slot_duration = target_tick_duration * gci.genesis_config.ticks_per_slot as u32;
  840. let transport = start_local_server(
  841. bank_forks.clone(),
  842. block_commitment_cache.clone(),
  843. target_tick_duration,
  844. )
  845. .await;
  846. let banks_client = start_client(transport)
  847. .await
  848. .unwrap_or_else(|err| panic!("Failed to start banks client: {err}"));
  849. // Run a simulated PohService to provide the client with new blockhashes. New blockhashes
  850. // are required when sending multiple otherwise identical transactions in series from a
  851. // test
  852. tokio::spawn(async move {
  853. loop {
  854. tokio::time::sleep(target_slot_duration).await;
  855. bank_forks
  856. .read()
  857. .unwrap()
  858. .working_bank()
  859. .register_unique_recent_blockhash_for_test();
  860. }
  861. });
  862. (banks_client, gci.mint_keypair, last_blockhash)
  863. }
  864. /// Start the test client
  865. ///
  866. /// Returns a `BanksClient` interface into the test environment as well as a payer `Keypair`
  867. /// with SOL for sending transactions
  868. pub async fn start_with_context(mut self) -> ProgramTestContext {
  869. let (bank_forks, block_commitment_cache, last_blockhash, gci) = self.setup_bank();
  870. let target_tick_duration = gci.genesis_config.poh_config.target_tick_duration;
  871. let transport = start_local_server(
  872. bank_forks.clone(),
  873. block_commitment_cache.clone(),
  874. target_tick_duration,
  875. )
  876. .await;
  877. let banks_client = start_client(transport)
  878. .await
  879. .unwrap_or_else(|err| panic!("Failed to start banks client: {err}"));
  880. ProgramTestContext::new(
  881. bank_forks,
  882. block_commitment_cache,
  883. banks_client,
  884. last_blockhash,
  885. gci,
  886. )
  887. }
  888. }
  889. #[async_trait]
  890. pub trait ProgramTestBanksClientExt {
  891. /// Get a new latest blockhash, similar in spirit to RpcClient::get_latest_blockhash()
  892. async fn get_new_latest_blockhash(&mut self, blockhash: &Hash) -> io::Result<Hash>;
  893. }
  894. #[async_trait]
  895. impl ProgramTestBanksClientExt for BanksClient {
  896. async fn get_new_latest_blockhash(&mut self, blockhash: &Hash) -> io::Result<Hash> {
  897. let mut num_retries = 0;
  898. let start = Instant::now();
  899. while start.elapsed().as_secs() < 5 {
  900. let new_blockhash = self.get_latest_blockhash().await?;
  901. if new_blockhash != *blockhash {
  902. return Ok(new_blockhash);
  903. }
  904. debug!("Got same blockhash ({blockhash:?}), will retry...");
  905. tokio::time::sleep(Duration::from_millis(200)).await;
  906. num_retries += 1;
  907. }
  908. Err(io::Error::other(format!(
  909. "Unable to get new blockhash after {}ms (retried {} times), stuck at {}",
  910. start.elapsed().as_millis(),
  911. num_retries,
  912. blockhash
  913. )))
  914. }
  915. }
  916. struct DroppableTask<T>(Arc<AtomicBool>, JoinHandle<T>);
  917. impl<T> Drop for DroppableTask<T> {
  918. fn drop(&mut self) {
  919. self.0.store(true, Ordering::Relaxed);
  920. trace!(
  921. "stopping task, which is currently {}",
  922. if self.1.is_finished() {
  923. "finished"
  924. } else {
  925. "running"
  926. }
  927. );
  928. }
  929. }
  930. pub struct ProgramTestContext {
  931. pub banks_client: BanksClient,
  932. pub last_blockhash: Hash,
  933. pub payer: Keypair,
  934. genesis_config: GenesisConfig,
  935. bank_forks: Arc<RwLock<BankForks>>,
  936. block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
  937. _bank_task: DroppableTask<()>,
  938. }
  939. impl ProgramTestContext {
  940. fn new(
  941. bank_forks: Arc<RwLock<BankForks>>,
  942. block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
  943. banks_client: BanksClient,
  944. last_blockhash: Hash,
  945. genesis_config_info: GenesisConfigInfo,
  946. ) -> Self {
  947. // Run a simulated PohService to provide the client with new blockhashes. New blockhashes
  948. // are required when sending multiple otherwise identical transactions in series from a
  949. // test
  950. let running_bank_forks = bank_forks.clone();
  951. let target_tick_duration = genesis_config_info
  952. .genesis_config
  953. .poh_config
  954. .target_tick_duration;
  955. let target_slot_duration =
  956. target_tick_duration * genesis_config_info.genesis_config.ticks_per_slot as u32;
  957. let exit = Arc::new(AtomicBool::new(false));
  958. let bank_task = DroppableTask(
  959. exit.clone(),
  960. tokio::spawn(async move {
  961. loop {
  962. if exit.load(Ordering::Relaxed) {
  963. break;
  964. }
  965. tokio::time::sleep(target_slot_duration).await;
  966. running_bank_forks
  967. .read()
  968. .unwrap()
  969. .working_bank()
  970. .register_unique_recent_blockhash_for_test();
  971. }
  972. }),
  973. );
  974. Self {
  975. banks_client,
  976. last_blockhash,
  977. payer: genesis_config_info.mint_keypair,
  978. genesis_config: genesis_config_info.genesis_config,
  979. bank_forks,
  980. block_commitment_cache,
  981. _bank_task: bank_task,
  982. }
  983. }
  984. pub fn genesis_config(&self) -> &GenesisConfig {
  985. &self.genesis_config
  986. }
  987. /// Manually increment vote credits for the current epoch in the specified vote account to simulate validator voting activity
  988. pub fn increment_vote_account_credits(
  989. &mut self,
  990. vote_account_address: &Pubkey,
  991. number_of_credits: u64,
  992. ) {
  993. let bank_forks = self.bank_forks.read().unwrap();
  994. let bank = bank_forks.working_bank();
  995. // generate some vote activity for rewards
  996. let mut vote_account = bank.get_account(vote_account_address).unwrap();
  997. let mut vote_state = vote_state::from(&vote_account).unwrap();
  998. let epoch = bank.epoch();
  999. for _ in 0..number_of_credits {
  1000. vote_state.increment_credits(epoch, 1);
  1001. }
  1002. let versioned = VoteStateVersions::new_v3(vote_state);
  1003. vote_state::to(&versioned, &mut vote_account).unwrap();
  1004. bank.store_account(vote_account_address, &vote_account);
  1005. }
  1006. /// Create or overwrite an account, subverting normal runtime checks.
  1007. ///
  1008. /// This method exists to make it easier to set up artificial situations
  1009. /// that would be difficult to replicate by sending individual transactions.
  1010. /// Beware that it can be used to create states that would not be reachable
  1011. /// by sending transactions!
  1012. pub fn set_account(&mut self, address: &Pubkey, account: &AccountSharedData) {
  1013. let bank_forks = self.bank_forks.read().unwrap();
  1014. let bank = bank_forks.working_bank();
  1015. bank.store_account(address, account);
  1016. }
  1017. /// Create or overwrite a sysvar, subverting normal runtime checks.
  1018. ///
  1019. /// This method exists to make it easier to set up artificial situations
  1020. /// that would be difficult to replicate on a new test cluster. Beware
  1021. /// that it can be used to create states that would not be reachable
  1022. /// under normal conditions!
  1023. pub fn set_sysvar<T: SysvarId + SysvarSerialize>(&self, sysvar: &T) {
  1024. let bank_forks = self.bank_forks.read().unwrap();
  1025. let bank = bank_forks.working_bank();
  1026. bank.set_sysvar_for_tests(sysvar);
  1027. }
  1028. /// Force the working bank ahead to a new slot
  1029. pub fn warp_to_slot(&mut self, warp_slot: Slot) -> Result<(), ProgramTestError> {
  1030. let mut bank_forks = self.bank_forks.write().unwrap();
  1031. let bank = bank_forks.working_bank();
  1032. // Fill ticks until a new blockhash is recorded, otherwise retried transactions will have
  1033. // the same signature
  1034. bank.fill_bank_with_ticks_for_tests();
  1035. // Ensure that we are actually progressing forward
  1036. let working_slot = bank.slot();
  1037. if warp_slot <= working_slot {
  1038. return Err(ProgramTestError::InvalidWarpSlot);
  1039. }
  1040. // Warp ahead to one slot *before* the desired slot because the bank
  1041. // from Bank::warp_from_parent() is frozen. If the desired slot is one
  1042. // slot *after* the working_slot, no need to warp at all.
  1043. let pre_warp_slot = warp_slot - 1;
  1044. let warp_bank = if pre_warp_slot == working_slot {
  1045. bank.freeze();
  1046. bank
  1047. } else {
  1048. bank_forks
  1049. .insert(Bank::warp_from_parent(
  1050. bank,
  1051. &Pubkey::default(),
  1052. pre_warp_slot,
  1053. ))
  1054. .clone_without_scheduler()
  1055. };
  1056. bank_forks
  1057. .set_root(
  1058. pre_warp_slot,
  1059. None, // snapshots are disabled
  1060. Some(pre_warp_slot),
  1061. )
  1062. .unwrap();
  1063. // warp_bank is frozen so go forward to get unfrozen bank at warp_slot
  1064. bank_forks.insert(Bank::new_from_parent(
  1065. warp_bank,
  1066. &Pubkey::default(),
  1067. warp_slot,
  1068. ));
  1069. // Update block commitment cache, otherwise banks server will poll at
  1070. // the wrong slot
  1071. let mut w_block_commitment_cache = self.block_commitment_cache.write().unwrap();
  1072. // HACK: The root set here should be `pre_warp_slot`, but since we're
  1073. // in a testing environment, the root bank never updates after a warp.
  1074. // The ticking thread only updates the working bank, and never the root
  1075. // bank.
  1076. w_block_commitment_cache.set_all_slots(warp_slot, warp_slot);
  1077. let bank = bank_forks.working_bank();
  1078. self.last_blockhash = bank.last_blockhash();
  1079. Ok(())
  1080. }
  1081. pub fn warp_to_epoch(&mut self, warp_epoch: Epoch) -> Result<(), ProgramTestError> {
  1082. let warp_slot = self
  1083. .genesis_config
  1084. .epoch_schedule
  1085. .get_first_slot_in_epoch(warp_epoch);
  1086. self.warp_to_slot(warp_slot)
  1087. }
  1088. /// warp forward one more slot and force reward interval end
  1089. pub fn warp_forward_force_reward_interval_end(&mut self) -> Result<(), ProgramTestError> {
  1090. let mut bank_forks = self.bank_forks.write().unwrap();
  1091. let bank = bank_forks.working_bank();
  1092. // Fill ticks until a new blockhash is recorded, otherwise retried transactions will have
  1093. // the same signature
  1094. bank.fill_bank_with_ticks_for_tests();
  1095. let pre_warp_slot = bank.slot();
  1096. bank_forks
  1097. .set_root(
  1098. pre_warp_slot,
  1099. None, // snapshot_controller
  1100. Some(pre_warp_slot),
  1101. )
  1102. .unwrap();
  1103. // warp_bank is frozen so go forward to get unfrozen bank at warp_slot
  1104. let warp_slot = pre_warp_slot + 1;
  1105. let mut warp_bank = Bank::new_from_parent(bank, &Pubkey::default(), warp_slot);
  1106. warp_bank.force_reward_interval_end_for_tests();
  1107. bank_forks.insert(warp_bank);
  1108. // Update block commitment cache, otherwise banks server will poll at
  1109. // the wrong slot
  1110. let mut w_block_commitment_cache = self.block_commitment_cache.write().unwrap();
  1111. // HACK: The root set here should be `pre_warp_slot`, but since we're
  1112. // in a testing environment, the root bank never updates after a warp.
  1113. // The ticking thread only updates the working bank, and never the root
  1114. // bank.
  1115. w_block_commitment_cache.set_all_slots(warp_slot, warp_slot);
  1116. let bank = bank_forks.working_bank();
  1117. self.last_blockhash = bank.last_blockhash();
  1118. Ok(())
  1119. }
  1120. /// Get a new latest blockhash, similar in spirit to RpcClient::get_latest_blockhash()
  1121. pub async fn get_new_latest_blockhash(&mut self) -> io::Result<Hash> {
  1122. let blockhash = self
  1123. .banks_client
  1124. .get_new_latest_blockhash(&self.last_blockhash)
  1125. .await?;
  1126. self.last_blockhash = blockhash;
  1127. Ok(blockhash)
  1128. }
  1129. /// record a hard fork slot in working bank; should be in the past
  1130. pub fn register_hard_fork(&mut self, hard_fork_slot: Slot) {
  1131. self.bank_forks
  1132. .read()
  1133. .unwrap()
  1134. .working_bank()
  1135. .register_hard_fork(hard_fork_slot)
  1136. }
  1137. }