bokken_runtime/
sol_syscalls.rs1use std::{sync::{Arc}, collections::{HashSet, HashMap}};
2
3use solana_program::{program_stubs::SyscallStubs, program_error::{UNSUPPORTED_SYSVAR, ProgramError}, entrypoint::ProgramResult, pubkey::Pubkey, instruction::Instruction, account_info::AccountInfo};
4use tokio::{sync::{Mutex, mpsc, RwLock}, task};
5use itertools::Itertools;
6
7use crate::{ipc_comm::IPCComm, debug_env::{BokkenRuntimeMessage, BokkenAccountData}, executor::{BokkenSolanaContext, execute_sol_program_thread, SolanaAccountsBlob}};
8
9#[derive(Debug)]
10pub(crate) enum BokkenSyscallMsg {
11 PushContext {
12 ctx: BokkenSolanaContext,
13 msg_sender_clone: mpsc::Sender<BokkenSyscallMsg>,
14 },
15 PopContext
16}
17
18#[derive(Debug)]
23pub(crate) struct BokkenSyscalls {
24 ipc: Arc<Mutex<IPCComm>>,
25 program_id: Pubkey,
26 invoke_result_senders: Arc<Mutex<HashMap<u64, mpsc::Sender<(u64, HashMap<Pubkey, BokkenAccountData>)>>>>,
27 return_data: Arc<Mutex<Option<(Pubkey, Vec<u8>)>>>,
29 contexts: Arc<Mutex<Vec<BokkenSolanaContext>>>,
30}
31impl BokkenSyscalls {
32
33 pub fn new(
40 ipc: Arc<Mutex<IPCComm>>,
41 program_id: Pubkey,
42 invoke_result_senders: Arc<Mutex<HashMap<u64, mpsc::Sender<(u64, HashMap<Pubkey, BokkenAccountData>)>>>>,
43 mut msg_receiver: mpsc::Receiver<BokkenSyscallMsg>
44 ) -> Self {
45 let contexts= Arc::new(Mutex::new(Vec::new()));
46 let contexts_clone = contexts.clone();
47 let ipc_clone = ipc.clone();
48 task::spawn(async move {
49 while let Some(msg) = msg_receiver.recv().await {
50 match msg {
51 BokkenSyscallMsg::PushContext { ctx, msg_sender_clone } => {
52 let blob = ctx.blob.clone();
53 let nonce = ctx.nonce();
54 contexts_clone.lock().await.push(ctx);
55 execute_sol_program_thread(nonce, blob, ipc_clone.clone(), msg_sender_clone).await;
56 },
57 BokkenSyscallMsg::PopContext => {
58 contexts_clone.lock().await.pop();
59 },
60 }
61 }
62 });
63 Self {
64 ipc,
65 program_id,
66 invoke_result_senders,
67 return_data: Arc::new(Mutex::new(None)),
68 contexts
69 }
70 }
71 fn stack_height(&self) -> u8 {
72 self.contexts.blocking_lock().last().expect("not be empty during program execution").cpi_height()
73 }
74 fn nonce(&self) -> u64 {
75 self.contexts.blocking_lock().last().expect("not be empty during program execution").nonce()
76 }
77 fn account_data_lock(&self) -> Arc<RwLock<SolanaAccountsBlob>> {
78 self.contexts.blocking_lock()
79 .last()
80 .expect("not be empty during program execution")
81 .blob
82 .clone()
83 }
84 fn is_valid_signer(&self, pubkey: &Pubkey) -> bool {
85 self.contexts
86 .blocking_lock()
87 .last()
88 .expect("not be empty during program execution")
89 .is_signer(pubkey)
90 }
91 fn is_valid_writable(&self, pubkey: &Pubkey) -> bool {
92 self.contexts
93 .blocking_lock()
94 .last()
95 .expect("not be empty during program execution")
96 .is_writable(pubkey)
97 }
98}
99
100impl SyscallStubs for BokkenSyscalls {
101 fn sol_log(&self, message: &str) {
102 let msg = format!("Program logged: {}", message);
103 println!("{}", msg);
104 let mut ipc = self.ipc.blocking_lock();
105 ipc.blocking_send_msg(
106 BokkenRuntimeMessage::Log {
107 nonce: self.nonce(),
108 message: msg
109 }
110 ).expect("Message encoding not to fail");
111 }
112 fn sol_log_compute_units(&self) {
113 self.sol_log("WARNING: sol_log_compute_units() not available");
114 }
115 fn sol_invoke_signed(
116 &self,
117 instruction: &Instruction,
118 account_infos: &[AccountInfo],
119 signers_seeds: &[&[&[u8]]],
120 ) -> ProgramResult {
121 let mut just_signed = HashSet::new();
122 for signing_seed in signers_seeds.iter() {
123 just_signed.insert(
124 Pubkey::create_program_address(signing_seed, &self.program_id)?
125 );
126 }
127 let mut outgoing_account_datas = HashMap::new();
128 let ctx_account_data_lock = self.account_data_lock();
129 {
130 let ctx_acocunt_datas = ctx_account_data_lock.blocking_read();
131 for (i, meta) in instruction.accounts.iter().enumerate() {
132 if *account_infos[i].key != meta.pubkey {
133 self.sol_log("Invoke: Accoune meta doesn't match account info");
134 return Err(ProgramError::InvalidAccountData);
135 }
136 if meta.is_writable && !self.is_valid_writable(&meta.pubkey) {
137 self.sol_log("Invoke: Cannot instruction requres an non-writable account to be writable");
139 return Err(ProgramError::Custom(0));
140 }
141 if meta.is_signer && !self.is_valid_signer(&meta.pubkey) && !just_signed.contains(&meta.pubkey) {
142 self.sol_log(format!(
143 "Invoke: Account {} needs to be signed, but it isn't and doesn't match any given PDA seeds",
144 meta.pubkey
145 ).as_str());
146 return Err(ProgramError::MissingRequiredSignature);
147 }
148 outgoing_account_datas.insert(
149 meta.pubkey.clone(),
150 ctx_acocunt_datas.get_account_data(&meta.pubkey).expect("To have the account info we were just passed")
151 );
152 }
153 }
155
156 let mut receiver = {
157 let (sender, receiver) = mpsc::channel(1);
158 self.invoke_result_senders.blocking_lock().insert(self.nonce(), sender);
159 receiver
160 };
162 {
163 self.ipc.blocking_lock().blocking_send_msg(
164 BokkenRuntimeMessage::CrossProgramInvoke {
165 nonce: self.nonce(),
166 program_id: self.program_id,
167 instruction: instruction.data.clone(),
168 account_metas: instruction.accounts.iter().map(|v|{v.into()}).collect(),
169 account_datas: HashMap::new(),
170 call_depth: self.stack_height()
171 }
172 ).expect("encoding to not fail");
173 }
175 let (return_code, account_datas) = receiver.blocking_recv().expect("get a response from CPI");
176 {
177 let mut ctx_acocunt_datas = ctx_account_data_lock.blocking_write();
178 for (pubkey, account_data) in account_datas.into_iter() {
180 ctx_acocunt_datas.set_account_data(&pubkey, account_data)?;
181 }
182 }
184 if return_code != 0 {
185 panic!("CPI failed wirth return code {}", return_code);
187 }
188 Ok(())
189 }
190 fn sol_get_clock_sysvar(&self, _var_addr: *mut u8) -> u64 {
191 UNSUPPORTED_SYSVAR
192 }
193 fn sol_get_epoch_schedule_sysvar(&self, _var_addr: *mut u8) -> u64 {
194 UNSUPPORTED_SYSVAR
195 }
196 fn sol_get_fees_sysvar(&self, _var_addr: *mut u8) -> u64 {
197 UNSUPPORTED_SYSVAR
198 }
199 fn sol_get_rent_sysvar(&self, _var_addr: *mut u8) -> u64 {
200 UNSUPPORTED_SYSVAR
201 }
202 fn sol_get_return_data(&self) -> Option<(Pubkey, Vec<u8>)> {
203 self.return_data.blocking_lock().clone()
204 }
205 fn sol_set_return_data(&self, data: &[u8]) {
206 let mut return_data = self.return_data.blocking_lock();
207 *return_data = Some((self.program_id, data.to_vec()));
208 }
209 fn sol_log_data(&self, fields: &[&[u8]]) {
210 self.sol_log(format!("data: {}", fields.iter().map(base64::encode).join(" ")).as_str());
211 }
212 fn sol_get_processed_sibling_instruction(&self, _index: usize) -> Option<Instruction> {
213 self.sol_log("WARNING: sol_get_processed_sibling_instruction() not available");
214 None
215 }
216 fn sol_get_stack_height(&self) -> u64 {
217 self.stack_height() as u64
218 }
219}