From 5c67b046251df69e58677a9842e1c7a6f1373944 Mon Sep 17 00:00:00 2001 From: acheron <98934430+acheroncrypto@users.noreply.github.com> Date: Thu, 29 Aug 2024 23:59:25 +0200 Subject: [PATCH] lang, ts: Update discriminator-related documentation (#3211) --- lang/attribute/account/src/lib.rs | 15 ++++++++---- lang/attribute/event/src/lib.rs | 2 +- lang/attribute/program/src/lib.rs | 2 +- lang/src/lib.rs | 24 +++++++++++++++---- lang/syn/src/codegen/program/dispatch.rs | 17 +++++-------- lang/syn/src/codegen/program/entry.rs | 15 ++++-------- .../anchor/src/coder/borsh/accounts.ts | 4 ++-- 7 files changed, 44 insertions(+), 35 deletions(-) diff --git a/lang/attribute/account/src/lib.rs b/lang/attribute/account/src/lib.rs index 26cd15ea43..53a699012b 100644 --- a/lang/attribute/account/src/lib.rs +++ b/lang/attribute/account/src/lib.rs @@ -25,14 +25,16 @@ mod id; /// - [`Owner`](./trait.Owner.html) /// /// When implementing account serialization traits the first 8 bytes are -/// reserved for a unique account discriminator, self described by the first 8 -/// bytes of the SHA256 of the account's Rust ident. +/// reserved for a unique account discriminator by default, self described by +/// the first 8 bytes of the SHA256 of the account's Rust ident. This is unless +/// the discriminator was overridden with the `discriminator` argument (see +/// [Arguments](#arguments)). /// /// As a result, any calls to `AccountDeserialize`'s `try_deserialize` will /// check this discriminator. If it doesn't match, an invalid account was given, /// and the account deserialization will exit with an error. /// -/// # Args +/// # Arguments /// /// - `discriminator`: Override the default 8-byte discriminator /// @@ -83,8 +85,11 @@ mod id; /// [`safety`](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html#safety) /// section before using. /// -/// Using `zero_copy` requires adding the following to your `cargo.toml` file: -/// `bytemuck = { version = "1.4.0", features = ["derive", "min_const_generics"]}` +/// Using `zero_copy` requires adding the following dependency to your `Cargo.toml` file: +/// +/// ```toml +/// bytemuck = { version = "1.17", features = ["derive", "min_const_generics"] } +/// ``` #[proc_macro_attribute] pub fn account( args: proc_macro::TokenStream, diff --git a/lang/attribute/event/src/lib.rs b/lang/attribute/event/src/lib.rs index 5365277453..76182fc465 100644 --- a/lang/attribute/event/src/lib.rs +++ b/lang/attribute/event/src/lib.rs @@ -11,7 +11,7 @@ use syn::parse_macro_input; /// their programs that clients can subscribe to. Currently, this macro is for /// structs only. /// -/// # Args +/// # Arguments /// /// - `discriminator`: Override the default 8-byte discriminator /// diff --git a/lang/attribute/program/src/lib.rs b/lang/attribute/program/src/lib.rs index e401e7e0e4..2eaa0f290a 100644 --- a/lang/attribute/program/src/lib.rs +++ b/lang/attribute/program/src/lib.rs @@ -111,7 +111,7 @@ pub fn interface( /// This attribute is used to override the Anchor defaults of program instructions. /// -/// # Args +/// # Arguments /// /// - `discriminator`: Override the default 8-byte discriminator /// diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 5fb3b24fb7..18c730e10f 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -273,9 +273,8 @@ pub trait AccountDeserialize: Sized { pub trait ZeroCopy: Discriminator + Copy + Clone + Zeroable + Pod {} /// Calculates the data for an instruction invocation, where the data is -/// `Sha256(:)[..8] || BorshSerialize(args)`. -/// `args` is a borsh serialized struct of named fields for each argument given -/// to an instruction. +/// `Discriminator + BorshSerialize(args)`. `args` is a borsh serialized +/// struct of named fields for each argument given to an instruction. pub trait InstructionData: Discriminator + AnchorSerialize { fn data(&self) -> Vec { let mut data = Vec::with_capacity(256); @@ -300,8 +299,25 @@ pub trait Event: AnchorSerialize + AnchorDeserialize + Discriminator { fn data(&self) -> Vec; } -/// 8 byte unique identifier for a type. +/// Unique identifier for a type. +/// +/// This is not a trait you should derive manually, as various Anchor macros already derive it +/// internally. +/// +/// Prior to Anchor v0.31, discriminators were always 8 bytes in size. However, starting with Anchor +/// v0.31, it is possible to override the default discriminators, and discriminator length is no +/// longer fixed, which means this trait can also be implemented for non-Anchor programs. +/// +/// It's important that the discriminator is always unique for the type you're implementing it +/// for. While the discriminator can be at any length (including zero), the IDL generation does not +/// currently allow empty discriminators for safety and convenience reasons. However, the trait +/// definition still allows empty discriminators because some non-Anchor programs, e.g. the SPL +/// Token program, don't have account discriminators. In that case, safety checks should never +/// depend on the discriminator. pub trait Discriminator { + /// Discriminator slice. + /// + /// See [`Discriminator`] trait documentation for more information. const DISCRIMINATOR: &'static [u8]; } diff --git a/lang/syn/src/codegen/program/dispatch.rs b/lang/syn/src/codegen/program/dispatch.rs index 71f8559d1e..936ff441a5 100644 --- a/lang/syn/src/codegen/program/dispatch.rs +++ b/lang/syn/src/codegen/program/dispatch.rs @@ -61,19 +61,14 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { quote! { /// Performs method dispatch. /// - /// Each method in an anchor program is uniquely defined by a namespace - /// and a rust identifier (i.e., the name given to the method). These - /// two pieces can be combined to create a method identifier, - /// specifically, Anchor uses + /// Each instruction's discriminator is checked until the given instruction data starts with + /// the current discriminator. /// - /// Sha256(":")[..8], + /// If a match is found, the instruction handler is called using the given instruction data + /// excluding the prepended discriminator bytes. /// - /// where the namespace can be one type. "global" for a - /// regular instruction. - /// - /// With this 8 byte identifier, Anchor performs method dispatch, - /// matching the given 8 byte identifier to the associated method - /// handler, which leads to user defined code being eventually invoked. + /// If no match is found, the fallback function is executed if it exists, or an error is + /// returned if it doesn't exist. fn dispatch<'info>( program_id: &Pubkey, accounts: &'info [AccountInfo<'info>], diff --git a/lang/syn/src/codegen/program/entry.rs b/lang/syn/src/codegen/program/entry.rs index 3b83ee91ed..350e814f3b 100644 --- a/lang/syn/src/codegen/program/entry.rs +++ b/lang/syn/src/codegen/program/entry.rs @@ -25,17 +25,10 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { /// The execution flow of the generated code can be roughly outlined: /// /// * Start program via the entrypoint. - /// * Strip method identifier off the first 8 bytes of the instruction - /// data and invoke the identified method. The method identifier - /// is a variant of sighash. See docs.rs for `anchor_lang` for details. - /// * If the method identifier is an IDL identifier, execute the IDL - /// instructions, which are a special set of hardcoded instructions - /// baked into every Anchor program. Then exit. - /// * Otherwise, the method identifier is for a user defined - /// instruction, i.e., one of the methods in the user defined - /// `#[program]` module. Perform method dispatch, i.e., execute the - /// big match statement mapping method identifier to method handler - /// wrapper. + /// * Check whether the declared program id matches the input program + /// id. If it's not, return an error. + /// * Find and invoke the method based on whether the instruction data + /// starts with the method's discriminator. /// * Run the method handler wrapper. This wraps the code the user /// actually wrote, deserializing the accounts, constructing the /// context, invoking the user's code, and finally running the exit diff --git a/ts/packages/anchor/src/coder/borsh/accounts.ts b/ts/packages/anchor/src/coder/borsh/accounts.ts index 6451962c8c..be94c89077 100644 --- a/ts/packages/anchor/src/coder/borsh/accounts.ts +++ b/ts/packages/anchor/src/coder/borsh/accounts.ts @@ -107,9 +107,9 @@ export class BorshAccountsCoder } /** - * Calculates and returns a unique 8 byte discriminator prepended to all anchor accounts. + * Get the unique discriminator prepended to all anchor accounts. * - * @param name The name of the account to calculate the discriminator. + * @param name The name of the account to get the discriminator of. */ public accountDiscriminator(name: string): Buffer { const account = this.idl.accounts?.find((acc) => acc.name === name);