1#![cfg_attr(not(feature = "std"), no_std)]
21
22extern crate alloc;
23
24use alloc::{borrow::Cow, string::String, vec, vec::Vec};
25use core::{iter::Iterator, marker::PhantomData, mem, result};
26
27#[cfg(not(all(feature = "std", feature = "wasmtime")))]
28#[macro_export]
29macro_rules! if_wasmtime_is_enabled {
30 ($($token:tt)*) => {};
31}
32
33#[cfg(all(feature = "std", feature = "wasmtime"))]
34#[macro_export]
35macro_rules! if_wasmtime_is_enabled {
36 ($($token:tt)*) => {
37 $($token)*
38 }
39}
40
41if_wasmtime_is_enabled! {
42 pub use wasmtime;
44
45 pub use anyhow;
47}
48
49pub type Result<T> = core::result::Result<T, String>;
51
52#[derive(Copy, Clone, PartialEq, Debug, Eq)]
54pub enum ValueType {
55 I32,
57 I64,
59 F32,
61 F64,
63}
64
65impl From<ValueType> for u8 {
66 fn from(val: ValueType) -> u8 {
67 match val {
68 ValueType::I32 => 0,
69 ValueType::I64 => 1,
70 ValueType::F32 => 2,
71 ValueType::F64 => 3,
72 }
73 }
74}
75
76impl TryFrom<u8> for ValueType {
77 type Error = ();
78
79 fn try_from(val: u8) -> core::result::Result<ValueType, ()> {
80 match val {
81 0 => Ok(Self::I32),
82 1 => Ok(Self::I64),
83 2 => Ok(Self::F32),
84 3 => Ok(Self::F64),
85 _ => Err(()),
86 }
87 }
88}
89
90#[derive(PartialEq, Debug, Clone, Copy, codec::Encode, codec::Decode)]
92pub enum Value {
93 I32(i32),
95 I64(i64),
97 F32(u32),
101 F64(u64),
105}
106
107impl Value {
108 pub fn value_type(&self) -> ValueType {
110 match self {
111 Value::I32(_) => ValueType::I32,
112 Value::I64(_) => ValueType::I64,
113 Value::F32(_) => ValueType::F32,
114 Value::F64(_) => ValueType::F64,
115 }
116 }
117
118 pub fn as_i32(&self) -> Option<i32> {
120 match self {
121 Self::I32(val) => Some(*val),
122 _ => None,
123 }
124 }
125}
126
127mod private {
130 pub trait Sealed {}
131
132 impl Sealed for u8 {}
133 impl Sealed for u16 {}
134 impl Sealed for u32 {}
135 impl Sealed for u64 {}
136
137 impl Sealed for i32 {}
138 impl Sealed for i64 {}
139}
140
141pub trait PointerType: Sized + private::Sealed {
145 const SIZE: u32 = mem::size_of::<Self>() as u32;
147}
148
149impl PointerType for u8 {}
150impl PointerType for u16 {}
151impl PointerType for u32 {}
152impl PointerType for u64 {}
153
154#[derive(Debug, PartialEq, Eq)]
156pub struct Pointer<T> {
157 ptr: u32,
158 _marker: PhantomData<T>,
159}
160
161impl<T> Copy for Pointer<T> {}
162impl<T> Clone for Pointer<T> {
163 fn clone(&self) -> Self {
164 Pointer { ptr: self.ptr, _marker: PhantomData }
165 }
166}
167
168impl<T: PointerType> Pointer<T> {
169 pub fn new(ptr: u32) -> Self {
171 Self { ptr, _marker: Default::default() }
172 }
173
174 pub fn offset(self, offset: u32) -> Option<Self> {
181 offset
182 .checked_mul(T::SIZE)
183 .and_then(|o| self.ptr.checked_add(o))
184 .map(|ptr| Self { ptr, _marker: Default::default() })
185 }
186
187 pub fn null() -> Self {
189 Self::new(0)
190 }
191
192 pub fn cast<R: PointerType>(self) -> Pointer<R> {
194 Pointer::new(self.ptr)
195 }
196}
197
198impl<T: PointerType> From<u32> for Pointer<T> {
199 fn from(ptr: u32) -> Self {
200 Pointer::new(ptr)
201 }
202}
203
204impl<T: PointerType> From<Pointer<T>> for u32 {
205 fn from(ptr: Pointer<T>) -> Self {
206 ptr.ptr
207 }
208}
209
210impl<T: PointerType> From<Pointer<T>> for u64 {
211 fn from(ptr: Pointer<T>) -> Self {
212 u64::from(ptr.ptr)
213 }
214}
215
216impl<T: PointerType> From<Pointer<T>> for usize {
217 fn from(ptr: Pointer<T>) -> Self {
218 ptr.ptr as _
219 }
220}
221
222impl<T: PointerType> IntoValue for Pointer<T> {
223 const VALUE_TYPE: ValueType = ValueType::I32;
224 fn into_value(self) -> Value {
225 Value::I32(self.ptr as _)
226 }
227}
228
229impl<T: PointerType> TryFromValue for Pointer<T> {
230 fn try_from_value(val: Value) -> Option<Self> {
231 match val {
232 Value::I32(val) => Some(Self::new(val as _)),
233 _ => None,
234 }
235 }
236}
237
238pub type WordSize = u32;
240
241#[derive(Eq, PartialEq, Debug, Clone)]
243pub struct Signature {
244 pub args: Cow<'static, [ValueType]>,
246 pub return_value: Option<ValueType>,
248}
249
250impl Signature {
251 pub fn new<T: Into<Cow<'static, [ValueType]>>>(
253 args: T,
254 return_value: Option<ValueType>,
255 ) -> Self {
256 Self { args: args.into(), return_value }
257 }
258
259 pub fn new_with_args<T: Into<Cow<'static, [ValueType]>>>(args: T) -> Self {
261 Self { args: args.into(), return_value: None }
262 }
263}
264
265#[cfg(feature = "std")]
267pub trait MaybeRefUnwindSafe: std::panic::RefUnwindSafe {}
268#[cfg(feature = "std")]
269impl<T: std::panic::RefUnwindSafe> MaybeRefUnwindSafe for T {}
270
271#[cfg(not(feature = "std"))]
273pub trait MaybeRefUnwindSafe {}
274#[cfg(not(feature = "std"))]
275impl<T> MaybeRefUnwindSafe for T {}
276
277pub trait Function: MaybeRefUnwindSafe + Send + Sync {
279 fn name(&self) -> &str;
281 fn signature(&self) -> Signature;
283 fn execute(
285 &self,
286 context: &mut dyn FunctionContext,
287 args: &mut dyn Iterator<Item = Value>,
288 ) -> Result<Option<Value>>;
289}
290
291impl PartialEq for dyn Function {
292 fn eq(&self, other: &Self) -> bool {
293 other.name() == self.name() && other.signature() == self.signature()
294 }
295}
296
297pub trait FunctionContext {
299 fn read_memory(&mut self, address: Pointer<u8>, size: WordSize) -> Result<Vec<u8>> {
301 let mut vec = vec![0; size as usize];
302 self.read_memory_into(address, &mut vec)?;
303 Ok(vec)
304 }
305 fn read_memory_into(&mut self, address: Pointer<u8>, dest: &mut [u8]) -> Result<()>;
307 fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> Result<()>;
309 fn allocate_memory(&mut self, size: WordSize) -> Result<Pointer<u8>>;
311 fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> Result<()>;
313 fn register_panic_error_message(&mut self, message: &str);
335 fn virtualization(&mut self) -> &mut dyn Virtualization;
337}
338
339pub enum ExecAction<'a> {
344 Execute(&'a str),
346 Resume(u64),
350}
351
352#[derive(Debug, PartialEq, Eq)]
356pub enum ExecOutcome {
357 Finished {
359 gas_left: i64,
361 },
362 Syscall {
365 gas_left: i64,
367 syscall_no: u32,
369 a0: u64,
371 a1: u64,
372 a2: u64,
373 a3: u64,
374 a4: u64,
375 a5: u64,
376 },
377}
378
379#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
381#[repr(transparent)]
382pub struct InstanceId(pub u32);
383
384pub trait Virtualization {
386 fn instantiate(&mut self, program: &[u8]) -> Result<result::Result<InstanceId, u8>>;
387
388 fn run(
396 &mut self,
397 instance_id: InstanceId,
398 gas_left: i64,
399 action: ExecAction<'_>,
400 ) -> Result<result::Result<ExecOutcome, u8>>;
401
402 fn destroy(&mut self, instance_id: InstanceId) -> Result<result::Result<(), u8>>;
403
404 fn read_memory(
405 &mut self,
406 instance_id: InstanceId,
407 offset: u32,
408 dest: &mut [u8],
409 ) -> Result<result::Result<(), u8>>;
410
411 fn write_memory(
412 &mut self,
413 instance_id: InstanceId,
414 offset: u32,
415 src: &[u8],
416 ) -> Result<result::Result<(), u8>>;
417}
418
419if_wasmtime_is_enabled! {
420 pub trait HostFunctionRegistry {
427 type State: 'static;
428 type Error;
429 type FunctionContext: FunctionContext;
430
431 fn with_function_context<R>(
434 caller: wasmtime::Caller<Self::State>,
435 callback: impl FnOnce(&mut dyn FunctionContext) -> R,
436 ) -> R;
437
438 fn register_static<Params, Results>(
443 &mut self,
444 fn_name: &str,
445 func: impl wasmtime::IntoFunc<Self::State, Params, Results> + 'static,
446 ) -> core::result::Result<(), Self::Error>;
447 }
448}
449
450pub trait HostFunctions: 'static + Send + Sync {
452 fn host_functions() -> Vec<&'static dyn Function>;
454
455 if_wasmtime_is_enabled! {
456 fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
458 where
459 T: HostFunctionRegistry;
460 }
461}
462
463#[impl_trait_for_tuples::impl_for_tuples(30)]
464impl HostFunctions for Tuple {
465 fn host_functions() -> Vec<&'static dyn Function> {
466 let mut host_functions = Vec::new();
467
468 for_tuples!( #( host_functions.extend(Tuple::host_functions()); )* );
469
470 host_functions
471 }
472
473 #[cfg(all(feature = "std", feature = "wasmtime"))]
474 fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
475 where
476 T: HostFunctionRegistry,
477 {
478 for_tuples!(
479 #( Tuple::register_static(registry)?; )*
480 );
481
482 Ok(())
483 }
484}
485
486pub struct ExtendedHostFunctions<Base, Overlay> {
489 phantom: PhantomData<(Base, Overlay)>,
490}
491
492impl<Base, Overlay> HostFunctions for ExtendedHostFunctions<Base, Overlay>
493where
494 Base: HostFunctions,
495 Overlay: HostFunctions,
496{
497 fn host_functions() -> Vec<&'static dyn Function> {
498 let mut base = Base::host_functions();
499 let overlay = Overlay::host_functions();
500 base.retain(|host_fn| {
501 !overlay.iter().any(|ext_host_fn| host_fn.name() == ext_host_fn.name())
502 });
503 base.extend(overlay);
504 base
505 }
506
507 if_wasmtime_is_enabled! {
508 fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
509 where
510 T: HostFunctionRegistry,
511 {
512 struct Proxy<'a, T> {
513 registry: &'a mut T,
514 seen_overlay: std::collections::HashSet<String>,
515 seen_base: std::collections::HashSet<String>,
516 overlay_registered: bool,
517 }
518
519 impl<'a, T> HostFunctionRegistry for Proxy<'a, T>
520 where
521 T: HostFunctionRegistry,
522 {
523 type State = T::State;
524 type Error = T::Error;
525 type FunctionContext = T::FunctionContext;
526
527 fn with_function_context<R>(
528 caller: wasmtime::Caller<Self::State>,
529 callback: impl FnOnce(&mut dyn FunctionContext) -> R,
530 ) -> R {
531 T::with_function_context(caller, callback)
532 }
533
534 fn register_static<Params, Results>(
535 &mut self,
536 fn_name: &str,
537 func: impl wasmtime::IntoFunc<Self::State, Params, Results> + 'static,
538 ) -> core::result::Result<(), Self::Error> {
539 if self.overlay_registered {
540 if !self.seen_base.insert(fn_name.to_owned()) {
541 log::warn!(
542 target: "extended_host_functions",
543 "Duplicate base host function: '{}'",
544 fn_name,
545 );
546
547 return Ok(())
549 }
550
551 if self.seen_overlay.contains(fn_name) {
552 log::debug!(
554 target: "extended_host_functions",
555 "Overriding base host function: '{}'",
556 fn_name,
557 );
558
559 return Ok(())
560 }
561 } else if !self.seen_overlay.insert(fn_name.to_owned()) {
562 log::warn!(
563 target: "extended_host_functions",
564 "Duplicate overlay host function: '{}'",
565 fn_name,
566 );
567
568 return Ok(())
570 }
571
572 self.registry.register_static(fn_name, func)
573 }
574 }
575
576 let mut proxy = Proxy {
577 registry,
578 seen_overlay: Default::default(),
579 seen_base: Default::default(),
580 overlay_registered: false,
581 };
582
583 Overlay::register_static(&mut proxy)?;
587 proxy.overlay_registered = true;
588 Base::register_static(&mut proxy)?;
589
590 Ok(())
591 }
592 }
593}
594
595#[cfg(all(feature = "std", feature = "wasmtime"))]
599pub trait WasmTy: wasmtime::WasmTy + private::Sealed {}
600
601#[cfg(not(all(feature = "std", feature = "wasmtime")))]
605pub trait WasmTy: private::Sealed {}
606
607impl WasmTy for i32 {}
608impl WasmTy for u32 {}
609impl WasmTy for i64 {}
610impl WasmTy for u64 {}
611
612pub trait IntoValue {
614 const VALUE_TYPE: ValueType;
616
617 fn into_value(self) -> Value;
619}
620
621pub trait TryFromValue: Sized {
623 fn try_from_value(val: Value) -> Option<Self>;
625}
626
627macro_rules! impl_into_and_from_value {
628 (
629 $(
630 $type:ty, $( < $gen:ident >, )? $value_variant:ident,
631 )*
632 ) => {
633 $(
634 impl $( <$gen> )? IntoValue for $type {
635 const VALUE_TYPE: ValueType = ValueType::$value_variant;
636 fn into_value(self) -> Value { Value::$value_variant(self as _) }
637 }
638
639 impl $( <$gen> )? TryFromValue for $type {
640 fn try_from_value(val: Value) -> Option<Self> {
641 match val {
642 Value::$value_variant(val) => Some(val as _),
643 _ => None,
644 }
645 }
646 }
647 )*
648 }
649}
650
651impl_into_and_from_value! {
652 u8, I32,
653 u16, I32,
654 u32, I32,
655 u64, I64,
656 i8, I32,
657 i16, I32,
658 i32, I32,
659 i64, I64,
660}
661
662#[derive(Clone, Copy, PartialEq, codec::Encode, codec::Decode, Debug)]
666pub enum ReturnValue {
667 Unit,
669 Value(Value),
671}
672
673impl From<Value> for ReturnValue {
674 fn from(v: Value) -> ReturnValue {
675 ReturnValue::Value(v)
676 }
677}
678
679impl ReturnValue {
680 pub const ENCODED_MAX_SIZE: usize = 10;
687}
688
689#[cfg(test)]
690mod tests {
691 use super::*;
692 use codec::Encode;
693
694 #[test]
695 fn pointer_offset_works() {
696 let ptr = Pointer::<u32>::null();
697
698 assert_eq!(ptr.offset(10).unwrap(), Pointer::new(40));
699 assert_eq!(ptr.offset(32).unwrap(), Pointer::new(128));
700
701 let ptr = Pointer::<u64>::null();
702
703 assert_eq!(ptr.offset(10).unwrap(), Pointer::new(80));
704 assert_eq!(ptr.offset(32).unwrap(), Pointer::new(256));
705 }
706
707 #[test]
708 fn return_value_encoded_max_size() {
709 let encoded = ReturnValue::Value(Value::I64(-1)).encode();
710 assert_eq!(encoded.len(), ReturnValue::ENCODED_MAX_SIZE);
711 }
712}