1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! 🎁 Applet module

/// Applet creation & update trait
pub trait Applet {
    /// Creation of a new module
    ///
    /// This is called once on startup of the module
    fn new() -> Self;

    /// Implement the update of the applet module here
    fn update(&mut self) {}
}

/// Persistable applet state accessor
#[cfg(feature = "with_serde")]
pub trait AppletPersist<T>
where
    T: serde::de::DeserializeOwned + serde::Serialize,
{
    /// Accessor to Serde-serializable structure for serializing and deserializing state
    fn persistable_state(&mut self) -> &mut T;

    /// Optional callback just before state is persisted
    fn pre_persist(&mut self) {}

    /// Optional callback after state has een restored
    fn post_restore(&mut self) {}
}

/// Implement an applet module
#[macro_export]
macro_rules! impl_applet {
    ($module: ty) => {
        use $crate::applet::Applet as _;

        static mut MODULE: Option<$module> = None;

        #[no_mangle]
        pub fn ark_initialize() {
            $crate::init();
            // SAFETY: Sound to access static as we do not use threads in Wasm and this is guaranteed to be called first in the module
            unsafe {
                MODULE = Some(<$module as $crate::applet::Applet>::new());
            }
        }

        #[no_mangle]
        pub fn ark_applet_update() {
            // SAFETY: Sound to access static as we do not use threads in Wasm and this is guaranteed to be initialized on startup with `ark_initialize` first
            unsafe {
                let module = MODULE.as_mut().unwrap();
                <$module as $crate::applet::Applet>::update(module);
            }
        }
    };
}

/// Implement an applet module that can have persistent state
#[macro_export]
#[cfg(feature = "with_serde")]
macro_rules! impl_applet_persist {
    ($module: ty) => {
        $crate::impl_applet!($module);

        use $crate::applet::AppletPersist as _;
        use $crate::ErrorCode;

        #[no_mangle]
        pub unsafe fn ark_state_persist() -> ErrorCode {
            if let Some(module) = MODULE.as_mut() {
                module.pre_persist();
                match $crate::applet::serialize_state(module.persistable_state()) {
                    Ok(state) => {
                        ark::core::return_slice(&state);
                        ErrorCode::Success
                    }
                    Err(err) => {
                        $crate::error!("Couldn't save state, err: {:?}", &err);
                        ErrorCode::InternalError
                    }
                }
            } else {
                ErrorCode::InternalError
            }
        }

        #[no_mangle]
        pub unsafe fn ark_state_restore(state_ptr: *const u8, state_size: u32) -> ErrorCode {
            let state_slice = std::slice::from_raw_parts(state_ptr, state_size as usize);

            if let Some(module) = MODULE.as_mut() {
                let module_state = module.persistable_state();

                match $crate::applet::deserialize_state(state_slice) {
                    Ok(new_state) => {
                        *module_state = new_state;
                        module.post_restore();
                        ErrorCode::Success
                    }
                    Err(err) => {
                        $crate::error!("Couldn't load state, err: {:?}", &err);
                        ErrorCode::InternalError
                    }
                }
            } else {
                ErrorCode::InternalError
            }
        }
    };
}

/// Serialize applet state to binary blob
///
/// This is an internal function used by the `impl_applet_persist` macro
#[doc(hidden)]
#[cfg(feature = "with_serde")]
pub fn serialize_state<T: ?Sized>(value: &T) -> Result<Vec<u8>, flexbuffers::SerializationError>
where
    T: serde::Serialize,
{
    let mut serializer = flexbuffers::FlexbufferSerializer::new();
    value.serialize(&mut serializer)?;
    let vec = serializer.take_buffer();
    Ok(vec)
}

/// Deserialize applet from binary blob
///
/// This is an internal function used by the `impl_applet_persist` macro
#[doc(hidden)]
#[cfg(feature = "with_serde")]
pub fn deserialize_state<'a, T>(bytes: &'a [u8]) -> Result<T, flexbuffers::DeserializationError>
where
    T: serde::Deserialize<'a>,
{
    let reader = flexbuffers::Reader::get_root(bytes)?;
    let v = T::deserialize(reader)?;
    Ok(v)
}