From 340df5aa61b7d807d4e5cb4ace2f21005528e69f Mon Sep 17 00:00:00 2001 From: febo Date: Fri, 29 Aug 2025 21:42:30 +0100 Subject: [PATCH 1/7] Add fast path entrypoint --- p-token/src/entrypoint.rs | 211 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 202 insertions(+), 9 deletions(-) diff --git a/p-token/src/entrypoint.rs b/p-token/src/entrypoint.rs index 2a483822..28c6b2cc 100644 --- a/p-token/src/entrypoint.rs +++ b/p-token/src/entrypoint.rs @@ -1,21 +1,218 @@ use { crate::processor::*, + core::{ + mem::{transmute, MaybeUninit}, + slice::from_raw_parts, + }, pinocchio::{ account_info::AccountInfo, - no_allocator, nostd_panic_handler, program_entrypoint, + entrypoint::deserialize_into, + hint::likely, + no_allocator, nostd_panic_handler, program_error::{ProgramError, ToStr}, - pubkey::Pubkey, - ProgramResult, + ProgramResult, MAX_TX_ACCOUNTS, SUCCESS, }, pinocchio_token_interface::error::TokenError, }; -program_entrypoint!(process_instruction); // Do not allocate memory. no_allocator!(); // Use the no_std panic handler. nostd_panic_handler!(); +/// Custom program entrypoint to give priority to `transfer` and +/// `transfer_checked` instructions. +/// +/// The entrypoint prioritizes the transfer instruction by validating +/// account data lengths and instruction data. When it can reliably +/// determine that the instruction is a transfer, it will invoke the +/// processor directly. +#[no_mangle] +#[allow(clippy::arithmetic_side_effects)] +pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { + // Constants that apply to both `transfer` and `transfer_checked`. + + /// Offset for the first account. + const ACCOUNT1_HEADER_OFFSET: usize = 0x0008; + + /// Offset for the first account data length. This is + /// expected to be a token account (165 bytes). + const ACCOUNT1_DATA_LEN: usize = 0x0058; + + /// Offset for the second account. + const ACCOUNT2_HEADER_OFFSET: usize = 0x2910; + + /// Offset for the second account data length. This is + /// expected to be a token account for `transfer` (165 bytes) + /// or a mint account for `transfer_checked` (82 bytes). + const ACCOUNT2_DATA_LEN: usize = 0x2960; + + // Constants that apply to `transfer_checked` (instruction 12). + + /// Offset for the third account. + const IX12_ACCOUNT3_HEADER_OFFSET: usize = 0x51c8; + + /// Offset for the third account data length. This is + /// expected to be a token account (165 bytes). + const IX12_ACCOUNT3_DATA_LEN: usize = 0x5218; + + /// Offset for the fourth account. + const IX12_ACCOUNT4_HEADER_OFFSET: usize = 0x7ad0; + + /// Offset for the fourth account data length. + /// + /// This is expected to be an account with variable data + /// length. + const IX12_ACCOUNT4_DATA_LEN: usize = 0x7b20; + + /// Expected offset for the instruction data in the case all + /// previous accounts have zero data. + /// + /// This value is adjusted before it is used. + const IX12_EXPECTED_INSTRUCTION_DATA_LEN_OFFSET: usize = 0xa330; + + // Constants that apply to `transfer` (instruction 3). + + /// Offset for the second account. + /// + /// Note that this assumes that both first and second accounts + /// have zero data, which is being validated before the offset + /// is used. + const IX3_ACCOUNT3_HEADER_OFFSET: usize = 0x5218; + + /// Offset for the third account data length. This is + /// expected to be a mint account (82 bytes). + const IX3_ACCOUNT3_DATA_LEN: usize = 0x5268; + + /// Expected offset for the instruction data in the case all + /// previous accounts have zero data. + /// + /// This value is adjusted before it is used. + const IX3_INSTRUCTION_DATA_LEN_OFFSET: usize = 0x7a28; + + /// Align an address to the next multiple of 8. + #[inline(always)] + fn align(input: u64) -> u64 { + (input + 7) & (!7) + } + + // Fast path for `transfer_checked`. + // + // It expects 4 accounts: + // 1. source: must be a token account (165 length) + // 2. mint: must be a mint account (82 length) + // 3. destination: must be a token account (165 length) + // 4. authority: can be any account (variable length) + // + // Instruction data is expected to be at least 9 bytes + // and discriminator equal to 12. + if *input == 4 + && (*input.add(ACCOUNT1_DATA_LEN).cast::() == 165) + && (*input.add(ACCOUNT2_HEADER_OFFSET) == 255) + && (*input.add(ACCOUNT2_DATA_LEN).cast::() == 82) + && (*input.add(IX12_ACCOUNT3_HEADER_OFFSET) == 255) + && (*input.add(IX12_ACCOUNT3_DATA_LEN).cast::() == 165) + && (*input.add(IX12_ACCOUNT4_HEADER_OFFSET) == 255) + { + // The `authority` account can have variable data length. + let account_4_data_len_aligned = + align(*input.add(IX12_ACCOUNT4_DATA_LEN).cast::()) as usize; + let offset = IX12_EXPECTED_INSTRUCTION_DATA_LEN_OFFSET + account_4_data_len_aligned; + + // Check that we have enough instruction data. + // + // Expected: instruction discriminator (u8) + amount (u64) + decimals (u8) + if input.add(offset).cast::().read() >= 10 { + let discriminator = input.add(offset + size_of::()).cast::().read(); + + // Check for transfer discriminator. + if likely(discriminator == 12) { + // instruction data length (u64) + discriminator (u8) + let instruction_data = unsafe { from_raw_parts(input.add(offset + 9), 9) }; + + let accounts = unsafe { + [ + transmute::<*mut u8, AccountInfo>(input.add(ACCOUNT1_HEADER_OFFSET)), + transmute::<*mut u8, AccountInfo>(input.add(ACCOUNT2_HEADER_OFFSET)), + transmute::<*mut u8, AccountInfo>(input.add(IX12_ACCOUNT3_HEADER_OFFSET)), + transmute::<*mut u8, AccountInfo>(input.add(IX12_ACCOUNT4_HEADER_OFFSET)), + ] + }; + + return match process_transfer_checked(&accounts, instruction_data) { + Ok(()) => SUCCESS, + Err(error) => { + log_error(&error); + error.into() + } + }; + } + } + } + // Fast path for `transfer`. + // + // It expects 3 accounts: + // 1. source: must be a token account (165 length) + // 2. destination: must be a token account (165 length) + // 3. authority: can be any account (variable length) + // + // Instruction data is expected to be at least 8 bytes + // and discriminator equal to 3. + else if *input == 3 + && (*input.add(ACCOUNT1_DATA_LEN).cast::() == 165) + && (*input.add(ACCOUNT2_HEADER_OFFSET) == 255) + && (*input.add(ACCOUNT2_DATA_LEN).cast::() == 165) + && (*input.add(IX3_ACCOUNT3_HEADER_OFFSET) == 255) + { + // The `authority` account can have variable data length. + let account_3_data_len_aligned = + align(*input.add(IX3_ACCOUNT3_DATA_LEN).cast::()) as usize; + let offset = IX3_INSTRUCTION_DATA_LEN_OFFSET + account_3_data_len_aligned; + + // Check that we have enough instruction data. + if likely(input.add(offset).cast::().read() >= 9) { + let discriminator = input.add(offset + size_of::()).cast::().read(); + + // Check for transfer discriminator. + if likely(discriminator == 3) { + let instruction_data = + unsafe { from_raw_parts(input.add(offset + 9), size_of::()) }; + + let accounts = unsafe { + [ + transmute::<*mut u8, AccountInfo>(input.add(ACCOUNT1_HEADER_OFFSET)), + transmute::<*mut u8, AccountInfo>(input.add(ACCOUNT2_HEADER_OFFSET)), + transmute::<*mut u8, AccountInfo>(input.add(IX3_ACCOUNT3_HEADER_OFFSET)), + ] + }; + + return match process_transfer(&accounts, instruction_data) { + Ok(()) => SUCCESS, + Err(error) => { + log_error(&error); + error.into() + } + }; + } + } + } + + // Entrypoint for the remaining instructions. + + const UNINIT: MaybeUninit = MaybeUninit::::uninit(); + let mut accounts = [UNINIT; { MAX_TX_ACCOUNTS }]; + + let (count, instruction_data) = deserialize_into(input, &mut accounts); + + match process_instruction( + from_raw_parts(accounts.as_ptr() as _, count as usize), + instruction_data, + ) { + Ok(()) => SUCCESS, + Err(error) => error.into(), + } +} + /// Log an error. #[cold] fn log_error(error: &ProgramError) { @@ -30,11 +227,7 @@ fn log_error(error: &ProgramError) { /// instructions, since it is not sound to have a "batch" instruction inside /// another "batch" instruction. #[inline(always)] -pub fn process_instruction( - _program_id: &Pubkey, - accounts: &[AccountInfo], - instruction_data: &[u8], -) -> ProgramResult { +pub fn process_instruction(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult { let [discriminator, remaining @ ..] = instruction_data else { return Err(TokenError::InvalidInstruction.into()); }; From a1cf74a4ec3d618fee969891d356b22c23ed0056 Mon Sep 17 00:00:00 2001 From: febo Date: Sat, 30 Aug 2025 14:38:33 +0100 Subject: [PATCH 2/7] Reorder processor --- p-token/src/entrypoint.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/p-token/src/entrypoint.rs b/p-token/src/entrypoint.rs index 28c6b2cc..43cec4a5 100644 --- a/p-token/src/entrypoint.rs +++ b/p-token/src/entrypoint.rs @@ -6,7 +6,7 @@ use { }, pinocchio::{ account_info::AccountInfo, - entrypoint::deserialize_into, + entrypoint::deserialize, hint::likely, no_allocator, nostd_panic_handler, program_error::{ProgramError, ToStr}, @@ -88,7 +88,7 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { /// previous accounts have zero data. /// /// This value is adjusted before it is used. - const IX3_INSTRUCTION_DATA_LEN_OFFSET: usize = 0x7a28; + const IX3_INSTRUCTION_DATA_LEN_OFFSET: usize = 0x7a78; /// Align an address to the next multiple of 8. #[inline(always)] @@ -202,10 +202,10 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { const UNINIT: MaybeUninit = MaybeUninit::::uninit(); let mut accounts = [UNINIT; { MAX_TX_ACCOUNTS }]; - let (count, instruction_data) = deserialize_into(input, &mut accounts); + let (_, count, instruction_data) = deserialize(input, &mut accounts); match process_instruction( - from_raw_parts(accounts.as_ptr() as _, count as usize), + from_raw_parts(accounts.as_ptr() as _, count), instruction_data, ) { Ok(()) => SUCCESS, @@ -331,12 +331,12 @@ pub(crate) fn inner_process_instruction( process_burn_checked(accounts, instruction_data) } - // 16 - InitializeAccount2 - 16 => { + // 17 - SyncNative + 17 => { #[cfg(feature = "logging")] - pinocchio::msg!("Instruction: InitializeAccount2"); + pinocchio::msg!("Instruction: SyncNative"); - process_initialize_account2(accounts, instruction_data) + process_sync_native(accounts) } // 18 - InitializeAccount3 18 => { @@ -352,6 +352,13 @@ pub(crate) fn inner_process_instruction( process_initialize_mint2(accounts, instruction_data) } + // 22 - InitializeImmutableOwner + 22 => { + #[cfg(feature = "logging")] + pinocchio::msg!("Instruction: InitializeImmutableOwner"); + + process_initialize_immutable_owner(accounts) + } d => inner_process_remaining_instruction(accounts, instruction_data, d), } } @@ -424,12 +431,12 @@ fn inner_process_remaining_instruction( process_mint_to_checked(accounts, instruction_data) } - // 17 - SyncNative - 17 => { + // 16 - InitializeAccount2 + 16 => { #[cfg(feature = "logging")] - pinocchio::msg!("Instruction: SyncNative"); + pinocchio::msg!("Instruction: InitializeAccount2"); - process_sync_native(accounts) + process_initialize_account2(accounts, instruction_data) } // 19 - InitializeMultisig2 19 => { @@ -445,13 +452,6 @@ fn inner_process_remaining_instruction( process_get_account_data_size(accounts) } - // 22 - InitializeImmutableOwner - 22 => { - #[cfg(feature = "logging")] - pinocchio::msg!("Instruction: InitializeImmutableOwner"); - - process_initialize_immutable_owner(accounts) - } // 23 - AmountToUiAmount 23 => { #[cfg(feature = "logging")] From 6e565fa6d2ebcfc4119c3e3c214480a591064a04 Mon Sep 17 00:00:00 2001 From: febo Date: Sat, 30 Aug 2025 15:55:18 +0100 Subject: [PATCH 3/7] Organize imports --- p-token/src/entrypoint.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/p-token/src/entrypoint.rs b/p-token/src/entrypoint.rs index 43cec4a5..73e966f2 100644 --- a/p-token/src/entrypoint.rs +++ b/p-token/src/entrypoint.rs @@ -1,18 +1,18 @@ use { crate::processor::*, core::{ - mem::{transmute, MaybeUninit}, + mem::{size_of, transmute, MaybeUninit}, slice::from_raw_parts, }, pinocchio::{ account_info::AccountInfo, entrypoint::deserialize, - hint::likely, + log::sol_log, no_allocator, nostd_panic_handler, program_error::{ProgramError, ToStr}, ProgramResult, MAX_TX_ACCOUNTS, SUCCESS, }, - pinocchio_token_interface::error::TokenError, + pinocchio_token_interface::{error::TokenError, likely}, }; // Do not allocate memory. @@ -216,7 +216,7 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { /// Log an error. #[cold] fn log_error(error: &ProgramError) { - pinocchio::log::sol_log(error.to_str::()); + sol_log(error.to_str::()); } /// Process an instruction. From 94a2a47e87a13735d72eea0ab93bf11097da294d Mon Sep 17 00:00:00 2001 From: febo Date: Sat, 6 Sep 2025 11:00:51 +0100 Subject: [PATCH 4/7] Add missing logs --- p-token/src/entrypoint.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/p-token/src/entrypoint.rs b/p-token/src/entrypoint.rs index 73e966f2..025f5eaa 100644 --- a/p-token/src/entrypoint.rs +++ b/p-token/src/entrypoint.rs @@ -139,6 +139,9 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { ] }; + #[cfg(feature = "logging")] + pinocchio::msg!("Instruction: TransferChecked"); + return match process_transfer_checked(&accounts, instruction_data) { Ok(()) => SUCCESS, Err(error) => { @@ -186,6 +189,9 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { ] }; + #[cfg(feature = "logging")] + pinocchio::msg!("Instruction: Transfer"); + return match process_transfer(&accounts, instruction_data) { Ok(()) => SUCCESS, Err(error) => { From 5815a345355e573f7368ead1a7ee26a761db7551 Mon Sep 17 00:00:00 2001 From: febo Date: Tue, 9 Sep 2025 13:57:47 +0100 Subject: [PATCH 5/7] Fix review comments --- p-token/src/entrypoint.rs | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/p-token/src/entrypoint.rs b/p-token/src/entrypoint.rs index 025f5eaa..1334a8cb 100644 --- a/p-token/src/entrypoint.rs +++ b/p-token/src/entrypoint.rs @@ -12,7 +12,12 @@ use { program_error::{ProgramError, ToStr}, ProgramResult, MAX_TX_ACCOUNTS, SUCCESS, }, - pinocchio_token_interface::{error::TokenError, likely}, + pinocchio_token_interface::{ + error::TokenError, + instruction::TokenInstruction, + likely, + state::{account::Account, mint::Mint, Transmutable}, + }, }; // Do not allocate memory. @@ -65,15 +70,15 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { /// length. const IX12_ACCOUNT4_DATA_LEN: usize = 0x7b20; - /// Expected offset for the instruction data in the case all - /// previous accounts have zero data. + /// Expected offset for the instruction data in the case the + /// fourth (authority) account has zero data. /// /// This value is adjusted before it is used. const IX12_EXPECTED_INSTRUCTION_DATA_LEN_OFFSET: usize = 0xa330; // Constants that apply to `transfer` (instruction 3). - /// Offset for the second account. + /// Offset for the third account. /// /// Note that this assumes that both first and second accounts /// have zero data, which is being validated before the offset @@ -84,8 +89,8 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { /// expected to be a mint account (82 bytes). const IX3_ACCOUNT3_DATA_LEN: usize = 0x5268; - /// Expected offset for the instruction data in the case all - /// previous accounts have zero data. + /// Expected offset for the instruction data in the case the + /// third (authority) account has zero data. /// /// This value is adjusted before it is used. const IX3_INSTRUCTION_DATA_LEN_OFFSET: usize = 0x7a78; @@ -107,11 +112,11 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { // Instruction data is expected to be at least 9 bytes // and discriminator equal to 12. if *input == 4 - && (*input.add(ACCOUNT1_DATA_LEN).cast::() == 165) + && (*input.add(ACCOUNT1_DATA_LEN).cast::() == Account::LEN as u64) && (*input.add(ACCOUNT2_HEADER_OFFSET) == 255) - && (*input.add(ACCOUNT2_DATA_LEN).cast::() == 82) + && (*input.add(ACCOUNT2_DATA_LEN).cast::() == Mint::LEN as u64) && (*input.add(IX12_ACCOUNT3_HEADER_OFFSET) == 255) - && (*input.add(IX12_ACCOUNT3_DATA_LEN).cast::() == 165) + && (*input.add(IX12_ACCOUNT3_DATA_LEN).cast::() == Account::LEN as u64) && (*input.add(IX12_ACCOUNT4_HEADER_OFFSET) == 255) { // The `authority` account can have variable data length. @@ -122,11 +127,11 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { // Check that we have enough instruction data. // // Expected: instruction discriminator (u8) + amount (u64) + decimals (u8) - if input.add(offset).cast::().read() >= 10 { + if input.add(offset).cast::().read() >= 10 { let discriminator = input.add(offset + size_of::()).cast::().read(); // Check for transfer discriminator. - if likely(discriminator == 12) { + if likely(discriminator == TokenInstruction::TransferChecked as u8) { // instruction data length (u64) + discriminator (u8) let instruction_data = unsafe { from_raw_parts(input.add(offset + 9), 9) }; @@ -162,9 +167,9 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { // Instruction data is expected to be at least 8 bytes // and discriminator equal to 3. else if *input == 3 - && (*input.add(ACCOUNT1_DATA_LEN).cast::() == 165) + && (*input.add(ACCOUNT1_DATA_LEN).cast::() == Account::LEN as u64) && (*input.add(ACCOUNT2_HEADER_OFFSET) == 255) - && (*input.add(ACCOUNT2_DATA_LEN).cast::() == 165) + && (*input.add(ACCOUNT2_DATA_LEN).cast::() == Account::LEN as u64) && (*input.add(IX3_ACCOUNT3_HEADER_OFFSET) == 255) { // The `authority` account can have variable data length. @@ -173,11 +178,11 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { let offset = IX3_INSTRUCTION_DATA_LEN_OFFSET + account_3_data_len_aligned; // Check that we have enough instruction data. - if likely(input.add(offset).cast::().read() >= 9) { + if likely(input.add(offset).cast::().read() >= 9) { let discriminator = input.add(offset + size_of::()).cast::().read(); // Check for transfer discriminator. - if likely(discriminator == 3) { + if likely(discriminator == TokenInstruction::Transfer as u8) { let instruction_data = unsafe { from_raw_parts(input.add(offset + 9), size_of::()) }; From efb236ed42c7804795141ae9c34f297cb48bce2e Mon Sep 17 00:00:00 2001 From: febo Date: Thu, 11 Sep 2025 11:39:26 +0100 Subject: [PATCH 6/7] Fix comment in constant --- p-token/src/entrypoint.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/p-token/src/entrypoint.rs b/p-token/src/entrypoint.rs index 1334a8cb..bcd4b952 100644 --- a/p-token/src/entrypoint.rs +++ b/p-token/src/entrypoint.rs @@ -85,8 +85,10 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { /// is used. const IX3_ACCOUNT3_HEADER_OFFSET: usize = 0x5218; - /// Offset for the third account data length. This is - /// expected to be a mint account (82 bytes). + /// Offset for the third account data length. + /// + /// This is expected to be an account with variable data + /// length. const IX3_ACCOUNT3_DATA_LEN: usize = 0x5268; /// Expected offset for the instruction data in the case the From c68c3d309b4046dbd6e79fc0685461deeeb7470e Mon Sep 17 00:00:00 2001 From: febo Date: Sat, 13 Sep 2025 15:20:13 +0100 Subject: [PATCH 7/7] Use non-duplicate constant --- p-token/src/entrypoint.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/p-token/src/entrypoint.rs b/p-token/src/entrypoint.rs index bcd4b952..9a0f4bd2 100644 --- a/p-token/src/entrypoint.rs +++ b/p-token/src/entrypoint.rs @@ -6,7 +6,8 @@ use { }, pinocchio::{ account_info::AccountInfo, - entrypoint::deserialize, + entrypoint::{deserialize, NON_DUP_MARKER}, + hint::likely, log::sol_log, no_allocator, nostd_panic_handler, program_error::{ProgramError, ToStr}, @@ -15,7 +16,6 @@ use { pinocchio_token_interface::{ error::TokenError, instruction::TokenInstruction, - likely, state::{account::Account, mint::Mint, Transmutable}, }, }; @@ -115,11 +115,11 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { // and discriminator equal to 12. if *input == 4 && (*input.add(ACCOUNT1_DATA_LEN).cast::() == Account::LEN as u64) - && (*input.add(ACCOUNT2_HEADER_OFFSET) == 255) + && (*input.add(ACCOUNT2_HEADER_OFFSET) == NON_DUP_MARKER) && (*input.add(ACCOUNT2_DATA_LEN).cast::() == Mint::LEN as u64) - && (*input.add(IX12_ACCOUNT3_HEADER_OFFSET) == 255) + && (*input.add(IX12_ACCOUNT3_HEADER_OFFSET) == NON_DUP_MARKER) && (*input.add(IX12_ACCOUNT3_DATA_LEN).cast::() == Account::LEN as u64) - && (*input.add(IX12_ACCOUNT4_HEADER_OFFSET) == 255) + && (*input.add(IX12_ACCOUNT4_HEADER_OFFSET) == NON_DUP_MARKER) { // The `authority` account can have variable data length. let account_4_data_len_aligned = @@ -170,9 +170,9 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { // and discriminator equal to 3. else if *input == 3 && (*input.add(ACCOUNT1_DATA_LEN).cast::() == Account::LEN as u64) - && (*input.add(ACCOUNT2_HEADER_OFFSET) == 255) + && (*input.add(ACCOUNT2_HEADER_OFFSET) == NON_DUP_MARKER) && (*input.add(ACCOUNT2_DATA_LEN).cast::() == Account::LEN as u64) - && (*input.add(IX3_ACCOUNT3_HEADER_OFFSET) == 255) + && (*input.add(IX3_ACCOUNT3_HEADER_OFFSET) == NON_DUP_MARKER) { // The `authority` account can have variable data length. let account_3_data_len_aligned =