manifest/program/processor/
shared.rs

1use std::{
2    cell::{Ref, RefMut},
3    mem::size_of,
4};
5
6use crate::{
7    require,
8    state::{
9        claimed_seat::ClaimedSeat, constants::MARKET_BLOCK_SIZE, DynamicAccount, GlobalFixed,
10        MarketFixed, MarketRefMut, GLOBAL_BLOCK_SIZE,
11    },
12    validation::{ManifestAccount, ManifestAccountInfo, Signer},
13};
14use bytemuck::Pod;
15use hypertree::{get_helper, get_mut_helper, DataIndex, Get, RBNode};
16#[cfg(not(feature = "certora"))]
17use solana_program::sysvar::Sysvar;
18use solana_program::{
19    account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction,
20    sysvar::slot_history::ProgramError,
21};
22
23use super::batch_update::MarketDataTreeNodeType;
24
25pub(crate) fn expand_market_if_needed<'a, 'info, T: ManifestAccount + Pod + Clone>(
26    payer: &Signer<'a, 'info>,
27    market_account_info: &ManifestAccountInfo<'a, 'info, T>,
28) -> ProgramResult {
29    let need_expand: bool = {
30        let market_data: &Ref<&mut [u8]> = &market_account_info.try_borrow_data()?;
31        let fixed: &MarketFixed = get_helper::<MarketFixed>(market_data, 0_u32);
32        !fixed.has_free_block()
33    };
34
35    if !need_expand {
36        return Ok(());
37    }
38    expand_market(payer, market_account_info)
39}
40
41pub(crate) fn expand_market<'a, 'info, T: ManifestAccount + Pod + Clone>(
42    payer: &Signer<'a, 'info>,
43    manifest_account: &ManifestAccountInfo<'a, 'info, T>,
44) -> ProgramResult {
45    expand_dynamic(payer, manifest_account, MARKET_BLOCK_SIZE)?;
46    expand_market_fixed(manifest_account.info)?;
47    Ok(())
48}
49
50// Expand is always needed because global doesnt free bytes ever.
51pub(crate) fn expand_global<'a, 'info, T: ManifestAccount + Pod + Clone>(
52    payer: &Signer<'a, 'info>,
53    manifest_account: &ManifestAccountInfo<'a, 'info, T>,
54) -> ProgramResult {
55    // Expand twice because of two trees at once.
56    expand_dynamic(payer, manifest_account, GLOBAL_BLOCK_SIZE)?;
57    expand_dynamic(payer, manifest_account, GLOBAL_BLOCK_SIZE)?;
58    expand_global_fixed(manifest_account.info)?;
59    Ok(())
60}
61
62#[cfg(feature = "certora")]
63fn expand_dynamic<'a, 'info, T: ManifestAccount + Pod + Clone>(
64    _payer: &Signer<'a, 'info>,
65    _manifest_account: &ManifestAccountInfo<'a, 'info, T>,
66    _block_size: usize,
67) -> ProgramResult {
68    Ok(())
69}
70#[cfg(not(feature = "certora"))]
71fn expand_dynamic<'a, 'info, T: ManifestAccount + Pod + Clone>(
72    payer: &Signer<'a, 'info>,
73    manifest_account: &ManifestAccountInfo<'a, 'info, T>,
74    block_size: usize,
75) -> ProgramResult {
76    // Account types were already validated, so do not need to reverify that the
77    // accounts are in order: payer, expandable_account, ...
78    let expandable_account: &AccountInfo = manifest_account.info;
79    let new_size: usize = expandable_account.data_len() + block_size;
80
81    let rent: solana_program::rent::Rent = solana_program::rent::Rent::get()?;
82    let new_minimum_balance: u64 = rent.minimum_balance(new_size);
83    let old_minimum_balance: u64 = rent.minimum_balance(expandable_account.data_len());
84    let lamports_diff: u64 = new_minimum_balance.saturating_sub(old_minimum_balance);
85
86    let payer: &AccountInfo = payer.info;
87
88    invoke(
89        &solana_program::system_instruction::transfer(
90            payer.key,
91            expandable_account.key,
92            lamports_diff,
93        ),
94        &[payer.clone(), expandable_account.clone()],
95    )?;
96
97    #[cfg(feature = "fuzz")]
98    {
99        solana_program::program::invoke(
100            &solana_program::system_instruction::allocate(expandable_account.key, new_size as u64),
101            &[expandable_account.clone()],
102        )?;
103    }
104    #[cfg(not(feature = "fuzz"))]
105    {
106        expandable_account.realloc(new_size, false)?;
107    }
108    Ok(())
109}
110
111fn expand_market_fixed(expandable_account: &AccountInfo) -> ProgramResult {
112    let market_data: &mut RefMut<&mut [u8]> = &mut expandable_account.try_borrow_mut_data()?;
113    let mut dynamic_account: DynamicAccount<&mut MarketFixed, &mut [u8]> =
114        get_mut_dynamic_account(market_data);
115    dynamic_account.market_expand()?;
116    Ok(())
117}
118
119fn expand_global_fixed(expandable_account: &AccountInfo) -> ProgramResult {
120    let global_data: &mut RefMut<&mut [u8]> = &mut expandable_account.try_borrow_mut_data()?;
121    let mut dynamic_account: DynamicAccount<&mut GlobalFixed, &mut [u8]> =
122        get_mut_dynamic_account(global_data);
123    dynamic_account.global_expand()?;
124    Ok(())
125}
126
127/// Generic get dynamic account from the data bytes of the account.
128pub fn get_dynamic_account<'a, T: Get>(
129    data: &'a Ref<'a, &'a mut [u8]>,
130) -> DynamicAccount<&'a T, &'a [u8]> {
131    let (fixed_data, dynamic) = data.split_at(size_of::<T>());
132    let fixed: &T = get_helper::<T>(fixed_data, 0_u32);
133
134    let dynamic_account: DynamicAccount<&'a T, &'a [u8]> = DynamicAccount { fixed, dynamic };
135    dynamic_account
136}
137
138/// Generic get mutable dynamic account from the data bytes of the account.
139pub fn get_mut_dynamic_account<'a, T: Get>(
140    data: &'a mut RefMut<'_, &mut [u8]>,
141) -> DynamicAccount<&'a mut T, &'a mut [u8]> {
142    let (fixed_data, dynamic) = data.split_at_mut(size_of::<T>());
143    let fixed: &mut T = get_mut_helper::<T>(fixed_data, 0_u32);
144
145    let dynamic_account: DynamicAccount<&'a mut T, &'a mut [u8]> =
146        DynamicAccount { fixed, dynamic };
147    dynamic_account
148}
149
150/// Generic get owned dynamic account from the data bytes of the account.
151pub fn get_dynamic_value<T: Get>(data: &[u8]) -> DynamicAccount<T, Vec<u8>> {
152    let (fixed_data, dynamic_data) = data.split_at(size_of::<T>());
153    let market_fixed: &T = get_helper::<T>(fixed_data, 0_u32);
154
155    let dynamic_account: DynamicAccount<T, Vec<u8>> = DynamicAccount {
156        fixed: *market_fixed,
157        dynamic: (dynamic_data).to_vec(),
158    };
159    dynamic_account
160}
161
162// Uses a MarketRefMut instead of a MarketRef because callers will have mutable data.
163pub(crate) fn get_trader_index_with_hint(
164    trader_index_hint: Option<DataIndex>,
165    dynamic_account: &MarketRefMut,
166    payer: &Signer,
167) -> Result<DataIndex, ProgramError> {
168    let trader_index: DataIndex = match trader_index_hint {
169        None => dynamic_account.get_trader_index(payer.key),
170        Some(hinted_index) => {
171            verify_trader_index_hint(hinted_index, &dynamic_account, &payer)?;
172            hinted_index
173        }
174    };
175    Ok(trader_index)
176}
177
178fn verify_trader_index_hint(
179    hinted_index: DataIndex,
180    dynamic_account: &MarketRefMut,
181    payer: &Signer,
182) -> ProgramResult {
183    require!(
184        hinted_index % (MARKET_BLOCK_SIZE as DataIndex) == 0,
185        crate::program::ManifestError::WrongIndexHintParams,
186        "Invalid trader hint index {} did not align",
187        hinted_index,
188    )?;
189    require!(
190        get_helper::<RBNode<ClaimedSeat>>(&dynamic_account.dynamic, hinted_index)
191            .get_payload_type()
192            == MarketDataTreeNodeType::ClaimedSeat as u8,
193        crate::program::ManifestError::WrongIndexHintParams,
194        "Invalid trader hint index {} is not a ClaimedSeat",
195        hinted_index,
196    )?;
197    require!(
198        payer
199            .key
200            .eq(dynamic_account.get_trader_key_by_index(hinted_index)),
201        crate::program::ManifestError::WrongIndexHintParams,
202        "Invalid trader hint index {} did not match payer",
203        hinted_index
204    )?;
205    Ok(())
206}
207
208// TODO: Same for invoke_signed
209
210pub fn invoke(ix: &Instruction, account_infos: &[AccountInfo<'_>]) -> ProgramResult {
211    #[cfg(target_os = "solana")]
212    {
213        solana_invoke::invoke_unchecked(ix, account_infos)
214    }
215    #[cfg(not(target_os = "solana"))]
216    {
217        solana_program::program::invoke(ix, account_infos)
218    }
219}