1use {
4 crate::{account::ExtraAccountMeta, error::AccountResolutionError},
5 solana_account_info::AccountInfo,
6 solana_instruction::{AccountMeta, Instruction},
7 solana_program_error::ProgramError,
8 solana_pubkey::Pubkey,
9 spl_discriminator::SplDiscriminate,
10 spl_pod::{
11 list::{self, ListView},
12 primitives::PodU32,
13 },
14 spl_type_length_value::state::{TlvState, TlvStateBorrowed, TlvStateMut},
15 std::future::Future,
16};
17
18pub type AccountDataResult = Result<Option<Vec<u8>>, AccountFetchError>;
21pub type AccountFetchError = Box<dyn std::error::Error + Send + Sync>;
24
25fn account_info_to_meta(account_info: &AccountInfo) -> AccountMeta {
27 AccountMeta {
28 pubkey: *account_info.key,
29 is_signer: account_info.is_signer,
30 is_writable: account_info.is_writable,
31 }
32}
33
34fn de_escalate_account_meta(account_meta: &mut AccountMeta, account_metas: &[AccountMeta]) {
36 let maybe_highest_privileges = account_metas
41 .iter()
42 .filter(|&x| x.pubkey == account_meta.pubkey)
43 .map(|x| (x.is_signer, x.is_writable))
44 .reduce(|acc, x| (acc.0 || x.0, acc.1 || x.1));
45 if let Some((is_signer, is_writable)) = maybe_highest_privileges {
47 if !is_signer && is_signer != account_meta.is_signer {
48 account_meta.is_signer = false;
51 }
52 if !is_writable && is_writable != account_meta.is_writable {
53 account_meta.is_writable = false;
56 }
57 }
58}
59
60pub struct ExtraAccountMetaList;
157impl ExtraAccountMetaList {
158 pub fn init<T: SplDiscriminate>(
161 data: &mut [u8],
162 extra_account_metas: &[ExtraAccountMeta],
163 ) -> Result<(), ProgramError> {
164 let mut state = TlvStateMut::unpack(data).unwrap();
165 let tlv_size = ListView::<ExtraAccountMeta>::size_of(extra_account_metas.len())?;
166 let (bytes, _) = state.alloc::<T>(tlv_size, false)?;
167 let mut validation_data = ListView::<ExtraAccountMeta>::init(bytes)?;
168 for meta in extra_account_metas {
169 validation_data.push(*meta)?;
170 }
171 Ok(())
172 }
173
174 pub fn update<T: SplDiscriminate>(
177 data: &mut [u8],
178 extra_account_metas: &[ExtraAccountMeta],
179 ) -> Result<(), ProgramError> {
180 let mut state = TlvStateMut::unpack(data).unwrap();
181 let tlv_size = ListView::<ExtraAccountMeta>::size_of(extra_account_metas.len())?;
182 let bytes = state.realloc_first::<T>(tlv_size)?;
183 let mut validation_data = ListView::<ExtraAccountMeta>::init(bytes)?;
184 for meta in extra_account_metas {
185 validation_data.push(*meta)?;
186 }
187 Ok(())
188 }
189
190 pub fn unpack_with_tlv_state<'a, T: SplDiscriminate>(
196 tlv_state: &'a TlvStateBorrowed,
197 ) -> Result<list::ListViewReadOnly<'a, ExtraAccountMeta, PodU32>, ProgramError> {
198 let bytes = tlv_state.get_first_bytes::<T>()?;
199 ListView::<ExtraAccountMeta>::unpack(bytes)
200 }
201
202 pub fn size_of(num_items: usize) -> Result<usize, ProgramError> {
204 Ok(TlvStateBorrowed::get_base_len()
205 .saturating_add(ListView::<ExtraAccountMeta>::size_of(num_items)?))
206 }
207
208 pub fn check_account_infos<T: SplDiscriminate>(
215 account_infos: &[AccountInfo],
216 instruction_data: &[u8],
217 program_id: &Pubkey,
218 data: &[u8],
219 ) -> Result<(), ProgramError> {
220 let state = TlvStateBorrowed::unpack(data).unwrap();
221 let extra_meta_list = ExtraAccountMetaList::unpack_with_tlv_state::<T>(&state)?;
222
223 let initial_accounts_len = account_infos.len() - extra_meta_list.len();
224
225 let provided_metas = account_infos
227 .iter()
228 .map(account_info_to_meta)
229 .collect::<Vec<_>>();
230
231 for (i, config) in extra_meta_list.iter().enumerate() {
232 let meta = {
233 let account_key_data_refs = account_infos
236 .iter()
237 .map(|info| {
238 let key = *info.key;
239 let data = info.try_borrow_data()?;
240 Ok((key, data))
241 })
242 .collect::<Result<Vec<_>, ProgramError>>()?;
243
244 config.resolve(instruction_data, program_id, |usize| {
245 account_key_data_refs
246 .get(usize)
247 .map(|(pubkey, opt_data)| (pubkey, Some(opt_data.as_ref())))
248 })?
249 };
250
251 let expected_index = i
253 .checked_add(initial_accounts_len)
254 .ok_or::<ProgramError>(AccountResolutionError::CalculationFailure.into())?;
255 if provided_metas.get(expected_index) != Some(&meta) {
256 return Err(AccountResolutionError::IncorrectAccount.into());
257 }
258 }
259
260 Ok(())
261 }
262
263 pub async fn add_to_instruction<T: SplDiscriminate, F, Fut>(
265 instruction: &mut Instruction,
266 fetch_account_data_fn: F,
267 data: &[u8],
268 ) -> Result<(), ProgramError>
269 where
270 F: Fn(Pubkey) -> Fut,
271 Fut: Future<Output = AccountDataResult>,
272 {
273 let state = TlvStateBorrowed::unpack(data)?;
274 let bytes = state.get_first_bytes::<T>()?;
275 let extra_account_metas = ListView::<ExtraAccountMeta>::unpack(bytes)?;
276
277 let mut account_key_datas = vec![];
279 for meta in instruction.accounts.iter() {
280 let account_data = fetch_account_data_fn(meta.pubkey)
281 .await
282 .map_err::<ProgramError, _>(|_| {
283 AccountResolutionError::AccountFetchFailed.into()
284 })?;
285 account_key_datas.push((meta.pubkey, account_data));
286 }
287
288 for extra_meta in extra_account_metas.iter() {
289 let mut meta =
290 extra_meta.resolve(&instruction.data, &instruction.program_id, |usize| {
291 account_key_datas
292 .get(usize)
293 .map(|(pubkey, opt_data)| (pubkey, opt_data.as_ref().map(|x| x.as_slice())))
294 })?;
295 de_escalate_account_meta(&mut meta, &instruction.accounts);
296
297 account_key_datas.push((
299 meta.pubkey,
300 fetch_account_data_fn(meta.pubkey)
301 .await
302 .map_err::<ProgramError, _>(|_| {
303 AccountResolutionError::AccountFetchFailed.into()
304 })?,
305 ));
306 instruction.accounts.push(meta);
307 }
308 Ok(())
309 }
310
311 pub fn add_to_cpi_instruction<'a, T: SplDiscriminate>(
313 cpi_instruction: &mut Instruction,
314 cpi_account_infos: &mut Vec<AccountInfo<'a>>,
315 data: &[u8],
316 account_infos: &[AccountInfo<'a>],
317 ) -> Result<(), ProgramError> {
318 let state = TlvStateBorrowed::unpack(data)?;
319 let bytes = state.get_first_bytes::<T>()?;
320 let extra_account_metas = ListView::<ExtraAccountMeta>::unpack(bytes)?;
321
322 for extra_meta in extra_account_metas.iter() {
323 let mut meta = {
324 let account_key_data_refs = cpi_account_infos
327 .iter()
328 .map(|info| {
329 let key = *info.key;
330 let data = info.try_borrow_data()?;
331 Ok((key, data))
332 })
333 .collect::<Result<Vec<_>, ProgramError>>()?;
334
335 extra_meta.resolve(
336 &cpi_instruction.data,
337 &cpi_instruction.program_id,
338 |usize| {
339 account_key_data_refs
340 .get(usize)
341 .map(|(pubkey, opt_data)| (pubkey, Some(opt_data.as_ref())))
342 },
343 )?
344 };
345 de_escalate_account_meta(&mut meta, &cpi_instruction.accounts);
346
347 let account_info = account_infos
348 .iter()
349 .find(|&x| *x.key == meta.pubkey)
350 .ok_or(AccountResolutionError::IncorrectAccount)?
351 .clone();
352
353 cpi_instruction.accounts.push(meta);
354 cpi_account_infos.push(account_info);
355 }
356 Ok(())
357 }
358}
359
360#[cfg(test)]
361mod tests {
362 use {
363 super::*,
364 crate::{pubkey_data::PubkeyData, seeds::Seed},
365 solana_instruction::AccountMeta,
366 solana_pubkey::Pubkey,
367 spl_discriminator::{ArrayDiscriminator, SplDiscriminate},
368 std::collections::HashMap,
369 };
370
371 pub struct TestInstruction;
372 impl SplDiscriminate for TestInstruction {
373 const SPL_DISCRIMINATOR: ArrayDiscriminator =
374 ArrayDiscriminator::new([1; ArrayDiscriminator::LENGTH]);
375 }
376
377 pub struct TestOtherInstruction;
378 impl SplDiscriminate for TestOtherInstruction {
379 const SPL_DISCRIMINATOR: ArrayDiscriminator =
380 ArrayDiscriminator::new([2; ArrayDiscriminator::LENGTH]);
381 }
382
383 pub struct MockRpc<'a> {
384 cache: HashMap<Pubkey, &'a AccountInfo<'a>>,
385 }
386 impl<'a> MockRpc<'a> {
387 pub fn setup(account_infos: &'a [AccountInfo<'a>]) -> Self {
388 let mut cache = HashMap::new();
389 for info in account_infos {
390 cache.insert(*info.key, info);
391 }
392 Self { cache }
393 }
394
395 pub async fn get_account_data(&self, pubkey: Pubkey) -> AccountDataResult {
396 Ok(self
397 .cache
398 .get(&pubkey)
399 .map(|account| account.try_borrow_data().unwrap().to_vec()))
400 }
401 }
402
403 #[tokio::test]
404 async fn init_with_metas() {
405 let metas = [
406 AccountMeta::new(Pubkey::new_unique(), false).into(),
407 AccountMeta::new(Pubkey::new_unique(), true).into(),
408 AccountMeta::new_readonly(Pubkey::new_unique(), true).into(),
409 AccountMeta::new_readonly(Pubkey::new_unique(), false).into(),
410 ];
411 let account_size = ExtraAccountMetaList::size_of(metas.len()).unwrap();
412 let mut buffer = vec![0; account_size];
413
414 ExtraAccountMetaList::init::<TestInstruction>(&mut buffer, &metas).unwrap();
415
416 let mock_rpc = MockRpc::setup(&[]);
417
418 let mut instruction = Instruction::new_with_bytes(Pubkey::new_unique(), &[], vec![]);
419 ExtraAccountMetaList::add_to_instruction::<TestInstruction, _, _>(
420 &mut instruction,
421 |pubkey| mock_rpc.get_account_data(pubkey),
422 &buffer,
423 )
424 .await
425 .unwrap();
426
427 let check_metas = metas
428 .iter()
429 .map(|e| AccountMeta::try_from(e).unwrap())
430 .collect::<Vec<_>>();
431
432 assert_eq!(instruction.accounts, check_metas,);
433 }
434
435 #[tokio::test]
436 async fn init_with_infos() {
437 let program_id = Pubkey::new_unique();
438
439 let pubkey1 = Pubkey::new_unique();
440 let mut lamports1 = 0;
441 let mut data1 = [];
442 let pubkey2 = Pubkey::new_unique();
443 let mut lamports2 = 0;
444 let mut data2 = [4, 4, 4, 6, 6, 6, 8, 8];
445 let pubkey3 = Pubkey::new_unique();
446 let mut lamports3 = 0;
447 let mut data3 = [];
448 let owner = Pubkey::new_unique();
449 let account_infos = [
450 AccountInfo::new(
451 &pubkey1,
452 false,
453 true,
454 &mut lamports1,
455 &mut data1,
456 &owner,
457 false,
458 ),
459 AccountInfo::new(
460 &pubkey2,
461 true,
462 false,
463 &mut lamports2,
464 &mut data2,
465 &owner,
466 false,
467 ),
468 AccountInfo::new(
469 &pubkey3,
470 false,
471 false,
472 &mut lamports3,
473 &mut data3,
474 &owner,
475 false,
476 ),
477 ];
478
479 let required_pda = ExtraAccountMeta::new_with_seeds(
480 &[
481 Seed::AccountKey { index: 0 },
482 Seed::AccountData {
483 account_index: 1,
484 data_index: 2,
485 length: 4,
486 },
487 ],
488 false,
489 true,
490 )
491 .unwrap();
492
493 let required_extra_accounts = [
495 ExtraAccountMeta::from(&account_infos[0]),
496 ExtraAccountMeta::from(&account_infos[1]),
497 ExtraAccountMeta::from(&account_infos[2]),
498 required_pda,
499 ];
500
501 let account_size = ExtraAccountMetaList::size_of(required_extra_accounts.len()).unwrap();
502 let mut buffer = vec![0; account_size];
503
504 ExtraAccountMetaList::init::<TestInstruction>(&mut buffer, &required_extra_accounts)
505 .unwrap();
506
507 let mock_rpc = MockRpc::setup(&account_infos);
508
509 let mut instruction = Instruction::new_with_bytes(program_id, &[], vec![]);
510 ExtraAccountMetaList::add_to_instruction::<TestInstruction, _, _>(
511 &mut instruction,
512 |pubkey| mock_rpc.get_account_data(pubkey),
513 &buffer,
514 )
515 .await
516 .unwrap();
517
518 let (check_required_pda, _) = Pubkey::find_program_address(
519 &[
520 account_infos[0].key.as_ref(), &account_infos[1].try_borrow_data().unwrap()[2..6], ],
523 &program_id,
524 );
525
526 let check_metas = [
528 account_info_to_meta(&account_infos[0]),
529 account_info_to_meta(&account_infos[1]),
530 account_info_to_meta(&account_infos[2]),
531 AccountMeta::new(check_required_pda, false),
532 ];
533
534 assert_eq!(instruction.accounts, check_metas,);
535
536 assert_eq!(
537 instruction.accounts.get(3).unwrap().pubkey,
538 check_required_pda
539 );
540 }
541
542 #[tokio::test]
543 async fn init_with_extra_account_metas() {
544 let program_id = Pubkey::new_unique();
545
546 let extra_meta3_literal_str = "seed_prefix";
547
548 let ix_account1 = AccountMeta::new(Pubkey::new_unique(), false);
549 let ix_account2 = AccountMeta::new(Pubkey::new_unique(), true);
550
551 let extra_meta1 = AccountMeta::new(Pubkey::new_unique(), false);
552 let extra_meta2 = AccountMeta::new(Pubkey::new_unique(), true);
553 let extra_meta3 = ExtraAccountMeta::new_with_seeds(
554 &[
555 Seed::Literal {
556 bytes: extra_meta3_literal_str.as_bytes().to_vec(),
557 },
558 Seed::InstructionData {
559 index: 1,
560 length: 1, },
562 Seed::AccountKey { index: 0 },
563 Seed::AccountKey { index: 2 },
564 ],
565 false,
566 true,
567 )
568 .unwrap();
569 let extra_meta4 = ExtraAccountMeta::new_with_pubkey_data(
570 &PubkeyData::InstructionData { index: 4 },
571 false,
572 true,
573 )
574 .unwrap();
575
576 let metas = [
577 ExtraAccountMeta::from(&extra_meta1),
578 ExtraAccountMeta::from(&extra_meta2),
579 extra_meta3,
580 extra_meta4,
581 ];
582
583 let mut ix_data = vec![1, 2, 3, 4];
584 let check_extra_meta4_pubkey = Pubkey::new_unique();
585 ix_data.extend_from_slice(check_extra_meta4_pubkey.as_ref());
586
587 let ix_accounts = vec![ix_account1.clone(), ix_account2.clone()];
588 let mut instruction = Instruction::new_with_bytes(program_id, &ix_data, ix_accounts);
589
590 let account_size = ExtraAccountMetaList::size_of(metas.len()).unwrap();
591 let mut buffer = vec![0; account_size];
592
593 ExtraAccountMetaList::init::<TestInstruction>(&mut buffer, &metas).unwrap();
594
595 let mock_rpc = MockRpc::setup(&[]);
596
597 ExtraAccountMetaList::add_to_instruction::<TestInstruction, _, _>(
598 &mut instruction,
599 |pubkey| mock_rpc.get_account_data(pubkey),
600 &buffer,
601 )
602 .await
603 .unwrap();
604
605 let check_extra_meta3_u8_arg = ix_data[1];
606 let check_extra_meta3_pubkey = Pubkey::find_program_address(
607 &[
608 extra_meta3_literal_str.as_bytes(),
609 &[check_extra_meta3_u8_arg],
610 ix_account1.pubkey.as_ref(),
611 extra_meta1.pubkey.as_ref(),
612 ],
613 &program_id,
614 )
615 .0;
616 let check_metas = [
617 ix_account1,
618 ix_account2,
619 extra_meta1,
620 extra_meta2,
621 AccountMeta::new(check_extra_meta3_pubkey, false),
622 AccountMeta::new(check_extra_meta4_pubkey, false),
623 ];
624
625 assert_eq!(
626 instruction.accounts.get(4).unwrap().pubkey,
627 check_extra_meta3_pubkey,
628 );
629 assert_eq!(
630 instruction.accounts.get(5).unwrap().pubkey,
631 check_extra_meta4_pubkey,
632 );
633 assert_eq!(instruction.accounts, check_metas,);
634 }
635
636 #[tokio::test]
637 async fn init_multiple() {
638 let extra_meta5_literal_str = "seed_prefix";
639 let extra_meta5_literal_u32 = 4u32;
640 let other_meta2_literal_str = "other_seed_prefix";
641
642 let extra_meta1 = AccountMeta::new(Pubkey::new_unique(), false);
643 let extra_meta2 = AccountMeta::new(Pubkey::new_unique(), true);
644 let extra_meta3 = AccountMeta::new_readonly(Pubkey::new_unique(), true);
645 let extra_meta4 = AccountMeta::new_readonly(Pubkey::new_unique(), false);
646 let extra_meta5 = ExtraAccountMeta::new_with_seeds(
647 &[
648 Seed::Literal {
649 bytes: extra_meta5_literal_str.as_bytes().to_vec(),
650 },
651 Seed::Literal {
652 bytes: extra_meta5_literal_u32.to_le_bytes().to_vec(),
653 },
654 Seed::InstructionData {
655 index: 5,
656 length: 1, },
658 Seed::AccountKey { index: 2 },
659 ],
660 false,
661 true,
662 )
663 .unwrap();
664 let extra_meta6 = ExtraAccountMeta::new_with_pubkey_data(
665 &PubkeyData::InstructionData { index: 8 },
666 false,
667 true,
668 )
669 .unwrap();
670
671 let other_meta1 = AccountMeta::new(Pubkey::new_unique(), false);
672 let other_meta2 = ExtraAccountMeta::new_with_seeds(
673 &[
674 Seed::Literal {
675 bytes: other_meta2_literal_str.as_bytes().to_vec(),
676 },
677 Seed::InstructionData {
678 index: 1,
679 length: 4, },
681 Seed::AccountKey { index: 0 },
682 ],
683 false,
684 true,
685 )
686 .unwrap();
687 let other_meta3 = ExtraAccountMeta::new_with_pubkey_data(
688 &PubkeyData::InstructionData { index: 7 },
689 false,
690 true,
691 )
692 .unwrap();
693
694 let metas = [
695 ExtraAccountMeta::from(&extra_meta1),
696 ExtraAccountMeta::from(&extra_meta2),
697 ExtraAccountMeta::from(&extra_meta3),
698 ExtraAccountMeta::from(&extra_meta4),
699 extra_meta5,
700 extra_meta6,
701 ];
702 let other_metas = [
703 ExtraAccountMeta::from(&other_meta1),
704 other_meta2,
705 other_meta3,
706 ];
707
708 let account_size = ExtraAccountMetaList::size_of(metas.len()).unwrap()
709 + ExtraAccountMetaList::size_of(other_metas.len()).unwrap();
710 let mut buffer = vec![0; account_size];
711
712 ExtraAccountMetaList::init::<TestInstruction>(&mut buffer, &metas).unwrap();
713 ExtraAccountMetaList::init::<TestOtherInstruction>(&mut buffer, &other_metas).unwrap();
714
715 let mock_rpc = MockRpc::setup(&[]);
716
717 let program_id = Pubkey::new_unique();
718
719 let mut ix_data = vec![0, 0, 0, 0, 0, 7, 0, 0];
720 let check_extra_meta6_pubkey = Pubkey::new_unique();
721 ix_data.extend_from_slice(check_extra_meta6_pubkey.as_ref());
722
723 let ix_accounts = vec![];
724
725 let mut instruction = Instruction::new_with_bytes(program_id, &ix_data, ix_accounts);
726 ExtraAccountMetaList::add_to_instruction::<TestInstruction, _, _>(
727 &mut instruction,
728 |pubkey| mock_rpc.get_account_data(pubkey),
729 &buffer,
730 )
731 .await
732 .unwrap();
733
734 let check_extra_meta5_u8_arg = ix_data[5];
735 let check_extra_meta5_pubkey = Pubkey::find_program_address(
736 &[
737 extra_meta5_literal_str.as_bytes(),
738 extra_meta5_literal_u32.to_le_bytes().as_ref(),
739 &[check_extra_meta5_u8_arg],
740 extra_meta3.pubkey.as_ref(),
741 ],
742 &program_id,
743 )
744 .0;
745 let check_metas = [
746 extra_meta1,
747 extra_meta2,
748 extra_meta3,
749 extra_meta4,
750 AccountMeta::new(check_extra_meta5_pubkey, false),
751 AccountMeta::new(check_extra_meta6_pubkey, false),
752 ];
753
754 assert_eq!(
755 instruction.accounts.get(4).unwrap().pubkey,
756 check_extra_meta5_pubkey,
757 );
758 assert_eq!(
759 instruction.accounts.get(5).unwrap().pubkey,
760 check_extra_meta6_pubkey,
761 );
762 assert_eq!(instruction.accounts, check_metas,);
763
764 let program_id = Pubkey::new_unique();
765
766 let ix_account1 = AccountMeta::new(Pubkey::new_unique(), false);
767 let ix_account2 = AccountMeta::new(Pubkey::new_unique(), true);
768 let ix_accounts = vec![ix_account1.clone(), ix_account2.clone()];
769
770 let mut ix_data = vec![0, 26, 0, 0, 0, 0, 0];
771 let check_other_meta3_pubkey = Pubkey::new_unique();
772 ix_data.extend_from_slice(check_other_meta3_pubkey.as_ref());
773
774 let mut instruction = Instruction::new_with_bytes(program_id, &ix_data, ix_accounts);
775 ExtraAccountMetaList::add_to_instruction::<TestOtherInstruction, _, _>(
776 &mut instruction,
777 |pubkey| mock_rpc.get_account_data(pubkey),
778 &buffer,
779 )
780 .await
781 .unwrap();
782
783 let check_other_meta2_u32_arg = u32::from_le_bytes(ix_data[1..5].try_into().unwrap());
784 let check_other_meta2_pubkey = Pubkey::find_program_address(
785 &[
786 other_meta2_literal_str.as_bytes(),
787 check_other_meta2_u32_arg.to_le_bytes().as_ref(),
788 ix_account1.pubkey.as_ref(),
789 ],
790 &program_id,
791 )
792 .0;
793 let check_other_metas = [
794 ix_account1,
795 ix_account2,
796 other_meta1,
797 AccountMeta::new(check_other_meta2_pubkey, false),
798 AccountMeta::new(check_other_meta3_pubkey, false),
799 ];
800
801 assert_eq!(
802 instruction.accounts.get(3).unwrap().pubkey,
803 check_other_meta2_pubkey,
804 );
805 assert_eq!(
806 instruction.accounts.get(4).unwrap().pubkey,
807 check_other_meta3_pubkey,
808 );
809 assert_eq!(instruction.accounts, check_other_metas,);
810 }
811
812 #[tokio::test]
813 async fn init_mixed() {
814 let extra_meta5_literal_str = "seed_prefix";
815 let extra_meta6_literal_u64 = 28u64;
816
817 let pubkey1 = Pubkey::new_unique();
818 let mut lamports1 = 0;
819 let mut data1 = [];
820 let pubkey2 = Pubkey::new_unique();
821 let mut lamports2 = 0;
822 let mut data2 = [];
823 let pubkey3 = Pubkey::new_unique();
824 let mut lamports3 = 0;
825 let mut data3 = [];
826 let owner = Pubkey::new_unique();
827 let account_infos = [
828 AccountInfo::new(
829 &pubkey1,
830 false,
831 true,
832 &mut lamports1,
833 &mut data1,
834 &owner,
835 false,
836 ),
837 AccountInfo::new(
838 &pubkey2,
839 true,
840 false,
841 &mut lamports2,
842 &mut data2,
843 &owner,
844 false,
845 ),
846 AccountInfo::new(
847 &pubkey3,
848 false,
849 false,
850 &mut lamports3,
851 &mut data3,
852 &owner,
853 false,
854 ),
855 ];
856
857 let extra_meta1 = AccountMeta::new(Pubkey::new_unique(), false);
858 let extra_meta2 = AccountMeta::new(Pubkey::new_unique(), true);
859 let extra_meta3 = AccountMeta::new_readonly(Pubkey::new_unique(), true);
860 let extra_meta4 = AccountMeta::new_readonly(Pubkey::new_unique(), false);
861 let extra_meta5 = ExtraAccountMeta::new_with_seeds(
862 &[
863 Seed::Literal {
864 bytes: extra_meta5_literal_str.as_bytes().to_vec(),
865 },
866 Seed::InstructionData {
867 index: 1,
868 length: 8, },
870 Seed::InstructionData {
871 index: 9,
872 length: 32, },
874 Seed::AccountKey { index: 2 },
875 ],
876 false,
877 true,
878 )
879 .unwrap();
880 let extra_meta6 = ExtraAccountMeta::new_with_seeds(
881 &[
882 Seed::Literal {
883 bytes: extra_meta6_literal_u64.to_le_bytes().to_vec(),
884 },
885 Seed::AccountKey { index: 1 },
886 Seed::AccountKey { index: 4 },
887 ],
888 false,
889 true,
890 )
891 .unwrap();
892 let extra_meta7 = ExtraAccountMeta::new_with_pubkey_data(
893 &PubkeyData::InstructionData { index: 41 }, false,
895 true,
896 )
897 .unwrap();
898
899 let test_ix_required_extra_accounts = account_infos
900 .iter()
901 .map(ExtraAccountMeta::from)
902 .collect::<Vec<_>>();
903 let test_other_ix_required_extra_accounts = [
904 ExtraAccountMeta::from(&extra_meta1),
905 ExtraAccountMeta::from(&extra_meta2),
906 ExtraAccountMeta::from(&extra_meta3),
907 ExtraAccountMeta::from(&extra_meta4),
908 extra_meta5,
909 extra_meta6,
910 extra_meta7,
911 ];
912
913 let account_size = ExtraAccountMetaList::size_of(test_ix_required_extra_accounts.len())
914 .unwrap()
915 + ExtraAccountMetaList::size_of(test_other_ix_required_extra_accounts.len()).unwrap();
916 let mut buffer = vec![0; account_size];
917
918 ExtraAccountMetaList::init::<TestInstruction>(
919 &mut buffer,
920 &test_ix_required_extra_accounts,
921 )
922 .unwrap();
923 ExtraAccountMetaList::init::<TestOtherInstruction>(
924 &mut buffer,
925 &test_other_ix_required_extra_accounts,
926 )
927 .unwrap();
928
929 let mock_rpc = MockRpc::setup(&account_infos);
930
931 let program_id = Pubkey::new_unique();
932 let mut instruction = Instruction::new_with_bytes(program_id, &[], vec![]);
933 ExtraAccountMetaList::add_to_instruction::<TestInstruction, _, _>(
934 &mut instruction,
935 |pubkey| mock_rpc.get_account_data(pubkey),
936 &buffer,
937 )
938 .await
939 .unwrap();
940
941 let test_ix_check_metas = account_infos
942 .iter()
943 .map(account_info_to_meta)
944 .collect::<Vec<_>>();
945 assert_eq!(instruction.accounts, test_ix_check_metas,);
946
947 let program_id = Pubkey::new_unique();
948
949 let instruction_u8array_arg = [1, 2, 3, 4, 5, 6, 7, 8];
950 let instruction_pubkey_arg = Pubkey::new_unique();
951 let instruction_key_data_pubkey_arg = Pubkey::new_unique();
952
953 let mut instruction_data = vec![0];
954 instruction_data.extend_from_slice(&instruction_u8array_arg);
955 instruction_data.extend_from_slice(instruction_pubkey_arg.as_ref());
956 instruction_data.extend_from_slice(instruction_key_data_pubkey_arg.as_ref());
957
958 let mut instruction = Instruction::new_with_bytes(program_id, &instruction_data, vec![]);
959 ExtraAccountMetaList::add_to_instruction::<TestOtherInstruction, _, _>(
960 &mut instruction,
961 |pubkey| mock_rpc.get_account_data(pubkey),
962 &buffer,
963 )
964 .await
965 .unwrap();
966
967 let check_extra_meta5_pubkey = Pubkey::find_program_address(
968 &[
969 extra_meta5_literal_str.as_bytes(),
970 &instruction_u8array_arg,
971 instruction_pubkey_arg.as_ref(),
972 extra_meta3.pubkey.as_ref(),
973 ],
974 &program_id,
975 )
976 .0;
977
978 let check_extra_meta6_pubkey = Pubkey::find_program_address(
979 &[
980 extra_meta6_literal_u64.to_le_bytes().as_ref(),
981 extra_meta2.pubkey.as_ref(),
982 check_extra_meta5_pubkey.as_ref(), ],
984 &program_id,
985 )
986 .0;
987
988 let test_other_ix_check_metas = vec![
989 extra_meta1,
990 extra_meta2,
991 extra_meta3,
992 extra_meta4,
993 AccountMeta::new(check_extra_meta5_pubkey, false),
994 AccountMeta::new(check_extra_meta6_pubkey, false),
995 AccountMeta::new(instruction_key_data_pubkey_arg, false),
996 ];
997
998 assert_eq!(
999 instruction.accounts.get(4).unwrap().pubkey,
1000 check_extra_meta5_pubkey,
1001 );
1002 assert_eq!(
1003 instruction.accounts.get(5).unwrap().pubkey,
1004 check_extra_meta6_pubkey,
1005 );
1006 assert_eq!(
1007 instruction.accounts.get(6).unwrap().pubkey,
1008 instruction_key_data_pubkey_arg,
1009 );
1010 assert_eq!(instruction.accounts, test_other_ix_check_metas,);
1011 }
1012
1013 #[tokio::test]
1014 async fn cpi_instruction() {
1015 let program_id = Pubkey::new_unique();
1021 let owner = Pubkey::new_unique();
1022
1023 let required_pda1_literal_string = "required_pda1";
1025 let required_pda2_literal_u32 = 4u32;
1026 let required_key_data_instruction_data = Pubkey::new_unique();
1027
1028 let instruction_u8array_arg = [1, 2, 3, 4, 5, 6, 7, 8];
1033 let instruction_u64_arg = 208u64;
1034 let mut instruction_data = vec![0];
1035 instruction_data.extend_from_slice(&instruction_u8array_arg);
1036 instruction_data.extend_from_slice(instruction_u64_arg.to_le_bytes().as_ref());
1037 instruction_data.extend_from_slice(required_key_data_instruction_data.as_ref());
1038
1039 let ix_accounts = vec![
1041 AccountMeta::new(Pubkey::new_unique(), false),
1042 AccountMeta::new(Pubkey::new_unique(), false),
1043 ];
1044
1045 let extra_meta1 = AccountMeta::new(Pubkey::new_unique(), false);
1047 let extra_meta2 = AccountMeta::new(Pubkey::new_unique(), true);
1048 let extra_meta3 = AccountMeta::new_readonly(Pubkey::new_unique(), false);
1049 let required_accounts = [
1050 ExtraAccountMeta::from(&extra_meta1),
1051 ExtraAccountMeta::from(&extra_meta2),
1052 ExtraAccountMeta::from(&extra_meta3),
1053 ExtraAccountMeta::new_with_seeds(
1054 &[
1055 Seed::Literal {
1056 bytes: required_pda1_literal_string.as_bytes().to_vec(),
1057 },
1058 Seed::InstructionData {
1059 index: 1,
1060 length: 8, },
1062 Seed::AccountKey { index: 1 },
1063 ],
1064 false,
1065 true,
1066 )
1067 .unwrap(),
1068 ExtraAccountMeta::new_with_seeds(
1069 &[
1070 Seed::Literal {
1071 bytes: required_pda2_literal_u32.to_le_bytes().to_vec(),
1072 },
1073 Seed::InstructionData {
1074 index: 9,
1075 length: 8, },
1077 Seed::AccountKey { index: 5 },
1078 ],
1079 false,
1080 true,
1081 )
1082 .unwrap(),
1083 ExtraAccountMeta::new_with_seeds(
1084 &[
1085 Seed::InstructionData {
1086 index: 0,
1087 length: 1, },
1089 Seed::AccountData {
1090 account_index: 2,
1091 data_index: 0,
1092 length: 8,
1093 },
1094 ],
1095 false,
1096 true,
1097 )
1098 .unwrap(),
1099 ExtraAccountMeta::new_with_seeds(
1100 &[
1101 Seed::AccountData {
1102 account_index: 5,
1103 data_index: 4,
1104 length: 4,
1105 }, ],
1107 false,
1108 true,
1109 )
1110 .unwrap(),
1111 ExtraAccountMeta::new_with_pubkey_data(
1112 &PubkeyData::InstructionData { index: 17 },
1113 false,
1114 true,
1115 )
1116 .unwrap(),
1117 ExtraAccountMeta::new_with_pubkey_data(
1118 &PubkeyData::AccountData {
1119 account_index: 6,
1120 data_index: 0,
1121 },
1122 false,
1123 true,
1124 )
1125 .unwrap(),
1126 ExtraAccountMeta::new_with_pubkey_data(
1127 &PubkeyData::AccountData {
1128 account_index: 7,
1129 data_index: 8,
1130 },
1131 false,
1132 true,
1133 )
1134 .unwrap(),
1135 ];
1136
1137 let check_required_pda1_pubkey = Pubkey::find_program_address(
1145 &[
1146 required_pda1_literal_string.as_bytes(),
1147 &instruction_u8array_arg,
1148 ix_accounts.get(1).unwrap().pubkey.as_ref(), ],
1150 &program_id,
1151 )
1152 .0;
1153 let check_required_pda2_pubkey = Pubkey::find_program_address(
1154 &[
1155 required_pda2_literal_u32.to_le_bytes().as_ref(),
1156 instruction_u64_arg.to_le_bytes().as_ref(),
1157 check_required_pda1_pubkey.as_ref(), ],
1159 &program_id,
1160 )
1161 .0;
1162 let check_required_pda3_pubkey = Pubkey::find_program_address(
1163 &[
1164 &[0], &[8; 8], ],
1167 &program_id,
1168 )
1169 .0;
1170 let check_required_pda4_pubkey = Pubkey::find_program_address(
1171 &[
1172 &[7; 4], ],
1175 &program_id,
1176 )
1177 .0;
1178 let check_key_data1_pubkey = required_key_data_instruction_data;
1179 let check_key_data2_pubkey = Pubkey::new_from_array([8; 32]);
1180 let check_key_data3_pubkey = Pubkey::new_from_array([9; 32]);
1181
1182 let pubkey_ix_1 = ix_accounts.first().unwrap().pubkey;
1184 let mut lamports_ix_1 = 0;
1185 let mut data_ix_1 = [];
1186 let pubkey_ix_2 = ix_accounts.get(1).unwrap().pubkey;
1187 let mut lamports_ix_2 = 0;
1188 let mut data_ix_2 = [];
1189
1190 let mut lamports1 = 0;
1192 let mut data1 = [8; 12];
1193 let mut lamports2 = 0;
1194 let mut data2 = [];
1195 let mut lamports3 = 0;
1196 let mut data3 = [];
1197 let mut lamports_pda1 = 0;
1198 let mut data_pda1 = [7; 12];
1199 let mut lamports_pda2 = 0;
1200 let mut data_pda2 = [8; 32];
1201 let mut lamports_pda3 = 0;
1202 let mut data_pda3 = [0; 40];
1203 data_pda3[8..].copy_from_slice(&[9; 32]); let mut lamports_pda4 = 0;
1205 let mut data_pda4 = [];
1206 let mut data_key_data1 = [];
1207 let mut lamports_key_data1 = 0;
1208 let mut data_key_data2 = [];
1209 let mut lamports_key_data2 = 0;
1210 let mut data_key_data3 = [];
1211 let mut lamports_key_data3 = 0;
1212
1213 let pubkey_arb_1 = Pubkey::new_unique();
1215 let mut lamports_arb_1 = 0;
1216 let mut data_arb_1 = [];
1217 let pubkey_arb_2 = Pubkey::new_unique();
1218 let mut lamports_arb_2 = 0;
1219 let mut data_arb_2 = [];
1220
1221 let all_account_infos = [
1222 AccountInfo::new(
1223 &pubkey_ix_1,
1224 ix_accounts.first().unwrap().is_signer,
1225 ix_accounts.first().unwrap().is_writable,
1226 &mut lamports_ix_1,
1227 &mut data_ix_1,
1228 &owner,
1229 false,
1230 ),
1231 AccountInfo::new(
1232 &pubkey_ix_2,
1233 ix_accounts.get(1).unwrap().is_signer,
1234 ix_accounts.get(1).unwrap().is_writable,
1235 &mut lamports_ix_2,
1236 &mut data_ix_2,
1237 &owner,
1238 false,
1239 ),
1240 AccountInfo::new(
1241 &extra_meta1.pubkey,
1242 required_accounts.first().unwrap().is_signer.into(),
1243 required_accounts.first().unwrap().is_writable.into(),
1244 &mut lamports1,
1245 &mut data1,
1246 &owner,
1247 false,
1248 ),
1249 AccountInfo::new(
1250 &extra_meta2.pubkey,
1251 required_accounts.get(1).unwrap().is_signer.into(),
1252 required_accounts.get(1).unwrap().is_writable.into(),
1253 &mut lamports2,
1254 &mut data2,
1255 &owner,
1256 false,
1257 ),
1258 AccountInfo::new(
1259 &extra_meta3.pubkey,
1260 required_accounts.get(2).unwrap().is_signer.into(),
1261 required_accounts.get(2).unwrap().is_writable.into(),
1262 &mut lamports3,
1263 &mut data3,
1264 &owner,
1265 false,
1266 ),
1267 AccountInfo::new(
1268 &check_required_pda1_pubkey,
1269 required_accounts.get(3).unwrap().is_signer.into(),
1270 required_accounts.get(3).unwrap().is_writable.into(),
1271 &mut lamports_pda1,
1272 &mut data_pda1,
1273 &owner,
1274 false,
1275 ),
1276 AccountInfo::new(
1277 &check_required_pda2_pubkey,
1278 required_accounts.get(4).unwrap().is_signer.into(),
1279 required_accounts.get(4).unwrap().is_writable.into(),
1280 &mut lamports_pda2,
1281 &mut data_pda2,
1282 &owner,
1283 false,
1284 ),
1285 AccountInfo::new(
1286 &check_required_pda3_pubkey,
1287 required_accounts.get(5).unwrap().is_signer.into(),
1288 required_accounts.get(5).unwrap().is_writable.into(),
1289 &mut lamports_pda3,
1290 &mut data_pda3,
1291 &owner,
1292 false,
1293 ),
1294 AccountInfo::new(
1295 &check_required_pda4_pubkey,
1296 required_accounts.get(6).unwrap().is_signer.into(),
1297 required_accounts.get(6).unwrap().is_writable.into(),
1298 &mut lamports_pda4,
1299 &mut data_pda4,
1300 &owner,
1301 false,
1302 ),
1303 AccountInfo::new(
1304 &check_key_data1_pubkey,
1305 required_accounts.get(7).unwrap().is_signer.into(),
1306 required_accounts.get(7).unwrap().is_writable.into(),
1307 &mut lamports_key_data1,
1308 &mut data_key_data1,
1309 &owner,
1310 false,
1311 ),
1312 AccountInfo::new(
1313 &check_key_data2_pubkey,
1314 required_accounts.get(8).unwrap().is_signer.into(),
1315 required_accounts.get(8).unwrap().is_writable.into(),
1316 &mut lamports_key_data2,
1317 &mut data_key_data2,
1318 &owner,
1319 false,
1320 ),
1321 AccountInfo::new(
1322 &check_key_data3_pubkey,
1323 required_accounts.get(9).unwrap().is_signer.into(),
1324 required_accounts.get(9).unwrap().is_writable.into(),
1325 &mut lamports_key_data3,
1326 &mut data_key_data3,
1327 &owner,
1328 false,
1329 ),
1330 AccountInfo::new(
1331 &pubkey_arb_1,
1332 false,
1333 true,
1334 &mut lamports_arb_1,
1335 &mut data_arb_1,
1336 &owner,
1337 false,
1338 ),
1339 AccountInfo::new(
1340 &pubkey_arb_2,
1341 false,
1342 true,
1343 &mut lamports_arb_2,
1344 &mut data_arb_2,
1345 &owner,
1346 false,
1347 ),
1348 ];
1349
1350 let rpc_account_infos = all_account_infos.clone();
1353 let mock_rpc = MockRpc::setup(&rpc_account_infos);
1354
1355 let account_size = ExtraAccountMetaList::size_of(required_accounts.len()).unwrap();
1356 let mut buffer = vec![0; account_size];
1357 ExtraAccountMetaList::init::<TestInstruction>(&mut buffer, &required_accounts).unwrap();
1358
1359 let mut instruction =
1360 Instruction::new_with_bytes(program_id, &instruction_data, ix_accounts.clone());
1361 ExtraAccountMetaList::add_to_instruction::<TestInstruction, _, _>(
1362 &mut instruction,
1363 |pubkey| mock_rpc.get_account_data(pubkey),
1364 &buffer,
1365 )
1366 .await
1367 .unwrap();
1368
1369 let mut cpi_instruction =
1373 Instruction::new_with_bytes(program_id, &instruction_data, ix_accounts);
1374
1375 let mut cpi_account_infos =
1377 vec![all_account_infos[0].clone(), all_account_infos[1].clone()];
1378
1379 let mut messed_account_infos = all_account_infos.clone();
1381 messed_account_infos.swap(0, 4);
1382 messed_account_infos.swap(1, 2);
1383 messed_account_infos.swap(3, 4);
1384 messed_account_infos.swap(5, 6);
1385 messed_account_infos.swap(8, 7);
1386
1387 ExtraAccountMetaList::add_to_cpi_instruction::<TestInstruction>(
1389 &mut cpi_instruction,
1390 &mut cpi_account_infos,
1391 &buffer,
1392 &messed_account_infos,
1393 )
1394 .unwrap();
1395
1396 assert_eq!(cpi_instruction, instruction);
1398
1399 let check_account_infos = &all_account_infos[..12];
1406 assert_eq!(cpi_account_infos.len(), check_account_infos.len());
1407 for (a, b) in std::iter::zip(cpi_account_infos, check_account_infos) {
1408 assert_eq!(a.key, b.key);
1409 assert_eq!(a.is_signer, b.is_signer);
1410 assert_eq!(a.is_writable, b.is_writable);
1411 }
1412 }
1413
1414 async fn update_and_assert_metas(
1415 program_id: Pubkey,
1416 buffer: &mut Vec<u8>,
1417 updated_metas: &[ExtraAccountMeta],
1418 check_metas: &[AccountMeta],
1419 ) {
1420 let account_size = ExtraAccountMetaList::size_of(updated_metas.len()).unwrap();
1422 if account_size > buffer.len() {
1423 buffer.resize(account_size, 0);
1424 }
1425
1426 ExtraAccountMetaList::update::<TestInstruction>(buffer, updated_metas).unwrap();
1428
1429 let state = TlvStateBorrowed::unpack(buffer).unwrap();
1431 let unpacked_metas_pod =
1432 ExtraAccountMetaList::unpack_with_tlv_state::<TestInstruction>(&state).unwrap();
1433 assert_eq!(
1434 &*unpacked_metas_pod, updated_metas,
1435 "The ExtraAccountMetas in the buffer should match the expected ones."
1436 );
1437
1438 let mock_rpc = MockRpc::setup(&[]);
1439
1440 let mut instruction = Instruction::new_with_bytes(program_id, &[], vec![]);
1441 ExtraAccountMetaList::add_to_instruction::<TestInstruction, _, _>(
1442 &mut instruction,
1443 |pubkey| mock_rpc.get_account_data(pubkey),
1444 buffer,
1445 )
1446 .await
1447 .unwrap();
1448
1449 assert_eq!(instruction.accounts, check_metas,);
1450 }
1451
1452 #[tokio::test]
1453 async fn update_extra_account_meta_list() {
1454 let program_id = Pubkey::new_unique();
1455
1456 let initial_metas = [
1458 ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), false, true).unwrap(),
1459 ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), true, false).unwrap(),
1460 ];
1461
1462 let initial_account_size = ExtraAccountMetaList::size_of(initial_metas.len()).unwrap();
1464 let mut buffer = vec![0; initial_account_size];
1465 ExtraAccountMetaList::init::<TestInstruction>(&mut buffer, &initial_metas).unwrap();
1466
1467 let updated_metas_1 = [
1469 ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), true, true).unwrap(),
1470 ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), false, false).unwrap(),
1471 ];
1472 let check_metas_1 = updated_metas_1
1473 .iter()
1474 .map(|e| AccountMeta::try_from(e).unwrap())
1475 .collect::<Vec<_>>();
1476 update_and_assert_metas(program_id, &mut buffer, &updated_metas_1, &check_metas_1).await;
1477
1478 let updated_metas_2 = [
1480 ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), true, true).unwrap(),
1481 ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), false, false).unwrap(),
1482 ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), false, true).unwrap(),
1483 ];
1484 let check_metas_2 = updated_metas_2
1485 .iter()
1486 .map(|e| AccountMeta::try_from(e).unwrap())
1487 .collect::<Vec<_>>();
1488 update_and_assert_metas(program_id, &mut buffer, &updated_metas_2, &check_metas_2).await;
1489
1490 let updated_metas_3 =
1492 [ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), true, true).unwrap()];
1493 let check_metas_3 = updated_metas_3
1494 .iter()
1495 .map(|e| AccountMeta::try_from(e).unwrap())
1496 .collect::<Vec<_>>();
1497 update_and_assert_metas(program_id, &mut buffer, &updated_metas_3, &check_metas_3).await;
1498
1499 let seed_pubkey = Pubkey::new_unique();
1501 let updated_metas_4 = [
1502 ExtraAccountMeta::new_with_pubkey(&seed_pubkey, true, true).unwrap(),
1503 ExtraAccountMeta::new_with_seeds(
1504 &[
1505 Seed::Literal {
1506 bytes: b"seed-prefix".to_vec(),
1507 },
1508 Seed::AccountKey { index: 0 },
1509 ],
1510 false,
1511 true,
1512 )
1513 .unwrap(),
1514 ];
1515 let simple_pda = Pubkey::find_program_address(
1516 &[
1517 b"seed-prefix", seed_pubkey.as_ref(), ],
1520 &program_id,
1521 )
1522 .0;
1523 let check_metas_4 = [
1524 AccountMeta::new(seed_pubkey, true),
1525 AccountMeta::new(simple_pda, false),
1526 ];
1527
1528 update_and_assert_metas(program_id, &mut buffer, &updated_metas_4, &check_metas_4).await;
1529 }
1530
1531 #[test]
1532 fn check_account_infos_test() {
1533 let program_id = Pubkey::new_unique();
1534 let owner = Pubkey::new_unique();
1535
1536 let pubkey1 = Pubkey::new_unique();
1538 let pubkey2 = Pubkey::new_unique();
1539 let required_accounts = [
1540 ExtraAccountMeta::new_with_pubkey(&pubkey1, false, true).unwrap(),
1541 ExtraAccountMeta::new_with_pubkey(&pubkey2, false, false).unwrap(),
1542 ExtraAccountMeta::new_with_seeds(
1543 &[
1544 Seed::Literal {
1545 bytes: b"lit_seed".to_vec(),
1546 },
1547 Seed::InstructionData {
1548 index: 0,
1549 length: 4,
1550 },
1551 Seed::AccountKey { index: 0 },
1552 ],
1553 false,
1554 true,
1555 )
1556 .unwrap(),
1557 ExtraAccountMeta::new_with_pubkey_data(
1558 &PubkeyData::InstructionData { index: 8 },
1559 false,
1560 true,
1561 )
1562 .unwrap(),
1563 ];
1564
1565 let account_size = ExtraAccountMetaList::size_of(required_accounts.len()).unwrap();
1567 let mut buffer = vec![0; account_size];
1568 ExtraAccountMetaList::init::<TestInstruction>(&mut buffer, &required_accounts).unwrap();
1569
1570 let mut instruction_data = vec![0, 1, 2, 3, 4, 5, 6, 7];
1572 let key_data_pubkey = Pubkey::new_unique();
1573 instruction_data.extend_from_slice(key_data_pubkey.as_ref());
1574
1575 let pubkey_ix_1 = Pubkey::new_unique();
1578 let mut lamports_ix_1 = 0;
1579 let mut data_ix_1 = [];
1580 let pubkey_ix_2 = Pubkey::new_unique();
1581 let mut lamports_ix_2 = 0;
1582 let mut data_ix_2 = [];
1583 let mut lamports1 = 0;
1584 let mut data1 = [];
1585 let mut lamports2 = 0;
1586 let mut data2 = [];
1587 let mut lamports3 = 0;
1588 let mut data3 = [];
1589 let mut lamports4 = 0;
1590 let mut data4 = [];
1591 let pda = Pubkey::find_program_address(
1592 &[b"lit_seed", &instruction_data[..4], pubkey_ix_1.as_ref()],
1593 &program_id,
1594 )
1595 .0;
1596 let account_infos = [
1597 AccountInfo::new(
1599 &pubkey_ix_1,
1600 false,
1601 true,
1602 &mut lamports_ix_1,
1603 &mut data_ix_1,
1604 &owner,
1605 false,
1606 ),
1607 AccountInfo::new(
1609 &pubkey_ix_2,
1610 false,
1611 true,
1612 &mut lamports_ix_2,
1613 &mut data_ix_2,
1614 &owner,
1615 false,
1616 ),
1617 AccountInfo::new(
1619 &pubkey1,
1620 false,
1621 true,
1622 &mut lamports1,
1623 &mut data1,
1624 &owner,
1625 false,
1626 ),
1627 AccountInfo::new(
1629 &pubkey2,
1630 false,
1631 false,
1632 &mut lamports2,
1633 &mut data2,
1634 &owner,
1635 false,
1636 ),
1637 AccountInfo::new(&pda, false, true, &mut lamports3, &mut data3, &owner, false),
1639 AccountInfo::new(
1641 &key_data_pubkey,
1642 false,
1643 true,
1644 &mut lamports4,
1645 &mut data4,
1646 &owner,
1647 false,
1648 ),
1649 ];
1650
1651 let mut messed_account_infos = account_infos.clone().to_vec();
1653 messed_account_infos.swap(0, 2);
1654 messed_account_infos.swap(1, 4);
1655 messed_account_infos.swap(3, 2);
1656 messed_account_infos.swap(5, 4);
1657
1658 assert_eq!(
1660 ExtraAccountMetaList::check_account_infos::<TestInstruction>(
1661 &messed_account_infos,
1662 &instruction_data,
1663 &program_id,
1664 &buffer,
1665 )
1666 .unwrap_err(),
1667 AccountResolutionError::IncorrectAccount.into(),
1668 );
1669
1670 assert_eq!(
1672 ExtraAccountMetaList::check_account_infos::<TestInstruction>(
1673 &account_infos,
1674 &instruction_data,
1675 &program_id,
1676 &buffer,
1677 ),
1678 Ok(()),
1679 );
1680 }
1681}