1use {
4 crate::{
5 bpf_writer::BpfWriter,
6 error::{Error, ErrorCode},
7 solana_program::{
8 account_info::AccountInfo, instruction::AccountMeta, pubkey::Pubkey, system_program,
9 },
10 AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Owner, Result,
11 ToAccountInfos, ToAccountMetas,
12 },
13 std::{
14 collections::BTreeSet,
15 ops::{Deref, DerefMut},
16 },
17};
18
19#[derive(Debug)]
21pub enum MigrationInner<From, To> {
22 From(From),
24 To(To),
26}
27
28#[derive(Debug)]
154pub struct Migration<'info, From, To>
155where
156 From: AccountDeserialize,
157 To: AccountSerialize,
158{
159 info: &'info AccountInfo<'info>,
161 inner: MigrationInner<From, To>,
163}
164
165impl<'info, From, To> Migration<'info, From, To>
166where
167 From: AccountDeserialize + Owner,
168 To: AccountSerialize + Owner,
169{
170 fn new(info: &'info AccountInfo<'info>, account: From) -> Self {
172 Self {
173 info,
174 inner: MigrationInner::From(account),
175 }
176 }
177
178 #[inline(always)]
180 pub fn is_migrated(&self) -> bool {
181 matches!(self.inner, MigrationInner::To(_))
182 }
183
184 pub fn try_as_from(&self) -> Result<&From> {
189 match &self.inner {
190 MigrationInner::From(from) => Ok(from),
191 MigrationInner::To(_) => Err(ErrorCode::AccountAlreadyMigrated.into()),
192 }
193 }
194
195 pub fn try_as_from_mut(&mut self) -> Result<&mut From> {
200 match &mut self.inner {
201 MigrationInner::From(from) => Ok(from),
202 MigrationInner::To(_) => Err(ErrorCode::AccountAlreadyMigrated.into()),
203 }
204 }
205
206 pub fn migrate(&mut self, new_data: To) -> Result<()> {
214 if self.is_migrated() {
215 return Err(ErrorCode::AccountAlreadyMigrated.into());
216 }
217
218 self.inner = MigrationInner::To(new_data);
219 Ok(())
220 }
221
222 pub fn into_inner(&mut self, new_data: To) -> &To {
248 if !self.is_migrated() {
249 self.inner = MigrationInner::To(new_data);
250 }
251
252 match &self.inner {
253 MigrationInner::To(to) => to,
254 _ => unreachable!(),
255 }
256 }
257
258 pub fn into_inner_mut(&mut self, new_data: To) -> &mut To {
284 if !self.is_migrated() {
285 self.inner = MigrationInner::To(new_data);
286 }
287
288 match &mut self.inner {
289 MigrationInner::To(to) => to,
290 _ => unreachable!(),
291 }
292 }
293
294 #[inline(never)]
299 pub fn try_from(info: &'info AccountInfo<'info>) -> Result<Self> {
300 if info.owner == &system_program::ID && info.lamports() == 0 {
301 return Err(ErrorCode::AccountNotInitialized.into());
302 }
303
304 if info.owner != &From::owner() {
305 return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram)
306 .with_pubkeys((*info.owner, From::owner())));
307 }
308
309 let mut data: &[u8] = &info.try_borrow_data()?;
310 Ok(Self::new(info, From::try_deserialize(&mut data)?))
311 }
312
313 #[inline(never)]
318 pub fn try_from_unchecked(info: &'info AccountInfo<'info>) -> Result<Self> {
319 if info.owner == &system_program::ID && info.lamports() == 0 {
320 return Err(ErrorCode::AccountNotInitialized.into());
321 }
322
323 if info.owner != &From::owner() {
324 return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram)
325 .with_pubkeys((*info.owner, From::owner())));
326 }
327
328 let mut data: &[u8] = &info.try_borrow_data()?;
329 Ok(Self::new(info, From::try_deserialize_unchecked(&mut data)?))
330 }
331}
332
333impl<'info, B, From, To> Accounts<'info, B> for Migration<'info, From, To>
334where
335 From: AccountDeserialize + Owner,
336 To: AccountSerialize + Owner,
337{
338 #[inline(never)]
339 fn try_accounts(
340 _program_id: &Pubkey,
341 accounts: &mut &'info [AccountInfo<'info>],
342 _ix_data: &[u8],
343 _bumps: &mut B,
344 _reallocs: &mut BTreeSet<Pubkey>,
345 ) -> Result<Self> {
346 if accounts.is_empty() {
347 return Err(ErrorCode::AccountNotEnoughKeys.into());
348 }
349 let account = &accounts[0];
350 *accounts = &accounts[1..];
351 Self::try_from(account)
352 }
353}
354
355impl<'info, From, To> AccountsExit<'info> for Migration<'info, From, To>
356where
357 From: AccountDeserialize + Owner,
358 To: AccountSerialize + Owner,
359{
360 fn exit(&self, program_id: &Pubkey) -> Result<()> {
361 if crate::common::is_closed(self.info) {
363 return Ok(());
364 }
365
366 match &self.inner {
368 MigrationInner::From(_) => {
369 return Err(ErrorCode::AccountNotMigrated.into());
371 }
372 MigrationInner::To(to) => {
373 let expected_owner = To::owner();
375 if &expected_owner != program_id {
376 return Err(Error::from(ErrorCode::InvalidProgramId)
377 .with_pubkeys((*program_id, expected_owner)));
378 }
379
380 let mut data = self.info.try_borrow_mut_data()?;
382 let dst: &mut [u8] = &mut data;
383 let mut writer = BpfWriter::new(dst);
384 to.try_serialize(&mut writer)?;
385 }
386 }
387
388 Ok(())
389 }
390}
391
392impl<From, To> ToAccountMetas for Migration<'_, From, To>
393where
394 From: AccountDeserialize,
395 To: AccountSerialize,
396{
397 fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
398 let is_signer = is_signer.unwrap_or(self.info.is_signer);
399 let meta = match self.info.is_writable {
400 false => AccountMeta::new_readonly(*self.info.key, is_signer),
401 true => AccountMeta::new(*self.info.key, is_signer),
402 };
403 vec![meta]
404 }
405}
406
407impl<'info, From, To> ToAccountInfos<'info> for Migration<'info, From, To>
408where
409 From: AccountDeserialize,
410 To: AccountSerialize,
411{
412 fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
413 vec![self.info.clone()]
414 }
415}
416
417impl<'info, From, To> AsRef<AccountInfo<'info>> for Migration<'info, From, To>
418where
419 From: AccountDeserialize,
420 To: AccountSerialize,
421{
422 fn as_ref(&self) -> &AccountInfo<'info> {
423 self.info
424 }
425}
426
427impl<From, To> Key for Migration<'_, From, To>
428where
429 From: AccountDeserialize,
430 To: AccountSerialize,
431{
432 fn key(&self) -> Pubkey {
433 *self.info.key
434 }
435}
436
437impl<'info, From, To> Deref for Migration<'info, From, To>
439where
440 From: AccountDeserialize,
441 To: AccountSerialize,
442{
443 type Target = From;
444
445 fn deref(&self) -> &Self::Target {
446 match &self.inner {
447 MigrationInner::From(from) => from,
448 MigrationInner::To(_) => {
449 crate::solana_program::msg!("Cannot deref to From: account is already migrated.");
450 panic!();
451 }
452 }
453 }
454}
455
456impl<'info, From, To> DerefMut for Migration<'info, From, To>
458where
459 From: AccountDeserialize,
460 To: AccountSerialize,
461{
462 fn deref_mut(&mut self) -> &mut Self::Target {
463 match &mut self.inner {
464 MigrationInner::From(from) => from,
465 MigrationInner::To(_) => {
466 crate::solana_program::msg!(
467 "Cannot deref_mut to From: account is already migrated."
468 );
469 panic!();
470 }
471 }
472 }
473}
474
475#[cfg(test)]
476mod tests {
477 use {
478 super::*,
479 crate::{AnchorDeserialize, AnchorSerialize, Discriminator},
480 };
481
482 const TEST_DISCRIMINATOR_V1: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
483 const TEST_DISCRIMINATOR_V2: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
484 const TEST_OWNER: Pubkey = Pubkey::new_from_array([1u8; 32]);
485 const TEST_OTHER_OWNER: Pubkey = Pubkey::new_from_array([2u8; 32]);
486
487 #[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize, PartialEq)]
488 struct AccountV1 {
489 pub data: u64,
490 }
491
492 impl Discriminator for AccountV1 {
493 const DISCRIMINATOR: &'static [u8] = &TEST_DISCRIMINATOR_V1;
494 }
495
496 impl Owner for AccountV1 {
497 fn owner() -> Pubkey {
498 TEST_OWNER
499 }
500 }
501
502 impl AccountSerialize for AccountV1 {
503 fn try_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
504 writer.write_all(&TEST_DISCRIMINATOR_V1)?;
505 AnchorSerialize::serialize(self, writer)?;
506 Ok(())
507 }
508 }
509
510 impl AccountDeserialize for AccountV1 {
511 fn try_deserialize(buf: &mut &[u8]) -> Result<Self> {
512 if buf.len() < 8 {
513 return Err(ErrorCode::AccountDiscriminatorNotFound.into());
514 }
515 let disc = &buf[..8];
516 if disc != TEST_DISCRIMINATOR_V1 {
517 return Err(ErrorCode::AccountDiscriminatorMismatch.into());
518 }
519 Self::try_deserialize_unchecked(buf)
520 }
521
522 fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self> {
523 let mut data = &buf[8..];
524 AnchorDeserialize::deserialize(&mut data)
525 .map_err(|_| ErrorCode::AccountDidNotDeserialize.into())
526 }
527 }
528
529 #[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize, PartialEq)]
530 struct AccountV2 {
531 pub data: u64,
532 pub new_field: u64,
533 }
534
535 impl Discriminator for AccountV2 {
536 const DISCRIMINATOR: &'static [u8] = &TEST_DISCRIMINATOR_V2;
537 }
538
539 impl Owner for AccountV2 {
540 fn owner() -> Pubkey {
541 TEST_OWNER
542 }
543 }
544
545 impl AccountSerialize for AccountV2 {
546 fn try_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
547 writer.write_all(&TEST_DISCRIMINATOR_V2)?;
548 AnchorSerialize::serialize(self, writer)?;
549 Ok(())
550 }
551 }
552
553 #[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize, PartialEq)]
554 struct AccountV2OtherOwner {
555 pub data: u64,
556 pub new_field: u64,
557 }
558
559 impl Owner for AccountV2OtherOwner {
560 fn owner() -> Pubkey {
561 TEST_OTHER_OWNER
562 }
563 }
564
565 impl AccountSerialize for AccountV2OtherOwner {
566 fn try_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
567 writer.write_all(&TEST_DISCRIMINATOR_V2)?;
568 AnchorSerialize::serialize(self, writer)?;
569 Ok(())
570 }
571 }
572
573 impl AccountDeserialize for AccountV2 {
574 fn try_deserialize(buf: &mut &[u8]) -> Result<Self> {
575 if buf.len() < 8 {
576 return Err(ErrorCode::AccountDiscriminatorNotFound.into());
577 }
578 let disc = &buf[..8];
579 if disc != TEST_DISCRIMINATOR_V2 {
580 return Err(ErrorCode::AccountDiscriminatorMismatch.into());
581 }
582 Self::try_deserialize_unchecked(buf)
583 }
584
585 fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self> {
586 let mut data = &buf[8..];
587 AnchorDeserialize::deserialize(&mut data)
588 .map_err(|_| ErrorCode::AccountDidNotDeserialize.into())
589 }
590 }
591
592 fn create_account_info<'a>(
593 key: &'a Pubkey,
594 owner: &'a Pubkey,
595 lamports: &'a mut u64,
596 data: &'a mut [u8],
597 ) -> AccountInfo<'a> {
598 AccountInfo::new(key, false, true, lamports, data, owner, false)
599 }
600
601 #[test]
604 fn test_is_migrated_returns_false_initially() {
605 let key = Pubkey::default();
606 let mut lamports = 100;
607 let v1 = AccountV1 { data: 42 };
608 let mut data = vec![0u8; 100];
609 data[..8].copy_from_slice(&TEST_DISCRIMINATOR_V1);
610 v1.serialize(&mut &mut data[8..]).unwrap();
611
612 let info = create_account_info(&key, &TEST_OWNER, &mut lamports, &mut data);
613 let migration: Migration<AccountV1, AccountV2> = Migration::try_from(&info).unwrap();
614
615 assert!(!migration.is_migrated());
616 }
617
618 #[test]
621 fn test_is_migrated_returns_true_after_migrate() {
622 let key = Pubkey::default();
623 let mut lamports = 100;
624 let v1 = AccountV1 { data: 42 };
625 let mut data = vec![0u8; 100];
626 data[..8].copy_from_slice(&TEST_DISCRIMINATOR_V1);
627 v1.serialize(&mut &mut data[8..]).unwrap();
628
629 let info = create_account_info(&key, &TEST_OWNER, &mut lamports, &mut data);
630 let mut migration: Migration<AccountV1, AccountV2> = Migration::try_from(&info).unwrap();
631
632 migration
633 .migrate(AccountV2 {
634 data: 42,
635 new_field: 100,
636 })
637 .unwrap();
638
639 assert!(migration.is_migrated());
640 }
641
642 #[test]
645 fn test_try_as_from_returns_data_before_migration() {
646 let key = Pubkey::default();
647 let mut lamports = 100;
648 let v1 = AccountV1 { data: 42 };
649 let mut data = vec![0u8; 100];
650 data[..8].copy_from_slice(&TEST_DISCRIMINATOR_V1);
651 v1.serialize(&mut &mut data[8..]).unwrap();
652
653 let info = create_account_info(&key, &TEST_OWNER, &mut lamports, &mut data);
654 let migration: Migration<AccountV1, AccountV2> = Migration::try_from(&info).unwrap();
655
656 let from = migration.try_as_from().unwrap();
657 assert_eq!(from.data, 42);
658 }
659
660 #[test]
663 fn test_try_as_from_returns_error_after_migration() {
664 let key = Pubkey::default();
665 let mut lamports = 100;
666 let v1 = AccountV1 { data: 42 };
667 let mut data = vec![0u8; 100];
668 data[..8].copy_from_slice(&TEST_DISCRIMINATOR_V1);
669 v1.serialize(&mut &mut data[8..]).unwrap();
670
671 let info = create_account_info(&key, &TEST_OWNER, &mut lamports, &mut data);
672 let mut migration: Migration<AccountV1, AccountV2> = Migration::try_from(&info).unwrap();
673
674 migration
675 .migrate(AccountV2 {
676 data: 42,
677 new_field: 100,
678 })
679 .unwrap();
680
681 assert!(migration.try_as_from().is_err());
682 }
683
684 #[test]
687 fn test_try_as_from_mut_works_before_migration() {
688 let key = Pubkey::default();
689 let mut lamports = 100;
690 let v1 = AccountV1 { data: 42 };
691 let mut data = vec![0u8; 100];
692 data[..8].copy_from_slice(&TEST_DISCRIMINATOR_V1);
693 v1.serialize(&mut &mut data[8..]).unwrap();
694
695 let info = create_account_info(&key, &TEST_OWNER, &mut lamports, &mut data);
696 let mut migration: Migration<AccountV1, AccountV2> = Migration::try_from(&info).unwrap();
697
698 let from = migration.try_as_from_mut().unwrap();
699 from.data = 100;
700 assert_eq!(migration.try_as_from().unwrap().data, 100);
701 }
702
703 #[test]
706 fn test_migrate_fails_if_already_migrated() {
707 let key = Pubkey::default();
708 let mut lamports = 100;
709 let v1 = AccountV1 { data: 42 };
710 let mut data = vec![0u8; 100];
711 data[..8].copy_from_slice(&TEST_DISCRIMINATOR_V1);
712 v1.serialize(&mut &mut data[8..]).unwrap();
713
714 let info = create_account_info(&key, &TEST_OWNER, &mut lamports, &mut data);
715 let mut migration: Migration<AccountV1, AccountV2> = Migration::try_from(&info).unwrap();
716
717 migration
718 .migrate(AccountV2 {
719 data: 42,
720 new_field: 100,
721 })
722 .unwrap();
723 let result = migration.migrate(AccountV2 {
724 data: 42,
725 new_field: 200,
726 });
727
728 assert!(result.is_err());
729 }
730
731 #[test]
734 fn test_into_inner_migrates_and_returns_reference() {
735 let key = Pubkey::default();
736 let mut lamports = 100;
737 let v1 = AccountV1 { data: 42 };
738 let mut data = vec![0u8; 100];
739 data[..8].copy_from_slice(&TEST_DISCRIMINATOR_V1);
740 v1.serialize(&mut &mut data[8..]).unwrap();
741
742 let info = create_account_info(&key, &TEST_OWNER, &mut lamports, &mut data);
743 let mut migration: Migration<AccountV1, AccountV2> = Migration::try_from(&info).unwrap();
744
745 let to = migration.into_inner(AccountV2 {
746 data: 42,
747 new_field: 100,
748 });
749
750 assert_eq!(to.data, 42);
751 assert_eq!(to.new_field, 100);
752 assert!(migration.is_migrated());
753 }
754
755 #[test]
758 fn test_into_inner_is_idempotent() {
759 let key = Pubkey::default();
760 let mut lamports = 100;
761 let v1 = AccountV1 { data: 42 };
762 let mut data = vec![0u8; 100];
763 data[..8].copy_from_slice(&TEST_DISCRIMINATOR_V1);
764 v1.serialize(&mut &mut data[8..]).unwrap();
765
766 let info = create_account_info(&key, &TEST_OWNER, &mut lamports, &mut data);
767 let mut migration: Migration<AccountV1, AccountV2> = Migration::try_from(&info).unwrap();
768
769 let to1 = migration.into_inner(AccountV2 {
770 data: 42,
771 new_field: 100,
772 });
773 assert_eq!(to1.new_field, 100);
774
775 let to2 = migration.into_inner(AccountV2 {
777 data: 42,
778 new_field: 999,
779 });
780 assert_eq!(to2.new_field, 100); }
782
783 #[test]
786 fn test_into_inner_mut_allows_mutation() {
787 let key = Pubkey::default();
788 let mut lamports = 100;
789 let v1 = AccountV1 { data: 42 };
790 let mut data = vec![0u8; 100];
791 data[..8].copy_from_slice(&TEST_DISCRIMINATOR_V1);
792 v1.serialize(&mut &mut data[8..]).unwrap();
793
794 let info = create_account_info(&key, &TEST_OWNER, &mut lamports, &mut data);
795 let mut migration: Migration<AccountV1, AccountV2> = Migration::try_from(&info).unwrap();
796
797 let to = migration.into_inner_mut(AccountV2 {
798 data: 42,
799 new_field: 100,
800 });
801 to.new_field = 200;
802
803 let to_ref = migration.into_inner(AccountV2 {
804 data: 0,
805 new_field: 0,
806 });
807 assert_eq!(to_ref.new_field, 200);
808 }
809
810 #[test]
813 fn test_deref_works_before_migration() {
814 let key = Pubkey::default();
815 let mut lamports = 100;
816 let v1 = AccountV1 { data: 42 };
817 let mut data = vec![0u8; 100];
818 data[..8].copy_from_slice(&TEST_DISCRIMINATOR_V1);
819 v1.serialize(&mut &mut data[8..]).unwrap();
820
821 let info = create_account_info(&key, &TEST_OWNER, &mut lamports, &mut data);
822 let migration: Migration<AccountV1, AccountV2> = Migration::try_from(&info).unwrap();
823
824 assert_eq!(migration.data, 42);
825 }
826
827 #[test]
830 #[should_panic]
831 fn test_deref_panics_after_migration() {
832 let key = Pubkey::default();
833 let mut lamports = 100;
834 let v1 = AccountV1 { data: 42 };
835 let mut data = vec![0u8; 100];
836 data[..8].copy_from_slice(&TEST_DISCRIMINATOR_V1);
837 v1.serialize(&mut &mut data[8..]).unwrap();
838
839 let info = create_account_info(&key, &TEST_OWNER, &mut lamports, &mut data);
840 let mut migration: Migration<AccountV1, AccountV2> = Migration::try_from(&info).unwrap();
841
842 migration
843 .migrate(AccountV2 {
844 data: 42,
845 new_field: 100,
846 })
847 .unwrap();
848
849 let _ = migration.data;
851 }
852
853 #[test]
856 fn test_try_from_fails_with_wrong_owner() {
857 let key = Pubkey::default();
858 let wrong_owner = Pubkey::new_from_array([99u8; 32]);
859 let mut lamports = 100;
860 let v1 = AccountV1 { data: 42 };
861 let mut data = vec![0u8; 100];
862 data[..8].copy_from_slice(&TEST_DISCRIMINATOR_V1);
863 v1.serialize(&mut &mut data[8..]).unwrap();
864
865 let info = create_account_info(&key, &wrong_owner, &mut lamports, &mut data);
866 let result: Result<Migration<AccountV1, AccountV2>> = Migration::try_from(&info);
867
868 assert!(result.is_err());
869 }
870
871 #[test]
874 fn test_try_from_fails_with_uninitialized_account() {
875 let key = Pubkey::default();
876 let mut lamports = 0;
877 let mut data = vec![0u8; 100];
878
879 let info = create_account_info(&key, &system_program::ID, &mut lamports, &mut data);
880 let result: Result<Migration<AccountV1, AccountV2>> = Migration::try_from(&info);
881
882 assert!(result.is_err());
883 }
884
885 #[test]
886 fn test_exit_fails_when_migrated_owner_does_not_match_program_id() {
887 let key = Pubkey::default();
888 let mut lamports = 100;
889 let v1 = AccountV1 { data: 42 };
890 let mut data = vec![0u8; 100];
891 data[..8].copy_from_slice(&TEST_DISCRIMINATOR_V1);
892 v1.serialize(&mut &mut data[8..]).unwrap();
893
894 let info = create_account_info(&key, &TEST_OWNER, &mut lamports, &mut data);
895 let mut migration: Migration<AccountV1, AccountV2OtherOwner> =
896 Migration::try_from(&info).unwrap();
897
898 migration
899 .migrate(AccountV2OtherOwner {
900 data: 42,
901 new_field: 100,
902 })
903 .unwrap();
904
905 let err = migration
906 .exit(&TEST_OWNER)
907 .expect_err("exit must fail when migrated data cannot be persisted");
908 match err {
909 Error::AnchorError(e) => {
910 assert_eq!(e.error_code_number, ErrorCode::InvalidProgramId as u32);
911 }
912 other => panic!("unexpected error variant: {other:?}"),
913 }
914
915 let mut persisted_data: &[u8] = &info.try_borrow_data().unwrap();
916 let persisted = AccountV1::try_deserialize(&mut persisted_data).unwrap();
917 assert_eq!(persisted.data, 42);
918 }
919}