1use futures::executor::block_on;
2use ic_kit_sys::ic0;
3use ic_kit_sys::ic0::runtime;
4use ic_kit_sys::ic0::Ic0CallHandler;
5use std::fmt::{Display, Formatter};
6use std::panic::set_hook;
7use std::thread::JoinHandle;
8use tokio::select;
9use tokio::sync::mpsc::{self, Receiver, Sender};
10
11pub struct Runtime {
12 processing: Option<Request>,
14 perform_calls: Vec<Call>,
16 call_factory: Option<IncompleteCall>,
19 execution_thread_handle: JoinHandle<()>,
21 task_tx: Sender<Box<dyn Fn() + Send>>,
23 task_returned_rx: Receiver<()>,
25 reply_tx: Sender<runtime::Response>,
27 request_rx: Receiver<runtime::Request>,
29}
30
31pub enum Request {
32 Init {
33 arg_data: Vec<u8>,
34 },
35 Update {
36 arg_data: Vec<u8>,
37 },
38 Query {
39 arg_data: Vec<u8>,
40 },
41 CanisterInspect {
42 arg_data: Vec<u8>,
43 method_name: String,
44 },
45 ReplyCallback {
46 arg_data: Vec<u8>,
47 },
48 RejectCallback {},
49 CleanupCallback {},
50 Heartbeat,
51 PostUpgrade,
52 PreUpgrade,
53}
54
55pub struct Call {}
56
57pub struct IncompleteCall {}
58
59impl Runtime {
60 pub fn new() -> Self {
61 let (request_tx, request_rx) = mpsc::channel(8);
62 let (reply_tx, reply_rx) = mpsc::channel(8);
63 let (task_tx, mut task_rx) = mpsc::channel::<Box<dyn Fn() + Send>>(8);
64 let (mut task_returned_tx, task_returned_rx) = mpsc::channel(8);
65
66 let thread = std::thread::spawn(move || {
67 let handle = runtime::RuntimeHandle::new(reply_rx, request_tx);
71 ic0::register_handler(handle);
72
73 let task_panicked_tx = task_returned_tx.clone();
79 set_hook(Box::new(move |_| {
80 block_on(async {
81 let trap_message = "Canister panicked";
82 unsafe {
83 ic0::trap(trap_message.as_ptr() as isize, trap_message.len() as isize)
84 };
85 task_panicked_tx.send(()).await.expect("ic-kit-runtime: Execution thread could not send task-completion signal to the main thread after panic.");
86 });
87 }));
88
89 block_on(async {
90 while let Some(task) = task_rx.recv().await {
91 task();
92 task_returned_tx.send(()).await.expect("ic-kit-runtime: Execution thread could not send task-completion signal to the main thread.")
93 }
94 });
95 });
96
97 Self {
98 processing: None,
99 perform_calls: vec![],
100 call_factory: None,
101 execution_thread_handle: thread,
102 task_tx,
103 task_returned_rx,
104 reply_tx,
105 request_rx,
106 }
107 }
108
109 pub async fn send(&mut self, request: Request) {
111 self.task_tx
112 .send(Box::new(|| {
113 println!("Some function related to the request.")
114 }))
115 .await
116 .unwrap_or_else(|_| {
117 panic!("ic-kit-runtime: Could not send the task to the execution thread.")
118 });
119
120 loop {
121 select! {
122 Some(()) = self.task_returned_rx.recv() => {
123 return;
125 },
126 Some(req) = self.request_rx.recv() => {
127 let res = req.proxy(self);
128 self.reply_tx
129 .send(res)
130 .await
131 .expect("ic-kit-runtime: Could not send the system API call's response to the execution thread.");
132 }
133 }
134 }
135 }
136
137 pub fn explicit_trap(&mut self, message: String) -> ! {
138 panic!("Canister Trapped: {}", message)
139 }
140}
141
142impl Ic0CallHandler for Runtime {
143 fn msg_arg_data_size(&mut self) -> isize {
144 let req = self
145 .processing
146 .as_ref()
147 .expect("Unexpected: No request is being processed.");
148
149 match req {
150 Request::Init { arg_data, .. } => arg_data.len() as isize,
151 Request::Update { arg_data, .. } => arg_data.len() as isize,
152 Request::Query { arg_data, .. } => arg_data.len() as isize,
153 Request::CanisterInspect { arg_data, .. } => arg_data.len() as isize,
154 Request::ReplyCallback { arg_data, .. } => arg_data.len() as isize,
155 r => self.explicit_trap(format!(
156 "ic0::msg_arg_data_size was invoked from canister_'{}'",
157 r
158 )),
159 }
160 }
161
162 fn msg_arg_data_copy(&mut self, dst: isize, offset: isize, size: isize) {
163 let req = self
164 .processing
165 .as_ref()
166 .expect("Unexpected: No request is being processed.");
167
168 let arg_data = match req {
169 Request::Init { arg_data, .. } => arg_data,
170 Request::Update { arg_data, .. } => arg_data,
171 Request::Query { arg_data, .. } => arg_data,
172 Request::CanisterInspect { arg_data, .. } => arg_data,
173 Request::ReplyCallback { arg_data, .. } => arg_data,
174 r => self.explicit_trap(format!(
175 "ic0::msg_arg_data_copy was invoked from canister_'{}'",
176 r
177 )),
178 };
179
180 copy_to_canister(dst, offset, size, arg_data)
181 .map_err(|e| self.explicit_trap(e))
182 .expect("TODO: panic message");
183 }
184
185 fn msg_caller_size(&mut self) -> isize {
186 todo!()
187 }
188
189 fn msg_caller_copy(&mut self, dst: isize, offset: isize, size: isize) {
190 todo!()
191 }
192
193 fn msg_reject_code(&mut self) -> i32 {
194 todo!()
195 }
196
197 fn msg_reject_msg_size(&mut self) -> isize {
198 todo!()
199 }
200
201 fn msg_reject_msg_copy(&mut self, dst: isize, offset: isize, size: isize) {
202 todo!()
203 }
204
205 fn msg_reply_data_append(&mut self, src: isize, size: isize) {
206 todo!()
207 }
208
209 fn msg_reply(&mut self) {
210 todo!()
211 }
212
213 fn msg_reject(&mut self, src: isize, size: isize) {
214 todo!()
215 }
216
217 fn msg_cycles_available(&mut self) -> i64 {
218 todo!()
219 }
220
221 fn msg_cycles_available128(&mut self, dst: isize) {
222 todo!()
223 }
224
225 fn msg_cycles_refunded(&mut self) -> i64 {
226 todo!()
227 }
228
229 fn msg_cycles_refunded128(&mut self, dst: isize) {
230 todo!()
231 }
232
233 fn msg_cycles_accept(&mut self, max_amount: i64) -> i64 {
234 todo!()
235 }
236
237 fn msg_cycles_accept128(&mut self, max_amount_high: i64, max_amount_low: i64, dst: isize) {
238 todo!()
239 }
240
241 fn canister_self_size(&mut self) -> isize {
242 todo!()
243 }
244
245 fn canister_self_copy(&mut self, dst: isize, offset: isize, size: isize) {
246 todo!()
247 }
248
249 fn canister_cycle_balance(&mut self) -> i64 {
250 todo!()
251 }
252
253 fn canister_cycle_balance128(&mut self, dst: isize) {
254 todo!()
255 }
256
257 fn canister_status(&mut self) -> i32 {
258 todo!()
259 }
260
261 fn msg_method_name_size(&mut self) -> isize {
262 todo!()
263 }
264
265 fn msg_method_name_copy(&mut self, dst: isize, offset: isize, size: isize) {
266 todo!()
267 }
268
269 fn accept_message(&mut self) {
270 todo!()
271 }
272
273 fn call_new(
274 &mut self,
275 callee_src: isize,
276 callee_size: isize,
277 name_src: isize,
278 name_size: isize,
279 reply_fun: isize,
280 reply_env: isize,
281 reject_fun: isize,
282 reject_env: isize,
283 ) {
284 todo!()
285 }
286
287 fn call_on_cleanup(&mut self, fun: isize, env: isize) {
288 todo!()
289 }
290
291 fn call_data_append(&mut self, src: isize, size: isize) {
292 todo!()
293 }
294
295 fn call_cycles_add(&mut self, amount: i64) {
296 todo!()
297 }
298
299 fn call_cycles_add128(&mut self, amount_high: i64, amount_low: i64) {
300 todo!()
301 }
302
303 fn call_perform(&mut self) -> i32 {
304 todo!()
305 }
306
307 fn stable_size(&mut self) -> i32 {
308 todo!()
309 }
310
311 fn stable_grow(&mut self, new_pages: i32) -> i32 {
312 todo!()
313 }
314
315 fn stable_write(&mut self, offset: i32, src: isize, size: isize) {
316 todo!()
317 }
318
319 fn stable_read(&mut self, dst: isize, offset: i32, size: isize) {
320 todo!()
321 }
322
323 fn stable64_size(&mut self) -> i64 {
324 todo!()
325 }
326
327 fn stable64_grow(&mut self, new_pages: i64) -> i64 {
328 todo!()
329 }
330
331 fn stable64_write(&mut self, offset: i64, src: i64, size: i64) {
332 todo!()
333 }
334
335 fn stable64_read(&mut self, dst: i64, offset: i64, size: i64) {
336 todo!()
337 }
338
339 fn certified_data_set(&mut self, src: isize, size: isize) {
340 todo!()
341 }
342
343 fn data_certificate_present(&mut self) -> i32 {
344 todo!()
345 }
346
347 fn data_certificate_size(&mut self) -> isize {
348 todo!()
349 }
350
351 fn data_certificate_copy(&mut self, dst: isize, offset: isize, size: isize) {
352 todo!()
353 }
354
355 fn time(&mut self) -> i64 {
356 todo!()
357 }
358
359 fn performance_counter(&mut self, counter_type: i32) -> i64 {
360 todo!()
361 }
362
363 fn debug_print(&mut self, src: isize, size: isize) {
364 todo!()
365 }
366
367 fn trap(&mut self, src: isize, size: isize) {
368 todo!()
369 }
370}
371
372impl Display for Request {
373 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
374 match self {
375 Request::Init { .. } => f.write_str("init"),
376 Request::Update { .. } => f.write_str("update"),
377 Request::Query { .. } => f.write_str("query"),
378 Request::CanisterInspect { .. } => f.write_str("inspect"),
379 Request::ReplyCallback { .. } => f.write_str("reply_callback"),
380 Request::RejectCallback { .. } => f.write_str("reject_callback"),
381 Request::CleanupCallback { .. } => f.write_str("cleanup_callback"),
382 Request::Heartbeat => f.write_str("heartbeat"),
383 Request::PostUpgrade => f.write_str("post_upgrade"),
384 Request::PreUpgrade => f.write_str("init"),
385 }
386 }
387}
388
389fn copy_to_canister(dst: isize, offset: isize, size: isize, data: &[u8]) -> Result<(), String> {
390 let dst = dst as usize;
391 let offset = offset as usize;
392 let size = size as usize;
393
394 if offset + size > data.len() {
395 return Err("Out of bound read.".into());
396 }
397
398 let slice = unsafe { std::slice::from_raw_parts_mut(dst as *mut u8, size) };
399 slice.copy_from_slice(&data[offset..offset + size]);
400 Ok(())
401}