Skip to content

Commit

Permalink
lang, ts: Update discriminator-related documentation (#3211)
Browse files Browse the repository at this point in the history
  • Loading branch information
acheroncrypto committed Aug 29, 2024
1 parent 65c159f commit 5c67b04
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 35 deletions.
15 changes: 10 additions & 5 deletions lang/attribute/account/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion lang/attribute/event/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand Down
2 changes: 1 addition & 1 deletion lang/attribute/program/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand Down
24 changes: 20 additions & 4 deletions lang/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(<namespace>:<method_name>)[..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<u8> {
let mut data = Vec::with_capacity(256);
Expand All @@ -300,8 +299,25 @@ pub trait Event: AnchorSerialize + AnchorDeserialize + Discriminator {
fn data(&self) -> Vec<u8>;
}

/// 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];
}

Expand Down
17 changes: 6 additions & 11 deletions lang/syn/src/codegen/program/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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("<namespace>:<rust-identifier>")[..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>],
Expand Down
15 changes: 4 additions & 11 deletions lang/syn/src/codegen/program/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions ts/packages/anchor/src/coder/borsh/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ export class BorshAccountsCoder<A extends string = string>
}

/**
* 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);
Expand Down

0 comments on commit 5c67b04

Please sign in to comment.