integration_test.rs 138 KB

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