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