manifest/program/processor/
shared.rs1use 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
50pub(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_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 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
127pub 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
138pub 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
150pub 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
162pub(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
208pub 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}