Browse Source

lang: deserialize programdata address on demand/lazily (#1723)

Paul 3 years ago
parent
commit
778e328753

+ 1 - 0
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
 

+ 1 - 1
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>,

+ 17 - 23
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<Pubkey>,
     _phantom: PhantomData<T>,
 }
 
 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<Pubkey>) -> 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<Option<Pubkey>> {
+        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<Pubkey> {
-        self.programdata_address
+            Ok(None)
+        }
     }
 }
 

+ 1 - 1
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>,