nitrate_program/
lib.rs

1// Copyright (c) 2024 nifty-oss maintainers
2// Copyright (c) 2024 Magnetar Fields
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16pub mod account_info;
17pub mod cpi;
18pub mod system;
19
20pub use account_info::*;
21
22use solana_program::{
23    entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, NON_DUP_MARKER},
24    pubkey::Pubkey,
25};
26use std::slice::from_raw_parts;
27
28/// Deserialize the input arguments.
29///
30/// This can only be called from the entrypoint function of a Solana program and with
31/// a buffer that was serialized by the runtime.
32#[allow(clippy::cast_ptr_alignment, clippy::missing_safety_doc)]
33#[inline(always)]
34pub unsafe fn deserialize<'a, const MAX_ACCOUNTS: usize>(
35    input: *mut u8,
36    accounts: *mut std::mem::MaybeUninit<AccountInfo>,
37) -> (&'a Pubkey, usize, &'a [u8]) {
38    let mut offset: usize = 0;
39
40    // total number of accounts present; it only process up to MAX_ACCOUNTS
41    let total_accounts = *(input.add(offset) as *const u64) as usize;
42
43    // number of processed accounts
44    let count = if total_accounts <= MAX_ACCOUNTS {
45        total_accounts
46    } else {
47        #[cfg(feature = "logging")]
48        solana_program::log::sol_log("🟡 Number of accounts exceeds MAX_ACCOUNTS");
49
50        MAX_ACCOUNTS
51    };
52
53    offset += std::mem::size_of::<u64>();
54
55    for i in 0..count {
56        let duplicate_info = *(input.add(offset) as *const u8);
57        if duplicate_info == NON_DUP_MARKER {
58            // MAGNETAR FIELDS: safety depends on alignment, size
59            // 1) we will always be 8 byte aligned due to align_offset
60            // 2) solana vm serialization format is consistent so size is ok
61            let account_info: *mut Account = input.add(offset) as *mut _;
62
63            offset += std::mem::size_of::<Account>();
64            offset += (*account_info).data_len as usize;
65            offset += MAX_PERMITTED_DATA_INCREASE;
66            offset += (offset as *const u8).align_offset(BPF_ALIGN_OF_U128);
67            offset += std::mem::size_of::<u64>(); // MAGNETAR FIELDS: ignore rent epoch
68
69            // MAGNETAR FIELDS: reset borrow state right before pushing
70            (*account_info).borrow_state = 0b_0000_0000;
71
72            std::ptr::write(
73                accounts.add(i),
74                std::mem::MaybeUninit::new(AccountInfo {
75                    raw: account_info as *const _ as *mut _,
76                }),
77            );
78        } else {
79            offset += 8;
80            // duplicate account, clone the original
81            std::ptr::copy_nonoverlapping(
82                accounts.add(duplicate_info as usize),
83                accounts.add(i),
84                1,
85            );
86        }
87    }
88
89    // process any remaining accounts to move the offset to the instruction
90    // data (there is a duplication of logic but we avoid testing whether we
91    // have space for the account or not)
92    for _ in count..total_accounts {
93        let duplicate_info = *(input.add(offset) as *const u8);
94
95        if duplicate_info == NON_DUP_MARKER {
96            let account_info: *mut Account = input.add(offset) as *mut _;
97            offset += std::mem::size_of::<Account>();
98            offset += (*account_info).data_len as usize;
99            offset += MAX_PERMITTED_DATA_INCREASE;
100            offset += (offset as *const u8).align_offset(BPF_ALIGN_OF_U128);
101            offset += std::mem::size_of::<u64>(); // MAGNETAR FIELDS: ignore rent epoch
102        } else {
103            offset += 8;
104        }
105    }
106
107    // instruction data
108    #[allow(clippy::cast_ptr_alignment)]
109    let instruction_data_len = *(input.add(offset) as *const u64) as usize;
110    offset += std::mem::size_of::<u64>();
111
112    let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) };
113    offset += instruction_data_len;
114
115    // program id
116    let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey);
117
118    (program_id, count, instruction_data)
119}