1use serde::Serialize;
2use std::cell::RefCell;
3
4use crate::{
5 errors::{HostError, Location, PanicContext},
6 logic::{sys, VMHostFunctions, VMLogicResult, DIGEST_SIZE},
7};
8
9thread_local! {
10 static CURRENT_CALLBACK_HANDLER: RefCell<Option<String>> = RefCell::new(None);
13}
14
15#[derive(Debug, Serialize)]
17#[non_exhaustive]
18pub struct Event {
19 pub kind: String,
21 pub data: Vec<u8>,
23 pub handler: Option<String>,
25}
26
27impl VMHostFunctions<'_> {
28 pub fn panic(&mut self, src_location_ptr: u64) -> VMLogicResult<()> {
43 let location =
44 unsafe { self.read_guest_memory_typed::<sys::Location<'_>>(src_location_ptr)? };
45
46 let file = self.read_guest_memory_str(&location.file())?.to_owned();
47 let line = location.line();
48 let column = location.column();
49
50 Err(HostError::Panic {
51 context: PanicContext::Guest,
52 message: "explicit panic".to_owned(),
53 location: Location::At { file, line, column },
54 }
55 .into())
56 }
57
58 pub fn panic_utf8(
75 &mut self,
76 src_panic_msg_ptr: u64,
77 src_location_ptr: u64,
78 ) -> VMLogicResult<()> {
79 let panic_message_buf =
80 unsafe { self.read_guest_memory_typed::<sys::Buffer<'_>>(src_panic_msg_ptr)? };
81 let location =
82 unsafe { self.read_guest_memory_typed::<sys::Location<'_>>(src_location_ptr)? };
83
84 let panic_message = self.read_guest_memory_str(&panic_message_buf)?.to_owned();
85 let file = self.read_guest_memory_str(&location.file())?.to_owned();
86 let line = location.line();
87 let column = location.column();
88
89 Err(HostError::Panic {
90 context: PanicContext::Guest,
91 message: panic_message,
92 location: Location::At { file, line, column },
93 }
94 .into())
95 }
96
97 pub fn register_len(&self, register_id: u64) -> VMLogicResult<u64> {
108 Ok(self
109 .borrow_logic()
110 .registers
111 .get_len(register_id)
112 .unwrap_or(u64::MAX))
113 }
114
115 pub fn read_register(&self, register_id: u64, dest_data_ptr: u64) -> VMLogicResult<u32> {
133 let dest_data =
134 unsafe { self.read_guest_memory_typed::<sys::BufferMut<'_>>(dest_data_ptr)? };
135
136 let data = self.borrow_logic().registers.get(register_id)?;
137
138 if data.len() != usize::try_from(dest_data.len()).map_err(|_| HostError::IntegerOverflow)? {
139 return Ok(0);
140 }
141
142 self.read_guest_memory_slice_mut(&dest_data)
143 .copy_from_slice(data);
144
145 Ok(1)
146 }
147
148 pub fn context_id(&mut self, dest_register_id: u64) -> VMLogicResult<()> {
158 self.with_logic_mut(|logic| {
159 logic
160 .registers
161 .set(logic.limits, dest_register_id, logic.context.context_id)
162 })
163 }
164
165 pub fn executor_id(&mut self, dest_register_id: u64) -> VMLogicResult<()> {
175 self.with_logic_mut(|logic| {
176 logic.registers.set(
177 logic.limits,
178 dest_register_id,
179 logic.context.executor_public_key,
180 )
181 })
182 }
183
184 pub fn input(&mut self, dest_register_id: u64) -> VMLogicResult<()> {
194 self.with_logic_mut(|logic| {
195 logic
196 .registers
197 .set(logic.limits, dest_register_id, &*logic.context.input)
198 })?;
199
200 Ok(())
201 }
202
203 pub fn value_return(&mut self, src_value_ptr: u64) -> VMLogicResult<()> {
217 let result =
218 unsafe { self.read_guest_memory_typed::<sys::ValueReturn<'_>>(src_value_ptr)? };
219
220 let result = match result {
221 sys::ValueReturn::Ok(value) => Ok(self.read_guest_memory_slice(&value).to_vec()),
222 sys::ValueReturn::Err(value) => Err(self.read_guest_memory_slice(&value).to_vec()),
223 };
224
225 self.with_logic_mut(|logic| logic.returns = Some(result));
226
227 Ok(())
228 }
229
230 pub fn log_utf8(&mut self, src_log_ptr: u64) -> VMLogicResult<()> {
243 let src_log_buf = unsafe { self.read_guest_memory_typed::<sys::Buffer<'_>>(src_log_ptr)? };
244
245 let logic = self.borrow_logic();
246
247 if logic.logs.len()
248 >= usize::try_from(logic.limits.max_logs).map_err(|_| HostError::IntegerOverflow)?
249 {
250 return Err(HostError::LogsOverflow.into());
251 }
252
253 let message = self.read_guest_memory_str(&src_log_buf)?.to_owned();
254
255 self.with_logic_mut(|logic| logic.logs.push(message));
256
257 Ok(())
258 }
259
260 pub fn emit(&mut self, src_event_ptr: u64) -> VMLogicResult<()> {
276 let event = unsafe { self.read_guest_memory_typed::<sys::Event<'_>>(src_event_ptr)? };
277
278 let kind_len = event.kind().len();
279 let data_len = event.data().len();
280
281 let logic = self.borrow_logic();
282
283 if kind_len > logic.limits.max_event_kind_size {
284 return Err(HostError::EventKindSizeOverflow.into());
285 }
286
287 if data_len > logic.limits.max_event_data_size {
288 return Err(HostError::EventDataSizeOverflow.into());
289 }
290
291 if logic.events.len()
292 >= usize::try_from(logic.limits.max_events).map_err(|_| HostError::IntegerOverflow)?
293 {
294 return Err(HostError::EventsOverflow.into());
295 }
296
297 let kind = self.read_guest_memory_str(event.kind())?.to_owned();
298 let data = self.read_guest_memory_slice(event.data()).to_vec();
299
300 let handler = CURRENT_CALLBACK_HANDLER.with(|name| name.borrow().clone());
302
303 self.with_logic_mut(|logic| {
304 logic.events.push(Event {
305 kind,
306 data,
307 handler,
308 })
309 });
310
311 Ok(())
312 }
313
314 pub fn emit_with_handler(
335 &mut self,
336 src_event_ptr: u64,
337 src_handler_ptr: u64,
338 ) -> VMLogicResult<()> {
339 let event = unsafe { self.read_guest_memory_typed::<sys::Event<'_>>(src_event_ptr)? };
340
341 let kind_len = event.kind().len();
342 let data_len = event.data().len();
343
344 let logic = self.borrow_logic();
345
346 if kind_len > logic.limits.max_event_kind_size {
347 return Err(HostError::EventKindSizeOverflow.into());
348 }
349
350 if data_len > logic.limits.max_event_data_size {
351 return Err(HostError::EventDataSizeOverflow.into());
352 }
353
354 if logic.events.len()
355 >= usize::try_from(logic.limits.max_events).map_err(|_| HostError::IntegerOverflow)?
356 {
357 return Err(HostError::EventsOverflow.into());
358 }
359
360 let kind = self.read_guest_memory_str(event.kind())?.to_owned();
361 let data = self.read_guest_memory_slice(event.data()).to_vec();
362
363 let handler = if src_handler_ptr == 0 {
365 None
366 } else {
367 let handler_buffer =
369 unsafe { self.read_guest_memory_typed::<sys::Buffer<'_>>(src_handler_ptr)? };
370 match self.read_guest_memory_str(&handler_buffer) {
371 Ok(handler_str) => Some(handler_str.to_owned()),
372 Err(_) => None, }
374 };
375
376 self.with_logic_mut(|logic| {
377 logic.events.push(Event {
378 kind,
379 data,
380 handler,
381 })
382 });
383
384 Ok(())
385 }
386
387 pub fn commit(&mut self, src_root_hash_ptr: u64, src_artifact_ptr: u64) -> VMLogicResult<()> {
401 let root_hash =
402 unsafe { self.read_guest_memory_typed::<sys::Buffer<'_>>(src_root_hash_ptr)? };
403 let artifact =
404 unsafe { self.read_guest_memory_typed::<sys::Buffer<'_>>(src_artifact_ptr)? };
405
406 let root_hash = *self.read_guest_memory_sized::<DIGEST_SIZE>(&root_hash)?;
407 let artifact = self.read_guest_memory_slice(&artifact).to_vec();
408
409 self.with_logic_mut(|logic| {
410 if logic.root_hash.is_some() {
411 return Err(HostError::InvalidMemoryAccess);
412 }
413
414 logic.root_hash = Some(root_hash);
415 logic.artifact = artifact;
416
417 Ok(())
418 })?;
419
420 Ok(())
421 }
422}
423
424#[cfg(test)]
425mod tests {
426 use wasmer::{AsStoreMut, Store};
427
428 use crate::errors::{HostError, Location};
429 use crate::logic::{
430 tests::{
431 prepare_guest_buf_descriptor, setup_vm, write_str, SimpleMockStorage, DESCRIPTOR_SIZE,
432 },
433 Cow, VMContext, VMLimits, VMLogic, VMLogicError, DIGEST_SIZE,
434 };
435
436 #[test]
438 fn test_input_and_basic_registers_api() {
439 let input = vec![1u8, 2, 3];
440 let input_len = input.len() as u64;
441 let mut storage = SimpleMockStorage::new();
442 let limits = VMLimits::default();
443 let (mut logic, mut store) = setup_vm!(&mut storage, &limits, input.clone());
444
445 {
446 let mut host = logic.host_functions(store.as_store_mut());
447 let register_id = 1u64;
448
449 host.input(register_id).expect("Input call failed");
451 assert_eq!(host.register_len(register_id).unwrap(), input_len);
453
454 let buf_ptr = 100u64;
455 let data_output_ptr = 200u64;
456 prepare_guest_buf_descriptor(&host, buf_ptr, data_output_ptr, input_len);
458
459 let res = host.read_register(register_id, buf_ptr).unwrap();
461 assert_eq!(res, 1);
463
464 let mut mem_buffer = vec![0u8; input_len as usize];
465 host.borrow_memory()
468 .read(data_output_ptr, &mut mem_buffer)
469 .unwrap();
470 assert_eq!(mem_buffer, input);
471 }
472 }
473
474 #[test]
478 fn test_context_and_executor_id() {
479 let context_id = [3u8; DIGEST_SIZE];
480 let executor_id = [5u8; DIGEST_SIZE];
481 let mut storage = SimpleMockStorage::new();
482 let limits = VMLimits::default();
483 let context = VMContext::new(Cow::Owned(vec![]), context_id, executor_id);
484 let mut logic = VMLogic::new(&mut storage, context, &limits, None);
485
486 let mut store = Store::default();
487 let memory =
488 wasmer::Memory::new(&mut store, wasmer::MemoryType::new(1, None, false)).unwrap();
489 let _ = logic.with_memory(memory);
490 let mut host = logic.host_functions(store.as_store_mut());
491
492 let context_id_register = 1;
493 host.context_id(context_id_register).unwrap();
496 let requested_context_id = host
498 .borrow_logic()
499 .registers
500 .get(context_id_register)
501 .unwrap();
502 assert_eq!(requested_context_id, context_id);
503
504 let executor_id_register = 2;
505 host.executor_id(executor_id_register).unwrap();
508 let requested_executor_id = host
510 .borrow_logic()
511 .registers
512 .get(executor_id_register)
513 .unwrap();
514 assert_eq!(requested_executor_id, executor_id);
515 }
516
517 #[test]
523 fn test_value_return() {
524 let mut storage = SimpleMockStorage::new();
525 let limits = VMLimits::default();
526 let (mut logic, mut store) = setup_vm!(&mut storage, &limits, vec![]);
527 let mut host = logic.host_functions(store.as_store_mut());
528
529 let ok_value = "this is Ok value";
531 let ok_value_ptr = 200u64;
532 write_str(&host, ok_value_ptr, ok_value);
534
535 let ok_discriminant = 0u8;
538 let ok_return_ptr = 32u64;
539 host.borrow_memory()
540 .write(ok_return_ptr, &[ok_discriminant])
541 .unwrap();
542 prepare_guest_buf_descriptor(
544 &host,
545 ok_return_ptr + 8,
546 ok_value_ptr,
547 ok_value.len() as u64,
548 );
549
550 host.value_return(ok_return_ptr).unwrap();
552 let returned_ok_value = host.borrow_logic().returns.clone().unwrap().unwrap();
553 let returned_ok_value_str = std::str::from_utf8(&returned_ok_value).unwrap();
554 assert_eq!(returned_ok_value_str, ok_value);
556
557 let err_value = "this is Err value";
559 let err_value_ptr = 400u64;
560 write_str(&host, err_value_ptr, err_value);
561
562 let err_discriminant = 1u8;
565 let err_return_ptr = 64u64;
566 host.borrow_memory()
567 .write(err_return_ptr, &[err_discriminant])
568 .unwrap();
569 prepare_guest_buf_descriptor(
571 &host,
572 err_return_ptr + 8,
573 err_value_ptr,
574 err_value.len() as u64,
575 );
576
577 host.value_return(err_return_ptr).unwrap();
579 let returned_err_value = host.borrow_logic().returns.clone().unwrap().unwrap_err();
580 let returned_err_value_str = std::str::from_utf8(&returned_err_value).unwrap();
581 assert_eq!(returned_err_value_str, err_value);
583 }
584
585 #[test]
587 fn test_log_utf8() {
588 let mut storage = SimpleMockStorage::new();
589 let limits = VMLimits::default();
590 let (mut logic, mut store) = setup_vm!(&mut storage, &limits, vec![]);
591 let mut host = logic.host_functions(store.as_store_mut());
592
593 let msg = "test log";
594 let msg_ptr = 200u64;
595 write_str(&host, msg_ptr, msg);
597
598 let buf_ptr = 10u64;
599 prepare_guest_buf_descriptor(&host, buf_ptr, msg_ptr, msg.len() as u64);
601 host.log_utf8(buf_ptr).expect("Log failed");
603
604 assert_eq!(host.borrow_logic().logs.len(), 1);
606 assert_eq!(host.borrow_logic().logs[0], "test log");
607 }
608
609 #[test]
612 fn test_log_utf8_overflow() {
613 let mut storage = SimpleMockStorage::new();
614 let mut limits = VMLimits::default();
615 limits.max_logs = 5;
616 let (mut logic, mut store) = setup_vm!(&mut storage, &limits, vec![]);
617 let mut host = logic.host_functions(store.as_store_mut());
618
619 let msg = "log";
620 let msg_ptr = 200u64;
621 write_str(&host, msg_ptr, msg);
623 let buf_ptr = 10u64;
624 prepare_guest_buf_descriptor(&host, buf_ptr, msg_ptr, msg.len() as u64);
626
627 for _ in 0..limits.max_logs {
629 host.log_utf8(buf_ptr).expect("Log failed");
630 }
631
632 assert_eq!(host.borrow_logic().logs.len(), limits.max_logs as usize);
634 let err = host.log_utf8(buf_ptr).unwrap_err();
636 assert_eq!(host.borrow_logic().logs.len(), limits.max_logs as usize);
638 assert!(matches!(
639 err,
640 VMLogicError::HostError(HostError::LogsOverflow)
641 ));
642 }
643
644 #[test]
648 fn test_log_utf8_with_bad_utf8() {
649 let mut storage = SimpleMockStorage::new();
650 let limits = VMLimits::default();
651 let (mut logic, mut store) = setup_vm!(&mut storage, &limits, vec![]);
652 let mut host = logic.host_functions(store.as_store_mut());
653
654 let invalid_utf8: &[u8] = &[0, 159, 146, 150];
656 let data_ptr = 200u64;
657 host.borrow_memory().write(data_ptr, invalid_utf8).unwrap();
658
659 let buf_ptr = 16u64;
660 prepare_guest_buf_descriptor(&host, buf_ptr, data_ptr, invalid_utf8.len() as u64);
661
662 let err = host.log_utf8(buf_ptr).unwrap_err();
664 assert!(matches!(err, VMLogicError::HostError(HostError::BadUTF8)));
665 }
666
667 #[test]
669 fn test_panic() {
670 let mut storage = SimpleMockStorage::new();
671 let limits = VMLimits::default();
672 let (mut logic, mut store) = setup_vm!(&mut storage, &limits, vec![]);
673 let mut host = logic.host_functions(store.as_store_mut());
674
675 let expected_file_name = "simple_panic.rs";
676 let file_ptr = 400u64;
677 write_str(&host, file_ptr, expected_file_name);
679
680 let loc_data_ptr = 300u64;
681 prepare_guest_buf_descriptor(
683 &host,
684 loc_data_ptr,
685 file_ptr,
686 expected_file_name.len() as u64,
687 );
688
689 let expected_line: u32 = 10;
690 let expected_column: u32 = 5;
691 let u32_size: u64 = (u32::BITS / 8).into();
692 host.borrow_memory()
696 .write(
697 loc_data_ptr + DESCRIPTOR_SIZE as u64,
698 &expected_line.to_le_bytes(),
699 )
700 .unwrap();
701 host.borrow_memory()
702 .write(
703 loc_data_ptr + DESCRIPTOR_SIZE as u64 + u32_size,
704 &expected_column.to_le_bytes(),
705 )
706 .unwrap();
707
708 let err = host.panic(loc_data_ptr).unwrap_err();
710 match err {
713 VMLogicError::HostError(HostError::Panic {
714 message, location, ..
715 }) => {
716 assert_eq!(message, "explicit panic");
717 match location {
718 Location::At { file, line, column } => {
719 assert_eq!(file, expected_file_name);
720 assert_eq!(line, expected_line);
721 assert_eq!(column, expected_column);
722 }
723 _ => panic!("Unexpected location variant"),
724 }
725 }
726 _ => panic!("Unexpected error variant"),
727 }
728 }
729
730 #[test]
732 fn test_panic_utf8() {
733 let mut storage = SimpleMockStorage::new();
734 let limits = VMLimits::default();
735 let (mut logic, mut store) = setup_vm!(&mut storage, &limits, vec![]);
736 let mut host = logic.host_functions(store.as_store_mut());
737
738 let expected_msg = "panic message";
739 let msg_ptr = 200u64;
740 write_str(&host, msg_ptr, expected_msg);
742 let msg_buf_ptr = 16u64;
743 prepare_guest_buf_descriptor(&host, msg_buf_ptr, msg_ptr, expected_msg.len() as u64);
745
746 let expected_file_name = "file.rs";
747 let file_ptr = 400u64;
748 write_str(&host, file_ptr, expected_file_name);
750
751 let loc_data_ptr = 300u64;
752 prepare_guest_buf_descriptor(
754 &host,
755 loc_data_ptr,
756 file_ptr,
757 expected_file_name.len() as u64,
758 );
759
760 let expected_line: u32 = 10;
761 let expected_column: u32 = 5;
762 let u32_size: u64 = (u32::BITS / 8).into();
763 host.borrow_memory()
767 .write(
768 loc_data_ptr + DESCRIPTOR_SIZE as u64,
769 &expected_line.to_le_bytes(),
770 )
771 .unwrap();
772 host.borrow_memory()
773 .write(
774 loc_data_ptr + DESCRIPTOR_SIZE as u64 + u32_size,
775 &expected_column.to_le_bytes(),
776 )
777 .unwrap();
778
779 let err = host.panic_utf8(msg_buf_ptr, loc_data_ptr).unwrap_err();
781 match err {
784 VMLogicError::HostError(HostError::Panic {
785 message, location, ..
786 }) => {
787 assert_eq!(message, expected_msg);
788 match location {
789 Location::At { file, line, column } => {
790 assert_eq!(file, expected_file_name);
791 assert_eq!(line, expected_line);
792 assert_eq!(column, expected_column);
793 }
794 _ => panic!("Unexpected location variant"),
795 }
796 }
797 _ => panic!("Unexpected error variant"),
798 }
799 }
800
801 #[test]
803 fn test_emit_and_events_overflow() {
804 let mut storage = SimpleMockStorage::new();
805 let limits = VMLimits::default();
806 let (mut logic, mut store) = setup_vm!(&mut storage, &limits, vec![]);
807 let mut host = logic.host_functions(store.as_store_mut());
808
809 let kind = "my-event";
811 let data = vec![1, 2, 3];
812 let kind_ptr = 200u64;
813 let data_ptr = 300u64;
814 write_str(&host, kind_ptr, kind);
816 host.borrow_memory().write(data_ptr, &data).unwrap();
817
818 let event_struct_ptr = 48u64;
820 let kind_buf_ptr = event_struct_ptr;
821 let data_buf_ptr = event_struct_ptr + DESCRIPTOR_SIZE as u64;
822 prepare_guest_buf_descriptor(&host, kind_buf_ptr, kind_ptr, kind.len() as u64);
823 prepare_guest_buf_descriptor(&host, data_buf_ptr, data_ptr, data.len() as u64);
824
825 host.emit(event_struct_ptr).unwrap();
827 assert_eq!(host.borrow_logic().events.len(), 1);
829 assert_eq!(host.borrow_logic().events[0].kind, kind);
830 assert_eq!(host.borrow_logic().events[0].data, data);
831
832 for _ in 1..limits.max_events {
834 host.emit(event_struct_ptr).unwrap();
835 }
836 assert_eq!(host.borrow_logic().events.len() as u64, limits.max_events);
837 let err = host.emit(event_struct_ptr).unwrap_err();
839 assert!(matches!(
841 err,
842 VMLogicError::HostError(HostError::EventsOverflow)
843 ));
844 }
845
846 #[test]
848 fn test_commit() {
849 let mut storage = SimpleMockStorage::new();
850 let limits = VMLimits::default();
851 let (mut logic, mut store) = setup_vm!(&mut storage, &limits, vec![]);
852 let mut host = logic.host_functions(store.as_store_mut());
853
854 let root_hash = [1u8; DIGEST_SIZE];
855 let artifact = vec![1, 2, 3];
856 let root_hash_ptr = 200u64;
857 let artifact_ptr = 300u64;
858 host.borrow_memory()
859 .write(root_hash_ptr, &root_hash)
860 .unwrap();
861 host.borrow_memory().write(artifact_ptr, &artifact).unwrap();
862
863 let root_hash_buf_ptr = 16u64;
864 let artifact_buf_ptr = 32u64;
865 prepare_guest_buf_descriptor(
867 &host,
868 root_hash_buf_ptr,
869 root_hash_ptr,
870 root_hash.len() as u64,
871 );
872 prepare_guest_buf_descriptor(&host, artifact_buf_ptr, artifact_ptr, artifact.len() as u64);
873
874 host.commit(root_hash_buf_ptr, artifact_buf_ptr).unwrap();
876 assert_eq!(host.borrow_logic().root_hash, Some(root_hash));
878 assert_eq!(host.borrow_logic().artifact, artifact);
879 }
880}