program.rs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. //! Type validating that the account is the given Program
  2. use crate::error::{Error, ErrorCode};
  3. use crate::{
  4. AccountDeserialize, Accounts, AccountsExit, Id, Key, Result, ToAccountInfos, ToAccountMetas,
  5. };
  6. use solana_program::account_info::AccountInfo;
  7. use solana_program::bpf_loader_upgradeable::{self, UpgradeableLoaderState};
  8. use solana_program::instruction::AccountMeta;
  9. use solana_program::pubkey::Pubkey;
  10. use std::collections::BTreeSet;
  11. use std::fmt;
  12. use std::marker::PhantomData;
  13. use std::ops::Deref;
  14. /// Type validating that the account is the given Program
  15. ///
  16. /// The type has a `programdata_address` function that will return `Option::Some`
  17. /// if the program is owned by the [`BPFUpgradeableLoader`](https://docs.rs/solana-program/latest/solana_program/bpf_loader_upgradeable/index.html)
  18. /// which will contain the `programdata_address` property of the `Program` variant of the [`UpgradeableLoaderState`](https://docs.rs/solana-program/latest/solana_program/bpf_loader_upgradeable/enum.UpgradeableLoaderState.html) enum.
  19. ///
  20. /// # Table of Contents
  21. /// - [Basic Functionality](#basic-functionality)
  22. /// - [Out of the Box Types](#out-of-the-box-types)
  23. ///
  24. /// # Basic Functionality
  25. ///
  26. /// Checks:
  27. ///
  28. /// - `account_info.key == expected_program`
  29. /// - `account_info.executable == true`
  30. ///
  31. /// # Example
  32. /// ```ignore
  33. /// #[program]
  34. /// mod my_program {
  35. /// fn set_admin_settings(...){...}
  36. /// }
  37. ///
  38. /// #[account]
  39. /// #[derive(Default)]
  40. /// pub struct AdminSettings {
  41. /// ...
  42. /// }
  43. ///
  44. /// #[derive(Accounts)]
  45. /// pub struct SetAdminSettings<'info> {
  46. /// #[account(mut, seeds = [b"admin"], bump)]
  47. /// pub admin_settings: Account<'info, AdminSettings>,
  48. /// #[account(constraint = program.programdata_address()? == Some(program_data.key()))]
  49. /// pub program: Program<'info, MyProgram>,
  50. /// #[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))]
  51. /// pub program_data: Account<'info, ProgramData>,
  52. /// pub authority: Signer<'info>,
  53. /// }
  54. /// ```
  55. /// The given program has a function with which the upgrade authority can set admin settings.
  56. ///
  57. /// The required constraints are as follows:
  58. ///
  59. /// - `program` is the account of the program itself.
  60. /// Its constraint checks that `program_data` is the account that contains the program's upgrade authority.
  61. /// Implicitly, this checks that `program` is a BPFUpgradeable program (`program.programdata_address()?`
  62. /// will be `None` if it's not).
  63. /// - `program_data`'s constraint checks that its upgrade authority is the `authority` account.
  64. /// - Finally, `authority` needs to sign the transaction.
  65. ///
  66. /// # Out of the Box Types
  67. ///
  68. /// Between the [`anchor_lang`](https://docs.rs/anchor-lang/latest/anchor_lang) and [`anchor_spl`](https://docs.rs/anchor_spl/latest/anchor_spl) crates,
  69. /// the following `Program` types are provided out of the box:
  70. ///
  71. /// - [`System`](https://docs.rs/anchor-lang/latest/anchor_lang/struct.System.html)
  72. /// - [`AssociatedToken`](https://docs.rs/anchor-spl/latest/anchor_spl/associated_token/struct.AssociatedToken.html)
  73. /// - [`Token`](https://docs.rs/anchor-spl/latest/anchor_spl/token/struct.Token.html)
  74. ///
  75. #[derive(Clone)]
  76. pub struct Program<'info, T> {
  77. info: &'info AccountInfo<'info>,
  78. _phantom: PhantomData<T>,
  79. }
  80. impl<'info, T: fmt::Debug> fmt::Debug for Program<'info, T> {
  81. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  82. f.debug_struct("Program").field("info", &self.info).finish()
  83. }
  84. }
  85. impl<'a, T> Program<'a, T> {
  86. pub(crate) fn new(info: &'a AccountInfo<'a>) -> Program<'a, T> {
  87. Self {
  88. info,
  89. _phantom: PhantomData,
  90. }
  91. }
  92. pub fn programdata_address(&self) -> Result<Option<Pubkey>> {
  93. if *self.info.owner == bpf_loader_upgradeable::ID {
  94. let mut data: &[u8] = &self.info.try_borrow_data()?;
  95. let upgradable_loader_state =
  96. UpgradeableLoaderState::try_deserialize_unchecked(&mut data)?;
  97. match upgradable_loader_state {
  98. UpgradeableLoaderState::Uninitialized
  99. | UpgradeableLoaderState::Buffer {
  100. authority_address: _,
  101. }
  102. | UpgradeableLoaderState::ProgramData {
  103. slot: _,
  104. upgrade_authority_address: _,
  105. } => {
  106. // Unreachable because check in try_from
  107. // ensures that program is executable
  108. // and therefore a program account.
  109. unreachable!()
  110. }
  111. UpgradeableLoaderState::Program {
  112. programdata_address,
  113. } => Ok(Some(programdata_address)),
  114. }
  115. } else {
  116. Ok(None)
  117. }
  118. }
  119. }
  120. impl<'a, T: Id> TryFrom<&'a AccountInfo<'a>> for Program<'a, T> {
  121. type Error = Error;
  122. /// Deserializes the given `info` into a `Program`.
  123. fn try_from(info: &'a AccountInfo<'a>) -> Result<Self> {
  124. if info.key != &T::id() {
  125. return Err(Error::from(ErrorCode::InvalidProgramId).with_pubkeys((*info.key, T::id())));
  126. }
  127. if !info.executable {
  128. return Err(ErrorCode::InvalidProgramExecutable.into());
  129. }
  130. Ok(Program::new(info))
  131. }
  132. }
  133. impl<'info, B, T: Id> Accounts<'info, B> for Program<'info, T> {
  134. #[inline(never)]
  135. fn try_accounts(
  136. _program_id: &Pubkey,
  137. accounts: &mut &'info [AccountInfo<'info>],
  138. _ix_data: &[u8],
  139. _bumps: &mut B,
  140. _reallocs: &mut BTreeSet<Pubkey>,
  141. ) -> Result<Self> {
  142. if accounts.is_empty() {
  143. return Err(ErrorCode::AccountNotEnoughKeys.into());
  144. }
  145. let account = &accounts[0];
  146. *accounts = &accounts[1..];
  147. Program::try_from(account)
  148. }
  149. }
  150. impl<'info, T> ToAccountMetas for Program<'info, T> {
  151. fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
  152. let is_signer = is_signer.unwrap_or(self.info.is_signer);
  153. let meta = match self.info.is_writable {
  154. false => AccountMeta::new_readonly(*self.info.key, is_signer),
  155. true => AccountMeta::new(*self.info.key, is_signer),
  156. };
  157. vec![meta]
  158. }
  159. }
  160. impl<'info, T> ToAccountInfos<'info> for Program<'info, T> {
  161. fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
  162. vec![self.info.clone()]
  163. }
  164. }
  165. impl<'info, T> AsRef<AccountInfo<'info>> for Program<'info, T> {
  166. fn as_ref(&self) -> &AccountInfo<'info> {
  167. self.info
  168. }
  169. }
  170. impl<'info, T> Deref for Program<'info, T> {
  171. type Target = AccountInfo<'info>;
  172. fn deref(&self) -> &Self::Target {
  173. self.info
  174. }
  175. }
  176. impl<'info, T: AccountDeserialize> AccountsExit<'info> for Program<'info, T> {}
  177. impl<'info, T: AccountDeserialize> Key for Program<'info, T> {
  178. fn key(&self) -> Pubkey {
  179. *self.info.key
  180. }
  181. }