integration_test.rs 131 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688
  1. #![cfg(test)]
  2. #![allow(clippy::arithmetic_side_effects)]
  3. use {
  4. crate::mock_bank::{
  5. create_custom_loader, deploy_program_with_upgrade_authority, load_program, program_address,
  6. program_data_size, register_builtins, MockBankCallback, MockForkGraph, EXECUTION_EPOCH,
  7. EXECUTION_SLOT, WALLCLOCK_TIME,
  8. },
  9. solana_account::{AccountSharedData, ReadableAccount, WritableAccount, PROGRAM_OWNERS},
  10. solana_clock::Slot,
  11. solana_compute_budget::compute_budget_limits::ComputeBudgetLimits,
  12. solana_compute_budget_interface::ComputeBudgetInstruction,
  13. solana_fee_structure::FeeDetails,
  14. solana_hash::Hash,
  15. solana_instruction::{AccountMeta, Instruction},
  16. solana_keypair::Keypair,
  17. solana_loader_v3_interface::{
  18. get_program_data_address, instruction as loaderv3_instruction,
  19. state::UpgradeableLoaderState,
  20. },
  21. solana_native_token::LAMPORTS_PER_SOL,
  22. solana_nonce::{self as nonce, state::DurableNonce},
  23. solana_program_entrypoint::MAX_PERMITTED_DATA_INCREASE,
  24. solana_program_runtime::execution_budget::{
  25. SVMTransactionExecutionAndFeeBudgetLimits, MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
  26. },
  27. solana_pubkey::Pubkey,
  28. solana_sdk_ids::{bpf_loader_upgradeable, compute_budget, native_loader},
  29. solana_signer::Signer,
  30. solana_svm::{
  31. account_loader::{CheckedTransactionDetails, TransactionCheckResult},
  32. nonce_info::NonceInfo,
  33. transaction_execution_result::TransactionExecutionDetails,
  34. transaction_processing_result::{
  35. ProcessedTransaction, TransactionProcessingResult,
  36. TransactionProcessingResultExtensions,
  37. },
  38. transaction_processor::{
  39. ExecutionRecordingConfig, LoadAndExecuteSanitizedTransactionsOutput,
  40. TransactionBatchProcessor, TransactionProcessingConfig,
  41. TransactionProcessingEnvironment,
  42. },
  43. },
  44. solana_svm_feature_set::SVMFeatureSet,
  45. solana_svm_transaction::{instruction::SVMInstruction, svm_message::SVMMessage},
  46. solana_svm_type_overrides::sync::{Arc, RwLock},
  47. solana_system_interface::{instruction as system_instruction, program as system_program},
  48. solana_system_transaction as system_transaction,
  49. solana_sysvar::rent::Rent,
  50. solana_transaction::{sanitized::SanitizedTransaction, Transaction},
  51. solana_transaction_context::TransactionReturnData,
  52. solana_transaction_error::TransactionError,
  53. std::{collections::HashMap, num::NonZeroU32, slice, sync::atomic::Ordering},
  54. test_case::test_case,
  55. };
  56. // This module contains the implementation of TransactionProcessingCallback
  57. mod mock_bank;
  58. // Local implementation of compute budget processing for tests.
  59. fn process_test_compute_budget_instructions<'a>(
  60. instructions: impl Iterator<Item = (&'a Pubkey, SVMInstruction<'a>)> + Clone,
  61. ) -> Result<ComputeBudgetLimits, TransactionError> {
  62. let mut loaded_accounts_data_size_limit = None;
  63. // Scan for compute budget instructions.
  64. // Only key on `SetLoadedAccountsDataSizeLimit`.
  65. for (program_id, instruction) in instructions {
  66. if *program_id == compute_budget::id()
  67. && instruction.data.len() >= 5
  68. && instruction.data[0] == 4
  69. {
  70. let size = u32::from_le_bytes([
  71. instruction.data[1],
  72. instruction.data[2],
  73. instruction.data[3],
  74. instruction.data[4],
  75. ]);
  76. loaded_accounts_data_size_limit = Some(size);
  77. }
  78. }
  79. let loaded_accounts_bytes =
  80. if let Some(requested_loaded_accounts_data_size_limit) = loaded_accounts_data_size_limit {
  81. NonZeroU32::new(requested_loaded_accounts_data_size_limit)
  82. .ok_or(TransactionError::InvalidLoadedAccountsDataSizeLimit)?
  83. } else {
  84. MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES
  85. }
  86. .min(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES);
  87. Ok(ComputeBudgetLimits {
  88. loaded_accounts_bytes,
  89. ..Default::default()
  90. })
  91. }
  92. const DEPLOYMENT_SLOT: u64 = 0;
  93. const LAMPORTS_PER_SIGNATURE: u64 = 5000;
  94. const LAST_BLOCKHASH: Hash = Hash::new_from_array([7; 32]); // Arbitrary constant hash for advancing nonces
  95. pub type AccountsMap = HashMap<Pubkey, AccountSharedData>;
  96. // container for everything needed to execute a test entry
  97. // care should be taken if reused, because we update bank account states, but otherwise leave it as-is
  98. // the environment is made available for tests that check it after processing
  99. pub struct SvmTestEnvironment<'a> {
  100. pub mock_bank: MockBankCallback,
  101. pub fork_graph: Arc<RwLock<MockForkGraph>>,
  102. pub batch_processor: TransactionBatchProcessor<MockForkGraph>,
  103. pub processing_config: TransactionProcessingConfig<'a>,
  104. pub processing_environment: TransactionProcessingEnvironment,
  105. pub test_entry: SvmTestEntry,
  106. }
  107. impl SvmTestEnvironment<'_> {
  108. pub fn create(test_entry: SvmTestEntry) -> Self {
  109. let mock_bank = MockBankCallback::default();
  110. for (name, slot, authority) in &test_entry.initial_programs {
  111. deploy_program_with_upgrade_authority(name.to_string(), *slot, &mock_bank, *authority);
  112. }
  113. for (pubkey, account) in &test_entry.initial_accounts {
  114. mock_bank
  115. .account_shared_data
  116. .write()
  117. .unwrap()
  118. .insert(*pubkey, account.clone());
  119. }
  120. let fork_graph = Arc::new(RwLock::new(MockForkGraph {}));
  121. let batch_processor = TransactionBatchProcessor::new(
  122. EXECUTION_SLOT,
  123. EXECUTION_EPOCH,
  124. Arc::downgrade(&fork_graph),
  125. Some(Arc::new(create_custom_loader())),
  126. None, // We are not using program runtime v2.
  127. );
  128. // The sysvars must be put in the cache
  129. mock_bank.configure_sysvars();
  130. batch_processor.fill_missing_sysvar_cache_entries(&mock_bank);
  131. register_builtins(&mock_bank, &batch_processor, test_entry.with_loader_v4);
  132. let processing_config = TransactionProcessingConfig {
  133. recording_config: ExecutionRecordingConfig {
  134. enable_log_recording: true,
  135. enable_return_data_recording: true,
  136. enable_cpi_recording: false,
  137. enable_transaction_balance_recording: false,
  138. },
  139. ..Default::default()
  140. };
  141. let processing_environment = TransactionProcessingEnvironment {
  142. blockhash: LAST_BLOCKHASH,
  143. feature_set: test_entry.feature_set,
  144. blockhash_lamports_per_signature: LAMPORTS_PER_SIGNATURE,
  145. ..TransactionProcessingEnvironment::default()
  146. };
  147. Self {
  148. mock_bank,
  149. fork_graph,
  150. batch_processor,
  151. processing_config,
  152. processing_environment,
  153. test_entry,
  154. }
  155. }
  156. pub fn execute(&self) -> LoadAndExecuteSanitizedTransactionsOutput {
  157. let (transactions, check_results) = self.test_entry.prepare_transactions();
  158. let batch_output = self
  159. .batch_processor
  160. .load_and_execute_sanitized_transactions(
  161. &self.mock_bank,
  162. &transactions,
  163. check_results,
  164. &self.processing_environment,
  165. &self.processing_config,
  166. );
  167. // build a hashmap of final account states incrementally
  168. // starting with all initial states, updating to all final states
  169. // with SIMD83, an account might change multiple times in the same batch
  170. // but it might not exist on all transactions
  171. let mut final_accounts_actual = self.test_entry.initial_accounts.clone();
  172. let update_or_dealloc_account =
  173. |final_accounts: &mut AccountsMap, pubkey, account: AccountSharedData| {
  174. if account.lamports() == 0 {
  175. final_accounts.insert(pubkey, AccountSharedData::default());
  176. } else {
  177. final_accounts.insert(pubkey, account);
  178. }
  179. };
  180. for (tx_index, processed_transaction) in batch_output.processing_results.iter().enumerate()
  181. {
  182. let sanitized_transaction = &transactions[tx_index];
  183. match processed_transaction {
  184. Ok(ProcessedTransaction::Executed(executed_transaction)) => {
  185. for (index, (pubkey, account_data)) in executed_transaction
  186. .loaded_transaction
  187. .accounts
  188. .iter()
  189. .enumerate()
  190. {
  191. if sanitized_transaction.is_writable(index) {
  192. update_or_dealloc_account(
  193. &mut final_accounts_actual,
  194. *pubkey,
  195. account_data.clone(),
  196. );
  197. }
  198. }
  199. }
  200. Ok(ProcessedTransaction::FeesOnly(fees_only_transaction)) => {
  201. for (pubkey, account_data) in &fees_only_transaction.rollback_accounts {
  202. update_or_dealloc_account(
  203. &mut final_accounts_actual,
  204. *pubkey,
  205. account_data.clone(),
  206. );
  207. }
  208. }
  209. Err(_) => {}
  210. }
  211. }
  212. // first assert all transaction states together, it makes test-driven development much less of a headache
  213. let (actual_statuses, expected_statuses): (Vec<_>, Vec<_>) = batch_output
  214. .processing_results
  215. .iter()
  216. .zip(self.test_entry.asserts())
  217. .map(|(processing_result, test_item_assert)| {
  218. (
  219. ExecutionStatus::from(processing_result),
  220. test_item_assert.status,
  221. )
  222. })
  223. .unzip();
  224. assert_eq!(
  225. expected_statuses,
  226. actual_statuses,
  227. "mismatch between expected and actual statuses. execution details:\n{}",
  228. batch_output
  229. .processing_results
  230. .iter()
  231. .enumerate()
  232. .map(|(i, tx)| match tx {
  233. Ok(ProcessedTransaction::Executed(executed)) => {
  234. format!("{} (executed): {:#?}", i, executed.execution_details)
  235. }
  236. Ok(ProcessedTransaction::FeesOnly(fee_only)) => {
  237. format!("{} (fee-only): {:?}", i, fee_only.load_error)
  238. }
  239. Err(e) => format!("{i} (discarded): {e:?}"),
  240. })
  241. .collect::<Vec<_>>()
  242. .join("\n"),
  243. );
  244. // check that all the account states we care about are present and correct
  245. for (pubkey, expected_account_data) in self.test_entry.final_accounts.iter() {
  246. let actual_account_data = final_accounts_actual.get(pubkey);
  247. assert_eq!(
  248. Some(expected_account_data),
  249. actual_account_data,
  250. "mismatch on account {pubkey}"
  251. );
  252. }
  253. // now run our transaction-by-transaction checks
  254. for (processing_result, test_item_asserts) in batch_output
  255. .processing_results
  256. .iter()
  257. .zip(self.test_entry.asserts())
  258. {
  259. match processing_result {
  260. Ok(ProcessedTransaction::Executed(executed_transaction)) => test_item_asserts
  261. .check_executed_transaction(&executed_transaction.execution_details),
  262. Ok(ProcessedTransaction::FeesOnly(_)) => {
  263. assert!(test_item_asserts.processed());
  264. assert!(!test_item_asserts.executed());
  265. }
  266. Err(_) => assert!(test_item_asserts.discarded()),
  267. }
  268. }
  269. // merge new account states into the bank for multi-batch tests
  270. let mut mock_bank_accounts = self.mock_bank.account_shared_data.write().unwrap();
  271. mock_bank_accounts.extend(final_accounts_actual);
  272. // update global program cache
  273. for processing_result in batch_output.processing_results.iter() {
  274. if let Some(ProcessedTransaction::Executed(executed_tx)) =
  275. processing_result.processed_transaction()
  276. {
  277. let programs_modified_by_tx = &executed_tx.programs_modified_by_tx;
  278. if executed_tx.was_successful() && !programs_modified_by_tx.is_empty() {
  279. self.batch_processor
  280. .global_program_cache
  281. .write()
  282. .unwrap()
  283. .merge(programs_modified_by_tx);
  284. }
  285. }
  286. }
  287. batch_output
  288. }
  289. pub fn is_program_blocked(&self, program_id: &Pubkey) -> bool {
  290. let (_, program_cache_entry) = self
  291. .batch_processor
  292. .global_program_cache
  293. .read()
  294. .unwrap()
  295. .get_flattened_entries_for_tests()
  296. .into_iter()
  297. .rev()
  298. .find(|(key, _)| key == program_id)
  299. .unwrap();
  300. // in the same batch, a new valid loaderv3 program may have a Loaded entry with a later execution slot
  301. // in a later batch, the same loaderv3 program will have a DelayedVisibility tombstone
  302. // a new loaderv1/v2 account will have a FailedVerification tombstone
  303. // and a closed loaderv3 program or any loaderv3 buffer will have a Closed tombstone
  304. program_cache_entry.effective_slot > EXECUTION_SLOT || program_cache_entry.is_tombstone()
  305. }
  306. }
  307. // container for a transaction batch and all data needed to run and verify it against svm
  308. #[derive(Clone)]
  309. pub struct SvmTestEntry {
  310. // features configuration for this test
  311. pub feature_set: SVMFeatureSet,
  312. // until LoaderV4 is live on mainnet, we default to omitting it, but can also test it
  313. pub with_loader_v4: bool,
  314. // programs to deploy to the new svm
  315. pub initial_programs: Vec<(String, Slot, Option<Pubkey>)>,
  316. // accounts to deploy to the new svm before transaction execution
  317. pub initial_accounts: AccountsMap,
  318. // transactions to execute and transaction-specific checks to perform on the results from svm
  319. pub transaction_batch: Vec<TransactionBatchItem>,
  320. // expected final account states, checked after transaction execution
  321. pub final_accounts: AccountsMap,
  322. }
  323. impl Default for SvmTestEntry {
  324. fn default() -> Self {
  325. Self {
  326. feature_set: SVMFeatureSet::all_enabled(),
  327. with_loader_v4: false,
  328. initial_programs: Vec::new(),
  329. initial_accounts: HashMap::new(),
  330. transaction_batch: Vec::new(),
  331. final_accounts: HashMap::new(),
  332. }
  333. }
  334. }
  335. impl SvmTestEntry {
  336. pub fn with_loader_v4() -> Self {
  337. Self {
  338. with_loader_v4: true,
  339. ..Self::default()
  340. }
  341. }
  342. // add a new a rent-exempt account that exists before the batch
  343. // inserts it into both account maps, assuming it lives unchanged (except for svm fixing rent epoch)
  344. // rent-paying accounts must be added by hand because svm will not set rent epoch to u64::MAX
  345. pub fn add_initial_account(&mut self, pubkey: Pubkey, account: &AccountSharedData) {
  346. assert!(self
  347. .initial_accounts
  348. .insert(pubkey, account.clone())
  349. .is_none());
  350. self.create_expected_account(pubkey, account);
  351. }
  352. // add an immutable program that will have been deployed before the slot we execute transactions in
  353. pub fn add_initial_program(&mut self, program_name: &str) {
  354. self.initial_programs
  355. .push((program_name.to_string(), DEPLOYMENT_SLOT, None));
  356. }
  357. // add a new rent-exempt account that is created by the transaction
  358. // inserts it only into the post account map
  359. pub fn create_expected_account(&mut self, pubkey: Pubkey, account: &AccountSharedData) {
  360. let mut account = account.clone();
  361. account.set_rent_epoch(u64::MAX);
  362. assert!(self.final_accounts.insert(pubkey, account).is_none());
  363. }
  364. // edit an existing account to reflect changes you expect the transaction to make to it
  365. pub fn update_expected_account_data(&mut self, pubkey: Pubkey, account: &AccountSharedData) {
  366. let mut account = account.clone();
  367. account.set_rent_epoch(u64::MAX);
  368. assert!(self.final_accounts.insert(pubkey, account).is_some());
  369. }
  370. // indicate that an existing account is expected to be deallocated
  371. pub fn drop_expected_account(&mut self, pubkey: Pubkey) {
  372. assert!(self
  373. .final_accounts
  374. .insert(pubkey, AccountSharedData::default())
  375. .is_some());
  376. }
  377. // add lamports to an existing expected final account state
  378. pub fn increase_expected_lamports(&mut self, pubkey: &Pubkey, lamports: u64) {
  379. self.final_accounts
  380. .get_mut(pubkey)
  381. .unwrap()
  382. .checked_add_lamports(lamports)
  383. .unwrap();
  384. }
  385. // subtract lamports from an existing expected final account state
  386. pub fn decrease_expected_lamports(&mut self, pubkey: &Pubkey, lamports: u64) {
  387. self.final_accounts
  388. .get_mut(pubkey)
  389. .unwrap()
  390. .checked_sub_lamports(lamports)
  391. .unwrap();
  392. }
  393. // convenience function that adds a transaction that is expected to succeed
  394. pub fn push_transaction(&mut self, transaction: Transaction) {
  395. self.push_transaction_with_status(transaction, ExecutionStatus::Succeeded)
  396. }
  397. // convenience function that adds a transaction with an expected execution status
  398. pub fn push_transaction_with_status(
  399. &mut self,
  400. transaction: Transaction,
  401. status: ExecutionStatus,
  402. ) {
  403. self.transaction_batch.push(TransactionBatchItem {
  404. transaction,
  405. asserts: TransactionBatchItemAsserts {
  406. status,
  407. ..TransactionBatchItemAsserts::default()
  408. },
  409. ..TransactionBatchItem::default()
  410. });
  411. }
  412. // convenience function that adds a nonce transaction that is expected to succeed
  413. // we accept the prior nonce state and advance it for the check status, since this happens before svm
  414. pub fn push_nonce_transaction(&mut self, transaction: Transaction, nonce_info: NonceInfo) {
  415. self.push_nonce_transaction_with_status(transaction, nonce_info, ExecutionStatus::Succeeded)
  416. }
  417. // convenience function that adds a nonce transaction with an expected execution status
  418. // we accept the prior nonce state and advance it for the check status, since this happens before svm
  419. pub fn push_nonce_transaction_with_status(
  420. &mut self,
  421. transaction: Transaction,
  422. mut nonce_info: NonceInfo,
  423. status: ExecutionStatus,
  424. ) {
  425. if status != ExecutionStatus::Discarded {
  426. nonce_info
  427. .try_advance_nonce(
  428. DurableNonce::from_blockhash(&LAST_BLOCKHASH),
  429. LAMPORTS_PER_SIGNATURE,
  430. )
  431. .unwrap();
  432. }
  433. self.transaction_batch.push(TransactionBatchItem {
  434. transaction,
  435. asserts: TransactionBatchItemAsserts {
  436. status,
  437. ..TransactionBatchItemAsserts::default()
  438. },
  439. ..TransactionBatchItem::with_nonce(nonce_info)
  440. });
  441. }
  442. // internal helper to gather SanitizedTransaction objects for execution
  443. fn prepare_transactions(&self) -> (Vec<SanitizedTransaction>, Vec<TransactionCheckResult>) {
  444. self.transaction_batch
  445. .iter()
  446. .cloned()
  447. .map(|item| {
  448. let message = SanitizedTransaction::from_transaction_for_tests(item.transaction);
  449. let check_result = item.check_result.map(|tx_details| {
  450. let compute_budget_limits = process_test_compute_budget_instructions(
  451. SVMMessage::program_instructions_iter(&message),
  452. );
  453. let signature_count = message
  454. .num_transaction_signatures()
  455. .saturating_add(message.num_ed25519_signatures())
  456. .saturating_add(message.num_secp256k1_signatures())
  457. .saturating_add(message.num_secp256r1_signatures());
  458. let compute_budget = compute_budget_limits.map(|v| {
  459. v.get_compute_budget_and_limits(
  460. v.loaded_accounts_bytes,
  461. FeeDetails::new(
  462. signature_count.saturating_mul(LAMPORTS_PER_SIGNATURE),
  463. v.get_prioritization_fee(),
  464. ),
  465. self.feature_set.raise_cpi_nesting_limit_to_8,
  466. )
  467. });
  468. CheckedTransactionDetails::new(tx_details.nonce, compute_budget)
  469. });
  470. (message, check_result)
  471. })
  472. .unzip()
  473. }
  474. // internal helper to gather test items for post-execution checks
  475. fn asserts(&self) -> Vec<TransactionBatchItemAsserts> {
  476. self.transaction_batch
  477. .iter()
  478. .cloned()
  479. .map(|item| item.asserts)
  480. .collect()
  481. }
  482. }
  483. // one transaction in a batch plus check results for svm and asserts for tests
  484. #[derive(Clone, Debug)]
  485. pub struct TransactionBatchItem {
  486. pub transaction: Transaction,
  487. pub check_result: TransactionCheckResult,
  488. pub asserts: TransactionBatchItemAsserts,
  489. }
  490. impl TransactionBatchItem {
  491. fn with_nonce(nonce_info: NonceInfo) -> Self {
  492. Self {
  493. check_result: Ok(CheckedTransactionDetails::new(
  494. Some(nonce_info),
  495. Ok(SVMTransactionExecutionAndFeeBudgetLimits::default()),
  496. )),
  497. ..Self::default()
  498. }
  499. }
  500. }
  501. impl Default for TransactionBatchItem {
  502. fn default() -> Self {
  503. Self {
  504. transaction: Transaction::default(),
  505. check_result: Ok(CheckedTransactionDetails::new(
  506. None,
  507. Ok(SVMTransactionExecutionAndFeeBudgetLimits::default()),
  508. )),
  509. asserts: TransactionBatchItemAsserts::default(),
  510. }
  511. }
  512. }
  513. // asserts for a given transaction in a batch
  514. // we can automatically check whether it executed, whether it succeeded
  515. // log items we expect to see (exect match only), and rodata
  516. #[derive(Clone, Debug, Default)]
  517. pub struct TransactionBatchItemAsserts {
  518. pub status: ExecutionStatus,
  519. pub logs: Vec<String>,
  520. pub return_data: ReturnDataAssert,
  521. }
  522. impl TransactionBatchItemAsserts {
  523. pub fn succeeded(&self) -> bool {
  524. self.status.succeeded()
  525. }
  526. pub fn executed(&self) -> bool {
  527. self.status.executed()
  528. }
  529. pub fn processed(&self) -> bool {
  530. self.status.processed()
  531. }
  532. pub fn discarded(&self) -> bool {
  533. self.status.discarded()
  534. }
  535. pub fn check_executed_transaction(&self, execution_details: &TransactionExecutionDetails) {
  536. assert!(self.executed());
  537. assert_eq!(self.succeeded(), execution_details.status.is_ok());
  538. if !self.logs.is_empty() {
  539. let actual_logs = execution_details.log_messages.as_ref().unwrap();
  540. for expected_log in &self.logs {
  541. assert!(actual_logs.contains(expected_log));
  542. }
  543. }
  544. if self.return_data != ReturnDataAssert::Skip {
  545. assert_eq!(
  546. self.return_data,
  547. execution_details.return_data.clone().into()
  548. );
  549. }
  550. }
  551. }
  552. impl From<ExecutionStatus> for TransactionBatchItemAsserts {
  553. fn from(status: ExecutionStatus) -> Self {
  554. Self {
  555. status,
  556. ..Self::default()
  557. }
  558. }
  559. }
  560. // states a transaction can end in after a trip through the batch processor:
  561. // * discarded: no-op. not even processed. a flawed transaction excluded from the entry
  562. // * processed-failed: aka fee (and nonce) only. charged and added to an entry but not executed, would have failed invariably
  563. // * executed-failed: failed during execution. as above, fees charged and nonce advanced
  564. // * succeeded: what we all aspire to be in our transaction processing lifecycles
  565. #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
  566. pub enum ExecutionStatus {
  567. Discarded,
  568. ProcessedFailed,
  569. ExecutedFailed,
  570. #[default]
  571. Succeeded,
  572. }
  573. // note we avoid the word "failed" because it is confusing
  574. // the batch processor uses it to mean "executed and not succeeded"
  575. // but intuitively (and from the point of a user) it could just as likely mean "any state other than succeeded"
  576. impl ExecutionStatus {
  577. pub fn succeeded(self) -> bool {
  578. self == Self::Succeeded
  579. }
  580. pub fn executed(self) -> bool {
  581. self > Self::ProcessedFailed
  582. }
  583. pub fn processed(self) -> bool {
  584. self != Self::Discarded
  585. }
  586. pub fn discarded(self) -> bool {
  587. self == Self::Discarded
  588. }
  589. }
  590. impl From<&TransactionProcessingResult> for ExecutionStatus {
  591. fn from(processing_result: &TransactionProcessingResult) -> Self {
  592. match processing_result {
  593. Ok(ProcessedTransaction::Executed(executed_transaction)) => {
  594. if executed_transaction.execution_details.status.is_ok() {
  595. ExecutionStatus::Succeeded
  596. } else {
  597. ExecutionStatus::ExecutedFailed
  598. }
  599. }
  600. Ok(ProcessedTransaction::FeesOnly(_)) => ExecutionStatus::ProcessedFailed,
  601. Err(_) => ExecutionStatus::Discarded,
  602. }
  603. }
  604. }
  605. #[derive(Clone, Debug, Default, PartialEq, Eq)]
  606. pub enum ReturnDataAssert {
  607. Some(TransactionReturnData),
  608. None,
  609. #[default]
  610. Skip,
  611. }
  612. impl From<Option<TransactionReturnData>> for ReturnDataAssert {
  613. fn from(option_ro_data: Option<TransactionReturnData>) -> Self {
  614. match option_ro_data {
  615. Some(ro_data) => Self::Some(ro_data),
  616. None => Self::None,
  617. }
  618. }
  619. }
  620. fn program_medley() -> Vec<SvmTestEntry> {
  621. let mut test_entry = SvmTestEntry::default();
  622. // 0: A transaction that works without any account
  623. {
  624. let program_name = "hello-solana";
  625. let program_id = program_address(program_name);
  626. test_entry.add_initial_program(program_name);
  627. let fee_payer_keypair = Keypair::new();
  628. let fee_payer = fee_payer_keypair.pubkey();
  629. let mut fee_payer_data = AccountSharedData::default();
  630. fee_payer_data.set_lamports(LAMPORTS_PER_SOL);
  631. test_entry.add_initial_account(fee_payer, &fee_payer_data);
  632. let instruction = Instruction::new_with_bytes(program_id, &[], vec![]);
  633. test_entry.push_transaction(Transaction::new_signed_with_payer(
  634. &[instruction],
  635. Some(&fee_payer),
  636. &[&fee_payer_keypair],
  637. Hash::default(),
  638. ));
  639. test_entry.transaction_batch[0]
  640. .asserts
  641. .logs
  642. .push("Program log: Hello, Solana!".to_string());
  643. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  644. }
  645. // 1: A simple funds transfer between accounts
  646. {
  647. let program_name = "simple-transfer";
  648. let program_id = program_address(program_name);
  649. test_entry.add_initial_program(program_name);
  650. let fee_payer_keypair = Keypair::new();
  651. let sender_keypair = Keypair::new();
  652. let fee_payer = fee_payer_keypair.pubkey();
  653. let sender = sender_keypair.pubkey();
  654. let recipient = Pubkey::new_unique();
  655. let transfer_amount = 10;
  656. let mut fee_payer_data = AccountSharedData::default();
  657. fee_payer_data.set_lamports(LAMPORTS_PER_SOL);
  658. test_entry.add_initial_account(fee_payer, &fee_payer_data);
  659. let mut sender_data = AccountSharedData::default();
  660. sender_data.set_lamports(LAMPORTS_PER_SOL);
  661. test_entry.add_initial_account(sender, &sender_data);
  662. let mut recipient_data = AccountSharedData::default();
  663. recipient_data.set_lamports(LAMPORTS_PER_SOL);
  664. test_entry.add_initial_account(recipient, &recipient_data);
  665. let instruction = Instruction::new_with_bytes(
  666. program_id,
  667. &u64::to_be_bytes(transfer_amount),
  668. vec![
  669. AccountMeta::new(sender, true),
  670. AccountMeta::new(recipient, false),
  671. AccountMeta::new_readonly(system_program::id(), false),
  672. ],
  673. );
  674. test_entry.push_transaction(Transaction::new_signed_with_payer(
  675. &[instruction],
  676. Some(&fee_payer),
  677. &[&fee_payer_keypair, &sender_keypair],
  678. Hash::default(),
  679. ));
  680. test_entry.increase_expected_lamports(&recipient, transfer_amount);
  681. test_entry.decrease_expected_lamports(&sender, transfer_amount);
  682. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE * 2);
  683. }
  684. // 2: A program that utilizes a Sysvar
  685. {
  686. let program_name = "clock-sysvar";
  687. let program_id = program_address(program_name);
  688. test_entry.add_initial_program(program_name);
  689. let fee_payer_keypair = Keypair::new();
  690. let fee_payer = fee_payer_keypair.pubkey();
  691. let mut fee_payer_data = AccountSharedData::default();
  692. fee_payer_data.set_lamports(LAMPORTS_PER_SOL);
  693. test_entry.add_initial_account(fee_payer, &fee_payer_data);
  694. let instruction = Instruction::new_with_bytes(program_id, &[], vec![]);
  695. test_entry.push_transaction(Transaction::new_signed_with_payer(
  696. &[instruction],
  697. Some(&fee_payer),
  698. &[&fee_payer_keypair],
  699. Hash::default(),
  700. ));
  701. let ro_data = TransactionReturnData {
  702. program_id,
  703. data: i64::to_be_bytes(WALLCLOCK_TIME).to_vec(),
  704. };
  705. test_entry.transaction_batch[2].asserts.return_data = ReturnDataAssert::Some(ro_data);
  706. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  707. }
  708. // 3: A transaction that fails
  709. {
  710. let program_id = program_address("simple-transfer");
  711. let fee_payer_keypair = Keypair::new();
  712. let sender_keypair = Keypair::new();
  713. let fee_payer = fee_payer_keypair.pubkey();
  714. let sender = sender_keypair.pubkey();
  715. let recipient = Pubkey::new_unique();
  716. let base_amount = 900_000;
  717. let transfer_amount = base_amount + 50;
  718. let mut fee_payer_data = AccountSharedData::default();
  719. fee_payer_data.set_lamports(LAMPORTS_PER_SOL);
  720. test_entry.add_initial_account(fee_payer, &fee_payer_data);
  721. let mut sender_data = AccountSharedData::default();
  722. sender_data.set_lamports(base_amount);
  723. test_entry.add_initial_account(sender, &sender_data);
  724. let mut recipient_data = AccountSharedData::default();
  725. recipient_data.set_lamports(base_amount);
  726. test_entry.add_initial_account(recipient, &recipient_data);
  727. let instruction = Instruction::new_with_bytes(
  728. program_id,
  729. &u64::to_be_bytes(transfer_amount),
  730. vec![
  731. AccountMeta::new(sender, true),
  732. AccountMeta::new(recipient, false),
  733. AccountMeta::new_readonly(system_program::id(), false),
  734. ],
  735. );
  736. test_entry.push_transaction_with_status(
  737. Transaction::new_signed_with_payer(
  738. &[instruction],
  739. Some(&fee_payer),
  740. &[&fee_payer_keypair, &sender_keypair],
  741. Hash::default(),
  742. ),
  743. ExecutionStatus::ExecutedFailed,
  744. );
  745. test_entry.transaction_batch[3]
  746. .asserts
  747. .logs
  748. .push("Transfer: insufficient lamports 900000, need 900050".to_string());
  749. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE * 2);
  750. }
  751. // 4: A transaction whose verification has already failed
  752. {
  753. let fee_payer_keypair = Keypair::new();
  754. let fee_payer = fee_payer_keypair.pubkey();
  755. test_entry.transaction_batch.push(TransactionBatchItem {
  756. transaction: Transaction::new_signed_with_payer(
  757. &[],
  758. Some(&fee_payer),
  759. &[&fee_payer_keypair],
  760. Hash::default(),
  761. ),
  762. check_result: Err(TransactionError::BlockhashNotFound),
  763. asserts: ExecutionStatus::Discarded.into(),
  764. });
  765. }
  766. vec![test_entry]
  767. }
  768. fn simple_transfer() -> Vec<SvmTestEntry> {
  769. let mut test_entry = SvmTestEntry::default();
  770. let transfer_amount = LAMPORTS_PER_SOL;
  771. // 0: a transfer that succeeds
  772. {
  773. let source_keypair = Keypair::new();
  774. let source = source_keypair.pubkey();
  775. let destination = Pubkey::new_unique();
  776. let mut source_data = AccountSharedData::default();
  777. let mut destination_data = AccountSharedData::default();
  778. source_data.set_lamports(LAMPORTS_PER_SOL * 10);
  779. test_entry.add_initial_account(source, &source_data);
  780. test_entry.push_transaction(system_transaction::transfer(
  781. &source_keypair,
  782. &destination,
  783. transfer_amount,
  784. Hash::default(),
  785. ));
  786. destination_data
  787. .checked_add_lamports(transfer_amount)
  788. .unwrap();
  789. test_entry.create_expected_account(destination, &destination_data);
  790. test_entry.decrease_expected_lamports(&source, transfer_amount + LAMPORTS_PER_SIGNATURE);
  791. }
  792. // 1: an executable transfer that fails
  793. {
  794. let source_keypair = Keypair::new();
  795. let source = source_keypair.pubkey();
  796. let mut source_data = AccountSharedData::default();
  797. source_data.set_lamports(transfer_amount - 1);
  798. test_entry.add_initial_account(source, &source_data);
  799. test_entry.push_transaction_with_status(
  800. system_transaction::transfer(
  801. &source_keypair,
  802. &Pubkey::new_unique(),
  803. transfer_amount,
  804. Hash::default(),
  805. ),
  806. ExecutionStatus::ExecutedFailed,
  807. );
  808. test_entry.decrease_expected_lamports(&source, LAMPORTS_PER_SIGNATURE);
  809. }
  810. // 2: a non-processable transfer that fails before loading
  811. {
  812. test_entry.transaction_batch.push(TransactionBatchItem {
  813. transaction: system_transaction::transfer(
  814. &Keypair::new(),
  815. &Pubkey::new_unique(),
  816. transfer_amount,
  817. Hash::default(),
  818. ),
  819. check_result: Err(TransactionError::BlockhashNotFound),
  820. asserts: ExecutionStatus::Discarded.into(),
  821. });
  822. }
  823. // 3: a non-processable transfer that fails loading the fee-payer
  824. {
  825. test_entry.push_transaction_with_status(
  826. system_transaction::transfer(
  827. &Keypair::new(),
  828. &Pubkey::new_unique(),
  829. transfer_amount,
  830. Hash::default(),
  831. ),
  832. ExecutionStatus::Discarded,
  833. );
  834. }
  835. // 4: a processable non-executable transfer that fails loading the program
  836. {
  837. let source_keypair = Keypair::new();
  838. let source = source_keypair.pubkey();
  839. let mut source_data = AccountSharedData::default();
  840. source_data.set_lamports(transfer_amount * 10);
  841. test_entry
  842. .initial_accounts
  843. .insert(source, source_data.clone());
  844. test_entry.final_accounts.insert(source, source_data);
  845. let mut instruction =
  846. system_instruction::transfer(&source, &Pubkey::new_unique(), transfer_amount);
  847. instruction.program_id = Pubkey::new_unique();
  848. test_entry.decrease_expected_lamports(&source, LAMPORTS_PER_SIGNATURE);
  849. test_entry.push_transaction_with_status(
  850. Transaction::new_signed_with_payer(
  851. &[instruction],
  852. Some(&source),
  853. &[&source_keypair],
  854. Hash::default(),
  855. ),
  856. ExecutionStatus::ProcessedFailed,
  857. );
  858. }
  859. vec![test_entry]
  860. }
  861. fn simple_nonce(fee_paying_nonce: bool) -> Vec<SvmTestEntry> {
  862. let mut test_entry = SvmTestEntry::default();
  863. let program_name = "hello-solana";
  864. let real_program_id = program_address(program_name);
  865. test_entry.add_initial_program(program_name);
  866. // create and return a transaction, fee payer, and nonce info
  867. // sets up initial account states but not final ones
  868. // there are four cases of fee_paying_nonce and fake_fee_payer:
  869. // * false/false: normal nonce account with rent minimum, normal fee payer account with 1sol
  870. // * true/false: normal nonce account used to pay fees with rent minimum plus 1sol
  871. // * false/true: normal nonce account with rent minimum, fee payer doesnt exist
  872. // * true/true: same account for both which does not exist
  873. // we also provide a side door to bring a fee-paying nonce account below rent-exemption
  874. let mk_nonce_transaction = |test_entry: &mut SvmTestEntry,
  875. program_id,
  876. fake_fee_payer: bool,
  877. rent_paying_nonce: bool| {
  878. let fee_payer_keypair = Keypair::new();
  879. let fee_payer = fee_payer_keypair.pubkey();
  880. let nonce_pubkey = if fee_paying_nonce {
  881. fee_payer
  882. } else {
  883. Pubkey::new_unique()
  884. };
  885. let nonce_size = nonce::state::State::size();
  886. let mut nonce_balance = Rent::default().minimum_balance(nonce_size);
  887. if !fake_fee_payer && !fee_paying_nonce {
  888. let mut fee_payer_data = AccountSharedData::default();
  889. fee_payer_data.set_lamports(LAMPORTS_PER_SOL);
  890. test_entry.add_initial_account(fee_payer, &fee_payer_data);
  891. } else if rent_paying_nonce {
  892. assert!(fee_paying_nonce);
  893. nonce_balance += LAMPORTS_PER_SIGNATURE;
  894. nonce_balance -= 1;
  895. } else if fee_paying_nonce {
  896. nonce_balance += LAMPORTS_PER_SOL;
  897. }
  898. let nonce_initial_hash = DurableNonce::from_blockhash(&Hash::new_unique());
  899. let nonce_data =
  900. nonce::state::Data::new(fee_payer, nonce_initial_hash, LAMPORTS_PER_SIGNATURE);
  901. let nonce_account = AccountSharedData::new_data(
  902. nonce_balance,
  903. &nonce::versions::Versions::new(nonce::state::State::Initialized(nonce_data.clone())),
  904. &system_program::id(),
  905. )
  906. .unwrap();
  907. let nonce_info = NonceInfo::new(nonce_pubkey, nonce_account.clone());
  908. if !(fake_fee_payer && fee_paying_nonce) {
  909. test_entry.add_initial_account(nonce_pubkey, &nonce_account);
  910. }
  911. let instructions = vec![
  912. system_instruction::advance_nonce_account(&nonce_pubkey, &fee_payer),
  913. Instruction::new_with_bytes(program_id, &[], vec![]),
  914. ];
  915. let transaction = Transaction::new_signed_with_payer(
  916. &instructions,
  917. Some(&fee_payer),
  918. &[&fee_payer_keypair],
  919. nonce_data.blockhash(),
  920. );
  921. (transaction, fee_payer, nonce_info)
  922. };
  923. // 0: successful nonce transaction, regardless of features
  924. {
  925. let (transaction, fee_payer, mut nonce_info) =
  926. mk_nonce_transaction(&mut test_entry, real_program_id, false, false);
  927. test_entry.push_nonce_transaction(transaction, nonce_info.clone());
  928. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  929. nonce_info
  930. .try_advance_nonce(
  931. DurableNonce::from_blockhash(&LAST_BLOCKHASH),
  932. LAMPORTS_PER_SIGNATURE,
  933. )
  934. .unwrap();
  935. test_entry
  936. .final_accounts
  937. .get_mut(nonce_info.address())
  938. .unwrap()
  939. .data_as_mut_slice()
  940. .copy_from_slice(nonce_info.account().data());
  941. }
  942. // 1: non-executing nonce transaction (fee payer doesnt exist) regardless of features
  943. {
  944. let (transaction, _fee_payer, nonce_info) =
  945. mk_nonce_transaction(&mut test_entry, real_program_id, true, false);
  946. test_entry
  947. .final_accounts
  948. .entry(*nonce_info.address())
  949. .and_modify(|account| account.set_rent_epoch(0));
  950. test_entry.push_nonce_transaction_with_status(
  951. transaction,
  952. nonce_info,
  953. ExecutionStatus::Discarded,
  954. );
  955. }
  956. // 2: failing nonce transaction (bad system instruction) regardless of features
  957. {
  958. let (transaction, fee_payer, mut nonce_info) =
  959. mk_nonce_transaction(&mut test_entry, system_program::id(), false, false);
  960. test_entry.push_nonce_transaction_with_status(
  961. transaction,
  962. nonce_info.clone(),
  963. ExecutionStatus::ExecutedFailed,
  964. );
  965. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  966. nonce_info
  967. .try_advance_nonce(
  968. DurableNonce::from_blockhash(&LAST_BLOCKHASH),
  969. LAMPORTS_PER_SIGNATURE,
  970. )
  971. .unwrap();
  972. test_entry
  973. .final_accounts
  974. .get_mut(nonce_info.address())
  975. .unwrap()
  976. .data_as_mut_slice()
  977. .copy_from_slice(nonce_info.account().data());
  978. }
  979. // 3: processable non-executable nonce transaction with fee-only enabled, otherwise discarded
  980. {
  981. let (transaction, fee_payer, mut nonce_info) =
  982. mk_nonce_transaction(&mut test_entry, Pubkey::new_unique(), false, false);
  983. test_entry.push_nonce_transaction_with_status(
  984. transaction,
  985. nonce_info.clone(),
  986. ExecutionStatus::ProcessedFailed,
  987. );
  988. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  989. nonce_info
  990. .try_advance_nonce(
  991. DurableNonce::from_blockhash(&LAST_BLOCKHASH),
  992. LAMPORTS_PER_SIGNATURE,
  993. )
  994. .unwrap();
  995. test_entry
  996. .final_accounts
  997. .get_mut(nonce_info.address())
  998. .unwrap()
  999. .data_as_mut_slice()
  1000. .copy_from_slice(nonce_info.account().data());
  1001. // if the nonce account pays fees, it keeps its new rent epoch, otherwise it resets
  1002. if !fee_paying_nonce {
  1003. test_entry
  1004. .final_accounts
  1005. .get_mut(nonce_info.address())
  1006. .unwrap()
  1007. .set_rent_epoch(0);
  1008. }
  1009. }
  1010. // 4: safety check that nonce fee-payers are required to be rent-exempt (blockhash fee-payers may be below rent-exemption)
  1011. // if this situation is ever allowed in the future, the nonce account MUST be hidden for fee-only transactions
  1012. // as an aside, nonce accounts closed by WithdrawNonceAccount are safe because they are ordinary executed transactions
  1013. // we also dont care whether a non-fee nonce (or any account) pays rent because rent is charged on executed transactions
  1014. if fee_paying_nonce {
  1015. let (transaction, _, nonce_info) =
  1016. mk_nonce_transaction(&mut test_entry, real_program_id, false, true);
  1017. test_entry
  1018. .final_accounts
  1019. .get_mut(nonce_info.address())
  1020. .unwrap()
  1021. .set_rent_epoch(0);
  1022. test_entry.push_nonce_transaction_with_status(
  1023. transaction,
  1024. nonce_info.clone(),
  1025. ExecutionStatus::Discarded,
  1026. );
  1027. }
  1028. // 5: rent-paying nonce fee-payers are also not charged for fee-only transactions
  1029. if fee_paying_nonce {
  1030. let (transaction, _, nonce_info) =
  1031. mk_nonce_transaction(&mut test_entry, Pubkey::new_unique(), false, true);
  1032. test_entry
  1033. .final_accounts
  1034. .get_mut(nonce_info.address())
  1035. .unwrap()
  1036. .set_rent_epoch(0);
  1037. test_entry.push_nonce_transaction_with_status(
  1038. transaction,
  1039. nonce_info.clone(),
  1040. ExecutionStatus::Discarded,
  1041. );
  1042. }
  1043. vec![test_entry]
  1044. }
  1045. fn simd83_intrabatch_account_reuse() -> Vec<SvmTestEntry> {
  1046. let mut test_entries = vec![];
  1047. let transfer_amount = LAMPORTS_PER_SOL;
  1048. let wallet_rent = Rent::default().minimum_balance(0);
  1049. // batch 0: two successful transfers from the same source
  1050. {
  1051. let mut test_entry = SvmTestEntry::default();
  1052. let source_keypair = Keypair::new();
  1053. let source = source_keypair.pubkey();
  1054. let destination1 = Pubkey::new_unique();
  1055. let destination2 = Pubkey::new_unique();
  1056. let mut source_data = AccountSharedData::default();
  1057. let destination1_data = AccountSharedData::default();
  1058. let destination2_data = AccountSharedData::default();
  1059. source_data.set_lamports(LAMPORTS_PER_SOL * 10);
  1060. test_entry.add_initial_account(source, &source_data);
  1061. for (destination, mut destination_data) in [
  1062. (destination1, destination1_data),
  1063. (destination2, destination2_data),
  1064. ] {
  1065. test_entry.push_transaction(system_transaction::transfer(
  1066. &source_keypair,
  1067. &destination,
  1068. transfer_amount,
  1069. Hash::default(),
  1070. ));
  1071. destination_data
  1072. .checked_add_lamports(transfer_amount)
  1073. .unwrap();
  1074. test_entry.create_expected_account(destination, &destination_data);
  1075. test_entry
  1076. .decrease_expected_lamports(&source, transfer_amount + LAMPORTS_PER_SIGNATURE);
  1077. }
  1078. test_entries.push(test_entry);
  1079. }
  1080. // batch 1:
  1081. // * successful transfer, source left with rent-exempt minimum
  1082. // * non-processable transfer due to underfunded fee-payer
  1083. {
  1084. let mut test_entry = SvmTestEntry::default();
  1085. let source_keypair = Keypair::new();
  1086. let source = source_keypair.pubkey();
  1087. let destination = Pubkey::new_unique();
  1088. let mut source_data = AccountSharedData::default();
  1089. let mut destination_data = AccountSharedData::default();
  1090. source_data.set_lamports(transfer_amount + LAMPORTS_PER_SIGNATURE + wallet_rent);
  1091. test_entry.add_initial_account(source, &source_data);
  1092. test_entry.push_transaction(system_transaction::transfer(
  1093. &source_keypair,
  1094. &destination,
  1095. transfer_amount,
  1096. Hash::default(),
  1097. ));
  1098. destination_data
  1099. .checked_add_lamports(transfer_amount)
  1100. .unwrap();
  1101. test_entry.create_expected_account(destination, &destination_data);
  1102. test_entry.decrease_expected_lamports(&source, transfer_amount + LAMPORTS_PER_SIGNATURE);
  1103. test_entry.push_transaction_with_status(
  1104. system_transaction::transfer(
  1105. &source_keypair,
  1106. &destination,
  1107. transfer_amount,
  1108. Hash::default(),
  1109. ),
  1110. ExecutionStatus::Discarded,
  1111. );
  1112. test_entries.push(test_entry);
  1113. }
  1114. // batch 2:
  1115. // * successful transfer to a previously unfunded account
  1116. // * successful transfer using the new account as a fee-payer in the same batch
  1117. {
  1118. let mut test_entry = SvmTestEntry::default();
  1119. let first_transfer_amount = transfer_amount + LAMPORTS_PER_SIGNATURE + wallet_rent;
  1120. let second_transfer_amount = transfer_amount;
  1121. let grandparent_keypair = Keypair::new();
  1122. let grandparent = grandparent_keypair.pubkey();
  1123. let parent_keypair = Keypair::new();
  1124. let parent = parent_keypair.pubkey();
  1125. let child = Pubkey::new_unique();
  1126. let mut grandparent_data = AccountSharedData::default();
  1127. let mut parent_data = AccountSharedData::default();
  1128. let mut child_data = AccountSharedData::default();
  1129. grandparent_data.set_lamports(LAMPORTS_PER_SOL * 10);
  1130. test_entry.add_initial_account(grandparent, &grandparent_data);
  1131. test_entry.push_transaction(system_transaction::transfer(
  1132. &grandparent_keypair,
  1133. &parent,
  1134. first_transfer_amount,
  1135. Hash::default(),
  1136. ));
  1137. parent_data
  1138. .checked_add_lamports(first_transfer_amount)
  1139. .unwrap();
  1140. test_entry.create_expected_account(parent, &parent_data);
  1141. test_entry.decrease_expected_lamports(
  1142. &grandparent,
  1143. first_transfer_amount + LAMPORTS_PER_SIGNATURE,
  1144. );
  1145. test_entry.push_transaction(system_transaction::transfer(
  1146. &parent_keypair,
  1147. &child,
  1148. second_transfer_amount,
  1149. Hash::default(),
  1150. ));
  1151. child_data
  1152. .checked_add_lamports(second_transfer_amount)
  1153. .unwrap();
  1154. test_entry.create_expected_account(child, &child_data);
  1155. test_entry
  1156. .decrease_expected_lamports(&parent, second_transfer_amount + LAMPORTS_PER_SIGNATURE);
  1157. test_entries.push(test_entry);
  1158. }
  1159. // batch 3:
  1160. // * non-processable transfer due to underfunded fee-payer (two signatures)
  1161. // * successful transfer with the same fee-payer (one signature)
  1162. {
  1163. let mut test_entry = SvmTestEntry::default();
  1164. let feepayer_keypair = Keypair::new();
  1165. let feepayer = feepayer_keypair.pubkey();
  1166. let separate_source_keypair = Keypair::new();
  1167. let separate_source = separate_source_keypair.pubkey();
  1168. let destination = Pubkey::new_unique();
  1169. let mut feepayer_data = AccountSharedData::default();
  1170. let mut separate_source_data = AccountSharedData::default();
  1171. let mut destination_data = AccountSharedData::default();
  1172. feepayer_data.set_lamports(1 + LAMPORTS_PER_SIGNATURE + wallet_rent);
  1173. test_entry.add_initial_account(feepayer, &feepayer_data);
  1174. separate_source_data.set_lamports(LAMPORTS_PER_SOL * 10);
  1175. test_entry.add_initial_account(separate_source, &separate_source_data);
  1176. test_entry.push_transaction_with_status(
  1177. Transaction::new_signed_with_payer(
  1178. &[system_instruction::transfer(
  1179. &separate_source,
  1180. &destination,
  1181. 1,
  1182. )],
  1183. Some(&feepayer),
  1184. &[&feepayer_keypair, &separate_source_keypair],
  1185. Hash::default(),
  1186. ),
  1187. ExecutionStatus::Discarded,
  1188. );
  1189. test_entry.push_transaction(system_transaction::transfer(
  1190. &feepayer_keypair,
  1191. &destination,
  1192. 1,
  1193. Hash::default(),
  1194. ));
  1195. destination_data.checked_add_lamports(1).unwrap();
  1196. test_entry.create_expected_account(destination, &destination_data);
  1197. test_entry.decrease_expected_lamports(&feepayer, 1 + LAMPORTS_PER_SIGNATURE);
  1198. }
  1199. // batch 4:
  1200. // * processable non-executable transaction
  1201. // * successful transfer
  1202. // this confirms we update the AccountsMap from RollbackAccounts intrabatch
  1203. {
  1204. let mut test_entry = SvmTestEntry::default();
  1205. let source_keypair = Keypair::new();
  1206. let source = source_keypair.pubkey();
  1207. let destination = Pubkey::new_unique();
  1208. let mut source_data = AccountSharedData::default();
  1209. let mut destination_data = AccountSharedData::default();
  1210. source_data.set_lamports(LAMPORTS_PER_SOL * 10);
  1211. test_entry.add_initial_account(source, &source_data);
  1212. let mut load_program_fail_instruction =
  1213. system_instruction::transfer(&source, &Pubkey::new_unique(), transfer_amount);
  1214. load_program_fail_instruction.program_id = Pubkey::new_unique();
  1215. test_entry.push_transaction_with_status(
  1216. Transaction::new_signed_with_payer(
  1217. &[load_program_fail_instruction],
  1218. Some(&source),
  1219. &[&source_keypair],
  1220. Hash::default(),
  1221. ),
  1222. ExecutionStatus::ProcessedFailed,
  1223. );
  1224. test_entry.push_transaction(system_transaction::transfer(
  1225. &source_keypair,
  1226. &destination,
  1227. transfer_amount,
  1228. Hash::default(),
  1229. ));
  1230. destination_data
  1231. .checked_add_lamports(transfer_amount)
  1232. .unwrap();
  1233. test_entry.create_expected_account(destination, &destination_data);
  1234. test_entry
  1235. .decrease_expected_lamports(&source, transfer_amount + LAMPORTS_PER_SIGNATURE * 2);
  1236. test_entries.push(test_entry);
  1237. }
  1238. test_entries
  1239. }
  1240. fn simd83_nonce_reuse(fee_paying_nonce: bool) -> Vec<SvmTestEntry> {
  1241. let mut test_entries = vec![];
  1242. let program_name = "hello-solana";
  1243. let program_id = program_address(program_name);
  1244. let fee_payer_keypair = Keypair::new();
  1245. let non_fee_nonce_keypair = Keypair::new();
  1246. let fee_payer = fee_payer_keypair.pubkey();
  1247. let nonce_pubkey = if fee_paying_nonce {
  1248. fee_payer
  1249. } else {
  1250. non_fee_nonce_keypair.pubkey()
  1251. };
  1252. let nonce_size = nonce::state::State::size();
  1253. let initial_durable = DurableNonce::from_blockhash(&Hash::new_unique());
  1254. let initial_nonce_data =
  1255. nonce::state::Data::new(fee_payer, initial_durable, LAMPORTS_PER_SIGNATURE);
  1256. let initial_nonce_account = AccountSharedData::new_data(
  1257. LAMPORTS_PER_SOL,
  1258. &nonce::versions::Versions::new(nonce::state::State::Initialized(
  1259. initial_nonce_data.clone(),
  1260. )),
  1261. &system_program::id(),
  1262. )
  1263. .unwrap();
  1264. let initial_nonce_info = NonceInfo::new(nonce_pubkey, initial_nonce_account.clone());
  1265. let advanced_durable = DurableNonce::from_blockhash(&LAST_BLOCKHASH);
  1266. let mut advanced_nonce_info = initial_nonce_info.clone();
  1267. advanced_nonce_info
  1268. .try_advance_nonce(advanced_durable, LAMPORTS_PER_SIGNATURE)
  1269. .unwrap();
  1270. let advance_instruction = system_instruction::advance_nonce_account(&nonce_pubkey, &fee_payer);
  1271. let withdraw_instruction = system_instruction::withdraw_nonce_account(
  1272. &nonce_pubkey,
  1273. &fee_payer,
  1274. &fee_payer,
  1275. LAMPORTS_PER_SOL,
  1276. );
  1277. let successful_noop_instruction = Instruction::new_with_bytes(program_id, &[], vec![]);
  1278. let failing_noop_instruction = Instruction::new_with_bytes(system_program::id(), &[], vec![]);
  1279. let fee_only_noop_instruction = Instruction::new_with_bytes(Pubkey::new_unique(), &[], vec![]);
  1280. let second_transaction = Transaction::new_signed_with_payer(
  1281. &[
  1282. advance_instruction.clone(),
  1283. successful_noop_instruction.clone(),
  1284. ],
  1285. Some(&fee_payer),
  1286. &[&fee_payer_keypair],
  1287. *advanced_durable.as_hash(),
  1288. );
  1289. let mut common_test_entry = SvmTestEntry::default();
  1290. common_test_entry.add_initial_account(nonce_pubkey, &initial_nonce_account);
  1291. if !fee_paying_nonce {
  1292. let mut fee_payer_data = AccountSharedData::default();
  1293. fee_payer_data.set_lamports(LAMPORTS_PER_SOL);
  1294. common_test_entry.add_initial_account(fee_payer, &fee_payer_data);
  1295. }
  1296. common_test_entry
  1297. .final_accounts
  1298. .get_mut(&nonce_pubkey)
  1299. .unwrap()
  1300. .data_as_mut_slice()
  1301. .copy_from_slice(advanced_nonce_info.account().data());
  1302. common_test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  1303. let common_test_entry = common_test_entry;
  1304. // batch 0: one transaction that advances the nonce twice
  1305. {
  1306. let mut test_entry = common_test_entry.clone();
  1307. let transaction = Transaction::new_signed_with_payer(
  1308. &[advance_instruction.clone(), advance_instruction.clone()],
  1309. Some(&fee_payer),
  1310. &[&fee_payer_keypair],
  1311. *initial_durable.as_hash(),
  1312. );
  1313. test_entry.push_nonce_transaction_with_status(
  1314. transaction,
  1315. initial_nonce_info.clone(),
  1316. ExecutionStatus::ExecutedFailed,
  1317. );
  1318. test_entries.push(test_entry);
  1319. }
  1320. // batch 1:
  1321. // * a successful nonce transaction
  1322. // * a nonce transaction that reuses the same nonce; this transaction must be dropped
  1323. {
  1324. let mut test_entry = common_test_entry.clone();
  1325. let first_transaction = Transaction::new_signed_with_payer(
  1326. &[
  1327. advance_instruction.clone(),
  1328. successful_noop_instruction.clone(),
  1329. ],
  1330. Some(&fee_payer),
  1331. &[&fee_payer_keypair],
  1332. *initial_durable.as_hash(),
  1333. );
  1334. test_entry.push_nonce_transaction(first_transaction, initial_nonce_info.clone());
  1335. test_entry.push_nonce_transaction_with_status(
  1336. second_transaction.clone(),
  1337. advanced_nonce_info.clone(),
  1338. ExecutionStatus::Discarded,
  1339. );
  1340. test_entries.push(test_entry);
  1341. }
  1342. // batch 2:
  1343. // * an executable failed nonce transaction
  1344. // * a nonce transaction that reuses the same nonce; this transaction must be dropped
  1345. {
  1346. let mut test_entry = common_test_entry.clone();
  1347. let first_transaction = Transaction::new_signed_with_payer(
  1348. &[advance_instruction.clone(), failing_noop_instruction],
  1349. Some(&fee_payer),
  1350. &[&fee_payer_keypair],
  1351. *initial_durable.as_hash(),
  1352. );
  1353. test_entry.push_nonce_transaction_with_status(
  1354. first_transaction,
  1355. initial_nonce_info.clone(),
  1356. ExecutionStatus::ExecutedFailed,
  1357. );
  1358. test_entry.push_nonce_transaction_with_status(
  1359. second_transaction.clone(),
  1360. advanced_nonce_info.clone(),
  1361. ExecutionStatus::Discarded,
  1362. );
  1363. test_entries.push(test_entry);
  1364. }
  1365. // batch 3:
  1366. // * a processable non-executable nonce transaction, if fee-only transactions are enabled
  1367. // * a nonce transaction that reuses the same nonce; this transaction must be dropped
  1368. {
  1369. let mut test_entry = common_test_entry.clone();
  1370. let first_transaction = Transaction::new_signed_with_payer(
  1371. &[advance_instruction.clone(), fee_only_noop_instruction],
  1372. Some(&fee_payer),
  1373. &[&fee_payer_keypair],
  1374. *initial_durable.as_hash(),
  1375. );
  1376. test_entry.push_nonce_transaction_with_status(
  1377. first_transaction,
  1378. initial_nonce_info.clone(),
  1379. ExecutionStatus::ProcessedFailed,
  1380. );
  1381. test_entry.push_nonce_transaction_with_status(
  1382. second_transaction.clone(),
  1383. advanced_nonce_info.clone(),
  1384. ExecutionStatus::Discarded,
  1385. );
  1386. // if the nonce account pays fees, it keeps its new rent epoch, otherwise it resets
  1387. if !fee_paying_nonce {
  1388. test_entry
  1389. .final_accounts
  1390. .get_mut(&nonce_pubkey)
  1391. .unwrap()
  1392. .set_rent_epoch(0);
  1393. }
  1394. test_entries.push(test_entry);
  1395. }
  1396. // batch 4:
  1397. // * a successful blockhash transaction that also advances the nonce
  1398. // * a nonce transaction that reuses the same nonce; this transaction must be dropped
  1399. {
  1400. let mut test_entry = common_test_entry.clone();
  1401. let first_transaction = Transaction::new_signed_with_payer(
  1402. &[
  1403. successful_noop_instruction.clone(),
  1404. advance_instruction.clone(),
  1405. ],
  1406. Some(&fee_payer),
  1407. &[&fee_payer_keypair],
  1408. Hash::default(),
  1409. );
  1410. test_entry.push_nonce_transaction(first_transaction, initial_nonce_info.clone());
  1411. test_entry.push_nonce_transaction_with_status(
  1412. second_transaction.clone(),
  1413. advanced_nonce_info.clone(),
  1414. ExecutionStatus::Discarded,
  1415. );
  1416. test_entries.push(test_entry);
  1417. }
  1418. for test_entry in &mut test_entries {
  1419. test_entry.add_initial_program(program_name);
  1420. }
  1421. // batch 5:
  1422. // * a successful blockhash transaction that closes the nonce
  1423. // * a nonce transaction that uses the nonce; this transaction must be dropped
  1424. if !fee_paying_nonce {
  1425. let mut test_entry = common_test_entry.clone();
  1426. let first_transaction = Transaction::new_signed_with_payer(
  1427. slice::from_ref(&withdraw_instruction),
  1428. Some(&fee_payer),
  1429. &[&fee_payer_keypair],
  1430. Hash::default(),
  1431. );
  1432. test_entry.push_transaction(first_transaction);
  1433. test_entry.push_nonce_transaction_with_status(
  1434. second_transaction.clone(),
  1435. advanced_nonce_info.clone(),
  1436. ExecutionStatus::Discarded,
  1437. );
  1438. test_entry.increase_expected_lamports(&fee_payer, LAMPORTS_PER_SOL);
  1439. test_entry.drop_expected_account(nonce_pubkey);
  1440. test_entries.push(test_entry);
  1441. }
  1442. // batch 6:
  1443. // * a successful blockhash transaction that closes the nonce
  1444. // * a successful blockhash transaction that funds the closed account
  1445. // * a nonce transaction that uses the account; this transaction must be dropped
  1446. if !fee_paying_nonce {
  1447. let mut test_entry = common_test_entry.clone();
  1448. let first_transaction = Transaction::new_signed_with_payer(
  1449. slice::from_ref(&withdraw_instruction),
  1450. Some(&fee_payer),
  1451. &[&fee_payer_keypair],
  1452. Hash::default(),
  1453. );
  1454. let middle_transaction = system_transaction::transfer(
  1455. &fee_payer_keypair,
  1456. &nonce_pubkey,
  1457. LAMPORTS_PER_SOL,
  1458. Hash::default(),
  1459. );
  1460. test_entry.push_transaction(first_transaction);
  1461. test_entry.push_transaction(middle_transaction);
  1462. test_entry.push_nonce_transaction_with_status(
  1463. second_transaction.clone(),
  1464. advanced_nonce_info.clone(),
  1465. ExecutionStatus::Discarded,
  1466. );
  1467. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  1468. let mut new_nonce_state = AccountSharedData::default();
  1469. new_nonce_state.set_lamports(LAMPORTS_PER_SOL);
  1470. test_entry.update_expected_account_data(nonce_pubkey, &new_nonce_state);
  1471. test_entries.push(test_entry);
  1472. }
  1473. // batch 7:
  1474. // * a successful blockhash transaction that closes the nonce
  1475. // * a successful blockhash transaction that reopens the account with proper nonce size
  1476. // * a nonce transaction that uses the account; this transaction must be dropped
  1477. if !fee_paying_nonce {
  1478. let mut test_entry = common_test_entry.clone();
  1479. let first_transaction = Transaction::new_signed_with_payer(
  1480. slice::from_ref(&withdraw_instruction),
  1481. Some(&fee_payer),
  1482. &[&fee_payer_keypair],
  1483. Hash::default(),
  1484. );
  1485. let middle_transaction = system_transaction::create_account(
  1486. &fee_payer_keypair,
  1487. &non_fee_nonce_keypair,
  1488. Hash::default(),
  1489. LAMPORTS_PER_SOL,
  1490. nonce_size as u64,
  1491. &system_program::id(),
  1492. );
  1493. test_entry.push_transaction(first_transaction);
  1494. test_entry.push_transaction(middle_transaction);
  1495. test_entry.push_nonce_transaction_with_status(
  1496. second_transaction.clone(),
  1497. advanced_nonce_info.clone(),
  1498. ExecutionStatus::Discarded,
  1499. );
  1500. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE * 2);
  1501. let new_nonce_state = AccountSharedData::create(
  1502. LAMPORTS_PER_SOL,
  1503. vec![0; nonce_size],
  1504. system_program::id(),
  1505. false,
  1506. u64::MAX,
  1507. );
  1508. test_entry.update_expected_account_data(nonce_pubkey, &new_nonce_state);
  1509. test_entries.push(test_entry);
  1510. }
  1511. // batch 8:
  1512. // * a successful blockhash transaction that closes the nonce
  1513. // * a successful blockhash transaction that reopens the nonce
  1514. // * a nonce transaction that uses the nonce; this transaction must be dropped
  1515. if !fee_paying_nonce {
  1516. let mut test_entry = common_test_entry.clone();
  1517. let first_transaction = Transaction::new_signed_with_payer(
  1518. slice::from_ref(&withdraw_instruction),
  1519. Some(&fee_payer),
  1520. &[&fee_payer_keypair],
  1521. Hash::default(),
  1522. );
  1523. let create_instructions = system_instruction::create_nonce_account(
  1524. &fee_payer,
  1525. &nonce_pubkey,
  1526. &fee_payer,
  1527. LAMPORTS_PER_SOL,
  1528. );
  1529. let middle_transaction = Transaction::new_signed_with_payer(
  1530. &create_instructions,
  1531. Some(&fee_payer),
  1532. &[&fee_payer_keypair, &non_fee_nonce_keypair],
  1533. Hash::default(),
  1534. );
  1535. test_entry.push_transaction(first_transaction);
  1536. test_entry.push_transaction(middle_transaction);
  1537. test_entry.push_nonce_transaction_with_status(
  1538. second_transaction.clone(),
  1539. advanced_nonce_info.clone(),
  1540. ExecutionStatus::Discarded,
  1541. );
  1542. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE * 2);
  1543. test_entries.push(test_entry);
  1544. }
  1545. // batch 9:
  1546. // * a successful blockhash noop transaction
  1547. // * a nonce transaction that uses a spoofed nonce account; this transaction must be dropped
  1548. // check_age would never let such a transaction through validation
  1549. // this simulates the case where someone closes a nonce account, then reuses the address in the same batch
  1550. // but as a non-system account that parses as an initialized nonce account
  1551. if !fee_paying_nonce {
  1552. let mut test_entry = common_test_entry.clone();
  1553. test_entry.initial_accounts.remove(&nonce_pubkey);
  1554. test_entry.final_accounts.remove(&nonce_pubkey);
  1555. let mut fake_nonce_account = initial_nonce_account.clone();
  1556. fake_nonce_account.set_rent_epoch(u64::MAX);
  1557. fake_nonce_account.set_owner(Pubkey::new_unique());
  1558. test_entry.add_initial_account(nonce_pubkey, &fake_nonce_account);
  1559. let first_transaction = Transaction::new_signed_with_payer(
  1560. slice::from_ref(&successful_noop_instruction),
  1561. Some(&fee_payer),
  1562. &[&fee_payer_keypair],
  1563. Hash::default(),
  1564. );
  1565. test_entry.push_transaction(first_transaction);
  1566. test_entry.push_nonce_transaction_with_status(
  1567. second_transaction.clone(),
  1568. advanced_nonce_info.clone(),
  1569. ExecutionStatus::Discarded,
  1570. );
  1571. test_entries.push(test_entry);
  1572. }
  1573. // batch 10:
  1574. // * a successful blockhash transaction that changes the nonce authority
  1575. // * a nonce transaction that uses the nonce with the old authority; this transaction must be dropped
  1576. if !fee_paying_nonce {
  1577. let mut test_entry = common_test_entry.clone();
  1578. let new_authority = Pubkey::new_unique();
  1579. let first_transaction = Transaction::new_signed_with_payer(
  1580. &[system_instruction::authorize_nonce_account(
  1581. &nonce_pubkey,
  1582. &fee_payer,
  1583. &new_authority,
  1584. )],
  1585. Some(&fee_payer),
  1586. &[&fee_payer_keypair],
  1587. Hash::default(),
  1588. );
  1589. test_entry.push_transaction(first_transaction);
  1590. test_entry.push_nonce_transaction_with_status(
  1591. second_transaction.clone(),
  1592. advanced_nonce_info.clone(),
  1593. ExecutionStatus::Discarded,
  1594. );
  1595. let final_nonce_data =
  1596. nonce::state::Data::new(new_authority, initial_durable, LAMPORTS_PER_SIGNATURE);
  1597. let final_nonce_account = AccountSharedData::new_data(
  1598. LAMPORTS_PER_SOL,
  1599. &nonce::versions::Versions::new(nonce::state::State::Initialized(final_nonce_data)),
  1600. &system_program::id(),
  1601. )
  1602. .unwrap();
  1603. test_entry.update_expected_account_data(nonce_pubkey, &final_nonce_account);
  1604. test_entries.push(test_entry);
  1605. }
  1606. // batch 11:
  1607. // * a successful blockhash transaction that changes the nonce authority
  1608. // * a nonce transaction that uses the nonce with the new authority; this transaction succeeds
  1609. if !fee_paying_nonce {
  1610. let mut test_entry = common_test_entry.clone();
  1611. let new_authority_keypair = Keypair::new();
  1612. let new_authority = new_authority_keypair.pubkey();
  1613. let first_transaction = Transaction::new_signed_with_payer(
  1614. &[system_instruction::authorize_nonce_account(
  1615. &nonce_pubkey,
  1616. &fee_payer,
  1617. &new_authority,
  1618. )],
  1619. Some(&fee_payer),
  1620. &[&fee_payer_keypair],
  1621. Hash::default(),
  1622. );
  1623. let second_transaction = Transaction::new_signed_with_payer(
  1624. &[
  1625. system_instruction::advance_nonce_account(&nonce_pubkey, &new_authority),
  1626. successful_noop_instruction.clone(),
  1627. ],
  1628. Some(&fee_payer),
  1629. &[&fee_payer_keypair, &new_authority_keypair],
  1630. *advanced_durable.as_hash(),
  1631. );
  1632. test_entry.push_transaction(first_transaction);
  1633. test_entry.push_nonce_transaction(second_transaction.clone(), advanced_nonce_info.clone());
  1634. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE * 2);
  1635. let final_nonce_data =
  1636. nonce::state::Data::new(new_authority, advanced_durable, LAMPORTS_PER_SIGNATURE);
  1637. let final_nonce_account = AccountSharedData::new_data(
  1638. LAMPORTS_PER_SOL,
  1639. &nonce::versions::Versions::new(nonce::state::State::Initialized(final_nonce_data)),
  1640. &system_program::id(),
  1641. )
  1642. .unwrap();
  1643. test_entry.update_expected_account_data(nonce_pubkey, &final_nonce_account);
  1644. test_entries.push(test_entry);
  1645. }
  1646. for test_entry in &mut test_entries {
  1647. test_entry.add_initial_program(program_name);
  1648. }
  1649. test_entries
  1650. }
  1651. #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  1652. enum WriteProgramInstruction {
  1653. Print,
  1654. Set,
  1655. Dealloc,
  1656. Realloc(usize),
  1657. }
  1658. impl WriteProgramInstruction {
  1659. fn create_transaction(
  1660. self,
  1661. program_id: Pubkey,
  1662. fee_payer: &Keypair,
  1663. target: Pubkey,
  1664. clamp_data_size: Option<u32>,
  1665. ) -> Transaction {
  1666. let (instruction_data, account_metas) = match self {
  1667. Self::Print => (vec![0], vec![AccountMeta::new_readonly(target, false)]),
  1668. Self::Set => (vec![1], vec![AccountMeta::new(target, false)]),
  1669. Self::Dealloc => (
  1670. vec![2],
  1671. vec![
  1672. AccountMeta::new(target, false),
  1673. AccountMeta::new(solana_sdk_ids::incinerator::id(), false),
  1674. ],
  1675. ),
  1676. Self::Realloc(new_size) => {
  1677. let mut instruction_data = vec![3];
  1678. instruction_data.extend_from_slice(&new_size.to_le_bytes());
  1679. (instruction_data, vec![AccountMeta::new(target, false)])
  1680. }
  1681. };
  1682. let mut instructions = vec![];
  1683. if let Some(size) = clamp_data_size {
  1684. instructions.push(ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(size));
  1685. }
  1686. instructions.push(Instruction::new_with_bytes(
  1687. program_id,
  1688. &instruction_data,
  1689. account_metas,
  1690. ));
  1691. Transaction::new_signed_with_payer(
  1692. &instructions,
  1693. Some(&fee_payer.pubkey()),
  1694. &[fee_payer],
  1695. Hash::default(),
  1696. )
  1697. }
  1698. }
  1699. fn simd83_account_deallocate() -> Vec<SvmTestEntry> {
  1700. let mut test_entries = vec![];
  1701. // batch 0: sanity check, the program actually sets data
  1702. // batch 1: removing lamports from account hides it from subsequent in-batch transactions
  1703. for remove_lamports in [false, true] {
  1704. let mut test_entry = SvmTestEntry::default();
  1705. let program_name = "write-to-account";
  1706. let program_id = program_address(program_name);
  1707. test_entry.add_initial_program(program_name);
  1708. let fee_payer_keypair = Keypair::new();
  1709. let fee_payer = fee_payer_keypair.pubkey();
  1710. let mut fee_payer_data = AccountSharedData::default();
  1711. fee_payer_data.set_lamports(LAMPORTS_PER_SOL);
  1712. test_entry.add_initial_account(fee_payer, &fee_payer_data);
  1713. let target = Pubkey::new_unique();
  1714. let mut target_data = AccountSharedData::create(
  1715. Rent::default().minimum_balance(1),
  1716. vec![0],
  1717. program_id,
  1718. false,
  1719. u64::MAX,
  1720. );
  1721. test_entry.add_initial_account(target, &target_data);
  1722. let set_data_transaction = WriteProgramInstruction::Set.create_transaction(
  1723. program_id,
  1724. &fee_payer_keypair,
  1725. target,
  1726. None,
  1727. );
  1728. test_entry.push_transaction(set_data_transaction);
  1729. target_data.data_as_mut_slice()[0] = 100;
  1730. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  1731. test_entry.update_expected_account_data(target, &target_data);
  1732. if remove_lamports {
  1733. let dealloc_transaction = WriteProgramInstruction::Dealloc.create_transaction(
  1734. program_id,
  1735. &fee_payer_keypair,
  1736. target,
  1737. None,
  1738. );
  1739. test_entry.push_transaction(dealloc_transaction);
  1740. let print_transaction = WriteProgramInstruction::Print.create_transaction(
  1741. program_id,
  1742. &fee_payer_keypair,
  1743. target,
  1744. None,
  1745. );
  1746. test_entry.push_transaction(print_transaction);
  1747. test_entry.transaction_batch[2]
  1748. .asserts
  1749. .logs
  1750. .push("Program log: account size 0".to_string());
  1751. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE * 2);
  1752. test_entry.drop_expected_account(target);
  1753. }
  1754. test_entries.push(test_entry);
  1755. }
  1756. test_entries
  1757. }
  1758. fn simd83_fee_payer_deallocate() -> Vec<SvmTestEntry> {
  1759. let mut test_entry = SvmTestEntry::default();
  1760. let program_name = "hello-solana";
  1761. let real_program_id = program_address(program_name);
  1762. test_entry.add_initial_program(program_name);
  1763. // 0/1: a rent-paying fee-payer goes to zero lamports on an executed transaction, the batch sees it as deallocated
  1764. // 2/3: the same, except if fee-only transactions are enabled, it goes to zero lamports from a fee-only transaction
  1765. for do_fee_only_transaction in [false, true] {
  1766. let dealloc_fee_payer_keypair = Keypair::new();
  1767. let dealloc_fee_payer = dealloc_fee_payer_keypair.pubkey();
  1768. let mut dealloc_fee_payer_data = AccountSharedData::default();
  1769. dealloc_fee_payer_data.set_lamports(LAMPORTS_PER_SIGNATURE);
  1770. dealloc_fee_payer_data.set_rent_epoch(u64::MAX - 1);
  1771. test_entry.add_initial_account(dealloc_fee_payer, &dealloc_fee_payer_data);
  1772. let stable_fee_payer_keypair = Keypair::new();
  1773. let stable_fee_payer = stable_fee_payer_keypair.pubkey();
  1774. let mut stable_fee_payer_data = AccountSharedData::default();
  1775. stable_fee_payer_data.set_lamports(LAMPORTS_PER_SOL);
  1776. test_entry.add_initial_account(stable_fee_payer, &stable_fee_payer_data);
  1777. // transaction which drains a fee-payer
  1778. let instruction = Instruction::new_with_bytes(
  1779. if do_fee_only_transaction {
  1780. Pubkey::new_unique()
  1781. } else {
  1782. real_program_id
  1783. },
  1784. &[],
  1785. vec![],
  1786. );
  1787. let transaction = Transaction::new_signed_with_payer(
  1788. &[instruction],
  1789. Some(&dealloc_fee_payer),
  1790. &[&dealloc_fee_payer_keypair],
  1791. Hash::default(),
  1792. );
  1793. test_entry.push_transaction_with_status(
  1794. transaction,
  1795. if do_fee_only_transaction {
  1796. ExecutionStatus::ProcessedFailed
  1797. } else {
  1798. ExecutionStatus::Succeeded
  1799. },
  1800. );
  1801. test_entry.decrease_expected_lamports(&dealloc_fee_payer, LAMPORTS_PER_SIGNATURE);
  1802. // as noted in `account_deallocate()` we must touch the account to see if anything actually happened
  1803. let instruction = Instruction::new_with_bytes(
  1804. real_program_id,
  1805. &[],
  1806. vec![AccountMeta::new_readonly(dealloc_fee_payer, false)],
  1807. );
  1808. test_entry.push_transaction(Transaction::new_signed_with_payer(
  1809. &[instruction],
  1810. Some(&stable_fee_payer),
  1811. &[&stable_fee_payer_keypair],
  1812. Hash::default(),
  1813. ));
  1814. test_entry.decrease_expected_lamports(&stable_fee_payer, LAMPORTS_PER_SIGNATURE);
  1815. test_entry.drop_expected_account(dealloc_fee_payer);
  1816. }
  1817. // 4: a rent-paying non-nonce fee-payer goes to zero on a fee-only nonce transaction, the batch sees it as deallocated
  1818. // we test in `simple_nonce()` that nonce fee-payers cannot as a rule be brought below rent-exemption
  1819. {
  1820. let dealloc_fee_payer_keypair = Keypair::new();
  1821. let dealloc_fee_payer = dealloc_fee_payer_keypair.pubkey();
  1822. let mut dealloc_fee_payer_data = AccountSharedData::default();
  1823. dealloc_fee_payer_data.set_lamports(LAMPORTS_PER_SIGNATURE);
  1824. dealloc_fee_payer_data.set_rent_epoch(u64::MAX - 1);
  1825. test_entry.add_initial_account(dealloc_fee_payer, &dealloc_fee_payer_data);
  1826. let stable_fee_payer_keypair = Keypair::new();
  1827. let stable_fee_payer = stable_fee_payer_keypair.pubkey();
  1828. let mut stable_fee_payer_data = AccountSharedData::default();
  1829. stable_fee_payer_data.set_lamports(LAMPORTS_PER_SOL);
  1830. test_entry.add_initial_account(stable_fee_payer, &stable_fee_payer_data);
  1831. let nonce_pubkey = Pubkey::new_unique();
  1832. let initial_durable = DurableNonce::from_blockhash(&Hash::new_unique());
  1833. let initial_nonce_data =
  1834. nonce::state::Data::new(dealloc_fee_payer, initial_durable, LAMPORTS_PER_SIGNATURE);
  1835. let initial_nonce_account = AccountSharedData::new_data(
  1836. LAMPORTS_PER_SOL,
  1837. &nonce::versions::Versions::new(nonce::state::State::Initialized(
  1838. initial_nonce_data.clone(),
  1839. )),
  1840. &system_program::id(),
  1841. )
  1842. .unwrap();
  1843. let initial_nonce_info = NonceInfo::new(nonce_pubkey, initial_nonce_account.clone());
  1844. let advanced_durable = DurableNonce::from_blockhash(&LAST_BLOCKHASH);
  1845. let mut advanced_nonce_info = initial_nonce_info.clone();
  1846. advanced_nonce_info
  1847. .try_advance_nonce(advanced_durable, LAMPORTS_PER_SIGNATURE)
  1848. .unwrap();
  1849. let advance_instruction =
  1850. system_instruction::advance_nonce_account(&nonce_pubkey, &dealloc_fee_payer);
  1851. let fee_only_noop_instruction =
  1852. Instruction::new_with_bytes(Pubkey::new_unique(), &[], vec![]);
  1853. // fee-only nonce transaction which drains a fee-payer
  1854. let transaction = Transaction::new_signed_with_payer(
  1855. &[advance_instruction, fee_only_noop_instruction],
  1856. Some(&dealloc_fee_payer),
  1857. &[&dealloc_fee_payer_keypair],
  1858. Hash::default(),
  1859. );
  1860. test_entry.push_transaction_with_status(transaction, ExecutionStatus::ProcessedFailed);
  1861. test_entry.decrease_expected_lamports(&dealloc_fee_payer, LAMPORTS_PER_SIGNATURE);
  1862. // as noted in `account_deallocate()` we must touch the account to see if anything actually happened
  1863. let instruction = Instruction::new_with_bytes(
  1864. real_program_id,
  1865. &[],
  1866. vec![AccountMeta::new_readonly(dealloc_fee_payer, false)],
  1867. );
  1868. test_entry.push_transaction(Transaction::new_signed_with_payer(
  1869. &[instruction],
  1870. Some(&stable_fee_payer),
  1871. &[&stable_fee_payer_keypair],
  1872. Hash::default(),
  1873. ));
  1874. test_entry.decrease_expected_lamports(&stable_fee_payer, LAMPORTS_PER_SIGNATURE);
  1875. test_entry.drop_expected_account(dealloc_fee_payer);
  1876. }
  1877. vec![test_entry]
  1878. }
  1879. fn simd83_account_reallocate(formalize_loaded_transaction_data_size: bool) -> Vec<SvmTestEntry> {
  1880. let mut test_entries = vec![];
  1881. let program_name = "write-to-account";
  1882. let program_id = program_address(program_name);
  1883. let program_size = program_data_size(program_name);
  1884. let mut common_test_entry = SvmTestEntry::default();
  1885. common_test_entry.add_initial_program(program_name);
  1886. if !formalize_loaded_transaction_data_size {
  1887. common_test_entry
  1888. .feature_set
  1889. .formalize_loaded_transaction_data_size = false;
  1890. }
  1891. let fee_payer_keypair = Keypair::new();
  1892. let fee_payer = fee_payer_keypair.pubkey();
  1893. let mut fee_payer_data = AccountSharedData::default();
  1894. fee_payer_data.set_lamports(LAMPORTS_PER_SOL);
  1895. common_test_entry.add_initial_account(fee_payer, &fee_payer_data);
  1896. let mk_target = |size| {
  1897. AccountSharedData::create(
  1898. LAMPORTS_PER_SOL * 10,
  1899. vec![0; size],
  1900. program_id,
  1901. false,
  1902. u64::MAX,
  1903. )
  1904. };
  1905. let target = Pubkey::new_unique();
  1906. let target_start_size = 100;
  1907. common_test_entry.add_initial_account(target, &mk_target(target_start_size));
  1908. // we set a budget that is enough pre-large-realloc but not enough post-large-realloc
  1909. // the relevant feature counts programdata size, so if enabled, we add breathing room
  1910. // this test has nothing to do with the feature
  1911. let size_budget = Some(if formalize_loaded_transaction_data_size {
  1912. (program_size + MAX_PERMITTED_DATA_INCREASE) as u32
  1913. } else {
  1914. MAX_PERMITTED_DATA_INCREASE as u32
  1915. });
  1916. let print_transaction = WriteProgramInstruction::Print.create_transaction(
  1917. program_id,
  1918. &fee_payer_keypair,
  1919. target,
  1920. size_budget,
  1921. );
  1922. common_test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE * 2);
  1923. let common_test_entry = common_test_entry;
  1924. // batch 0/1:
  1925. // * successful realloc up/down
  1926. // * change reflected in same batch
  1927. for new_target_size in [target_start_size + 1, target_start_size - 1] {
  1928. let mut test_entry = common_test_entry.clone();
  1929. let realloc_transaction = WriteProgramInstruction::Realloc(new_target_size)
  1930. .create_transaction(program_id, &fee_payer_keypair, target, None);
  1931. test_entry.push_transaction(realloc_transaction);
  1932. test_entry.push_transaction(print_transaction.clone());
  1933. test_entry.transaction_batch[1]
  1934. .asserts
  1935. .logs
  1936. .push(format!("Program log: account size {new_target_size}"));
  1937. test_entry.update_expected_account_data(target, &mk_target(new_target_size));
  1938. test_entries.push(test_entry);
  1939. }
  1940. // batch 2:
  1941. // * successful large realloc up
  1942. // * transaction is aborted based on the new transaction data size post-realloc
  1943. {
  1944. let mut test_entry = common_test_entry.clone();
  1945. let new_target_size = target_start_size + MAX_PERMITTED_DATA_INCREASE;
  1946. let realloc_transaction = WriteProgramInstruction::Realloc(new_target_size)
  1947. .create_transaction(program_id, &fee_payer_keypair, target, None);
  1948. test_entry.push_transaction(realloc_transaction);
  1949. test_entry.push_transaction_with_status(
  1950. print_transaction.clone(),
  1951. ExecutionStatus::ProcessedFailed,
  1952. );
  1953. test_entry.update_expected_account_data(target, &mk_target(new_target_size));
  1954. test_entries.push(test_entry);
  1955. }
  1956. test_entries
  1957. }
  1958. #[test_case(program_medley())]
  1959. #[test_case(simple_transfer())]
  1960. #[test_case(simple_nonce(false))]
  1961. #[test_case(simple_nonce(true))]
  1962. #[test_case(simd83_intrabatch_account_reuse())]
  1963. #[test_case(simd83_nonce_reuse(false))]
  1964. #[test_case(simd83_nonce_reuse(true))]
  1965. #[test_case(simd83_account_deallocate())]
  1966. #[test_case(simd83_fee_payer_deallocate())]
  1967. #[test_case(simd83_account_reallocate(false))]
  1968. #[test_case(simd83_account_reallocate(true))]
  1969. fn svm_integration(test_entries: Vec<SvmTestEntry>) {
  1970. for test_entry in test_entries {
  1971. let env = SvmTestEnvironment::create(test_entry);
  1972. env.execute();
  1973. }
  1974. }
  1975. #[test]
  1976. fn program_cache_create_account() {
  1977. for loader_id in PROGRAM_OWNERS {
  1978. let mut test_entry = SvmTestEntry::with_loader_v4();
  1979. let fee_payer_keypair = Keypair::new();
  1980. let fee_payer = fee_payer_keypair.pubkey();
  1981. let mut fee_payer_data = AccountSharedData::default();
  1982. fee_payer_data.set_lamports(LAMPORTS_PER_SOL * 10);
  1983. test_entry.add_initial_account(fee_payer, &fee_payer_data);
  1984. let new_account_keypair = Keypair::new();
  1985. let program_id = new_account_keypair.pubkey();
  1986. // create an account owned by a loader
  1987. let create_transaction = system_transaction::create_account(
  1988. &fee_payer_keypair,
  1989. &new_account_keypair,
  1990. Hash::default(),
  1991. LAMPORTS_PER_SOL,
  1992. 0,
  1993. loader_id,
  1994. );
  1995. test_entry.push_transaction(create_transaction);
  1996. test_entry
  1997. .decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SOL + LAMPORTS_PER_SIGNATURE * 2);
  1998. // attempt to invoke the new account
  1999. let invoke_transaction = Transaction::new_signed_with_payer(
  2000. &[Instruction::new_with_bytes(program_id, &[], vec![])],
  2001. Some(&fee_payer),
  2002. &[&fee_payer_keypair],
  2003. Hash::default(),
  2004. );
  2005. test_entry.push_transaction_with_status(
  2006. invoke_transaction.clone(),
  2007. ExecutionStatus::ExecutedFailed,
  2008. );
  2009. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  2010. let mut env = SvmTestEnvironment::create(test_entry);
  2011. // test in same entry as account creation
  2012. env.execute();
  2013. let mut test_entry = SvmTestEntry {
  2014. initial_accounts: env.test_entry.final_accounts.clone(),
  2015. final_accounts: env.test_entry.final_accounts.clone(),
  2016. ..SvmTestEntry::default()
  2017. };
  2018. test_entry
  2019. .push_transaction_with_status(invoke_transaction, ExecutionStatus::ExecutedFailed);
  2020. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  2021. // test in different entry same slot
  2022. env.test_entry = test_entry;
  2023. env.execute();
  2024. }
  2025. }
  2026. #[test_case(false, false; "close::scan_only")]
  2027. #[test_case(false, true; "close::invoke")]
  2028. #[test_case(true, false; "upgrade::scan_only")]
  2029. #[test_case(true, true; "upgrade::invoke")]
  2030. fn program_cache_loaderv3_update_tombstone(upgrade_program: bool, invoke_changed_program: bool) {
  2031. let mut test_entry = SvmTestEntry::default();
  2032. let program_name = "hello-solana";
  2033. let program_id = program_address(program_name);
  2034. let fee_payer_keypair = Keypair::new();
  2035. let fee_payer = fee_payer_keypair.pubkey();
  2036. let mut fee_payer_data = AccountSharedData::default();
  2037. fee_payer_data.set_lamports(LAMPORTS_PER_SOL);
  2038. test_entry.add_initial_account(fee_payer, &fee_payer_data);
  2039. test_entry
  2040. .initial_programs
  2041. .push((program_name.to_string(), DEPLOYMENT_SLOT, Some(fee_payer)));
  2042. let buffer_address = Pubkey::new_unique();
  2043. // upgrade or close a deployed program
  2044. let change_instruction = if upgrade_program {
  2045. let mut data = bincode::serialize(&UpgradeableLoaderState::Buffer {
  2046. authority_address: Some(fee_payer),
  2047. })
  2048. .unwrap();
  2049. let mut program_bytecode = load_program(program_name.to_string());
  2050. data.append(&mut program_bytecode);
  2051. let buffer_account = AccountSharedData::create(
  2052. LAMPORTS_PER_SOL,
  2053. data,
  2054. bpf_loader_upgradeable::id(),
  2055. true,
  2056. u64::MAX,
  2057. );
  2058. test_entry.add_initial_account(buffer_address, &buffer_account);
  2059. test_entry.drop_expected_account(buffer_address);
  2060. loaderv3_instruction::upgrade(
  2061. &program_id,
  2062. &buffer_address,
  2063. &fee_payer,
  2064. &Pubkey::new_unique(),
  2065. )
  2066. } else {
  2067. loaderv3_instruction::close_any(
  2068. &get_program_data_address(&program_id),
  2069. &Pubkey::new_unique(),
  2070. Some(&fee_payer),
  2071. Some(&program_id),
  2072. )
  2073. };
  2074. test_entry.push_transaction(Transaction::new_signed_with_payer(
  2075. &[change_instruction],
  2076. Some(&fee_payer),
  2077. &[&fee_payer_keypair],
  2078. Hash::default(),
  2079. ));
  2080. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  2081. let invoke_transaction = Transaction::new_signed_with_payer(
  2082. &[Instruction::new_with_bytes(program_id, &[], vec![])],
  2083. Some(&fee_payer),
  2084. &[&fee_payer_keypair],
  2085. Hash::default(),
  2086. );
  2087. // attempt to invoke the program, which must fail
  2088. // this ensures the local program cache reflects the change of state
  2089. // we have cases without this so we can assert the cache *before* the invoke contains the tombstone
  2090. if invoke_changed_program {
  2091. test_entry.push_transaction_with_status(
  2092. invoke_transaction.clone(),
  2093. ExecutionStatus::ExecutedFailed,
  2094. );
  2095. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  2096. }
  2097. let mut env = SvmTestEnvironment::create(test_entry);
  2098. // test in same entry as program change
  2099. env.execute();
  2100. assert!(env.is_program_blocked(&program_id));
  2101. let mut test_entry = SvmTestEntry {
  2102. initial_accounts: env.test_entry.final_accounts.clone(),
  2103. final_accounts: env.test_entry.final_accounts.clone(),
  2104. ..SvmTestEntry::default()
  2105. };
  2106. test_entry.push_transaction_with_status(invoke_transaction, ExecutionStatus::ExecutedFailed);
  2107. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  2108. // test in different entry same slot
  2109. env.test_entry = test_entry;
  2110. env.execute();
  2111. assert!(env.is_program_blocked(&program_id));
  2112. }
  2113. #[test_case(false; "upgrade::scan_only")]
  2114. #[test_case(true; "upgrade::invoke")]
  2115. fn program_cache_loaderv3_buffer_swap(invoke_changed_program: bool) {
  2116. let mut test_entry = SvmTestEntry::default();
  2117. let program_name = "hello-solana";
  2118. let fee_payer_keypair = Keypair::new();
  2119. let fee_payer = fee_payer_keypair.pubkey();
  2120. let mut fee_payer_data = AccountSharedData::default();
  2121. fee_payer_data.set_lamports(LAMPORTS_PER_SOL * 10);
  2122. test_entry.add_initial_account(fee_payer, &fee_payer_data);
  2123. // this account will start as a buffer and then become a program
  2124. // buffers make their way into the program cache
  2125. // so we test that pathological address reuse is not a problem
  2126. let target_keypair = Keypair::new();
  2127. let target = target_keypair.pubkey();
  2128. let programdata_address = get_program_data_address(&target);
  2129. // we have the same buffer ready at a different address to deploy from
  2130. let deploy_keypair = Keypair::new();
  2131. let deploy = deploy_keypair.pubkey();
  2132. let mut buffer_data = bincode::serialize(&UpgradeableLoaderState::Buffer {
  2133. authority_address: Some(fee_payer),
  2134. })
  2135. .unwrap();
  2136. let mut program_bytecode = load_program(program_name.to_string());
  2137. buffer_data.append(&mut program_bytecode);
  2138. let buffer_account = AccountSharedData::create(
  2139. LAMPORTS_PER_SOL,
  2140. buffer_data.clone(),
  2141. bpf_loader_upgradeable::id(),
  2142. true,
  2143. u64::MAX,
  2144. );
  2145. test_entry.add_initial_account(target, &buffer_account);
  2146. test_entry.add_initial_account(deploy, &buffer_account);
  2147. let program_data = bincode::serialize(&UpgradeableLoaderState::Program {
  2148. programdata_address,
  2149. })
  2150. .unwrap();
  2151. let program_account = AccountSharedData::create(
  2152. LAMPORTS_PER_SOL,
  2153. program_data,
  2154. bpf_loader_upgradeable::id(),
  2155. true,
  2156. u64::MAX,
  2157. );
  2158. test_entry.update_expected_account_data(target, &program_account);
  2159. test_entry.drop_expected_account(deploy);
  2160. // close the buffer
  2161. let close_instruction =
  2162. loaderv3_instruction::close_any(&target, &Pubkey::new_unique(), Some(&fee_payer), None);
  2163. // reopen as a program
  2164. #[allow(deprecated)]
  2165. let deploy_instruction = loaderv3_instruction::deploy_with_max_program_len(
  2166. &fee_payer,
  2167. &target,
  2168. &deploy,
  2169. &fee_payer,
  2170. LAMPORTS_PER_SOL,
  2171. buffer_data.len(),
  2172. )
  2173. .unwrap();
  2174. test_entry.push_transaction(Transaction::new_signed_with_payer(
  2175. &[close_instruction],
  2176. Some(&fee_payer),
  2177. &[&fee_payer_keypair],
  2178. Hash::default(),
  2179. ));
  2180. test_entry.push_transaction(Transaction::new_signed_with_payer(
  2181. &deploy_instruction,
  2182. Some(&fee_payer),
  2183. &[&fee_payer_keypair, &target_keypair],
  2184. Hash::default(),
  2185. ));
  2186. test_entry.decrease_expected_lamports(
  2187. &fee_payer,
  2188. Rent::default().minimum_balance(
  2189. UpgradeableLoaderState::size_of_programdata_metadata() + buffer_data.len(),
  2190. ) + LAMPORTS_PER_SIGNATURE * 3,
  2191. );
  2192. let invoke_transaction = Transaction::new_signed_with_payer(
  2193. &[Instruction::new_with_bytes(target, &[], vec![])],
  2194. Some(&fee_payer),
  2195. &[&fee_payer_keypair],
  2196. Hash::default(),
  2197. );
  2198. if invoke_changed_program {
  2199. test_entry.push_transaction_with_status(
  2200. invoke_transaction.clone(),
  2201. ExecutionStatus::ExecutedFailed,
  2202. );
  2203. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  2204. }
  2205. let mut env = SvmTestEnvironment::create(test_entry);
  2206. // test in same entry as program change
  2207. env.execute();
  2208. assert!(env.is_program_blocked(&target));
  2209. let mut test_entry = SvmTestEntry {
  2210. initial_accounts: env.test_entry.final_accounts.clone(),
  2211. final_accounts: env.test_entry.final_accounts.clone(),
  2212. ..SvmTestEntry::default()
  2213. };
  2214. test_entry.push_transaction_with_status(invoke_transaction, ExecutionStatus::ExecutedFailed);
  2215. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  2216. // test in different entry same slot
  2217. env.test_entry = test_entry;
  2218. env.execute();
  2219. assert!(env.is_program_blocked(&target));
  2220. }
  2221. #[test]
  2222. fn program_cache_stats() {
  2223. let mut test_entry = SvmTestEntry::default();
  2224. let program_name = "hello-solana";
  2225. let noop_program = program_address(program_name);
  2226. let fee_payer_keypair = Keypair::new();
  2227. let fee_payer = fee_payer_keypair.pubkey();
  2228. let mut fee_payer_data = AccountSharedData::default();
  2229. fee_payer_data.set_lamports(LAMPORTS_PER_SOL * 100);
  2230. test_entry.add_initial_account(fee_payer, &fee_payer_data);
  2231. test_entry
  2232. .initial_programs
  2233. .push((program_name.to_string(), DEPLOYMENT_SLOT, Some(fee_payer)));
  2234. let missing_program = Pubkey::new_unique();
  2235. // set up a future upgrade after the first batch
  2236. let buffer_address = Pubkey::new_unique();
  2237. {
  2238. let mut data = bincode::serialize(&UpgradeableLoaderState::Buffer {
  2239. authority_address: Some(fee_payer),
  2240. })
  2241. .unwrap();
  2242. let mut program_bytecode = load_program(program_name.to_string());
  2243. data.append(&mut program_bytecode);
  2244. let buffer_account = AccountSharedData::create(
  2245. LAMPORTS_PER_SOL,
  2246. data,
  2247. bpf_loader_upgradeable::id(),
  2248. true,
  2249. u64::MAX,
  2250. );
  2251. test_entry.add_initial_account(buffer_address, &buffer_account);
  2252. }
  2253. let make_transaction = |instructions: &[Instruction]| {
  2254. Transaction::new_signed_with_payer(
  2255. instructions,
  2256. Some(&fee_payer),
  2257. &[&fee_payer_keypair],
  2258. Hash::default(),
  2259. )
  2260. };
  2261. let succesful_noop_instruction = Instruction::new_with_bytes(noop_program, &[], vec![]);
  2262. let succesful_transfer_instruction =
  2263. system_instruction::transfer(&fee_payer, &Pubkey::new_unique(), LAMPORTS_PER_SOL);
  2264. let failing_transfer_instruction =
  2265. system_instruction::transfer(&fee_payer, &Pubkey::new_unique(), LAMPORTS_PER_SOL * 1000);
  2266. let fee_only_noop_instruction = Instruction::new_with_bytes(missing_program, &[], vec![]);
  2267. let mut noop_tx_usage = 0;
  2268. let mut system_tx_usage = 0;
  2269. let mut successful_transfers = 0;
  2270. test_entry.push_transaction(make_transaction(slice::from_ref(
  2271. &succesful_noop_instruction,
  2272. )));
  2273. noop_tx_usage += 1;
  2274. test_entry.push_transaction(make_transaction(slice::from_ref(
  2275. &succesful_transfer_instruction,
  2276. )));
  2277. system_tx_usage += 1;
  2278. successful_transfers += 1;
  2279. test_entry.push_transaction_with_status(
  2280. make_transaction(slice::from_ref(&failing_transfer_instruction)),
  2281. ExecutionStatus::ExecutedFailed,
  2282. );
  2283. system_tx_usage += 1;
  2284. test_entry.push_transaction(make_transaction(&[
  2285. succesful_noop_instruction.clone(),
  2286. succesful_noop_instruction.clone(),
  2287. succesful_transfer_instruction.clone(),
  2288. succesful_transfer_instruction.clone(),
  2289. succesful_noop_instruction.clone(),
  2290. ]));
  2291. noop_tx_usage += 1;
  2292. system_tx_usage += 1;
  2293. successful_transfers += 2;
  2294. test_entry.push_transaction_with_status(
  2295. make_transaction(&[
  2296. failing_transfer_instruction.clone(),
  2297. succesful_noop_instruction.clone(),
  2298. succesful_transfer_instruction.clone(),
  2299. ]),
  2300. ExecutionStatus::ExecutedFailed,
  2301. );
  2302. noop_tx_usage += 1;
  2303. system_tx_usage += 1;
  2304. // load failure/fee-only does not touch the program cache
  2305. test_entry.push_transaction_with_status(
  2306. make_transaction(&[
  2307. succesful_noop_instruction.clone(),
  2308. fee_only_noop_instruction.clone(),
  2309. ]),
  2310. ExecutionStatus::ProcessedFailed,
  2311. );
  2312. test_entry.decrease_expected_lamports(
  2313. &fee_payer,
  2314. LAMPORTS_PER_SIGNATURE * test_entry.transaction_batch.len() as u64
  2315. + LAMPORTS_PER_SOL * successful_transfers,
  2316. );
  2317. // nor does discard
  2318. test_entry.transaction_batch.push(TransactionBatchItem {
  2319. transaction: make_transaction(slice::from_ref(&succesful_transfer_instruction)),
  2320. check_result: Err(TransactionError::BlockhashNotFound),
  2321. asserts: ExecutionStatus::Discarded.into(),
  2322. });
  2323. let mut env = SvmTestEnvironment::create(test_entry);
  2324. env.execute();
  2325. // check all usage stats are as we expect
  2326. let global_program_cache = env
  2327. .batch_processor
  2328. .global_program_cache
  2329. .read()
  2330. .unwrap()
  2331. .get_flattened_entries_for_tests()
  2332. .into_iter()
  2333. .rev()
  2334. .collect::<Vec<_>>();
  2335. let (_, noop_entry) = global_program_cache
  2336. .iter()
  2337. .find(|(pubkey, _)| *pubkey == noop_program)
  2338. .unwrap();
  2339. assert_eq!(
  2340. noop_entry.tx_usage_counter.load(Ordering::Relaxed),
  2341. noop_tx_usage,
  2342. "noop_tx_usage matches"
  2343. );
  2344. let (_, system_entry) = global_program_cache
  2345. .iter()
  2346. .find(|(pubkey, _)| *pubkey == system_program::id())
  2347. .unwrap();
  2348. assert_eq!(
  2349. system_entry.tx_usage_counter.load(Ordering::Relaxed),
  2350. system_tx_usage,
  2351. "system_tx_usage matches"
  2352. );
  2353. assert!(
  2354. !global_program_cache
  2355. .iter()
  2356. .any(|(pubkey, _)| *pubkey == missing_program),
  2357. "missing_program is missing"
  2358. );
  2359. // set up the second batch
  2360. let mut test_entry = SvmTestEntry {
  2361. initial_accounts: env.test_entry.final_accounts.clone(),
  2362. final_accounts: env.test_entry.final_accounts.clone(),
  2363. ..SvmTestEntry::default()
  2364. };
  2365. // upgrade the program. this blocks execution but does not create a tombstone
  2366. // the main thing we are testing is the tx counter is ported across upgrades
  2367. //
  2368. // note the upgrade transaction actually counts as a usage, per the existing rules
  2369. // the program cache must load the program because it has no idea if it will be used for cpi
  2370. test_entry.push_transaction(Transaction::new_signed_with_payer(
  2371. &[loaderv3_instruction::upgrade(
  2372. &noop_program,
  2373. &buffer_address,
  2374. &fee_payer,
  2375. &Pubkey::new_unique(),
  2376. )],
  2377. Some(&fee_payer),
  2378. &[&fee_payer_keypair],
  2379. Hash::default(),
  2380. ));
  2381. noop_tx_usage += 1;
  2382. test_entry.drop_expected_account(buffer_address);
  2383. test_entry.push_transaction_with_status(
  2384. make_transaction(slice::from_ref(&succesful_noop_instruction)),
  2385. ExecutionStatus::ExecutedFailed,
  2386. );
  2387. noop_tx_usage += 1;
  2388. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE * 2);
  2389. env.test_entry = test_entry;
  2390. env.execute();
  2391. let (_, noop_entry) = env
  2392. .batch_processor
  2393. .global_program_cache
  2394. .read()
  2395. .unwrap()
  2396. .get_flattened_entries_for_tests()
  2397. .into_iter()
  2398. .rev()
  2399. .find(|(pubkey, _)| *pubkey == noop_program)
  2400. .unwrap();
  2401. assert_eq!(
  2402. noop_entry.tx_usage_counter.load(Ordering::Relaxed),
  2403. noop_tx_usage,
  2404. "noop_tx_usage matches"
  2405. );
  2406. // third batch, this creates a delayed visibility tombstone
  2407. let mut test_entry = SvmTestEntry {
  2408. initial_accounts: env.test_entry.final_accounts.clone(),
  2409. final_accounts: env.test_entry.final_accounts.clone(),
  2410. ..SvmTestEntry::default()
  2411. };
  2412. test_entry.push_transaction_with_status(
  2413. make_transaction(slice::from_ref(&succesful_noop_instruction)),
  2414. ExecutionStatus::ExecutedFailed,
  2415. );
  2416. noop_tx_usage += 1;
  2417. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE);
  2418. env.test_entry = test_entry;
  2419. env.execute();
  2420. let (_, noop_entry) = env
  2421. .batch_processor
  2422. .global_program_cache
  2423. .read()
  2424. .unwrap()
  2425. .get_flattened_entries_for_tests()
  2426. .into_iter()
  2427. .rev()
  2428. .find(|(pubkey, _)| *pubkey == noop_program)
  2429. .unwrap();
  2430. assert_eq!(
  2431. noop_entry.tx_usage_counter.load(Ordering::Relaxed),
  2432. noop_tx_usage,
  2433. "noop_tx_usage matches"
  2434. );
  2435. }
  2436. #[derive(Clone, PartialEq, Eq)]
  2437. enum Inspect<'a> {
  2438. LiveRead(&'a AccountSharedData),
  2439. LiveWrite(&'a AccountSharedData),
  2440. #[allow(dead_code)]
  2441. DeadRead,
  2442. DeadWrite,
  2443. }
  2444. impl From<Inspect<'_>> for (Option<AccountSharedData>, bool) {
  2445. fn from(inspect: Inspect) -> Self {
  2446. match inspect {
  2447. Inspect::LiveRead(account) => (Some(account.clone()), false),
  2448. Inspect::LiveWrite(account) => (Some(account.clone()), true),
  2449. Inspect::DeadRead => (None, false),
  2450. Inspect::DeadWrite => (None, true),
  2451. }
  2452. }
  2453. }
  2454. #[derive(Clone, Default)]
  2455. struct InspectedAccounts(pub HashMap<Pubkey, Vec<(Option<AccountSharedData>, bool)>>);
  2456. impl InspectedAccounts {
  2457. fn inspect(&mut self, pubkey: Pubkey, inspect: Inspect) {
  2458. self.0.entry(pubkey).or_default().push(inspect.into())
  2459. }
  2460. }
  2461. #[test_case(false, false; "separate_nonce::old")]
  2462. #[test_case(false, true; "separate_nonce::simd186")]
  2463. #[test_case(true, false; "fee_paying_nonce::old")]
  2464. #[test_case(true, true; "fee_paying_nonce::simd186")]
  2465. fn svm_inspect_nonce_load_failure(
  2466. fee_paying_nonce: bool,
  2467. formalize_loaded_transaction_data_size: bool,
  2468. ) {
  2469. let mut test_entry = SvmTestEntry::default();
  2470. let mut expected_inspected_accounts = InspectedAccounts::default();
  2471. if !formalize_loaded_transaction_data_size {
  2472. test_entry
  2473. .feature_set
  2474. .formalize_loaded_transaction_data_size = false;
  2475. }
  2476. let fee_payer_keypair = Keypair::new();
  2477. let dummy_keypair = Keypair::new();
  2478. let separate_nonce_keypair = Keypair::new();
  2479. let fee_payer = fee_payer_keypair.pubkey();
  2480. let dummy = dummy_keypair.pubkey();
  2481. let nonce_pubkey = if fee_paying_nonce {
  2482. fee_payer
  2483. } else {
  2484. separate_nonce_keypair.pubkey()
  2485. };
  2486. let initial_durable = DurableNonce::from_blockhash(&Hash::new_unique());
  2487. let initial_nonce_data =
  2488. nonce::state::Data::new(fee_payer, initial_durable, LAMPORTS_PER_SIGNATURE);
  2489. let mut initial_nonce_account = AccountSharedData::new_data(
  2490. LAMPORTS_PER_SOL,
  2491. &nonce::versions::Versions::new(nonce::state::State::Initialized(
  2492. initial_nonce_data.clone(),
  2493. )),
  2494. &system_program::id(),
  2495. )
  2496. .unwrap();
  2497. initial_nonce_account.set_rent_epoch(u64::MAX);
  2498. let initial_nonce_account = initial_nonce_account;
  2499. let initial_nonce_info = NonceInfo::new(nonce_pubkey, initial_nonce_account.clone());
  2500. let advanced_durable = DurableNonce::from_blockhash(&LAST_BLOCKHASH);
  2501. let mut advanced_nonce_info = initial_nonce_info.clone();
  2502. advanced_nonce_info
  2503. .try_advance_nonce(advanced_durable, LAMPORTS_PER_SIGNATURE)
  2504. .unwrap();
  2505. let compute_instruction = ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(1);
  2506. let advance_instruction = system_instruction::advance_nonce_account(&nonce_pubkey, &fee_payer);
  2507. let fee_only_noop_instruction = Instruction::new_with_bytes(
  2508. Pubkey::new_unique(),
  2509. &[],
  2510. vec![AccountMeta {
  2511. pubkey: dummy,
  2512. is_writable: true,
  2513. is_signer: true,
  2514. }],
  2515. );
  2516. test_entry.add_initial_account(nonce_pubkey, &initial_nonce_account);
  2517. let mut separate_fee_payer_account = AccountSharedData::default();
  2518. separate_fee_payer_account.set_lamports(LAMPORTS_PER_SOL);
  2519. let separate_fee_payer_account = separate_fee_payer_account;
  2520. let dummy_account =
  2521. AccountSharedData::create(1, vec![0; 2], system_program::id(), false, u64::MAX);
  2522. test_entry.add_initial_account(dummy, &dummy_account);
  2523. // we always inspect the nonce at least once
  2524. expected_inspected_accounts.inspect(nonce_pubkey, Inspect::LiveWrite(&initial_nonce_account));
  2525. // if we have a fee-paying nonce, we happen to inspect it again
  2526. // this is an unimportant implementation detail and also means these cases are trivial
  2527. // the true test is a separate nonce, to ensure we inspect it in pre-checks
  2528. if fee_paying_nonce {
  2529. expected_inspected_accounts
  2530. .inspect(nonce_pubkey, Inspect::LiveWrite(&initial_nonce_account));
  2531. } else {
  2532. test_entry.add_initial_account(fee_payer, &separate_fee_payer_account);
  2533. expected_inspected_accounts
  2534. .inspect(fee_payer, Inspect::LiveWrite(&separate_fee_payer_account));
  2535. }
  2536. // with simd186, transaction loading aborts when we hit the fee-payer because of TRANSACTION_ACCOUNT_BASE_SIZE
  2537. // without simd186, transaction loading aborts on the dummy account, so it also happens to be inspected
  2538. // the difference is immaterial to the test as long as it happens before the nonce is loaded for the transaction
  2539. if !fee_paying_nonce && !formalize_loaded_transaction_data_size {
  2540. expected_inspected_accounts.inspect(dummy, Inspect::LiveWrite(&dummy_account));
  2541. }
  2542. // by signing with the dummy account we ensure it precedes a separate nonce
  2543. let transaction = Transaction::new_signed_with_payer(
  2544. &[
  2545. compute_instruction,
  2546. advance_instruction,
  2547. fee_only_noop_instruction,
  2548. ],
  2549. Some(&fee_payer),
  2550. &[&fee_payer_keypair, &dummy_keypair],
  2551. *initial_durable.as_hash(),
  2552. );
  2553. if !fee_paying_nonce {
  2554. let sanitized = SanitizedTransaction::from_transaction_for_tests(transaction.clone());
  2555. let dummy_index = sanitized
  2556. .account_keys()
  2557. .iter()
  2558. .position(|key| *key == dummy)
  2559. .unwrap();
  2560. let nonce_index = sanitized
  2561. .account_keys()
  2562. .iter()
  2563. .position(|key| *key == nonce_pubkey)
  2564. .unwrap();
  2565. assert!(dummy_index < nonce_index);
  2566. }
  2567. test_entry.push_nonce_transaction_with_status(
  2568. transaction,
  2569. initial_nonce_info.clone(),
  2570. ExecutionStatus::ProcessedFailed,
  2571. );
  2572. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE * 2);
  2573. test_entry
  2574. .final_accounts
  2575. .get_mut(&nonce_pubkey)
  2576. .unwrap()
  2577. .data_as_mut_slice()
  2578. .copy_from_slice(advanced_nonce_info.account().data());
  2579. let env = SvmTestEnvironment::create(test_entry.clone());
  2580. env.execute();
  2581. let actual_inspected_accounts = env.mock_bank.inspected_accounts.read().unwrap().clone();
  2582. for (expected_pubkey, expected_account) in &expected_inspected_accounts.0 {
  2583. let actual_account = actual_inspected_accounts.get(expected_pubkey).unwrap();
  2584. assert_eq!(
  2585. expected_account, actual_account,
  2586. "pubkey: {expected_pubkey}",
  2587. );
  2588. }
  2589. }
  2590. #[test]
  2591. fn svm_inspect_account() {
  2592. let mut initial_test_entry = SvmTestEntry::default();
  2593. let mut expected_inspected_accounts = InspectedAccounts::default();
  2594. let fee_payer_keypair = Keypair::new();
  2595. let sender_keypair = Keypair::new();
  2596. let fee_payer = fee_payer_keypair.pubkey();
  2597. let sender = sender_keypair.pubkey();
  2598. let recipient = Pubkey::new_unique();
  2599. // Setting up the accounts for the transfer
  2600. // fee payer
  2601. let mut fee_payer_account = AccountSharedData::default();
  2602. fee_payer_account.set_lamports(85_000);
  2603. fee_payer_account.set_rent_epoch(u64::MAX);
  2604. initial_test_entry.add_initial_account(fee_payer, &fee_payer_account);
  2605. expected_inspected_accounts.inspect(fee_payer, Inspect::LiveWrite(&fee_payer_account));
  2606. // sender
  2607. let mut sender_account = AccountSharedData::default();
  2608. sender_account.set_lamports(11_000_000);
  2609. sender_account.set_rent_epoch(u64::MAX);
  2610. initial_test_entry.add_initial_account(sender, &sender_account);
  2611. expected_inspected_accounts.inspect(sender, Inspect::LiveWrite(&sender_account));
  2612. // recipient -- initially dead
  2613. expected_inspected_accounts.inspect(recipient, Inspect::DeadWrite);
  2614. // system program
  2615. let system_account = AccountSharedData::create(
  2616. 5000,
  2617. "system_program".as_bytes().to_vec(),
  2618. native_loader::id(),
  2619. true,
  2620. 0,
  2621. );
  2622. expected_inspected_accounts.inspect(system_program::id(), Inspect::LiveRead(&system_account));
  2623. let transfer_amount = 1_000_000;
  2624. let transaction = Transaction::new_signed_with_payer(
  2625. &[system_instruction::transfer(
  2626. &sender,
  2627. &recipient,
  2628. transfer_amount,
  2629. )],
  2630. Some(&fee_payer),
  2631. &[&fee_payer_keypair, &sender_keypair],
  2632. Hash::default(),
  2633. );
  2634. initial_test_entry.push_transaction(transaction);
  2635. let mut recipient_account = AccountSharedData::default();
  2636. recipient_account.set_lamports(transfer_amount);
  2637. initial_test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE * 2);
  2638. initial_test_entry.decrease_expected_lamports(&sender, transfer_amount);
  2639. initial_test_entry.create_expected_account(recipient, &recipient_account);
  2640. let initial_test_entry = initial_test_entry;
  2641. // Load and execute the transaction
  2642. let mut env = SvmTestEnvironment::create(initial_test_entry.clone());
  2643. env.execute();
  2644. // do another transfer; recipient should be alive now
  2645. // fee payer
  2646. let intermediate_fee_payer_account = initial_test_entry
  2647. .final_accounts
  2648. .get(&fee_payer)
  2649. .cloned()
  2650. .unwrap();
  2651. expected_inspected_accounts.inspect(
  2652. fee_payer,
  2653. Inspect::LiveWrite(&intermediate_fee_payer_account),
  2654. );
  2655. // sender
  2656. let intermediate_sender_account = initial_test_entry
  2657. .final_accounts
  2658. .get(&sender)
  2659. .cloned()
  2660. .unwrap();
  2661. expected_inspected_accounts.inspect(sender, Inspect::LiveWrite(&intermediate_sender_account));
  2662. // recipient -- now alive
  2663. let intermediate_recipient_account = initial_test_entry
  2664. .final_accounts
  2665. .get(&recipient)
  2666. .cloned()
  2667. .unwrap();
  2668. expected_inspected_accounts.inspect(
  2669. recipient,
  2670. Inspect::LiveWrite(&intermediate_recipient_account),
  2671. );
  2672. // system program
  2673. expected_inspected_accounts.inspect(system_program::id(), Inspect::LiveRead(&system_account));
  2674. let mut final_test_entry = SvmTestEntry {
  2675. initial_accounts: initial_test_entry.final_accounts.clone(),
  2676. final_accounts: initial_test_entry.final_accounts.clone(),
  2677. ..SvmTestEntry::default()
  2678. };
  2679. let transfer_amount = 456;
  2680. let transaction = Transaction::new_signed_with_payer(
  2681. &[system_instruction::transfer(
  2682. &sender,
  2683. &recipient,
  2684. transfer_amount,
  2685. )],
  2686. Some(&fee_payer),
  2687. &[&fee_payer_keypair, &sender_keypair],
  2688. Hash::default(),
  2689. );
  2690. final_test_entry.push_transaction(transaction);
  2691. final_test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE * 2);
  2692. final_test_entry.decrease_expected_lamports(&sender, transfer_amount);
  2693. final_test_entry.increase_expected_lamports(&recipient, transfer_amount);
  2694. // Load and execute the second transaction
  2695. env.test_entry = final_test_entry;
  2696. env.execute();
  2697. // Ensure all the expected inspected accounts were inspected
  2698. let actual_inspected_accounts = env.mock_bank.inspected_accounts.read().unwrap().clone();
  2699. for (expected_pubkey, expected_account) in &expected_inspected_accounts.0 {
  2700. let actual_account = actual_inspected_accounts.get(expected_pubkey).unwrap();
  2701. assert_eq!(
  2702. expected_account, actual_account,
  2703. "pubkey: {expected_pubkey}",
  2704. );
  2705. }
  2706. let num_expected_inspected_accounts: usize =
  2707. expected_inspected_accounts.0.values().map(Vec::len).sum();
  2708. let num_actual_inspected_accounts: usize =
  2709. actual_inspected_accounts.values().map(Vec::len).sum();
  2710. assert_eq!(
  2711. num_expected_inspected_accounts,
  2712. num_actual_inspected_accounts,
  2713. );
  2714. }
  2715. // Tests for proper accumulation of metrics across loaded programs in a batch.
  2716. #[test]
  2717. fn svm_metrics_accumulation() {
  2718. for test_entry in program_medley() {
  2719. let env = SvmTestEnvironment::create(test_entry);
  2720. let (transactions, check_results) = env.test_entry.prepare_transactions();
  2721. let result = env.batch_processor.load_and_execute_sanitized_transactions(
  2722. &env.mock_bank,
  2723. &transactions,
  2724. check_results,
  2725. &env.processing_environment,
  2726. &env.processing_config,
  2727. );
  2728. // jit compilation only happens on non-windows && x86_64
  2729. #[cfg(all(not(target_os = "windows"), target_arch = "x86_64"))]
  2730. {
  2731. assert_ne!(
  2732. result
  2733. .execute_timings
  2734. .details
  2735. .create_executor_jit_compile_us
  2736. .0,
  2737. 0
  2738. );
  2739. }
  2740. assert_ne!(
  2741. result.execute_timings.details.create_executor_load_elf_us.0,
  2742. 0
  2743. );
  2744. assert_ne!(
  2745. result
  2746. .execute_timings
  2747. .details
  2748. .create_executor_verify_code_us
  2749. .0,
  2750. 0
  2751. );
  2752. }
  2753. }
  2754. // NOTE this could be moved to its own file in the future, but it requires a total refactor of the test runner
  2755. mod balance_collector {
  2756. use {
  2757. super::*,
  2758. rand0_7::prelude::*,
  2759. solana_program_pack::Pack,
  2760. spl_generic_token::token_2022,
  2761. spl_token_interface::state::{
  2762. Account as TokenAccount, AccountState as TokenAccountState, Mint,
  2763. },
  2764. test_case::test_case,
  2765. };
  2766. const STARTING_BALANCE: u64 = LAMPORTS_PER_SOL * 100;
  2767. // a helper for constructing a transfer instruction, agnostic over system/token
  2768. // it also pulls double duty as a record of what the *result* of a transfer should be
  2769. // so we can instantiate a Transfer, gen the instruction, change it to fail, change the record to amount 0
  2770. // and then the final test confirms the pre/post balances are unchanged with no special casing
  2771. #[derive(Debug, Default)]
  2772. struct Transfer {
  2773. from: Pubkey,
  2774. to: Pubkey,
  2775. amount: u64,
  2776. }
  2777. impl Transfer {
  2778. // given a set of users, picks two randomly and does a random transfer between them
  2779. fn new_rand(users: &[Pubkey]) -> Self {
  2780. let mut rng = rand0_7::thread_rng();
  2781. let [from_idx, to_idx] = (0..users.len()).choose_multiple(&mut rng, 2)[..] else {
  2782. unreachable!()
  2783. };
  2784. let from = users[from_idx];
  2785. let to = users[to_idx];
  2786. let amount = rng.gen_range(1, STARTING_BALANCE / 100);
  2787. Self { from, to, amount }
  2788. }
  2789. fn to_system_instruction(&self) -> Instruction {
  2790. system_instruction::transfer(&self.from, &self.to, self.amount)
  2791. }
  2792. fn to_token_instruction(&self, fee_payer: &Pubkey) -> Instruction {
  2793. // true tokenkeg connoisseurs will note we shouldnt have to sign the sender
  2794. // we use a common account owner, the fee-payer, to conveniently reuse account state
  2795. // so why do we sign? to force the sender and receiver to be in a consistent order in account keys
  2796. // which means we can grab them by index in our final test instead of searching by key
  2797. let mut instruction = spl_token_interface::instruction::transfer(
  2798. &spl_token_interface::id(),
  2799. &self.from,
  2800. &self.to,
  2801. fee_payer,
  2802. &[],
  2803. self.amount,
  2804. )
  2805. .unwrap();
  2806. instruction.accounts[0].is_signer = true;
  2807. instruction
  2808. }
  2809. fn to_instruction(&self, fee_payer: &Pubkey, use_tokens: bool) -> Instruction {
  2810. if use_tokens {
  2811. self.to_token_instruction(fee_payer)
  2812. } else {
  2813. self.to_system_instruction()
  2814. }
  2815. }
  2816. }
  2817. #[test_case(false; "native")]
  2818. #[test_case(true; "token")]
  2819. fn svm_collect_balances(use_tokens: bool) {
  2820. let mut rng = rand0_7::thread_rng();
  2821. let fee_payer_keypair = Keypair::new();
  2822. let fake_fee_payer_keypair = Keypair::new();
  2823. let alice_keypair = Keypair::new();
  2824. let bob_keypair = Keypair::new();
  2825. let charlie_keypair = Keypair::new();
  2826. let fee_payer = fee_payer_keypair.pubkey();
  2827. let fake_fee_payer = fake_fee_payer_keypair.pubkey();
  2828. let mint = Pubkey::new_unique();
  2829. let alice = alice_keypair.pubkey();
  2830. let bob = bob_keypair.pubkey();
  2831. let charlie = charlie_keypair.pubkey();
  2832. let native_state = AccountSharedData::create(
  2833. STARTING_BALANCE,
  2834. vec![],
  2835. system_program::id(),
  2836. false,
  2837. u64::MAX,
  2838. );
  2839. let mut mint_buf = vec![0; Mint::get_packed_len()];
  2840. Mint {
  2841. decimals: 9,
  2842. is_initialized: true,
  2843. ..Mint::default()
  2844. }
  2845. .pack_into_slice(&mut mint_buf);
  2846. let mint_state = AccountSharedData::create(
  2847. LAMPORTS_PER_SOL,
  2848. mint_buf,
  2849. spl_token_interface::id(),
  2850. false,
  2851. u64::MAX,
  2852. );
  2853. let token_account_for_tests = || TokenAccount {
  2854. mint,
  2855. owner: fee_payer,
  2856. amount: STARTING_BALANCE,
  2857. state: TokenAccountState::Initialized,
  2858. ..TokenAccount::default()
  2859. };
  2860. let mut token_buf = vec![0; TokenAccount::get_packed_len()];
  2861. token_account_for_tests().pack_into_slice(&mut token_buf);
  2862. let token_state = AccountSharedData::create(
  2863. LAMPORTS_PER_SOL,
  2864. token_buf,
  2865. spl_token_interface::id(),
  2866. false,
  2867. u64::MAX,
  2868. );
  2869. let (_, spl_token) =
  2870. solana_program_binaries::by_id(&spl_token_interface::id(), &Rent::default())
  2871. .unwrap()
  2872. .swap_remove(0);
  2873. for _ in 0..100 {
  2874. let mut test_entry = SvmTestEntry::default();
  2875. test_entry.add_initial_account(fee_payer, &native_state.clone());
  2876. if use_tokens {
  2877. test_entry.add_initial_account(spl_token_interface::id(), &spl_token);
  2878. test_entry.add_initial_account(mint, &mint_state);
  2879. test_entry.add_initial_account(alice, &token_state);
  2880. test_entry.add_initial_account(bob, &token_state);
  2881. test_entry.add_initial_account(charlie, &token_state);
  2882. } else {
  2883. test_entry.add_initial_account(alice, &native_state);
  2884. test_entry.add_initial_account(bob, &native_state);
  2885. test_entry.add_initial_account(charlie, &native_state);
  2886. }
  2887. // test that fee-payer balances are reported correctly
  2888. // all we need to know is whether the transaction is processed or dropped
  2889. let mut transaction_discards = vec![];
  2890. // every time we perform a transfer, we mutate user_balances
  2891. // and then clone and push it into user_balance_history
  2892. // this lets us go through every svm balance record and confirm correctness
  2893. let mut user_balances = HashMap::new();
  2894. user_balances.insert(alice, STARTING_BALANCE);
  2895. user_balances.insert(bob, STARTING_BALANCE);
  2896. user_balances.insert(charlie, STARTING_BALANCE);
  2897. let mut user_balance_history = vec![(Transfer::default(), user_balances.clone())];
  2898. for _ in 0..50 {
  2899. // failures result in no balance changes (note we use a separate fee-payer)
  2900. // we mix some in with the successes to test that we never record changes for failures
  2901. let expected_status = match rng.gen::<f64>() {
  2902. n if n < 0.85 => ExecutionStatus::Succeeded,
  2903. n if n < 0.90 => ExecutionStatus::ExecutedFailed,
  2904. n if n < 0.95 => ExecutionStatus::ProcessedFailed,
  2905. _ => ExecutionStatus::Discarded,
  2906. };
  2907. transaction_discards.push(expected_status == ExecutionStatus::Discarded);
  2908. let mut transfer = Transfer::new_rand(&[alice, bob, charlie]);
  2909. let from_signer = vec![&alice_keypair, &bob_keypair, &charlie_keypair]
  2910. .into_iter()
  2911. .find(|k| k.pubkey() == transfer.from)
  2912. .unwrap();
  2913. let instructions = match expected_status {
  2914. // a success results in balance changes and is a normal transaction
  2915. ExecutionStatus::Succeeded => {
  2916. user_balances
  2917. .entry(transfer.from)
  2918. .and_modify(|v| *v -= transfer.amount);
  2919. user_balances
  2920. .entry(transfer.to)
  2921. .and_modify(|v| *v += transfer.amount);
  2922. vec![transfer.to_instruction(&fee_payer, use_tokens)]
  2923. }
  2924. // transfer an unreasonable amount to fail execution
  2925. ExecutionStatus::ExecutedFailed => {
  2926. transfer.amount = u64::MAX / 2;
  2927. let instruction = transfer.to_instruction(&fee_payer, use_tokens);
  2928. transfer.amount = 0;
  2929. vec![instruction]
  2930. }
  2931. // use a non-existent program to fail loading
  2932. // token22 is very convenient because its presence ensures token bals are recorded
  2933. // if we had to use a random program id we would need to push a token program onto account keys
  2934. ExecutionStatus::ProcessedFailed => {
  2935. let mut instruction = transfer.to_instruction(&fee_payer, use_tokens);
  2936. instruction.program_id = token_2022::id();
  2937. transfer.amount = 0;
  2938. vec![instruction]
  2939. }
  2940. // use a non-existent fee-payer to trigger a discard
  2941. ExecutionStatus::Discarded => {
  2942. let mut instruction = transfer.to_instruction(&fee_payer, use_tokens);
  2943. if use_tokens {
  2944. instruction.accounts[2].pubkey = fake_fee_payer;
  2945. }
  2946. transfer.amount = 0;
  2947. vec![instruction]
  2948. }
  2949. };
  2950. let transaction = if expected_status.discarded() {
  2951. Transaction::new_signed_with_payer(
  2952. &instructions,
  2953. Some(&fake_fee_payer),
  2954. &[&fake_fee_payer_keypair, from_signer],
  2955. Hash::default(),
  2956. )
  2957. } else {
  2958. test_entry.decrease_expected_lamports(&fee_payer, LAMPORTS_PER_SIGNATURE * 2);
  2959. Transaction::new_signed_with_payer(
  2960. &instructions,
  2961. Some(&fee_payer),
  2962. &[&fee_payer_keypair, from_signer],
  2963. Hash::default(),
  2964. )
  2965. };
  2966. test_entry.push_transaction_with_status(transaction, expected_status);
  2967. user_balance_history.push((transfer, user_balances.clone()));
  2968. }
  2969. // this block just updates the SvmTestEntry final account states to be accurate
  2970. // doing this instead of skipping it, we validate that user_balances is definitely correct
  2971. // because env.execute() will assert all these states match the final bank state
  2972. if use_tokens {
  2973. let mut token_account = token_account_for_tests();
  2974. let mut token_buf = vec![0; TokenAccount::get_packed_len()];
  2975. token_account.amount = *user_balances.get(&alice).unwrap();
  2976. token_account.pack_into_slice(&mut token_buf);
  2977. let final_token_state = AccountSharedData::create(
  2978. LAMPORTS_PER_SOL,
  2979. token_buf.clone(),
  2980. spl_token_interface::id(),
  2981. false,
  2982. u64::MAX,
  2983. );
  2984. test_entry.update_expected_account_data(alice, &final_token_state);
  2985. token_account.amount = *user_balances.get(&bob).unwrap();
  2986. token_account.pack_into_slice(&mut token_buf);
  2987. let final_token_state = AccountSharedData::create(
  2988. LAMPORTS_PER_SOL,
  2989. token_buf.clone(),
  2990. spl_token_interface::id(),
  2991. false,
  2992. u64::MAX,
  2993. );
  2994. test_entry.update_expected_account_data(bob, &final_token_state);
  2995. token_account.amount = *user_balances.get(&charlie).unwrap();
  2996. token_account.pack_into_slice(&mut token_buf);
  2997. let final_token_state = AccountSharedData::create(
  2998. LAMPORTS_PER_SOL,
  2999. token_buf.clone(),
  3000. spl_token_interface::id(),
  3001. false,
  3002. u64::MAX,
  3003. );
  3004. test_entry.update_expected_account_data(charlie, &final_token_state);
  3005. } else {
  3006. let mut alice_final_state = native_state.clone();
  3007. alice_final_state.set_lamports(*user_balances.get(&alice).unwrap());
  3008. test_entry.update_expected_account_data(alice, &alice_final_state);
  3009. let mut bob_final_state = native_state.clone();
  3010. bob_final_state.set_lamports(*user_balances.get(&bob).unwrap());
  3011. test_entry.update_expected_account_data(bob, &bob_final_state);
  3012. let mut charlie_final_state = native_state.clone();
  3013. charlie_final_state.set_lamports(*user_balances.get(&charlie).unwrap());
  3014. test_entry.update_expected_account_data(charlie, &charlie_final_state);
  3015. }
  3016. // turn on balance recording and run the batch
  3017. let mut env = SvmTestEnvironment::create(test_entry);
  3018. env.processing_config
  3019. .recording_config
  3020. .enable_transaction_balance_recording = true;
  3021. let batch_output = env.execute();
  3022. let (pre_lamport_vecs, post_lamport_vecs, pre_token_vecs, post_token_vecs) =
  3023. batch_output.balance_collector.unwrap().into_vecs();
  3024. // first test the fee-payer balances
  3025. let mut running_fee_payer_balance = STARTING_BALANCE;
  3026. for (pre_bal, post_bal, was_discarded) in pre_lamport_vecs
  3027. .iter()
  3028. .zip(post_lamport_vecs.clone())
  3029. .zip(transaction_discards)
  3030. .map(|((pres, posts), discard)| (pres[0], posts[0], discard))
  3031. {
  3032. // we trigger discards with a non-existent fee-payer
  3033. if was_discarded {
  3034. assert_eq!(pre_bal, 0);
  3035. assert_eq!(post_bal, 0);
  3036. continue;
  3037. }
  3038. let expected_post_balance = running_fee_payer_balance - LAMPORTS_PER_SIGNATURE * 2;
  3039. assert_eq!(pre_bal, running_fee_payer_balance);
  3040. assert_eq!(post_bal, expected_post_balance);
  3041. running_fee_payer_balance = expected_post_balance;
  3042. }
  3043. // thanks to execute() we know user_balances is correct
  3044. // now we test that every step in user_balance_history matches the svm recorded balances
  3045. // in other words, the test effectively has three balance trackers and we can test they *all* agree
  3046. // first get the collected balances in a manner that is system/token agnostic
  3047. let (batch_pre, batch_post) = if use_tokens {
  3048. let pre_tupls: Vec<_> = pre_token_vecs
  3049. .iter()
  3050. .map(|bals| (bals[0].amount, bals[1].amount))
  3051. .collect();
  3052. let post_tupls: Vec<_> = post_token_vecs
  3053. .iter()
  3054. .map(|bals| (bals[0].amount, bals[1].amount))
  3055. .collect();
  3056. (pre_tupls, post_tupls)
  3057. } else {
  3058. let pre_tupls: Vec<_> = pre_lamport_vecs
  3059. .iter()
  3060. .map(|bals| (bals[1], bals[2]))
  3061. .collect();
  3062. let post_tupls: Vec<_> = post_lamport_vecs
  3063. .iter()
  3064. .map(|bals| (bals[1], bals[2]))
  3065. .collect();
  3066. (pre_tupls, post_tupls)
  3067. };
  3068. // these two asserts are trivially true. we include them just to make it clearer what these vecs are
  3069. // for n transactions, we have n pre-balance sets and n post-balance sets from svm
  3070. // but we have *n+1* test balance sets: we push initial state, and then push post-tx bals once per tx
  3071. // this mismatch is not strange at all. we also only have n+1 distinct svm timesteps despite 2n records
  3072. // pre-balances: (0 1 2 3)
  3073. // post-balances: (1 2 3 4)
  3074. // this does not mean time-overlapping svm records are equal. svm only captures the two accounts used by transfer
  3075. // whereas our test balances capture all three accounts at every timestep, so we require no pre/post separation
  3076. assert_eq!(user_balance_history.len(), batch_pre.len() + 1);
  3077. assert_eq!(user_balance_history.len(), batch_post.len() + 1);
  3078. // these are the real tests
  3079. for (i, (svm_pre_balances, svm_post_balances)) in
  3080. batch_pre.into_iter().zip(batch_post).enumerate()
  3081. {
  3082. let (_, ref expected_pre_balances) = user_balance_history[i];
  3083. let (ref transfer, ref expected_post_balances) = user_balance_history[i + 1];
  3084. assert_eq!(
  3085. svm_pre_balances.0,
  3086. *expected_pre_balances.get(&transfer.from).unwrap()
  3087. );
  3088. assert_eq!(
  3089. svm_pre_balances.1,
  3090. *expected_pre_balances.get(&transfer.to).unwrap()
  3091. );
  3092. assert_eq!(
  3093. svm_post_balances.0,
  3094. *expected_post_balances.get(&transfer.from).unwrap()
  3095. );
  3096. assert_eq!(
  3097. svm_post_balances.1,
  3098. *expected_post_balances.get(&transfer.to).unwrap()
  3099. );
  3100. }
  3101. }
  3102. }
  3103. }