gemachain_program/
entrypoint.rs1extern crate alloc;
5use crate::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey};
6use alloc::vec::Vec;
7use std::{
8 alloc::Layout,
9 cell::RefCell,
10 mem::{align_of, size_of},
11 ptr::null_mut,
12 rc::Rc,
13 result::Result as ResultGeneric,
15 slice::{from_raw_parts, from_raw_parts_mut},
16};
17
18pub type ProgramResult = ResultGeneric<(), ProgramError>;
19
20pub type ProcessInstruction =
25 fn(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult;
26
27pub const SUCCESS: u64 = 0;
29
30pub const HEAP_START_ADDRESS: usize = 0x300000000;
32pub const HEAP_LENGTH: usize = 32 * 1024;
34
35#[macro_export]
42macro_rules! entrypoint {
43 ($process_instruction:ident) => {
44 #[no_mangle]
46 pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
47 let (program_id, accounts, instruction_data) =
48 unsafe { $crate::entrypoint::deserialize(input) };
49 match $process_instruction(&program_id, &accounts, &instruction_data) {
50 Ok(()) => $crate::entrypoint::SUCCESS,
51 Err(error) => error.into(),
52 }
53 }
54 $crate::custom_heap_default!();
55 $crate::custom_panic_default!();
56 };
57}
58
59#[macro_export]
61macro_rules! custom_heap_default {
62 () => {
63 #[cfg(all(not(feature = "custom-heap"), target_arch = "bpf"))]
71 #[global_allocator]
72 static A: $crate::entrypoint::BumpAllocator = $crate::entrypoint::BumpAllocator {
73 start: $crate::entrypoint::HEAP_START_ADDRESS,
74 len: $crate::entrypoint::HEAP_LENGTH,
75 };
76 };
77}
78
79#[macro_export]
82macro_rules! custom_panic_default {
83 () => {
84 #[cfg(all(not(feature = "custom-panic"), target_arch = "bpf"))]
93 #[no_mangle]
94 fn custom_panic(info: &core::panic::PanicInfo<'_>) {
95 $crate::msg!("{}", info);
97 }
98 };
99}
100
101pub struct BumpAllocator {
103 pub start: usize,
104 pub len: usize,
105}
106#[allow(clippy::integer_arithmetic)]
110unsafe impl std::alloc::GlobalAlloc for BumpAllocator {
111 #[inline]
112 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
113 let pos_ptr = self.start as *mut usize;
114
115 let mut pos = *pos_ptr;
116 if pos == 0 {
117 pos = self.start + self.len;
119 }
120 pos = pos.saturating_sub(layout.size());
121 pos &= !(layout.align().wrapping_sub(1));
122 if pos < self.start + size_of::<*mut u8>() {
123 return null_mut();
124 }
125 *pos_ptr = pos;
126 pos as *mut u8
127 }
128 #[inline]
129 unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
130 }
132}
133
134pub const MAX_PERMITTED_DATA_INCREASE: usize = 1_024 * 10;
136
137#[allow(clippy::integer_arithmetic)]
143#[allow(clippy::type_complexity)]
146pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec<AccountInfo<'a>>, &'a [u8]) {
147 let mut offset: usize = 0;
148
149 #[allow(clippy::cast_ptr_alignment)]
152 let num_accounts = *(input.add(offset) as *const u64) as usize;
153 offset += size_of::<u64>();
154
155 let mut accounts = Vec::with_capacity(num_accounts);
158 for _ in 0..num_accounts {
159 let dup_info = *(input.add(offset) as *const u8);
160 offset += size_of::<u8>();
161 if dup_info == std::u8::MAX {
162 #[allow(clippy::cast_ptr_alignment)]
163 let is_signer = *(input.add(offset) as *const u8) != 0;
164 offset += size_of::<u8>();
165
166 #[allow(clippy::cast_ptr_alignment)]
167 let is_writable = *(input.add(offset) as *const u8) != 0;
168 offset += size_of::<u8>();
169
170 #[allow(clippy::cast_ptr_alignment)]
171 let executable = *(input.add(offset) as *const u8) != 0;
172 offset += size_of::<u8>();
173
174 offset += size_of::<u32>(); let key: &Pubkey = &*(input.add(offset) as *const Pubkey);
177 offset += size_of::<Pubkey>();
178
179 let owner: &Pubkey = &*(input.add(offset) as *const Pubkey);
180 offset += size_of::<Pubkey>();
181
182 #[allow(clippy::cast_ptr_alignment)]
183 let carats = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64)));
184 offset += size_of::<u64>();
185
186 #[allow(clippy::cast_ptr_alignment)]
187 let data_len = *(input.add(offset) as *const u64) as usize;
188 offset += size_of::<u64>();
189
190 let data = Rc::new(RefCell::new({
191 from_raw_parts_mut(input.add(offset), data_len)
192 }));
193 offset += data_len + MAX_PERMITTED_DATA_INCREASE;
194 offset += (offset as *const u8).align_offset(align_of::<u128>()); #[allow(clippy::cast_ptr_alignment)]
197 let rent_epoch = *(input.add(offset) as *const u64);
198 offset += size_of::<u64>();
199
200 accounts.push(AccountInfo {
201 key,
202 is_signer,
203 is_writable,
204 carats,
205 data,
206 owner,
207 executable,
208 rent_epoch,
209 });
210 } else {
211 offset += 7; accounts.push(accounts[dup_info as usize].clone());
215 }
216 }
217
218 #[allow(clippy::cast_ptr_alignment)]
221 let instruction_data_len = *(input.add(offset) as *const u64) as usize;
222 offset += size_of::<u64>();
223
224 let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) };
225 offset += instruction_data_len;
226
227 let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey);
230
231 (program_id, accounts, instruction_data)
232}
233
234#[cfg(test)]
235mod test {
236 use super::*;
237 use std::alloc::GlobalAlloc;
238
239 #[test]
240 fn test_bump_allocator() {
241 {
243 let heap = vec![0u8; 128];
244 let allocator = BumpAllocator {
245 start: heap.as_ptr() as *const _ as usize,
246 len: heap.len(),
247 };
248 for i in 0..128 - size_of::<*mut u8>() {
249 let ptr = unsafe {
250 allocator.alloc(Layout::from_size_align(1, size_of::<u8>()).unwrap())
251 };
252 assert_eq!(
253 ptr as *const _ as usize,
254 heap.as_ptr() as *const _ as usize + heap.len() - 1 - i
255 );
256 }
257 assert_eq!(null_mut(), unsafe {
258 allocator.alloc(Layout::from_size_align(1, 1).unwrap())
259 });
260 }
261 {
263 let heap = vec![0u8; 128];
264 let allocator = BumpAllocator {
265 start: heap.as_ptr() as *const _ as usize,
266 len: heap.len(),
267 };
268 let ptr =
269 unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u8>()).unwrap()) };
270 assert_eq!(0, ptr.align_offset(size_of::<u8>()));
271 let ptr =
272 unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u16>()).unwrap()) };
273 assert_eq!(0, ptr.align_offset(size_of::<u16>()));
274 let ptr =
275 unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u32>()).unwrap()) };
276 assert_eq!(0, ptr.align_offset(size_of::<u32>()));
277 let ptr =
278 unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u64>()).unwrap()) };
279 assert_eq!(0, ptr.align_offset(size_of::<u64>()));
280 let ptr =
281 unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u128>()).unwrap()) };
282 assert_eq!(0, ptr.align_offset(size_of::<u128>()));
283 let ptr = unsafe { allocator.alloc(Layout::from_size_align(1, 64).unwrap()) };
284 assert_eq!(0, ptr.align_offset(64));
285 }
286 {
288 let heap = vec![0u8; 128];
289 let allocator = BumpAllocator {
290 start: heap.as_ptr() as *const _ as usize,
291 len: heap.len(),
292 };
293 let ptr =
294 unsafe { allocator.alloc(Layout::from_size_align(120, size_of::<u8>()).unwrap()) };
295 assert_ne!(ptr, null_mut());
296 assert_eq!(0, ptr.align_offset(size_of::<u64>()));
297 }
298 }
299}