11//! Account container that checks ownership on deserialization.
22
33use crate :: accounts:: account:: Account ;
4- use crate :: error:: { Error , ErrorCode } ;
4+ use crate :: error:: ErrorCode ;
55use crate :: solana_program:: account_info:: AccountInfo ;
66use crate :: solana_program:: instruction:: AccountMeta ;
77use crate :: solana_program:: pubkey:: Pubkey ;
88use crate :: solana_program:: system_program;
99use crate :: {
1010 AccountDeserialize , AccountSerialize , Accounts , AccountsClose , AccountsExit , CheckOwner , Key ,
11- Owners , Result , ToAccountInfo , ToAccountInfos , ToAccountMetas ,
11+ Owners , Result , ToAccountInfos , ToAccountMetas ,
1212} ;
1313use std:: collections:: BTreeSet ;
1414use std:: fmt;
@@ -182,32 +182,6 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> InterfaceAccount<'a,
182182 }
183183 }
184184
185- /// Reloads the account from storage. This is useful, for example, when
186- /// observing side effects after CPI.
187- ///
188- /// No Anchor discriminator is checked during reload. Instead, this method enforces
189- /// owner stability by verifying that `info.owner == self.owner` (i.e., the pubkey
190- /// validated at construction) to avoid TOCTOU issues, and then re-deserializes `T`
191- /// from the updated bytes.
192- ///
193- /// If you need discriminator validation on reload, use `Account<T>` with an Anchor
194- /// #[account] type.
195- pub fn reload ( & mut self ) -> Result < ( ) > {
196- let info = self . account . to_account_info ( ) ;
197-
198- // Enforce owner stability: must match the one validated at construction.
199- if info. owner != & self . owner {
200- return Err ( Error :: from ( ErrorCode :: AccountOwnedByWrongProgram )
201- . with_pubkeys ( ( * info. owner , self . owner ) ) ) ;
202- }
203-
204- // Re-deserialize fresh data into the inner account.
205- let mut data: & [ u8 ] = & info. try_borrow_data ( ) ?;
206- let new_val = T :: try_deserialize_unchecked ( & mut data) ?;
207- self . account . set_inner ( new_val) ;
208- Ok ( ( ) )
209- }
210-
211185 pub fn into_inner ( self ) -> T {
212186 self . account . into_inner ( )
213187 }
@@ -234,28 +208,19 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> InterfaceAccount<'a,
234208}
235209
236210impl < ' a , T : AccountSerialize + AccountDeserialize + CheckOwner + Clone > InterfaceAccount < ' a , T > {
237- /// Deserializes the given `info` into a `InterfaceAccount`.
238- ///
239- /// This **does not** check an Anchor discriminator. It first validates
240- /// program ownership via `T::check_owner`, then deserializes using
241- /// `AccountDeserialize::try_deserialize_unchecked`.
211+ /// Deserializes the given `info` into an `InterfaceAccount`.
242212 #[ inline( never) ]
243213 pub fn try_from ( info : & ' a AccountInfo < ' a > ) -> Result < Self > {
244- // `InterfaceAccount` targets foreign program accounts (e.g., SPL Token
245- // accounts) that do not have Anchor discriminators. Because of that, we
246- // intentionally skip the Anchor discriminator check here and instead:
247- //
248- // 1) Validate program ownership via `T::check_owner(info.owner)?`
249- // 2) Deserialize without a discriminator by delegating to
250- // `T::try_deserialize_unchecked`
251- Self :: try_from_unchecked ( info)
214+ if info. owner == & system_program:: ID && info. lamports ( ) == 0 {
215+ return Err ( ErrorCode :: AccountNotInitialized . into ( ) ) ;
216+ }
217+ T :: check_owner ( info. owner ) ?;
218+ let mut data: & [ u8 ] = & info. try_borrow_data ( ) ?;
219+ Ok ( Self :: new ( info, T :: try_deserialize ( & mut data) ?) )
252220 }
253221
254- /// Deserializes the given `info` into a `InterfaceAccount` **without** checking
255- /// the account discriminator. This is intended for foreign program accounts.
256- /// Prefer `Self::try_from` when you also want the ownership check, but note
257- /// that both skip Anchor discriminator checks, and `try_from` additionally
258- /// enforces ownership.
222+ /// Deserializes the given `info` into an `InterfaceAccount` without checking the account
223+ /// discriminator. Be careful when using this and avoid it if possible.
259224 #[ inline( never) ]
260225 pub fn try_from_unchecked ( info : & ' a AccountInfo < ' a > ) -> Result < Self > {
261226 if info. owner == & system_program:: ID && info. lamports ( ) == 0 {
@@ -265,6 +230,22 @@ impl<'a, T: AccountSerialize + AccountDeserialize + CheckOwner + Clone> Interfac
265230 let mut data: & [ u8 ] = & info. try_borrow_data ( ) ?;
266231 Ok ( Self :: new ( info, T :: try_deserialize_unchecked ( & mut data) ?) )
267232 }
233+
234+ /// Reloads the account from storage. This is useful, for example, when observing side effects
235+ /// after CPI.
236+ ///
237+ /// This method also validates that the account is owned by one of the expected programs.
238+ pub fn reload ( & mut self ) -> Result < ( ) > {
239+ let info: & AccountInfo = self . account . as_ref ( ) ;
240+ T :: check_owner ( info. owner ) ?;
241+
242+ // Re-deserialize fresh data into the inner account.
243+ self . account . set_inner ( {
244+ let mut data: & [ u8 ] = & info. try_borrow_data ( ) ?;
245+ T :: try_deserialize ( & mut data) ?
246+ } ) ;
247+ Ok ( ( ) )
248+ }
268249}
269250
270251impl < ' info , B , T : AccountSerialize + AccountDeserialize + CheckOwner + Clone > Accounts < ' info , B >
0 commit comments