bokken_runtime/
executor.rs1use std::{mem::size_of, collections::HashMap, sync::Arc, thread};
2
3
4use bytemuck::{Zeroable, Pod};
5use solana_program::{
6 entrypoint::MAX_PERMITTED_DATA_INCREASE,
7 pubkey::Pubkey,
8 program_error::ProgramError, instruction::AccountMeta
9};
10use tokio::{sync::{Mutex, RwLock, mpsc}};
11
12use crate::{debug_env::{BokkenAccountData, BokkenRuntimeMessage}, ipc_comm::IPCComm, sol_syscalls::BokkenSyscallMsg};
13
14#[derive(PartialEq, Eq, Debug, Clone, Copy, Zeroable, Pod)]
16#[repr(C)]
17pub(crate) struct AccountInfoHeader {
18 _0xff: u8,
19 is_signer: u8, is_writable: u8, executable: u8, pub original_data_len: u32,
23 pub pubkey: Pubkey,
24 pub owner: Pubkey,
25 pub lamports: u64,
26 pub data_len: u64
27}
28impl AccountInfoHeader {
29 pub fn is_signer(&self) -> bool {
30 self.is_signer > 0
31 }
32 pub fn is_writable(&self) -> bool {
33 self.is_writable > 0
34 }
35 pub fn executable(&self) -> bool {
36 self.executable > 0
37 }
38}
39
40#[derive(Debug)]
43pub(crate) struct SolanaAccountsBlob {
44 pub account_offsets: HashMap<Pubkey, usize>,
45 pub bytes: Vec<u8>
46}
47impl SolanaAccountsBlob {
48 pub fn new(
50 program_id: Pubkey,
51 instruction: Vec<u8>,
52 account_metas: Vec<AccountMeta>,
53 mut account_datas: HashMap<Pubkey, BokkenAccountData>
54 ) -> Self {
55 let mut blob: Vec<u8> = Vec::with_capacity(
56 account_metas.len() * 20480 + size_of::<u64>() +
58 instruction.len() +
59 size_of::<Pubkey>()
60 );
61 blob.extend((account_metas.len() as u64).to_le_bytes());
62
63 let mut account_indices: HashMap<Pubkey, usize> = HashMap::new();
64 let mut account_offsets: HashMap<Pubkey, usize> = HashMap::new();
65 for (index, account_meta) in account_metas.iter().enumerate() {
66 if let Some(entry_index) = account_indices.get(&account_meta.pubkey) {
67 blob.extend((*entry_index as u64).to_le_bytes());
68 }else{
69 let account_data = account_datas.remove(&account_meta.pubkey)
70 .expect("The account metas should reference accounts in the account datas");
71 account_indices.insert(account_meta.pubkey, index);
72 account_offsets.insert(account_meta.pubkey, blob.len());
73
74 blob.push(u8::MAX);
75 blob.push(account_meta.is_signer as u8);
76 blob.push(account_meta.is_writable as u8);
77 blob.push(account_data.executable as u8);
78 blob.extend((account_data.data.len() as u32).to_le_bytes()); blob.extend(account_meta.pubkey.as_ref());
80 blob.extend(account_data.owner.as_ref());
81 blob.extend((account_data.lamports).to_le_bytes());
82 blob.extend((account_data.data.len() as u64).to_le_bytes());
83 blob.extend(account_data.data);
84 blob.extend(vec![0; MAX_PERMITTED_DATA_INCREASE]);
85 blob.extend(vec![0; blob.len() % 8]);
86 blob.extend(account_data.rent_epoch.to_le_bytes());
87 }
88 }
89 blob.extend((instruction.len() as u64).to_le_bytes());
90 blob.extend(instruction);
91 blob.extend(program_id.as_ref());
92 Self {
93 bytes: blob,
94 account_offsets
95 }
96 }
97
98 pub fn get_account_data(&self, pubkey: &Pubkey) -> Option<BokkenAccountData> {
102 if let Some(account_offset) = self.account_offsets.get(pubkey) {
103 let account_data_offset = *account_offset + std::mem::size_of::<AccountInfoHeader>();
104 let account_header = bytemuck::from_bytes::<AccountInfoHeader>(
105 &self.bytes[*account_offset..account_data_offset]
106 );
107 let rent_epoch_offset =
108 account_data_offset +
109 account_header.original_data_len as usize +
110 MAX_PERMITTED_DATA_INCREASE +
111 (account_header.original_data_len as usize % 8);
112
113 Some( BokkenAccountData {
114 lamports: account_header.lamports,
115 data: self.bytes[account_data_offset..{account_data_offset + account_header.data_len as usize}].to_vec(),
116 owner: account_header.owner,
117 executable: account_header.executable > 0,
118 rent_epoch: u64::from_le_bytes(self.bytes[rent_epoch_offset..{rent_epoch_offset + 8}].try_into().unwrap())
119 })
120 }else{
121 None
122 }
123 }
124
125 pub fn set_account_data(&mut self, pubkey: &Pubkey, account_data: BokkenAccountData) -> Result<(), ProgramError> {
127 if let Some(account_offset) = self.account_offsets.get(pubkey) {
128 let account_data_offset = *account_offset + std::mem::size_of::<AccountInfoHeader>();
129 let account_header = bytemuck::from_bytes_mut::<AccountInfoHeader>(
130 &mut self.bytes[*account_offset..account_data_offset]
131 );
132 if account_data.data.len() > account_header.original_data_len as usize + MAX_PERMITTED_DATA_INCREASE {
133 println!("Debug runtime: set_account_data: {} was grown too much", pubkey);
134 return Err(ProgramError::InvalidRealloc);
135 }
136 account_header.data_len = account_data.data.len() as u64;
137 account_header.lamports = account_data.lamports;
138 account_header.owner = account_data.owner;
139 self.bytes[account_data_offset..{account_data_offset + account_data.data.len()}].copy_from_slice(&account_data.data);
140 Ok(())
141 }else{
142 println!(
143 "Debug runtime: set_account_data called with {} but we have no idea what that account is",
144 pubkey
145 );
146 Err(ProgramError::UninitializedAccount)
147 }
148 }
149
150 pub fn get_account_data_header(&self, pubkey: &Pubkey) -> Option<&AccountInfoHeader> {
152 if let Some(account_offset) = self.account_offsets.get(pubkey) {
153 let account_data_offset = *account_offset + std::mem::size_of::<AccountInfoHeader>();
154 let account_header = bytemuck::from_bytes::<AccountInfoHeader>(
155 &self.bytes[*account_offset..account_data_offset]
156 );
157 Some(account_header)
158 }else{
159 None
160 }
161 }
162
163 pub fn is_writable(&self, pubkey: &Pubkey) -> bool {
165 if let Some(account_header) = self.get_account_data_header(pubkey) {
166 account_header.is_writable() && !account_header.executable()
167 }else{
168 false
169 }
170 }
171
172 pub fn is_signer(&self, pubkey: &Pubkey) -> bool {
174 if let Some(account_header) = self.get_account_data_header(pubkey) {
175 account_header.is_signer()
176 }else{
177 false
178 }
179 }
180
181 pub fn get_account_datas(&self) -> HashMap<Pubkey, BokkenAccountData> {
183 let mut result = HashMap::new();
184 for pubkey in self.account_offsets.keys() {
185 result.insert(
186 *pubkey,
187 self.get_account_data(pubkey).expect("the value of the keys we are iterating over")
188 );
189 }
190 result
191 }
192}
193
194#[derive(Debug)]
196pub(crate) struct BokkenSolanaContext {
197 pub blob: Arc<RwLock<SolanaAccountsBlob>>,
199 nonce: u64,
200 cpi_height: u8
201}
202impl BokkenSolanaContext {
203 pub fn new(
204 program_id: Pubkey,
205 instruction: Vec<u8>,
206 account_metas: Vec<AccountMeta>,
207 account_datas: HashMap<Pubkey, BokkenAccountData>,
208 nonce: u64,
209 cpi_height: u8,
210 ) -> Self {
211
212 Self {
213 blob: Arc::new(RwLock::new(
215 SolanaAccountsBlob::new(
216 program_id,
217 instruction,
218 account_metas,
219 account_datas
220 )
221 )),
222 nonce,
223 cpi_height
224 }
225 }
226 pub fn get_account_data(&self, pubkey: &Pubkey) -> Option<BokkenAccountData> {
227 self.blob.blocking_read().get_account_data(pubkey)
228 }
229 pub fn is_writable(&self, pubkey: &Pubkey) -> bool {
230 self.blob.blocking_read().is_writable(pubkey)
231 }
232 pub fn is_signer(&self, pubkey: &Pubkey) -> bool {
233 self.blob.blocking_read().is_signer(pubkey)
234 }
235
236 pub fn get_account_datas(&self) -> HashMap<Pubkey, BokkenAccountData> {
237 self.blob.blocking_read().get_account_datas()
238 }
239 pub fn cpi_height(&self) -> u8 {
240 self.cpi_height
241 }
242 pub fn nonce(&self) -> u64 {
243 self.nonce
244 }
245}
246
247pub(crate) async fn execute_sol_program_thread(
253 nonce: u64,
254 blob: Arc<RwLock<SolanaAccountsBlob>>,
255 comm: Arc<Mutex<IPCComm>>,
256 context_drop_notifier: mpsc::Sender<BokkenSyscallMsg>
257) {
258 let blob_ptr = {
262 blob.read().await.bytes.as_ptr() as usize
264 };
265
266 thread::spawn(move || {
269 let result = thread::spawn(move || {
272 extern "C" {
273 fn entrypoint(input: *mut u8) -> u64;
276 }
277 let result = unsafe {
278 entrypoint(blob_ptr as *mut u8)
279 };
280 result
281 }).join();
282 let mut comm = comm.blocking_lock();
283 context_drop_notifier.blocking_send(
284 BokkenSyscallMsg::PopContext
285 ).expect("mpsc::Sender to not fail");
286 let account_datas = blob.blocking_read().get_account_datas();
287 match result {
288 Ok(return_code) => {
289 comm.blocking_send_msg(
290 BokkenRuntimeMessage::Executed{
291 nonce,
292 return_code,
293 account_datas
294 }
295 ).expect("encoding to not fail");
296 },
297 Err(err) => {
298 let panic_msg = match err.downcast_ref::<&str>() {
299 Some(str) => str.to_string(),
300 None => {
301 match err.downcast_ref::<String>() {
302 Some(str) => str.clone(),
303 None => String::from("<Unknown panic message>")
304 }
305 },
306 };
307 comm.blocking_send_msg(
308 BokkenRuntimeMessage::Log{
309 nonce,
310 message: format!("Program panicked: {}", panic_msg)
311 }
312 ).expect("encoding to not fail");
313 comm.blocking_send_msg(
314 BokkenRuntimeMessage::Executed{
316 nonce,
317 return_code: ProgramError::Custom(0).into(),
318 account_datas
319 }
320 ).expect("encoding to not fail");
321 },
322 }
323 });
324}