manifest/validation/
manifest_checker.rs1use bytemuck::Pod;
2use hypertree::{get_helper, Get};
3use solana_program::{
4 account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError,
5 pubkey::Pubkey,
6};
7use std::{cell::Ref, mem::size_of, ops::Deref};
8
9use crate::require;
10
11#[derive(Clone)]
13pub struct ManifestAccountInfo<'a, 'info, T: ManifestAccount + Pod + Clone> {
14 pub info: &'a AccountInfo<'info>,
15
16 phantom: std::marker::PhantomData<T>,
17}
18
19impl<'a, 'info, T: ManifestAccount + Get + Clone> ManifestAccountInfo<'a, 'info, T> {
20 pub fn new(
21 info: &'a AccountInfo<'info>,
22 ) -> Result<ManifestAccountInfo<'a, 'info, T>, ProgramError> {
23 verify_owned_by_manifest(info.owner)?;
24
25 let bytes: Ref<&mut [u8]> = info.try_borrow_data()?;
26 let (header_bytes, _) = bytes.split_at(size_of::<T>());
27 let header: &T = get_helper::<T>(header_bytes, 0_u32);
28 header.verify_discriminant()?;
29
30 Ok(Self {
31 info,
32 phantom: std::marker::PhantomData,
33 })
34 }
35
36 pub fn new_init(
37 info: &'a AccountInfo<'info>,
38 ) -> Result<ManifestAccountInfo<'a, 'info, T>, ProgramError> {
39 verify_owned_by_manifest(info.owner)?;
40 verify_uninitialized::<T>(info)?;
41 Ok(Self {
42 info,
43 phantom: std::marker::PhantomData,
44 })
45 }
46
47 pub fn get_fixed(&self) -> Result<Ref<'_, T>, ProgramError> {
48 let data: Ref<&mut [u8]> = self.info.try_borrow_data()?;
49 Ok(Ref::map(data, |data| {
50 return get_helper::<T>(data, 0_u32);
51 }))
52 }
53}
54
55impl<'a, 'info, T: ManifestAccount + Pod + Clone> Deref for ManifestAccountInfo<'a, 'info, T> {
56 type Target = AccountInfo<'info>;
57
58 fn deref(&self) -> &Self::Target {
59 self.info
60 }
61}
62
63pub trait ManifestAccount {
64 fn verify_discriminant(&self) -> ProgramResult;
65}
66
67fn verify_owned_by_manifest(owner: &Pubkey) -> ProgramResult {
68 require!(
69 owner == &crate::ID,
70 ProgramError::IllegalOwner,
71 "Account must be owned by the Manifest program expected:{} actual:{}",
72 crate::ID,
73 owner
74 )?;
75 Ok(())
76}
77
78fn verify_uninitialized<T: Pod + ManifestAccount>(info: &AccountInfo) -> ProgramResult {
79 let bytes: Ref<&mut [u8]> = info.try_borrow_data()?;
80 require!(
81 size_of::<T>() == bytes.len(),
82 ProgramError::InvalidAccountData,
83 "Incorrect length for uninitialized header expected: {} actual: {}",
84 size_of::<T>(),
85 bytes.len()
86 )?;
87
88 require!(
94 bytes.iter().all(|&byte| byte == 0),
95 ProgramError::InvalidAccountData,
96 "Expected zeroed",
97 )?;
98 Ok(())
99}
100
101#[cfg(test)]
102mod test {
103 use crate::state::{
104 GlobalFixed, MarketFixed, GLOBAL_FIXED_DISCRIMINANT, MARKET_FIXED_DISCRIMINANT,
105 };
106
107 #[test]
108 fn test_market_fixed_discriminant() {
109 let discriminant: u64 = crate::utils::get_discriminant::<MarketFixed>().unwrap();
110 assert_eq!(discriminant, MARKET_FIXED_DISCRIMINANT);
111 }
112
113 #[test]
114 fn test_global_fixed_discriminant() {
115 let discriminant: u64 = crate::utils::get_discriminant::<GlobalFixed>().unwrap();
116 assert_eq!(discriminant, GLOBAL_FIXED_DISCRIMINANT);
117 }
118}
119
120macro_rules! global_seeds {
121 ( $mint:expr ) => {
122 &[b"global", $mint.as_ref()]
123 };
124}
125
126#[macro_export]
127macro_rules! global_seeds_with_bump {
128 ( $mint:expr, $bump:expr ) => {
129 &[&[b"global", $mint.as_ref(), &[$bump]]]
130 };
131}
132
133pub fn get_global_address(mint: &Pubkey) -> (Pubkey, u8) {
134 Pubkey::find_program_address(global_seeds!(mint), &crate::ID)
135}