soil_client/executor/common/runtime_blob/
runtime_blob.rs1use crate::executor::common::{error::WasmError, wasm_runtime::HeapAllocStrategy};
8use polkavm::ArcBytes;
9use wasm_instrument::parity_wasm::elements::{
10 deserialize_buffer, serialize, ExportEntry, External, Internal, MemorySection, MemoryType,
11 Module, Section,
12};
13
14#[derive(Clone)]
16pub struct RuntimeBlob(BlobKind);
17
18#[derive(Clone)]
19enum BlobKind {
20 WebAssembly(Module),
21 PolkaVM((polkavm::ProgramBlob, ArcBytes)),
22}
23
24impl RuntimeBlob {
25 pub fn uncompress_if_needed(wasm_code: &[u8]) -> Result<Self, WasmError> {
29 use crate::maybe_compressed_blob::CODE_BLOB_BOMB_LIMIT;
30 let wasm_code = crate::maybe_compressed_blob::decompress(wasm_code, CODE_BLOB_BOMB_LIMIT)
31 .map_err(|e| WasmError::Other(format!("Decompression error: {:?}", e)))?;
32 Self::new(&wasm_code)
33 }
34
35 pub fn new(raw_blob: &[u8]) -> Result<Self, WasmError> {
42 if raw_blob.starts_with(b"PVM\0") {
43 if crate::executor::common::is_polkavm_enabled() {
44 let raw = ArcBytes::from(raw_blob);
45 let blob = polkavm::ProgramBlob::parse(raw.clone())?;
46 return Ok(Self(BlobKind::PolkaVM((blob, raw))));
47 } else {
48 return Err(WasmError::Other("expected a WASM runtime blob, found a PolkaVM runtime blob; set the 'SUBSTRATE_ENABLE_POLKAVM' environment variable to enable the experimental PolkaVM-based executor".to_string()));
49 }
50 }
51
52 let raw_module: Module = deserialize_buffer(raw_blob)
53 .map_err(|e| WasmError::Other(format!("cannot deserialize module: {:?}", e)))?;
54 Ok(Self(BlobKind::WebAssembly(raw_module)))
55 }
56
57 pub fn inject_stack_depth_metering(self, stack_depth_limit: u32) -> Result<Self, WasmError> {
70 let injected_module =
71 wasm_instrument::inject_stack_limiter(self.into_webassembly_blob()?, stack_depth_limit)
72 .map_err(|e| {
73 WasmError::Other(format!("cannot inject the stack limiter: {:?}", e))
74 })?;
75
76 Ok(Self(BlobKind::WebAssembly(injected_module)))
77 }
78
79 pub fn convert_memory_import_into_export(&mut self) -> Result<(), WasmError> {
87 let raw_module = self.as_webassembly_blob_mut()?;
88 let import_section = match raw_module.import_section_mut() {
89 Some(import_section) => import_section,
90 None => return Ok(()),
91 };
92
93 let import_entries = import_section.entries_mut();
94 for index in 0..import_entries.len() {
95 let entry = &import_entries[index];
96 let memory_ty = match entry.external() {
97 External::Memory(memory_ty) => *memory_ty,
98 _ => continue,
99 };
100
101 let memory_name = entry.field().to_owned();
102 import_entries.remove(index);
103
104 raw_module
105 .insert_section(Section::Memory(MemorySection::with_entries(vec![memory_ty])))
106 .map_err(|error| {
107 WasmError::Other(format!(
108 "can't convert a memory import into an export: failed to insert a new memory section: {}",
109 error
110 ))
111 })?;
112
113 if raw_module.export_section_mut().is_none() {
114 raw_module
117 .insert_section(Section::Export(Default::default()))
118 .expect("an export section can be always inserted if it doesn't exist; qed");
119 }
120 raw_module
121 .export_section_mut()
122 .expect("export section already existed or we just added it above, so it always exists; qed")
123 .entries_mut()
124 .push(ExportEntry::new(memory_name, Internal::Memory(0)));
125
126 break;
127 }
128
129 Ok(())
130 }
131
132 pub fn setup_memory_according_to_heap_alloc_strategy(
139 &mut self,
140 heap_alloc_strategy: HeapAllocStrategy,
141 ) -> Result<(), WasmError> {
142 let raw_module = self.as_webassembly_blob_mut()?;
143 let memory_section = raw_module
144 .memory_section_mut()
145 .ok_or_else(|| WasmError::Other("no memory section found".into()))?;
146
147 if memory_section.entries().is_empty() {
148 return Err(WasmError::Other("memory section is empty".into()));
149 }
150 for memory_ty in memory_section.entries_mut() {
151 let initial = memory_ty.limits().initial();
152 let (min, max) = match heap_alloc_strategy {
153 HeapAllocStrategy::Dynamic { maximum_pages } => {
154 (maximum_pages.map(|m| m.min(initial)).unwrap_or(initial), maximum_pages)
156 },
157 HeapAllocStrategy::Static { extra_pages } => {
158 let pages = initial.saturating_add(extra_pages);
159 (pages, Some(pages))
160 },
161 };
162 *memory_ty = MemoryType::new(min, max);
163 }
164 Ok(())
165 }
166
167 pub fn custom_section_contents(&self, section_name: &str) -> Option<&[u8]> {
172 self.as_webassembly_blob()
173 .ok()?
174 .custom_sections()
175 .find(|cs| cs.name() == section_name)
176 .map(|cs| cs.payload())
177 }
178
179 pub fn serialize(self) -> Vec<u8> {
181 match self.0 {
182 BlobKind::WebAssembly(raw_module) => {
183 serialize(raw_module).expect("serializing into a vec should succeed; qed")
184 },
185 BlobKind::PolkaVM(ref blob) => blob.1.to_vec(),
186 }
187 }
188
189 fn as_webassembly_blob(&self) -> Result<&Module, WasmError> {
190 match self.0 {
191 BlobKind::WebAssembly(ref raw_module) => Ok(raw_module),
192 BlobKind::PolkaVM(..) => Err(WasmError::Other(
193 "expected a WebAssembly program; found a PolkaVM program blob".into(),
194 )),
195 }
196 }
197
198 fn as_webassembly_blob_mut(&mut self) -> Result<&mut Module, WasmError> {
199 match self.0 {
200 BlobKind::WebAssembly(ref mut raw_module) => Ok(raw_module),
201 BlobKind::PolkaVM(..) => Err(WasmError::Other(
202 "expected a WebAssembly program; found a PolkaVM program blob".into(),
203 )),
204 }
205 }
206
207 fn into_webassembly_blob(self) -> Result<Module, WasmError> {
208 match self.0 {
209 BlobKind::WebAssembly(raw_module) => Ok(raw_module),
210 BlobKind::PolkaVM(..) => Err(WasmError::Other(
211 "expected a WebAssembly program; found a PolkaVM program blob".into(),
212 )),
213 }
214 }
215
216 pub fn as_polkavm_blob(&self) -> Option<&polkavm::ProgramBlob> {
218 match self.0 {
219 BlobKind::WebAssembly(..) => None,
220 BlobKind::PolkaVM((ref blob, _)) => Some(blob),
221 }
222 }
223}