solana_program_entrypoint/
lib.rs

1//! The Rust-based BPF program entrypoint supported by the latest BPF loader.
2//!
3//! For more information see the [`bpf_loader`] module.
4//!
5//! [`bpf_loader`]: crate::bpf_loader
6
7extern crate alloc;
8use {
9    alloc::vec::Vec,
10    solana_account_info::AccountInfo,
11    solana_pubkey::Pubkey,
12    std::{
13        alloc::Layout,
14        cell::RefCell,
15        mem::{size_of, MaybeUninit},
16        ptr::null_mut,
17        rc::Rc,
18        slice::{from_raw_parts, from_raw_parts_mut},
19    },
20};
21// need to re-export msg for custom_heap_default macro, `AccountInfo` and `Pubkey` for
22// entrypoint_no_alloc macro
23pub use {
24    solana_account_info::AccountInfo as __AccountInfo,
25    solana_account_info::MAX_PERMITTED_DATA_INCREASE, solana_msg::msg as __msg,
26    solana_program_error::ProgramResult, solana_pubkey::Pubkey as __Pubkey,
27};
28
29/// User implemented function to process an instruction
30///
31/// program_id: Program ID of the currently executing program accounts: Accounts
32/// passed as part of the instruction instruction_data: Instruction data
33pub type ProcessInstruction =
34    fn(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult;
35
36/// Programs indicate success with a return value of 0
37pub const SUCCESS: u64 = 0;
38
39/// Start address of the memory region used for program heap.
40pub const HEAP_START_ADDRESS: u64 = 0x300000000;
41/// Length of the heap memory region used for program heap.
42pub const HEAP_LENGTH: usize = 32 * 1024;
43
44/// Value used to indicate that a serialized account is not a duplicate
45pub const NON_DUP_MARKER: u8 = u8::MAX;
46
47/// Declare the program entrypoint and set up global handlers.
48///
49/// This macro emits the common boilerplate necessary to begin program
50/// execution, calling a provided function to process the program instruction
51/// supplied by the runtime, and reporting its result to the runtime.
52///
53/// It also sets up a [global allocator] and [panic handler], using the
54/// [`custom_heap_default`] and [`custom_panic_default`] macros.
55///
56/// [`custom_heap_default`]: crate::custom_heap_default
57/// [`custom_panic_default`]: crate::custom_panic_default
58///
59/// [global allocator]: https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html
60/// [panic handler]: https://doc.rust-lang.org/nomicon/panic-handler.html
61///
62/// The argument is the name of a function with this type signature:
63///
64/// ```ignore
65/// fn process_instruction(
66///     program_id: &Pubkey,      // Public key of the account the program was loaded into
67///     accounts: &[AccountInfo], // All accounts required to process the instruction
68///     instruction_data: &[u8],  // Serialized instruction-specific data
69/// ) -> ProgramResult;
70/// ```
71///
72/// # Cargo features
73///
74/// This macro emits symbols and definitions that may only be defined once
75/// globally. As such, if linked to other Rust crates it will cause compiler
76/// errors. To avoid this, it is common for Solana programs to define an
77/// optional [Cargo feature] called `no-entrypoint`, and use it to conditionally
78/// disable the `entrypoint` macro invocation, as well as the
79/// `process_instruction` function. See a typical pattern for this in the
80/// example below.
81///
82/// [Cargo feature]: https://doc.rust-lang.org/cargo/reference/features.html
83///
84/// The code emitted by this macro can be customized by adding cargo features
85/// _to your own crate_ (the one that calls this macro) and enabling them:
86///
87/// - If the `custom-heap` feature is defined then the macro will not set up the
88///   global allocator, allowing `entrypoint` to be used with your own
89///   allocator. See documentation for the [`custom_heap_default`] macro for
90///   details of customizing the global allocator.
91///
92/// - If the `custom-panic` feature is defined then the macro will not define a
93///   panic handler, allowing `entrypoint` to be used with your own panic
94///   handler. See documentation for the [`custom_panic_default`] macro for
95///   details of customizing the panic handler.
96///
97/// # Examples
98///
99/// Defining an entrypoint and making it conditional on the `no-entrypoint`
100/// feature. Although the `entrypoint` module is written inline in this example,
101/// it is common to put it into its own file.
102///
103/// ```no_run
104/// #[cfg(not(feature = "no-entrypoint"))]
105/// pub mod entrypoint {
106///
107///     use solana_account_info::AccountInfo;
108///     use solana_program_entrypoint::entrypoint;
109///     use solana_program_entrypoint::ProgramResult;
110///     use solana_msg::msg;
111///     use solana_pubkey::Pubkey;
112///
113///     entrypoint!(process_instruction);
114///
115///     pub fn process_instruction(
116///         program_id: &Pubkey,
117///         accounts: &[AccountInfo],
118///         instruction_data: &[u8],
119///     ) -> ProgramResult {
120///         msg!("Hello world");
121///
122///         Ok(())
123///     }
124///
125/// }
126/// ```
127#[macro_export]
128macro_rules! entrypoint {
129    ($process_instruction:ident) => {
130        /// # Safety
131        #[no_mangle]
132        pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
133            let (program_id, accounts, instruction_data) = unsafe { $crate::deserialize(input) };
134            match $process_instruction(program_id, &accounts, instruction_data) {
135                Ok(()) => $crate::SUCCESS,
136                Err(error) => error.into(),
137            }
138        }
139        $crate::custom_heap_default!();
140        $crate::custom_panic_default!();
141    };
142}
143
144/// Declare the program entrypoint and set up global handlers.
145///
146/// This is similar to the `entrypoint!` macro, except that it does not perform
147/// any dynamic allocations, and instead writes the input accounts into a pre-
148/// allocated array.
149///
150/// This version reduces compute unit usage by 20-30 compute units per unique
151/// account in the instruction. It may become the default option in a future
152/// release.
153///
154/// For more information about how the program entrypoint behaves and what it
155/// does, please see the documentation for [`entrypoint!`].
156///
157/// NOTE: This entrypoint has a hard-coded limit of 64 input accounts.
158#[macro_export]
159macro_rules! entrypoint_no_alloc {
160    ($process_instruction:ident) => {
161        /// # Safety
162        #[no_mangle]
163        pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
164            use std::mem::MaybeUninit;
165            // Clippy complains about this because a `const` with interior
166            // mutability `RefCell` should use `static` instead to make it
167            // clear that it can change.
168            // In our case, however, we want to create an array of `AccountInfo`s,
169            // and the only way to do it is through a `const` expression, and
170            // we don't expect to mutate the internals of this `const` type.
171            #[allow(clippy::declare_interior_mutable_const)]
172            const UNINIT_ACCOUNT_INFO: MaybeUninit<$crate::__AccountInfo> =
173                MaybeUninit::<$crate::__AccountInfo>::uninit();
174            const MAX_ACCOUNT_INFOS: usize = 64;
175            let mut accounts = [UNINIT_ACCOUNT_INFO; MAX_ACCOUNT_INFOS];
176            let (program_id, num_accounts, instruction_data) =
177                unsafe { $crate::deserialize_into(input, &mut accounts) };
178            // Use `slice_assume_init_ref` once it's stabilized
179            let accounts = &*(&accounts[..num_accounts]
180                as *const [MaybeUninit<$crate::__AccountInfo<'_>>]
181                as *const [$crate::__AccountInfo<'_>]);
182
183            #[inline(never)]
184            fn call_program(
185                program_id: &$crate::__Pubkey,
186                accounts: &[$crate::__AccountInfo],
187                data: &[u8],
188            ) -> u64 {
189                match $process_instruction(program_id, accounts, data) {
190                    Ok(()) => $crate::SUCCESS,
191                    Err(error) => error.into(),
192                }
193            }
194
195            call_program(&program_id, accounts, &instruction_data)
196        }
197        $crate::custom_heap_default!();
198        $crate::custom_panic_default!();
199    };
200}
201
202/// Define the default global allocator.
203///
204/// The default global allocator is enabled only if the calling crate has not
205/// disabled it using [Cargo features] as described below. It is only defined
206/// for [BPF] targets.
207///
208/// [Cargo features]: https://doc.rust-lang.org/cargo/reference/features.html
209/// [BPF]: https://solana.com/docs/programs/faq#berkeley-packet-filter-bpf
210///
211/// # Cargo features
212///
213/// A crate that calls this macro can provide its own custom heap
214/// implementation, or allow others to provide their own custom heap
215/// implementation, by adding a `custom-heap` feature to its `Cargo.toml`. After
216/// enabling the feature, one may define their own [global allocator] in the
217/// standard way.
218///
219/// [global allocator]: https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html
220///
221#[macro_export]
222macro_rules! custom_heap_default {
223    () => {
224        #[cfg(all(not(feature = "custom-heap"), target_os = "solana"))]
225        #[global_allocator]
226        #[allow(deprecated)] //we get to use deprecated pub fields
227        static A: $crate::BumpAllocator = $crate::BumpAllocator {
228            start: $crate::HEAP_START_ADDRESS as usize,
229            len: $crate::HEAP_LENGTH,
230        };
231    };
232}
233
234/// Define the default global panic handler.
235///
236/// This must be used if the [`entrypoint`] macro is not used, and no other
237/// panic handler has been defined; otherwise compilation will fail with a
238/// missing `custom_panic` symbol.
239///
240/// The default global allocator is enabled only if the calling crate has not
241/// disabled it using [Cargo features] as described below. It is only defined
242/// for [BPF] targets.
243///
244/// [Cargo features]: https://doc.rust-lang.org/cargo/reference/features.html
245/// [BPF]: https://solana.com/docs/programs/faq#berkeley-packet-filter-bpf
246///
247/// # Cargo features
248///
249/// A crate that calls this macro can provide its own custom panic handler, or
250/// allow others to provide their own custom panic handler, by adding a
251/// `custom-panic` feature to its `Cargo.toml`. After enabling the feature, one
252/// may define their own panic handler.
253///
254/// A good way to reduce the final size of the program is to provide a
255/// `custom_panic` implementation that does nothing. Doing so will cut ~25kb
256/// from a noop program. That number goes down the more the programs pulls in
257/// Rust's standard library for other purposes.
258///
259/// # Defining a panic handler for Solana
260///
261/// _The mechanism for defining a Solana panic handler is different [from most
262/// Rust programs][rpanic]._
263///
264/// [rpanic]: https://doc.rust-lang.org/nomicon/panic-handler.html
265///
266/// To define a panic handler one must define a `custom_panic` function
267/// with the `#[no_mangle]` attribute, as below:
268///
269/// ```ignore
270/// #[cfg(all(feature = "custom-panic", target_os = "solana"))]
271/// #[no_mangle]
272/// fn custom_panic(info: &core::panic::PanicInfo<'_>) {
273///     $crate::msg!("{}", info);
274/// }
275/// ```
276///
277/// The above is how Solana defines the default panic handler.
278#[macro_export]
279macro_rules! custom_panic_default {
280    () => {
281        #[cfg(all(not(feature = "custom-panic"), target_os = "solana"))]
282        #[no_mangle]
283        fn custom_panic(info: &core::panic::PanicInfo<'_>) {
284            // Full panic reporting
285            $crate::__msg!("{}", info);
286        }
287    };
288}
289
290/// The bump allocator used as the default rust heap when running programs.
291pub struct BumpAllocator {
292    #[deprecated(
293        since = "2.2.2",
294        note = "This field should not be accessed directly. It will become private in future versions"
295    )]
296    pub start: usize,
297    #[deprecated(
298        since = "2.2.2",
299        note = "This field should not be accessed directly. It will become private in future versions"
300    )]
301    pub len: usize,
302}
303
304impl BumpAllocator {
305    /// Creates the allocator tied to a provided slice.
306    /// This will not initialize the provided memory, except for the first
307    /// bytes where the pointer is stored.
308    ///
309    /// # Safety
310    /// As long as BumpAllocator or any of its allocations are alive,
311    /// writing into or deallocating the arena will cause UB.
312    ///
313    /// Integer arithmetic in this global allocator implementation is safe when
314    /// operating on the prescribed `HEAP_START_ADDRESS` and `HEAP_LENGTH`. Any
315    /// other use may overflow and is thus unsupported and at one's own risk.
316    #[inline]
317    #[allow(clippy::arithmetic_side_effects)]
318    pub unsafe fn new(arena: &mut [u8]) -> Self {
319        debug_assert!(
320            arena.len() > size_of::<usize>(),
321            "Arena should be larger than usize"
322        );
323
324        // create a pointer to the start of the arena
325        // that will hold an address of the byte following free space
326        let pos_ptr = arena.as_mut_ptr() as *mut usize;
327        // initialize the data there
328        *pos_ptr = pos_ptr as usize + arena.len();
329
330        #[allow(deprecated)] //we get to use deprecated pub fields
331        Self {
332            start: pos_ptr as usize,
333            len: arena.len(),
334        }
335    }
336}
337
338/// Integer arithmetic in this global allocator implementation is safe when
339/// operating on the prescribed `HEAP_START_ADDRESS` and `HEAP_LENGTH`. Any
340/// other use may overflow and is thus unsupported and at one's own risk.
341#[allow(clippy::arithmetic_side_effects)]
342unsafe impl std::alloc::GlobalAlloc for BumpAllocator {
343    #[inline]
344    #[allow(deprecated)] //we get to use deprecated pub fields
345    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
346        let pos_ptr = self.start as *mut usize;
347        let mut pos = *pos_ptr;
348        if pos == 0 {
349            // First time, set starting position
350            pos = self.start + self.len;
351        }
352        pos = pos.saturating_sub(layout.size());
353        pos &= !(layout.align().wrapping_sub(1));
354        if pos < self.start + size_of::<*mut u8>() {
355            return null_mut();
356        }
357        *pos_ptr = pos;
358        pos as *mut u8
359    }
360    #[inline]
361    unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
362        // I'm a bump allocator, I don't free
363    }
364}
365
366/// `assert_eq(std::mem::align_of::<u128>(), 8)` is true for BPF but not for some host machines
367pub const BPF_ALIGN_OF_U128: usize = 8;
368
369#[allow(clippy::arithmetic_side_effects)]
370#[inline(always)] // this reduces CU usage
371unsafe fn deserialize_instruction_data<'a>(input: *mut u8, mut offset: usize) -> (&'a [u8], usize) {
372    #[allow(clippy::cast_ptr_alignment)]
373    let instruction_data_len = *(input.add(offset) as *const u64) as usize;
374    offset += size_of::<u64>();
375
376    let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) };
377    offset += instruction_data_len;
378
379    (instruction_data, offset)
380}
381
382#[allow(clippy::arithmetic_side_effects)]
383#[inline(always)] // this reduces CU usage by half!
384unsafe fn deserialize_account_info<'a>(
385    input: *mut u8,
386    mut offset: usize,
387) -> (AccountInfo<'a>, usize) {
388    #[allow(clippy::cast_ptr_alignment)]
389    let is_signer = *(input.add(offset) as *const u8) != 0;
390    offset += size_of::<u8>();
391
392    #[allow(clippy::cast_ptr_alignment)]
393    let is_writable = *(input.add(offset) as *const u8) != 0;
394    offset += size_of::<u8>();
395
396    #[allow(clippy::cast_ptr_alignment)]
397    let executable = *(input.add(offset) as *const u8) != 0;
398    offset += size_of::<u8>();
399
400    // The original data length is stored here because these 4 bytes were
401    // originally only used for padding and served as a good location to
402    // track the original size of the account data in a compatible way.
403    let original_data_len_offset = offset;
404    offset += size_of::<u32>();
405
406    let key: &Pubkey = &*(input.add(offset) as *const Pubkey);
407    offset += size_of::<Pubkey>();
408
409    let owner: &Pubkey = &*(input.add(offset) as *const Pubkey);
410    offset += size_of::<Pubkey>();
411
412    #[allow(clippy::cast_ptr_alignment)]
413    let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64)));
414    offset += size_of::<u64>();
415
416    #[allow(clippy::cast_ptr_alignment)]
417    let data_len = *(input.add(offset) as *const u64) as usize;
418    offset += size_of::<u64>();
419
420    // Store the original data length for detecting invalid reallocations and
421    // requires that MAX_PERMITTED_DATA_LENGTH fits in a u32
422    *(input.add(original_data_len_offset) as *mut u32) = data_len as u32;
423
424    let data = Rc::new(RefCell::new({
425        from_raw_parts_mut(input.add(offset), data_len)
426    }));
427    offset += data_len + MAX_PERMITTED_DATA_INCREASE;
428    offset += (offset as *const u8).align_offset(BPF_ALIGN_OF_U128); // padding
429
430    #[allow(clippy::cast_ptr_alignment)]
431    let rent_epoch = *(input.add(offset) as *const u64);
432    offset += size_of::<u64>();
433
434    (
435        AccountInfo {
436            key,
437            is_signer,
438            is_writable,
439            lamports,
440            data,
441            owner,
442            executable,
443            rent_epoch,
444        },
445        offset,
446    )
447}
448
449/// Deserialize the input arguments
450///
451/// The integer arithmetic in this method is safe when called on a buffer that was
452/// serialized by runtime. Use with buffers serialized otherwise is unsupported and
453/// done at one's own risk.
454///
455/// # Safety
456#[allow(clippy::arithmetic_side_effects)]
457pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec<AccountInfo<'a>>, &'a [u8]) {
458    let mut offset: usize = 0;
459
460    // Number of accounts present
461
462    #[allow(clippy::cast_ptr_alignment)]
463    let num_accounts = *(input.add(offset) as *const u64) as usize;
464    offset += size_of::<u64>();
465
466    // Account Infos
467
468    let mut accounts = Vec::with_capacity(num_accounts);
469    for _ in 0..num_accounts {
470        let dup_info = *(input.add(offset) as *const u8);
471        offset += size_of::<u8>();
472        if dup_info == NON_DUP_MARKER {
473            let (account_info, new_offset) = deserialize_account_info(input, offset);
474            offset = new_offset;
475            accounts.push(account_info);
476        } else {
477            offset += 7; // padding
478
479            // Duplicate account, clone the original
480            accounts.push(accounts[dup_info as usize].clone());
481        }
482    }
483
484    // Instruction data
485
486    let (instruction_data, new_offset) = deserialize_instruction_data(input, offset);
487    offset = new_offset;
488
489    // Program Id
490
491    let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey);
492
493    (program_id, accounts, instruction_data)
494}
495
496/// Deserialize the input arguments
497///
498/// Differs from `deserialize` by writing the account infos into an uninitialized
499/// slice, which provides better performance, roughly 30 CUs per unique account
500/// provided to the instruction.
501///
502/// Panics if the input slice is not large enough.
503///
504/// The integer arithmetic in this method is safe when called on a buffer that was
505/// serialized by runtime. Use with buffers serialized otherwise is unsupported and
506/// done at one's own risk.
507///
508/// # Safety
509#[allow(clippy::arithmetic_side_effects)]
510pub unsafe fn deserialize_into<'a>(
511    input: *mut u8,
512    accounts: &mut [MaybeUninit<AccountInfo<'a>>],
513) -> (&'a Pubkey, usize, &'a [u8]) {
514    let mut offset: usize = 0;
515
516    // Number of accounts present
517
518    #[allow(clippy::cast_ptr_alignment)]
519    let num_accounts = *(input.add(offset) as *const u64) as usize;
520    offset += size_of::<u64>();
521
522    if num_accounts > accounts.len() {
523        panic!(
524            "{} accounts provided, but only {} are supported",
525            num_accounts,
526            accounts.len()
527        );
528    }
529
530    // Account Infos
531
532    for i in 0..num_accounts {
533        let dup_info = *(input.add(offset) as *const u8);
534        offset += size_of::<u8>();
535        if dup_info == NON_DUP_MARKER {
536            let (account_info, new_offset) = deserialize_account_info(input, offset);
537            offset = new_offset;
538            accounts[i].write(account_info);
539        } else {
540            offset += 7; // padding
541
542            // Duplicate account, clone the original
543            accounts[i].write(accounts[dup_info as usize].assume_init_ref().clone());
544        }
545    }
546
547    // Instruction data
548
549    let (instruction_data, new_offset) = deserialize_instruction_data(input, offset);
550    offset = new_offset;
551
552    // Program Id
553
554    let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey);
555
556    (program_id, num_accounts, instruction_data)
557}
558
559#[cfg(test)]
560mod test {
561    use {super::*, std::alloc::GlobalAlloc};
562
563    #[test]
564    fn test_bump_allocator() {
565        // alloc the entire
566        {
567            let mut heap = [0u8; 128];
568            let allocator = unsafe { BumpAllocator::new(&mut heap) };
569            for i in 0..128 - size_of::<*mut u8>() {
570                let ptr = unsafe {
571                    allocator.alloc(Layout::from_size_align(1, size_of::<u8>()).unwrap())
572                };
573                assert_eq!(ptr as usize, heap.as_ptr() as usize + heap.len() - 1 - i);
574            }
575            assert_eq!(null_mut(), unsafe {
576                allocator.alloc(Layout::from_size_align(1, 1).unwrap())
577            });
578        }
579        // check alignment
580        {
581            let mut heap = [0u8; 128];
582            let allocator = unsafe { BumpAllocator::new(&mut heap) };
583            let ptr =
584                unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u8>()).unwrap()) };
585            assert_eq!(0, ptr.align_offset(size_of::<u8>()));
586            let ptr =
587                unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u16>()).unwrap()) };
588            assert_eq!(0, ptr.align_offset(size_of::<u16>()));
589            let ptr =
590                unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u32>()).unwrap()) };
591            assert_eq!(0, ptr.align_offset(size_of::<u32>()));
592            let ptr =
593                unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u64>()).unwrap()) };
594            assert_eq!(0, ptr.align_offset(size_of::<u64>()));
595            let ptr =
596                unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u128>()).unwrap()) };
597            assert_eq!(0, ptr.align_offset(size_of::<u128>()));
598            let ptr = unsafe { allocator.alloc(Layout::from_size_align(1, 64).unwrap()) };
599            assert_eq!(0, ptr.align_offset(64));
600        }
601        // alloc entire block (minus the pos ptr)
602        {
603            let mut heap = [0u8; 128];
604            let allocator = unsafe { BumpAllocator::new(&mut heap) };
605            let ptr = unsafe {
606                allocator.alloc(
607                    Layout::from_size_align(heap.len() - size_of::<usize>(), size_of::<u8>())
608                        .unwrap(),
609                )
610            };
611            assert_ne!(ptr, null_mut());
612            assert_eq!(0, ptr.align_offset(size_of::<u64>()));
613        }
614    }
615}