Skip to main content

cronos_anchor_lang/accounts/
program.rs

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