program.rs 6.3 KB

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