From ebcfe32f7f6ef1103ffc8d0a502bd61ae113c554 Mon Sep 17 00:00:00 2001 From: taco-paco Date: Wed, 15 Oct 2025 14:25:38 +0300 Subject: [PATCH 1/2] fix: respect "no-entrypoint" feature --- src/entrypoint.rs | 147 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 152 +--------------------------------------------- 2 files changed, 150 insertions(+), 149 deletions(-) create mode 100644 src/entrypoint.rs diff --git a/src/entrypoint.rs b/src/entrypoint.rs new file mode 100644 index 00000000..c9a7eae5 --- /dev/null +++ b/src/entrypoint.rs @@ -0,0 +1,147 @@ +use crate::discriminator::DlpDiscriminator; +use crate::processor::process_call_handler; +use crate::{discriminator, processor}; + +use pinocchio_log::log; +use solana_program::account_info::AccountInfo; +use solana_program::entrypoint::ProgramResult; +use solana_program::program_error::ProgramError; +use solana_program::pubkey::Pubkey; +use solana_program::{entrypoint, msg}; + +entrypoint::custom_heap_default!(); +entrypoint::custom_panic_default!(); + +/// # Safety +/// +/// It's pretty close to the code generated by entrypoint!() macro, with one minor tweak to +/// support fallback branch. +#[no_mangle] +pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { + const UNINIT: core::mem::MaybeUninit = + core::mem::MaybeUninit::::uninit(); + let mut accounts = [UNINIT; { pinocchio::MAX_TX_ACCOUNTS }]; + + let (program_id, count, data) = + pinocchio::entrypoint::deserialize::<{ pinocchio::MAX_TX_ACCOUNTS }>(input, &mut accounts); + match fast_process_instruction( + program_id, + core::slice::from_raw_parts(accounts.as_ptr() as _, count), + data, + ) { + Some(Ok(())) => pinocchio::SUCCESS, + Some(Err(error)) => error.into(), + + // Fallback to the slow path that does not use pinocchio SDK. + None => slow_entrypoint(input), + } +} + +/// # Safety +/// +/// It's pretty close to the code generated by entrypoint!() macro, with one difference: the +/// function name is slow_entrypoint() as opposed to entrypoint() because this is a fallback +/// entrypoint (a slow one). +pub unsafe fn slow_entrypoint(input: *mut u8) -> u64 { + let (program_id, accounts, instruction_data) = unsafe { entrypoint::deserialize(input) }; + match slow_process_instruction(program_id, &accounts, instruction_data) { + Ok(()) => entrypoint::SUCCESS, + Err(error) => error.into(), + } +} + +pub fn fast_process_instruction( + program_id: &pinocchio::pubkey::Pubkey, + accounts: &[pinocchio::account_info::AccountInfo], + data: &[u8], +) -> Option { + if data.len() < 8 { + return Some(Err( + pinocchio::program_error::ProgramError::InvalidInstructionData, + )); + } + + let (discriminator_bytes, data) = data.split_at(8); + + let discriminator = match DlpDiscriminator::try_from(discriminator_bytes[0]) { + Ok(discriminator) => discriminator, + Err(_) => { + log!("Failed to read and parse discriminator"); + return Some(Err( + pinocchio::program_error::ProgramError::InvalidInstructionData, + )); + } + }; + + match discriminator { + discriminator::DlpDiscriminator::Delegate => Some(processor::fast::process_delegate( + program_id, accounts, data, + )), + discriminator::DlpDiscriminator::CommitState => Some( + processor::fast::process_commit_state(program_id, accounts, data), + ), + discriminator::DlpDiscriminator::CommitStateFromBuffer => Some( + processor::fast::process_commit_state_from_buffer(program_id, accounts, data), + ), + discriminator::DlpDiscriminator::Finalize => Some(processor::fast::process_finalize( + program_id, accounts, data, + )), + discriminator::DlpDiscriminator::Undelegate => Some(processor::fast::process_undelegate( + program_id, accounts, data, + )), + _ => None, + } +} + +pub fn slow_process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + data: &[u8], +) -> ProgramResult { + if data.len() < 8 { + return Err(ProgramError::InvalidInstructionData); + } + + let (tag, data) = data.split_at(8); + let ix = discriminator::DlpDiscriminator::try_from(tag[0]) + .or(Err(ProgramError::InvalidInstructionData))?; + + msg!("Processing instruction: {:?}", ix); + match ix { + discriminator::DlpDiscriminator::InitValidatorFeesVault => { + processor::process_init_validator_fees_vault(program_id, accounts, data)? + } + discriminator::DlpDiscriminator::InitProtocolFeesVault => { + processor::process_init_protocol_fees_vault(program_id, accounts, data)? + } + discriminator::DlpDiscriminator::ValidatorClaimFees => { + processor::process_validator_claim_fees(program_id, accounts, data)? + } + discriminator::DlpDiscriminator::WhitelistValidatorForProgram => { + processor::process_whitelist_validator_for_program(program_id, accounts, data)? + } + discriminator::DlpDiscriminator::TopUpEphemeralBalance => { + processor::process_top_up_ephemeral_balance(program_id, accounts, data)? + } + discriminator::DlpDiscriminator::DelegateEphemeralBalance => { + processor::process_delegate_ephemeral_balance(program_id, accounts, data)? + } + discriminator::DlpDiscriminator::CloseEphemeralBalance => { + processor::process_close_ephemeral_balance(program_id, accounts, data)? + } + discriminator::DlpDiscriminator::ProtocolClaimFees => { + processor::process_protocol_claim_fees(program_id, accounts, data)? + } + discriminator::DlpDiscriminator::CloseValidatorFeesVault => { + processor::process_close_validator_fees_vault(program_id, accounts, data)? + } + discriminator::DlpDiscriminator::CallHandler => { + process_call_handler(program_id, accounts, data)? + } + _ => { + log!("PANIC: Instruction must be processed by fast_process_instruction"); + return Err(ProgramError::InvalidInstructionData); + } + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 15a25c76..3406eb70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,6 @@ #![allow(unexpected_cfgs)] // silence clippy for target_os solana and other solana program custom features -use crate::processor::process_call_handler; -use pinocchio_log::log; -use solana_program::{ - account_info::AccountInfo, - declare_id, - entrypoint::{self, ProgramResult}, - msg, - program_error::ProgramError, - pubkey::Pubkey, -}; - -use discriminator::DlpDiscriminator; +use solana_program::declare_id; pub mod args; pub mod consts; @@ -24,6 +13,8 @@ pub mod state; #[cfg(feature = "log-cost")] mod cu; +#[cfg(not(feature = "no-entrypoint"))] +mod entrypoint; declare_id!("DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh"); @@ -31,9 +22,6 @@ pub mod fast { pinocchio_pubkey::declare_id!("DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh"); } -entrypoint::custom_heap_default!(); -entrypoint::custom_panic_default!(); - #[cfg(all(not(feature = "no-entrypoint"), feature = "solana-security-txt"))] solana_security_txt::security_txt! { name: "MagicBlock Delegation Program", @@ -43,137 +31,3 @@ solana_security_txt::security_txt! { preferred_languages: "en", source_code: "https://github.com/magicblock-labs/delegation-program" } - -/// # Safety -/// -/// It's pretty close to the code generated by entrypoint!() macro, with one minor tweak to -/// support fallback branch. -#[no_mangle] -pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { - const UNINIT: core::mem::MaybeUninit = - core::mem::MaybeUninit::::uninit(); - let mut accounts = [UNINIT; { pinocchio::MAX_TX_ACCOUNTS }]; - - let (program_id, count, data) = - pinocchio::entrypoint::deserialize::<{ pinocchio::MAX_TX_ACCOUNTS }>(input, &mut accounts); - match fast_process_instruction( - program_id, - core::slice::from_raw_parts(accounts.as_ptr() as _, count), - data, - ) { - Some(Ok(())) => pinocchio::SUCCESS, - Some(Err(error)) => error.into(), - - // Fallback to the slow path that does not use pinocchio SDK. - None => slow_entrypoint(input), - } -} - -/// # Safety -/// -/// It's pretty close to the code generated by entrypoint!() macro, with one difference: the -/// function name is slow_entrypoint() as opposed to entrypoint() because this is a fallback -/// entrypoint (a slow one). -pub unsafe fn slow_entrypoint(input: *mut u8) -> u64 { - let (program_id, accounts, instruction_data) = unsafe { entrypoint::deserialize(input) }; - match slow_process_instruction(program_id, &accounts, instruction_data) { - Ok(()) => entrypoint::SUCCESS, - Err(error) => error.into(), - } -} - -pub fn fast_process_instruction( - program_id: &pinocchio::pubkey::Pubkey, - accounts: &[pinocchio::account_info::AccountInfo], - data: &[u8], -) -> Option { - if data.len() < 8 { - return Some(Err( - pinocchio::program_error::ProgramError::InvalidInstructionData, - )); - } - - let (discriminator_bytes, data) = data.split_at(8); - - let discriminator = match DlpDiscriminator::try_from(discriminator_bytes[0]) { - Ok(discriminator) => discriminator, - Err(_) => { - log!("Failed to read and parse discriminator"); - return Some(Err( - pinocchio::program_error::ProgramError::InvalidInstructionData, - )); - } - }; - - match discriminator { - discriminator::DlpDiscriminator::Delegate => Some(processor::fast::process_delegate( - program_id, accounts, data, - )), - discriminator::DlpDiscriminator::CommitState => Some( - processor::fast::process_commit_state(program_id, accounts, data), - ), - discriminator::DlpDiscriminator::CommitStateFromBuffer => Some( - processor::fast::process_commit_state_from_buffer(program_id, accounts, data), - ), - discriminator::DlpDiscriminator::Finalize => Some(processor::fast::process_finalize( - program_id, accounts, data, - )), - discriminator::DlpDiscriminator::Undelegate => Some(processor::fast::process_undelegate( - program_id, accounts, data, - )), - _ => None, - } -} - -pub fn slow_process_instruction( - program_id: &Pubkey, - accounts: &[AccountInfo], - data: &[u8], -) -> ProgramResult { - if data.len() < 8 { - return Err(ProgramError::InvalidInstructionData); - } - - let (tag, data) = data.split_at(8); - let ix = discriminator::DlpDiscriminator::try_from(tag[0]) - .or(Err(ProgramError::InvalidInstructionData))?; - - msg!("Processing instruction: {:?}", ix); - match ix { - discriminator::DlpDiscriminator::InitValidatorFeesVault => { - processor::process_init_validator_fees_vault(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::InitProtocolFeesVault => { - processor::process_init_protocol_fees_vault(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::ValidatorClaimFees => { - processor::process_validator_claim_fees(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::WhitelistValidatorForProgram => { - processor::process_whitelist_validator_for_program(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::TopUpEphemeralBalance => { - processor::process_top_up_ephemeral_balance(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::DelegateEphemeralBalance => { - processor::process_delegate_ephemeral_balance(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::CloseEphemeralBalance => { - processor::process_close_ephemeral_balance(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::ProtocolClaimFees => { - processor::process_protocol_claim_fees(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::CloseValidatorFeesVault => { - processor::process_close_validator_fees_vault(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::CallHandler => { - process_call_handler(program_id, accounts, data)? - } - _ => { - log!("PANIC: Instruction must be processed by fast_process_instruction"); - return Err(ProgramError::InvalidInstructionData); - } - } - Ok(()) -} From b0c7d4018ab8b6a8630950682e5aec61a6dfeebd Mon Sep 17 00:00:00 2001 From: taco-paco Date: Wed, 15 Oct 2025 15:24:27 +0300 Subject: [PATCH 2/2] refactor: processor_* shall be exposed --- src/entrypoint.rs | 107 +--------------------------------------------- src/lib.rs | 103 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 106 deletions(-) diff --git a/src/entrypoint.rs b/src/entrypoint.rs index c9a7eae5..180f10c4 100644 --- a/src/entrypoint.rs +++ b/src/entrypoint.rs @@ -1,13 +1,6 @@ -use crate::discriminator::DlpDiscriminator; -use crate::processor::process_call_handler; -use crate::{discriminator, processor}; +use crate::{fast_process_instruction, slow_process_instruction}; -use pinocchio_log::log; -use solana_program::account_info::AccountInfo; -use solana_program::entrypoint::ProgramResult; -use solana_program::program_error::ProgramError; -use solana_program::pubkey::Pubkey; -use solana_program::{entrypoint, msg}; +use solana_program::entrypoint; entrypoint::custom_heap_default!(); entrypoint::custom_panic_default!(); @@ -49,99 +42,3 @@ pub unsafe fn slow_entrypoint(input: *mut u8) -> u64 { Err(error) => error.into(), } } - -pub fn fast_process_instruction( - program_id: &pinocchio::pubkey::Pubkey, - accounts: &[pinocchio::account_info::AccountInfo], - data: &[u8], -) -> Option { - if data.len() < 8 { - return Some(Err( - pinocchio::program_error::ProgramError::InvalidInstructionData, - )); - } - - let (discriminator_bytes, data) = data.split_at(8); - - let discriminator = match DlpDiscriminator::try_from(discriminator_bytes[0]) { - Ok(discriminator) => discriminator, - Err(_) => { - log!("Failed to read and parse discriminator"); - return Some(Err( - pinocchio::program_error::ProgramError::InvalidInstructionData, - )); - } - }; - - match discriminator { - discriminator::DlpDiscriminator::Delegate => Some(processor::fast::process_delegate( - program_id, accounts, data, - )), - discriminator::DlpDiscriminator::CommitState => Some( - processor::fast::process_commit_state(program_id, accounts, data), - ), - discriminator::DlpDiscriminator::CommitStateFromBuffer => Some( - processor::fast::process_commit_state_from_buffer(program_id, accounts, data), - ), - discriminator::DlpDiscriminator::Finalize => Some(processor::fast::process_finalize( - program_id, accounts, data, - )), - discriminator::DlpDiscriminator::Undelegate => Some(processor::fast::process_undelegate( - program_id, accounts, data, - )), - _ => None, - } -} - -pub fn slow_process_instruction( - program_id: &Pubkey, - accounts: &[AccountInfo], - data: &[u8], -) -> ProgramResult { - if data.len() < 8 { - return Err(ProgramError::InvalidInstructionData); - } - - let (tag, data) = data.split_at(8); - let ix = discriminator::DlpDiscriminator::try_from(tag[0]) - .or(Err(ProgramError::InvalidInstructionData))?; - - msg!("Processing instruction: {:?}", ix); - match ix { - discriminator::DlpDiscriminator::InitValidatorFeesVault => { - processor::process_init_validator_fees_vault(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::InitProtocolFeesVault => { - processor::process_init_protocol_fees_vault(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::ValidatorClaimFees => { - processor::process_validator_claim_fees(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::WhitelistValidatorForProgram => { - processor::process_whitelist_validator_for_program(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::TopUpEphemeralBalance => { - processor::process_top_up_ephemeral_balance(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::DelegateEphemeralBalance => { - processor::process_delegate_ephemeral_balance(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::CloseEphemeralBalance => { - processor::process_close_ephemeral_balance(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::ProtocolClaimFees => { - processor::process_protocol_claim_fees(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::CloseValidatorFeesVault => { - processor::process_close_validator_fees_vault(program_id, accounts, data)? - } - discriminator::DlpDiscriminator::CallHandler => { - process_call_handler(program_id, accounts, data)? - } - _ => { - log!("PANIC: Instruction must be processed by fast_process_instruction"); - return Err(ProgramError::InvalidInstructionData); - } - } - Ok(()) -} diff --git a/src/lib.rs b/src/lib.rs index 3406eb70..b3cf4396 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,12 @@ #![allow(unexpected_cfgs)] // silence clippy for target_os solana and other solana program custom features -use solana_program::declare_id; +use crate::discriminator::DlpDiscriminator; +use pinocchio_log::log; +use solana_program::account_info::AccountInfo; +use solana_program::entrypoint::ProgramResult; +use solana_program::program_error::ProgramError; +use solana_program::pubkey::Pubkey; +use solana_program::{declare_id, msg}; pub mod args; pub mod consts; @@ -31,3 +37,98 @@ solana_security_txt::security_txt! { preferred_languages: "en", source_code: "https://github.com/magicblock-labs/delegation-program" } + +pub fn fast_process_instruction( + program_id: &pinocchio::pubkey::Pubkey, + accounts: &[pinocchio::account_info::AccountInfo], + data: &[u8], +) -> Option { + if data.len() < 8 { + return Some(Err( + pinocchio::program_error::ProgramError::InvalidInstructionData, + )); + } + + let (discriminator_bytes, data) = data.split_at(8); + + let discriminator = match DlpDiscriminator::try_from(discriminator_bytes[0]) { + Ok(discriminator) => discriminator, + Err(_) => { + log!("Failed to read and parse discriminator"); + return Some(Err( + pinocchio::program_error::ProgramError::InvalidInstructionData, + )); + } + }; + + match discriminator { + DlpDiscriminator::Delegate => Some(processor::fast::process_delegate( + program_id, accounts, data, + )), + DlpDiscriminator::CommitState => Some(processor::fast::process_commit_state( + program_id, accounts, data, + )), + DlpDiscriminator::CommitStateFromBuffer => Some( + processor::fast::process_commit_state_from_buffer(program_id, accounts, data), + ), + DlpDiscriminator::Finalize => Some(processor::fast::process_finalize( + program_id, accounts, data, + )), + DlpDiscriminator::Undelegate => Some(processor::fast::process_undelegate( + program_id, accounts, data, + )), + _ => None, + } +} + +pub fn slow_process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + data: &[u8], +) -> ProgramResult { + if data.len() < 8 { + return Err(ProgramError::InvalidInstructionData); + } + + let (tag, data) = data.split_at(8); + let ix = DlpDiscriminator::try_from(tag[0]).or(Err(ProgramError::InvalidInstructionData))?; + + msg!("Processing instruction: {:?}", ix); + match ix { + DlpDiscriminator::InitValidatorFeesVault => { + processor::process_init_validator_fees_vault(program_id, accounts, data)? + } + DlpDiscriminator::InitProtocolFeesVault => { + processor::process_init_protocol_fees_vault(program_id, accounts, data)? + } + DlpDiscriminator::ValidatorClaimFees => { + processor::process_validator_claim_fees(program_id, accounts, data)? + } + DlpDiscriminator::WhitelistValidatorForProgram => { + processor::process_whitelist_validator_for_program(program_id, accounts, data)? + } + DlpDiscriminator::TopUpEphemeralBalance => { + processor::process_top_up_ephemeral_balance(program_id, accounts, data)? + } + DlpDiscriminator::DelegateEphemeralBalance => { + processor::process_delegate_ephemeral_balance(program_id, accounts, data)? + } + DlpDiscriminator::CloseEphemeralBalance => { + processor::process_close_ephemeral_balance(program_id, accounts, data)? + } + DlpDiscriminator::ProtocolClaimFees => { + processor::process_protocol_claim_fees(program_id, accounts, data)? + } + DlpDiscriminator::CloseValidatorFeesVault => { + processor::process_close_validator_fees_vault(program_id, accounts, data)? + } + DlpDiscriminator::CallHandler => { + processor::process_call_handler(program_id, accounts, data)? + } + _ => { + log!("PANIC: Instruction must be processed by fast_process_instruction"); + return Err(ProgramError::InvalidInstructionData); + } + } + Ok(()) +}