#entrypoint #solana #account #programs #pubkey #instructions #layout

solana-nostd-entrypoint

A zerocopy, no_alloc/no_std entrypoint for solana programs

9 releases (4 breaking)

new 0.6.2 Dec 17, 2024
0.6.0 Nov 7, 2024
0.3.0 Apr 9, 2024
0.1.0 Feb 24, 2024

#259 in Magic Beans

Download history 70/week @ 2024-08-28 34/week @ 2024-09-11 16/week @ 2024-09-18 21/week @ 2024-09-25 51/week @ 2024-10-02 582/week @ 2024-10-09 99/week @ 2024-10-16 191/week @ 2024-10-23 92/week @ 2024-10-30 182/week @ 2024-11-06 51/week @ 2024-11-13 110/week @ 2024-11-20 64/week @ 2024-11-27 99/week @ 2024-12-04 111/week @ 2024-12-11

405 downloads per month

MIT/Apache

49KB
972 lines

solana-nostd-entrypoint

The entrypoint function in solana_program is grossly inefficient. With an empty process_instruction function, it uses upwards of 8000 bpf cus when the program receives 32 non-duplicate accounts. We use a new NoStdAccountInfo struct whose layout is consistent with that in the vm flat buffer input: *mut u8; unlike the usual entrypoint, it reads everything with no copies and no allocations.

This crate also includes a simple reference program that invokes another program. See lib.rs:

#[cfg(feature = "example-program")]
pub mod entrypoint {
    use super::*;
    use solana_program::{
        entrypoint::ProgramResult, log, program_error::ProgramError, pubkey::Pubkey, system_program,
    };

    entrypoint_nostd!(process_instruction, 32);

    pub const ID: Pubkey = solana_program::pubkey!("EWUt9PAjn26zCUALRRt56Gutaj52Bpb8ifbf7GZX3h1k");

    noalloc_allocator!();
    basic_panic_impl!();

    pub fn process_instruction(
        _program_id: &Pubkey,
        accounts: &[NoStdAccountInfo],
        _data: &[u8],
    ) -> ProgramResult {
        log::sol_log("nostd_c");

        // Unpack accounts
        let [user, config, _rem @ ..] = accounts else {
            return Err(ProgramError::NotEnoughAccountKeys);
        };

        // Transfer has discriminant 2_u32 (little endian), followed u64 lamport amount
        let mut instruction_data = [0; 12];
        instruction_data[0] = 2;
        instruction_data[4..12].copy_from_slice(&100_000_000_u64.to_le_bytes());

        // Instruction accounts are are from, to
        let instruction_accounts = [user.to_meta_c(), config.to_meta_c()];

        // Build instruction expected by sol_invoke_signed_c
        let instruction = InstructionC {
            program_id: &system_program::ID,
            accounts: instruction_accounts.as_ptr(),
            accounts_len: instruction_accounts.len() as u64,
            data: instruction_data.as_ptr(),
            data_len: instruction_data.len() as u64,
        };

        // Get infos and seeds
        let infos = [user.to_info_c(), config.to_info_c()];
        let seeds: &[&[&[u8]]] = &[];

        // Invoke system program
        #[cfg(target_os = "solana")]
        unsafe {
            solana_program::syscalls::sol_invoke_signed_c(
                &instruction as *const InstructionC as *const u8,
                infos.as_ptr() as *const u8,
                infos.len() as u64,
                seeds.as_ptr() as *const u8,
                seeds.len() as u64,
            );
        }

        // For clippy
        #[cfg(not(target_os = "solana"))]
        core::hint::black_box(&(&instruction, &infos, &seeds));

        Ok(())
    }
}

Dependencies

~16–25MB
~415K SLoC