manifest/validation/
manifest_checker.rs

1use 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/// Validation for manifest accounts.
12#[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    // This can't happen because for Market, we increase the size of the account
89    // with a free block when it gets init, so the first check fails. For
90    // global, we dont use new_init because the account is a PDA, so it is not
91    // at an existing account. Keep the check for thoroughness in case a new
92    // type is ever added.
93    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}