1use {
4 crate::{
5 error::{Error, ErrorCode},
6 solana_program::{
7 account_info::AccountInfo,
8 bpf_loader_upgradeable::{self, UpgradeableLoaderState},
9 instruction::AccountMeta,
10 pubkey::Pubkey,
11 },
12 AccountDeserialize, Accounts, AccountsExit, Id, Key, Result, ToAccountInfos,
13 ToAccountMetas,
14 },
15 std::{collections::BTreeSet, fmt, marker::PhantomData, ops::Deref},
16};
17
18#[derive(Clone)]
99pub struct Program<'info, T = ()> {
100 info: &'info AccountInfo<'info>,
101 _phantom: PhantomData<T>,
102}
103
104impl<T: fmt::Debug> fmt::Debug for Program<'_, T> {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 f.debug_struct("Program").field("info", &self.info).finish()
107 }
108}
109
110impl<'a, T> Program<'a, T> {
111 pub(crate) fn new(info: &'a AccountInfo<'a>) -> Program<'a, T> {
112 Self {
113 info,
114 _phantom: PhantomData,
115 }
116 }
117
118 pub fn programdata_address(&self) -> Result<Option<Pubkey>> {
119 if *self.info.owner == bpf_loader_upgradeable::ID {
120 let mut data: &[u8] = &self.info.try_borrow_data()?;
121 let upgradable_loader_state =
122 UpgradeableLoaderState::try_deserialize_unchecked(&mut data)?;
123
124 match upgradable_loader_state {
125 UpgradeableLoaderState::Uninitialized
126 | UpgradeableLoaderState::Buffer {
127 authority_address: _,
128 }
129 | UpgradeableLoaderState::ProgramData {
130 slot: _,
131 upgrade_authority_address: _,
132 } => {
133 unreachable!()
137 }
138 UpgradeableLoaderState::Program {
139 programdata_address,
140 } => Ok(Some(programdata_address)),
141 }
142 } else {
143 Ok(None)
144 }
145 }
146}
147
148impl<'a, T: Id> TryFrom<&'a AccountInfo<'a>> for Program<'a, T> {
149 type Error = Error;
150 fn try_from(info: &'a AccountInfo<'a>) -> Result<Self> {
153 if info.key != &T::id() {
154 return Err(Error::from(ErrorCode::InvalidProgramId).with_pubkeys((*info.key, T::id())));
155 }
156 if !info.executable {
157 return Err(ErrorCode::InvalidProgramExecutable.into());
158 }
159 Ok(Program::new(info))
160 }
161}
162
163impl<'a> TryFrom<&'a AccountInfo<'a>> for Program<'a, ()> {
166 type Error = Error;
167 fn try_from(info: &'a AccountInfo<'a>) -> Result<Self> {
169 if !info.executable {
170 return Err(ErrorCode::InvalidProgramExecutable.into());
171 }
172 Ok(Program::new(info))
173 }
174}
175
176impl<'info, B, T: Id> Accounts<'info, B> for Program<'info, T> {
177 #[inline(never)]
178 fn try_accounts(
179 _program_id: &Pubkey,
180 accounts: &mut &'info [AccountInfo<'info>],
181 _ix_data: &[u8],
182 _bumps: &mut B,
183 _reallocs: &mut BTreeSet<Pubkey>,
184 ) -> Result<Self> {
185 if accounts.is_empty() {
186 return Err(ErrorCode::AccountNotEnoughKeys.into());
187 }
188 let account = &accounts[0];
189 *accounts = &accounts[1..];
190 Program::try_from(account)
191 }
192}
193
194impl<'info, B> Accounts<'info, B> for Program<'info, ()> {
195 #[inline(never)]
196 fn try_accounts(
197 _program_id: &Pubkey,
198 accounts: &mut &'info [AccountInfo<'info>],
199 _ix_data: &[u8],
200 _bumps: &mut B,
201 _reallocs: &mut BTreeSet<Pubkey>,
202 ) -> Result<Self> {
203 if accounts.is_empty() {
204 return Err(ErrorCode::AccountNotEnoughKeys.into());
205 }
206 let account = &accounts[0];
207 *accounts = &accounts[1..];
208 Program::try_from(account)
209 }
210}
211
212impl<T> ToAccountMetas for Program<'_, T> {
213 fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
214 let is_signer = is_signer.unwrap_or(self.info.is_signer);
215 let meta = match self.info.is_writable {
216 false => AccountMeta::new_readonly(*self.info.key, is_signer),
217 true => AccountMeta::new(*self.info.key, is_signer),
218 };
219 vec![meta]
220 }
221}
222
223impl<'info, T> ToAccountInfos<'info> for Program<'info, T> {
224 fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
225 vec![self.info.clone()]
226 }
227}
228
229impl<'info, T> AsRef<AccountInfo<'info>> for Program<'info, T> {
230 fn as_ref(&self) -> &AccountInfo<'info> {
231 self.info
232 }
233}
234
235impl<'info, T> Deref for Program<'info, T> {
236 type Target = AccountInfo<'info>;
237
238 fn deref(&self) -> &Self::Target {
239 self.info
240 }
241}
242
243impl<'info, T: AccountDeserialize> AccountsExit<'info> for Program<'info, T> {}
244
245impl<T: AccountDeserialize> Key for Program<'_, T> {
246 fn key(&self) -> Pubkey {
247 *self.info.key
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254 use crate::system_program::System;
255
256 fn account_info<'a>(
257 key: &'a Pubkey,
258 owner: &'a Pubkey,
259 lamports: &'a mut u64,
260 data: &'a mut [u8],
261 executable: bool,
262 ) -> AccountInfo<'a> {
263 AccountInfo::new(key, false, false, lamports, data, owner, executable)
264 }
265
266 #[test]
267 fn program_system_rejects_non_system_account() {
268 let wrong_key = Pubkey::new_from_array([7u8; 32]);
269 let owner = Pubkey::default();
270 let mut lamports = 0u64;
271 let mut data = [];
272 let info = account_info(&wrong_key, &owner, &mut lamports, &mut data, true);
273
274 let err = Program::<System>::try_from(&info)
275 .err()
276 .expect("non-system key must be rejected");
277 match err {
278 Error::AnchorError(e) => assert_eq!(
279 e.error_code_number,
280 ErrorCode::InvalidProgramId as u32,
281 ),
282 other => panic!("unexpected error variant: {other:?}"),
283 }
284 }
285
286 #[test]
287 fn program_system_accepts_system_account() {
288 let key = System::id();
289 let owner = Pubkey::default();
290 let mut lamports = 0u64;
291 let mut data = [];
292 let info = account_info(&key, &owner, &mut lamports, &mut data, true);
293
294 Program::<System>::try_from(&info).expect("system program account must be accepted");
295 }
296
297 #[test]
298 fn program_system_rejects_non_executable_system_account() {
299 let key = System::id();
300 let owner = Pubkey::default();
301 let mut lamports = 0u64;
302 let mut data = [];
303 let info = account_info(&key, &owner, &mut lamports, &mut data, false);
304
305 let err = Program::<System>::try_from(&info)
306 .err()
307 .expect("non-executable account must be rejected");
308 match err {
309 Error::AnchorError(e) => assert_eq!(
310 e.error_code_number,
311 ErrorCode::InvalidProgramExecutable as u32,
312 ),
313 other => panic!("unexpected error variant: {other:?}"),
314 }
315 }
316
317 #[test]
319 fn program_unit_accepts_arbitrary_executable() {
320 let key = Pubkey::new_from_array([7u8; 32]);
321 let owner = Pubkey::default();
322 let mut lamports = 0u64;
323 let mut data = [];
324 let info = account_info(&key, &owner, &mut lamports, &mut data, true);
325
326 Program::<()>::try_from(&info).expect("any executable account must be accepted");
327 }
328}