integration_test.rs 139 KB

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