diff --git a/CHANGELOG.md b/CHANGELOG.md index f4888f8b5c..85dffb1f41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ The minor version will be incremented upon a breaking change and the patch versi * spl: Re-export the `spl_token` crate ([#1665](https://github.com/project-serum/anchor/pull/1665)). * lang, cli, spl: Update solana toolchain to v1.9.13 ([#1653](https://github.com/project-serum/anchor/pull/1653)). * lang: Use fallback function if ix data length smaller than `8` instead of panicking ([#1721](https://github.com/project-serum/anchor/pull/1721)). +* lang: `Program` type now deserializes `programdata_address` only on demand ([#1723](https://github.com/project-serum/anchor/pull/1723)). ## [0.23.0] - 2022-03-20 diff --git a/lang/src/accounts/account.rs b/lang/src/accounts/account.rs index 12b428ad16..f8306ac0ad 100644 --- a/lang/src/accounts/account.rs +++ b/lang/src/accounts/account.rs @@ -186,7 +186,7 @@ use std::ops::{Deref, DerefMut}; /// pub admin_settings: Account<'info, AdminSettings>, /// #[account(mut)] /// pub authority: Signer<'info>, -/// #[account(constraint = program.programdata_address() == Some(program_data.key()))] +/// #[account(constraint = program.programdata_address()? == Some(program_data.key()))] /// pub program: Program<'info, MyProgram>, /// #[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))] /// pub program_data: Account<'info, ProgramData>, diff --git a/lang/src/accounts/program.rs b/lang/src/accounts/program.rs index e8aba5c026..fcdc0be552 100644 --- a/lang/src/accounts/program.rs +++ b/lang/src/accounts/program.rs @@ -15,9 +15,9 @@ use std::ops::Deref; /// Type validating that the account is the given Program /// -/// The type has a `programdata_address` property that will be set +/// The type has a `programdata_address` function that will return `Option::Some` /// if the program is owned by the [`BPFUpgradeableLoader`](https://docs.rs/solana-program/latest/solana_program/bpf_loader_upgradeable/index.html) -/// 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. +/// 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. /// /// # Table of Contents /// - [Basic Functionality](#basic-functionality) @@ -47,7 +47,7 @@ use std::ops::Deref; /// pub struct SetAdminSettings<'info> { /// #[account(mut, seeds = [b"admin"], bump)] /// pub admin_settings: Account<'info, AdminSettings>, -/// #[account(constraint = program.programdata_address() == Some(program_data.key()))] +/// #[account(constraint = program.programdata_address()? == Some(program_data.key()))] /// pub program: Program<'info, MyProgram>, /// #[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))] /// pub program_data: Account<'info, ProgramData>, @@ -60,7 +60,7 @@ use std::ops::Deref; /// /// - `program` is the account of the program itself. /// Its constraint checks that `program_data` is the account that contains the program's upgrade authority. -/// Implicitly, this checks that `program` is a BPFUpgradeable program (`program.programdata_address()` +/// Implicitly, this checks that `program` is a BPFUpgradeable program (`program.programdata_address()?` /// will be `None` if it's not). /// - `program_data`'s constraint checks that its upgrade authority is the `authority` account. /// - Finally, `authority` needs to sign the transaction. @@ -77,24 +77,19 @@ use std::ops::Deref; #[derive(Clone)] pub struct Program<'info, T: Id + Clone> { info: AccountInfo<'info>, - programdata_address: Option, _phantom: PhantomData, } impl<'info, T: Id + Clone + fmt::Debug> fmt::Debug for Program<'info, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Program") - .field("info", &self.info) - .field("programdata_address", &self.programdata_address) - .finish() + f.debug_struct("Program").field("info", &self.info).finish() } } impl<'a, T: Id + Clone> Program<'a, T> { - fn new(info: AccountInfo<'a>, programdata_address: Option) -> Program<'a, T> { + fn new(info: AccountInfo<'a>) -> Program<'a, T> { Self { info, - programdata_address, _phantom: PhantomData, } } @@ -108,8 +103,13 @@ impl<'a, T: Id + Clone> Program<'a, T> { if !info.executable { return Err(ErrorCode::InvalidProgramExecutable.into()); } - let programdata_address = if *info.owner == bpf_loader_upgradeable::ID { - let mut data: &[u8] = &info.try_borrow_data()?; + + Ok(Program::new(info.clone())) + } + + pub fn programdata_address(&self) -> Result> { + if *self.info.owner == bpf_loader_upgradeable::ID { + let mut data: &[u8] = &self.info.try_borrow_data()?; let upgradable_loader_state = UpgradeableLoaderState::try_deserialize_unchecked(&mut data)?; @@ -122,24 +122,18 @@ impl<'a, T: Id + Clone> Program<'a, T> { slot: _, upgrade_authority_address: _, } => { - // Unreachable because check above already + // Unreachable because check in try_from // ensures that program is executable // and therefore a program account. unreachable!() } UpgradeableLoaderState::Program { programdata_address, - } => Some(programdata_address), + } => Ok(Some(programdata_address)), } } else { - None - }; - - Ok(Program::new(info.clone(), programdata_address)) - } - - pub fn programdata_address(&self) -> Option { - self.programdata_address + Ok(None) + } } } diff --git a/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/src/lib.rs b/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/src/lib.rs index fca03bb7df..53fdf43592 100644 --- a/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/src/lib.rs +++ b/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/src/lib.rs @@ -72,7 +72,7 @@ pub struct SetAdminSettingsUseProgramState<'info> { pub settings: Account<'info, Settings>, #[account(mut)] pub authority: Signer<'info>, - #[account(constraint = program.programdata_address() == Some(program_data.key()))] + #[account(constraint = program.programdata_address()? == Some(program_data.key()))] pub program: Program<'info, crate::program::BpfUpgradeableState>, #[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))] pub program_data: Account<'info, ProgramData>,