spl_shared_memory/
lib.rs

1#![allow(clippy::integer_arithmetic)]
2#![deny(missing_docs)]
3//! Shared memory program for the Solana blockchain.
4//
5// Useful for returning data from cross-program invoked programs to the invoker.
6//
7// This program is highly optimized for its particular use case and does not
8// implement the typical `process_instruction` entrypoint.
9
10extern crate solana_program;
11use arrayref::{array_refs, mut_array_refs};
12use solana_program::{
13    declare_id, entrypoint::MAX_PERMITTED_DATA_INCREASE, entrypoint::SUCCESS,
14    program_error::ProgramError, pubkey::Pubkey,
15};
16use std::{
17    mem::{align_of, size_of},
18    ptr::read,
19    slice::{from_raw_parts, from_raw_parts_mut},
20};
21
22declare_id!("shmFc7FxFPiLQHchdHLMLWAS8yjroAP56dpToM3sHz1");
23
24/// A more efficient `copy_from_slice` implementation.
25fn fast_copy(mut src: &[u8], mut dst: &mut [u8]) {
26    while src.len() >= 8 {
27        #[allow(clippy::ptr_offset_with_cast)]
28        let (src_word, src_rem) = array_refs![src, 8; ..;];
29        #[allow(clippy::ptr_offset_with_cast)]
30        let (dst_word, dst_rem) = mut_array_refs![dst, 8; ..;];
31        *dst_word = *src_word;
32        src = src_rem;
33        dst = dst_rem;
34    }
35    unsafe {
36        std::ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), src.len());
37    }
38}
39
40/// Deserializes only the particular input parameters that the shared memory
41/// program uses.  For more information about the format of the serialized input
42/// parameters see `solana_sdk::entrypoint::deserialize`
43unsafe fn deserialize_input_parameters<'a>(
44    input: *mut u8,
45) -> Result<(&'a mut [u8], &'a [u8]), u64> {
46    // Only one account expected
47    let num_accounts = read(input as *const u64);
48    if num_accounts == 0 {
49        return Err(ProgramError::NotEnoughAccountKeys.into());
50    } else if num_accounts > 1 {
51        return Err(ProgramError::InvalidArgument.into());
52    }
53
54    // Offset to the first (and only) account's data length
55    let data_len_offset = size_of::<u64>()
56        + size_of::<u8>()
57        + size_of::<u8>()
58        + size_of::<u8>()
59        + size_of::<u8>()
60        + size_of::<u32>()
61        + size_of::<Pubkey>()
62        + size_of::<Pubkey>()
63        + size_of::<u64>();
64
65    let account_data_len = read(input.add(data_len_offset) as *const usize);
66    let data_ptr = input.add(data_len_offset + size_of::<u64>());
67    let account_data = from_raw_parts_mut(data_ptr, account_data_len);
68
69    // Offset from the account data pointer to the instruction's data length
70    let instruction_len_offset = account_data_len
71        + MAX_PERMITTED_DATA_INCREASE
72        + (account_data_len as *const u8).align_offset(align_of::<u128>())
73        + size_of::<u64>();
74
75    let instruction_data_len = read(data_ptr.add(instruction_len_offset) as *const usize);
76    let instruction_data = from_raw_parts(
77        data_ptr.add(instruction_len_offset + size_of::<u64>()),
78        instruction_data_len,
79    );
80
81    Ok((account_data, instruction_data))
82}
83
84/// This program expects one account and writes instruction data into the
85/// account's data.  The first 8 bytes of the instruction data contain the
86/// little-endian offset into the account data.  The rest of the instruction
87/// data is written into the account data starting at that offset.
88///
89/// This program uses the raw Solana runtime's entrypoint which takes a pointer
90/// to serialized input parameters.  For more information about the format of
91/// the serialized input parameters see `solana_sdk::entrypoint::deserialize`
92///
93/// # Safety
94#[no_mangle]
95pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
96    match deserialize_input_parameters(input) {
97        Ok((account_data, instruction_data)) => {
98            if instruction_data.len() < 8 {
99                return ProgramError::AccountDataTooSmall.into();
100            }
101            #[allow(clippy::ptr_offset_with_cast)]
102            let (offset, content) = array_refs![instruction_data, 8; ..;];
103            let offset = usize::from_le_bytes(*offset);
104            if account_data.len() < offset + content.len() {
105                return ProgramError::AccountDataTooSmall.into();
106            }
107            let data_ptr = account_data.as_mut_ptr() as usize;
108            let data = from_raw_parts_mut((data_ptr + offset) as *mut u8, content.len());
109            fast_copy(content, data);
110        }
111        Err(err) => return err,
112    }
113    SUCCESS
114}