kms_secp256k1_api/
wasm_loader.rs1use std::{error::Error, sync::Arc};
2use tokio::sync::OnceCell;
3use tracing::info;
4use wasmtime::{Engine, Func, Instance, Memory, Module, Store};
5
6pub struct WasmLoader {
7 engine: Engine,
8 module: Module,
9}
10
11pub struct WasmInstance {
12 pub memory: Memory,
13 pub alloc: Func,
14 pub free: Func,
15 pub public_key: Func,
16 pub convert: Func,
17 pub unconvert: Func,
18 pub verify: Func,
19 pub verify_eip155: Func,
20 pub recover_v: Func,
21 pub address_eth: Func,
22 pub address_cosmos: Func,
23 pub store: Store<()>,
24 pub instance: Instance,
25}
26
27static WASM_INSTANCE: OnceCell<Arc<WasmLoader>> = OnceCell::const_new();
28
29impl WasmLoader {
30 pub async fn new(wasm_path: &str) -> Result<Arc<Self>, Box<dyn Error>> {
38 WASM_INSTANCE
39 .get_or_try_init(|| async move {
40 let engine = Engine::default();
41 let module = Module::from_file(&engine, wasm_path)?;
42 info!("WASM module loaded from {wasm_path}");
43 Ok(Arc::new(Self { engine, module }))
44 })
45 .await
46 .map(Arc::clone)
47 }
48
49 pub fn instantiate(&self) -> Result<WasmInstance, Box<dyn Error>> {
58 let mut store = Store::new(&self.engine, ());
59 let instance = Instance::new(&mut store, &self.module, &[])?;
60
61 let memory = instance
62 .get_memory(&mut store, "memory")
63 .ok_or("failed to find memory export")?;
64
65 let alloc = instance
66 .get_func(&mut store, "alloc")
67 .ok_or("failed to find alloc export")?;
68
69 let free = instance
70 .get_func(&mut store, "free")
71 .ok_or("failed to find free export")?;
72
73 let public_key = instance
74 .get_func(&mut store, "public_key")
75 .ok_or("failed to find public_key export")?;
76
77 let verify = instance
78 .get_func(&mut store, "verify")
79 .ok_or("failed to find verify export")?;
80
81 let verify_eip155 = instance
82 .get_func(&mut store, "verify_eip155")
83 .ok_or("failed to find verify_eip155 export")?;
84
85 let convert = instance
86 .get_func(&mut store, "convert")
87 .ok_or("failed to find convert export")?;
88
89 let unconvert = instance
90 .get_func(&mut store, "unconvert")
91 .ok_or("failed to find unconvert export")?;
92
93 let recover_v = instance
94 .get_func(&mut store, "recover_v")
95 .ok_or("failed to find recover_v export")?;
96
97 let address_eth = instance
98 .get_func(&mut store, "address_eth")
99 .ok_or("failed to find address_eth export")?;
100
101 let address_cosmos = instance
102 .get_func(&mut store, "address_cosmos")
103 .ok_or("failed to find address_cosmos export")?;
104
105 Ok(WasmInstance {
106 memory,
107 alloc,
108 free,
109 public_key,
110 convert,
111 unconvert,
112 verify,
113 verify_eip155,
114 recover_v,
115 address_eth,
116 address_cosmos,
117 store,
118 instance,
119 })
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use crate::constants::WASM_PATH;
126
127 use super::*;
128
129 #[tokio::test]
130 async fn test_wasm_loader_singleton_loads_module() {
131 let loader1 = WasmLoader::new(WASM_PATH).await.unwrap();
133
134 let loader2 = WasmLoader::new(WASM_PATH).await.unwrap();
136
137 assert!(Arc::ptr_eq(&loader1, &loader2));
138
139 let module_bytes = loader1.module.serialize().unwrap();
140 assert!(!module_bytes.is_empty(), "Module bytes should not be empty");
141 }
142
143 #[tokio::test]
144 async fn test_instantiate_wasm_instance() {
145 let loader = WasmLoader::new(WASM_PATH)
146 .await
147 .expect("Failed to load wasm module");
148
149 let wasm_instance = loader
150 .instantiate()
151 .expect("Failed to instantiate wasm instance");
152
153 assert!(
155 wasm_instance.memory.size(&wasm_instance.store) > 0,
156 "Memory size should be > 0"
157 );
158
159 let alloc_ty = wasm_instance.alloc.ty(&wasm_instance.store);
161 assert_eq!(alloc_ty.params().len(), 1, "alloc should take 1 parameter");
162 assert_eq!(alloc_ty.results().len(), 1, "alloc should return 1 result");
163
164 let free_ty = wasm_instance.free.ty(&wasm_instance.store);
166 assert_eq!(free_ty.params().len(), 2, "free should take 2 parameters");
167 assert_eq!(free_ty.results().len(), 0, "free should return no results");
168
169 let public_key_ty = wasm_instance.public_key.ty(&wasm_instance.store);
171 assert!(
172 public_key_ty.params().len() > 0,
173 "public_key should have at least one parameter"
174 );
175
176 let verify_ty = wasm_instance.verify.ty(&wasm_instance.store);
178 assert!(
179 verify_ty.params().len() > 0,
180 "verify should have at least one parameter"
181 );
182
183 let convert_ty = wasm_instance.convert.ty(&wasm_instance.store);
185 assert!(
186 convert_ty.params().len() > 0,
187 "convert should have at least one parameter"
188 );
189
190 let unconvert_ty = wasm_instance.unconvert.ty(&wasm_instance.store);
192 assert!(
193 unconvert_ty.params().len() > 0,
194 "unconvert should have at least one parameter"
195 );
196
197 let recover_v_ty = wasm_instance.recover_v.ty(&wasm_instance.store);
199 assert!(
200 recover_v_ty.params().len() > 0,
201 "recover_v should have at least one parameter"
202 );
203
204 let address_eth_ty = wasm_instance.address_eth.ty(&wasm_instance.store);
205 assert!(
206 address_eth_ty.params().len() > 0,
207 "address_eth should have at least one parameter"
208 );
209
210 let address_cosmos_ty = wasm_instance.address_cosmos.ty(&wasm_instance.store);
211 assert!(
212 address_cosmos_ty.params().len() > 0,
213 "address_cosmos should have at least one parameter"
214 );
215 }
216}