Armani Ferrante 3 ani în urmă
părinte
comite
3b680a6cf9

+ 4 - 15
client/src/lib.rs

@@ -1,6 +1,7 @@
 //! `anchor_client` provides an RPC client to send transactions and fetch
 //! deserialized accounts from Solana programs written in `anchor_lang`.
 
+use anchor_lang::accounts::header;
 use anchor_lang::solana_program::instruction::{AccountMeta, Instruction};
 use anchor_lang::solana_program::program_error::ProgramError;
 use anchor_lang::solana_program::pubkey::Pubkey;
@@ -292,23 +293,11 @@ fn handle_program_log<T: anchor_lang::Event + anchor_lang::AnchorDeserialize>(
 
         let mut slice: &[u8] = &borsh_bytes[..];
 
-        #[cfg(feature = "deprecated-layout")]
-        let disc: [u8; 8] = {
-            let mut disc = [0; 8];
-            disc.copy_from_slice(&borsh_bytes[..8]);
-            slice = &slice[8..];
-            disc
-        };
-        #[cfg(not(feature = "deprecated-layout"))]
-        let disc: [u8; 4] = {
-            let mut disc = [0; 4];
-            disc.copy_from_slice(&borsh_bytes[2..6]);
-            slice = &slice[8..];
-            disc
-        };
+        let disc = header::read_discriminator(slice);
+        slice = &slice[8..];
 
         let mut event = None;
-        if disc == T::discriminator() {
+        if disc == &T::discriminator() {
             let e: T = anchor_lang::AnchorDeserialize::deserialize(&mut slice)
                 .map_err(|e| ClientError::LogParseError(e.to_string()))?;
             event = Some(e);

+ 2 - 4
lang/src/accounts/account.rs

@@ -1,5 +1,6 @@
 //! Account container that checks ownership on deserialization.
 
+use crate::accounts::header;
 use crate::error::ErrorCode;
 use crate::*;
 use solana_program::account_info::AccountInfo;
@@ -334,10 +335,7 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsEx
         if &T::owner() == program_id {
             let info = self.to_account_info();
             let mut data = info.try_borrow_mut_data()?;
-
-            // Chop off the header.
-            let dst: &mut [u8] = &mut data[8..];
-
+            let dst = header::read_data_mut(&mut data);
             let mut cursor = std::io::Cursor::new(dst);
             self.account.try_serialize(&mut cursor)?;
         }

+ 4 - 19
lang/src/accounts/account_loader.rs

@@ -1,8 +1,8 @@
 //! Type facilitating on demand zero copy deserialization.
 
+use crate::accounts::header;
 use crate::error::ErrorCode;
 use crate::*;
-use arrayref::array_ref;
 use solana_program::account_info::AccountInfo;
 use solana_program::entrypoint::ProgramResult;
 use solana_program::instruction::AccountMeta;
@@ -120,12 +120,7 @@ impl<'info, T: ZeroCopy + Owner> AccountLoader<'info, T> {
             return Err(ErrorCode::AccountOwnedByWrongProgram.into());
         }
         let data: &[u8] = &acc_info.try_borrow_data()?;
-        // Discriminator must match.
-        #[cfg(feature = "deprecated-layout")]
-        let disc_bytes = array_ref![data, 0, 8];
-        #[cfg(not(feature = "deprecated-layout"))]
-        let disc_bytes = array_ref![data, 2, 4];
-
+        let disc_bytes = header::read_discriminator(data);
         if disc_bytes != &T::discriminator() {
             return Err(ErrorCode::AccountDiscriminatorMismatch.into());
         }
@@ -147,12 +142,7 @@ impl<'info, T: ZeroCopy + Owner> AccountLoader<'info, T> {
     /// Returns a Ref to the account data structure for reading.
     pub fn load(&self) -> Result<Ref<T>, ProgramError> {
         let data = self.acc_info.try_borrow_data()?;
-
-        #[cfg(feature = "deprecated-layout")]
-        let disc_bytes = array_ref![data, 0, 8];
-        #[cfg(not(feature = "deprecated-layout"))]
-        let disc_bytes = array_ref![data, 2, 4];
-
+        let disc_bytes = header::read_discriminator(&data);
         if disc_bytes != &T::discriminator() {
             return Err(ErrorCode::AccountDiscriminatorMismatch.into());
         }
@@ -170,12 +160,7 @@ impl<'info, T: ZeroCopy + Owner> AccountLoader<'info, T> {
         }
 
         let data = self.acc_info.try_borrow_mut_data()?;
-
-        #[cfg(feature = "deprecated-layout")]
-        let disc_bytes = array_ref![data, 0, 8];
-        #[cfg(not(feature = "deprecated-layout"))]
-        let disc_bytes = array_ref![data, 2, 4];
-
+        let disc_bytes = header::read_discriminator(&data);
         if disc_bytes != &T::discriminator() {
             return Err(ErrorCode::AccountDiscriminatorMismatch.into());
         }

+ 40 - 0
lang/src/accounts/header.rs

@@ -0,0 +1,40 @@
+use arrayref::array_ref;
+
+#[cfg(feature = "deprecated-layout")]
+pub fn read_discriminator(data: &[u8]) -> &[u8; 8] {
+    array_ref![data, 0, 8]
+}
+
+#[cfg(not(feature = "deprecated-layout"))]
+pub fn read_discriminator<'a>(data: &[u8]) -> &[u8; 4] {
+    array_ref![data, 2, 4]
+}
+
+#[cfg(feature = "deprecated-layout")]
+pub fn create_discriminator(account_name: &str, namespace: Option<&str>) -> [u8; 8] {
+    let discriminator_preimage = format!("{}:{}", namespace.unwrap_or("account"), account_name);
+    let mut discriminator = [0u8; 8];
+    discriminator.copy_from_slice(
+        &crate::solana_program::hash::hash(discriminator_preimage.as_bytes()).to_bytes()[..8],
+    );
+    discriminator
+}
+
+#[cfg(not(feature = "deprecated-layout"))]
+pub fn create_discriminator(account_name: &str, namespace: Option<&str>) -> [u8; 4] {
+    let discriminator_preimage = format!("{}:{}", namespace.unwrap_or("account"), account_name);
+    let mut discriminator = [0u8; 4];
+    discriminator.copy_from_slice(
+        &crate::solana_program::hash::hash(discriminator_preimage.as_bytes()).to_bytes()[..4],
+    );
+    discriminator
+}
+
+// Header is 8 bytes regardless of layout.
+pub fn read_data(account_data: &[u8]) -> &[u8] {
+    &account_data[8..]
+}
+
+pub fn read_data_mut(account_data: &mut [u8]) -> &mut [u8] {
+    &mut account_data[8..]
+}

+ 4 - 19
lang/src/accounts/loader.rs

@@ -1,3 +1,4 @@
+use crate::accounts::header;
 use crate::error::ErrorCode;
 use crate::*;
 use arrayref::array_ref;
@@ -59,13 +60,7 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
             return Err(ErrorCode::AccountOwnedByWrongProgram.into());
         }
         let data: &[u8] = &acc_info.try_borrow_data()?;
-
-        // Discriminator must match.
-        #[cfg(feature = "deprecated-layout")]
-        let disc_bytes = array_ref![data, 0, 8];
-        #[cfg(not(feature = "deprecated-layout"))]
-        let disc_bytes = array_ref![data, 2, 4];
-
+        let disc_bytes = header::read_discriminator(data);
         if disc_bytes != &T::discriminator() {
             return Err(ErrorCode::AccountDiscriminatorMismatch.into());
         }
@@ -90,12 +85,7 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
     #[allow(deprecated)]
     pub fn load(&self) -> Result<Ref<T>, ProgramError> {
         let data = self.acc_info.try_borrow_data()?;
-
-        #[cfg(feature = "deprecated-layout")]
-        let disc_bytes = array_ref![data, 0, 8];
-        #[cfg(not(feature = "deprecated-layout"))]
-        let disc_bytes = array_ref![data, 2, 4];
-
+        let disc_bytes = header::read_discriminator(&data);
         if disc_bytes != &T::discriminator() {
             return Err(ErrorCode::AccountDiscriminatorMismatch.into());
         }
@@ -113,12 +103,7 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
         }
 
         let data = self.acc_info.try_borrow_mut_data()?;
-
-        #[cfg(feature = "deprecated-layout")]
-        let disc_bytes = array_ref![data, 0, 8];
-        #[cfg(not(feature = "deprecated-layout"))]
-        let disc_bytes = array_ref![data, 2, 4];
-
+        let disc_bytes = header::read_discriminator(&data);
         if disc_bytes != &T::discriminator() {
             return Err(ErrorCode::AccountDiscriminatorMismatch.into());
         }

+ 1 - 0
lang/src/accounts/mod.rs

@@ -10,6 +10,7 @@ pub mod cpi_account;
 #[doc(hidden)]
 #[allow(deprecated)]
 pub mod cpi_state;
+pub mod header;
 #[doc(hidden)]
 #[allow(deprecated)]
 pub mod loader;

+ 2 - 4
lang/src/accounts/program_account.rs

@@ -1,5 +1,6 @@
 #[allow(deprecated)]
 use crate::accounts::cpi_account::CpiAccount;
+use crate::accounts::header;
 use crate::error::ErrorCode;
 use crate::*;
 use solana_program::account_info::AccountInfo;
@@ -99,10 +100,7 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info
     fn exit(&self, _program_id: &Pubkey) -> ProgramResult {
         let info = self.to_account_info();
         let mut data = info.try_borrow_mut_data()?;
-
-        // Chop off the header.
-        let dst: &mut [u8] = &mut data[8..];
-
+        let dst = header::read_data_mut(&mut data);
         let mut cursor = std::io::Cursor::new(dst);
         self.inner.account.try_serialize(&mut cursor)?;
         Ok(())

+ 1 - 1
ts/src/coder/borsh/accounts.ts

@@ -19,7 +19,7 @@ const ACCOUNT_HEADER_SIZE = 8;
  * Number of bytes of the account discriminator.
  */
 const ACCOUNT_DISCRIMINATOR_SIZE = 4;
-const DEPRECATED_ACCOUNT_DISCRIMINATOR_SIZE = 4;
+const DEPRECATED_ACCOUNT_DISCRIMINATOR_SIZE = 8;
 
 /**
  * Encodes and decodes account objects.

+ 6 - 10
ts/src/program/namespace/state.ts

@@ -24,7 +24,7 @@ import InstructionNamespaceFactory from "./instruction.js";
 import RpcNamespaceFactory from "./rpc.js";
 import TransactionNamespaceFactory from "./transaction.js";
 import { IdlTypes, TypeDef } from "./types.js";
-import * as features from "../../utils/features.js";
+import { BorshAccountHeader } from "../../coder/borsh/accounts.js";
 
 export default class StateFactory {
   public static build<IDL extends Idl>(
@@ -175,15 +175,11 @@ export class StateClient<IDL extends Idl> {
     }
 
     const expectedDiscriminator = await stateDiscriminator(state.struct.name);
-
-    if (features.isSet("deprecated-layout")) {
-      if (expectedDiscriminator.compare(accountInfo.data.slice(0, 8))) {
-        throw new Error("Invalid state discriminator");
-      }
-    } else {
-      if (expectedDiscriminator.compare(accountInfo.data.slice(2, 6))) {
-        throw new Error("Invalid state discriminator");
-      }
+    const discriminator = BorshAccountHeader.parseDiscriminator(
+      accountInfo.data
+    );
+    if (discriminator.compare(expectedDiscriminator)) {
+      throw new Error("Invalid state discriminator");
     }
 
     return this.coder.state.decode(accountInfo.data);