sc_executor_common/runtime_blob/
runtime_blob.rs1use crate::{error::WasmError, wasm_runtime::HeapAllocStrategy};
20use polkavm::ArcBytes;
21use wasm_instrument::parity_wasm::elements::{
22 deserialize_buffer, serialize, ExportEntry, External, Internal, MemorySection, MemoryType,
23 Module, Section,
24};
25
26#[derive(Clone)]
28pub struct RuntimeBlob(BlobKind);
29
30#[derive(Clone)]
31enum BlobKind {
32 WebAssembly(Module),
33 PolkaVM((polkavm::ProgramBlob, ArcBytes)),
34}
35
36impl RuntimeBlob {
37 pub fn uncompress_if_needed(wasm_code: &[u8]) -> Result<Self, WasmError> {
41 use sp_maybe_compressed_blob::CODE_BLOB_BOMB_LIMIT;
42 let wasm_code = sp_maybe_compressed_blob::decompress(wasm_code, CODE_BLOB_BOMB_LIMIT)
43 .map_err(|e| WasmError::Other(format!("Decompression error: {:?}", e)))?;
44 Self::new(&wasm_code)
45 }
46
47 pub fn new(raw_blob: &[u8]) -> Result<Self, WasmError> {
54 if raw_blob.starts_with(b"PVM\0") {
55 if crate::is_polkavm_enabled() {
56 let raw = ArcBytes::from(raw_blob);
57 let blob = polkavm::ProgramBlob::parse(raw.clone())?;
58 return Ok(Self(BlobKind::PolkaVM((blob, raw))));
59 } else {
60 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()));
61 }
62 }
63
64 let raw_module: Module = deserialize_buffer(raw_blob)
65 .map_err(|e| WasmError::Other(format!("cannot deserialize module: {:?}", e)))?;
66 Ok(Self(BlobKind::WebAssembly(raw_module)))
67 }
68
69 pub fn inject_stack_depth_metering(self, stack_depth_limit: u32) -> Result<Self, WasmError> {
82 let injected_module =
83 wasm_instrument::inject_stack_limiter(self.into_webassembly_blob()?, stack_depth_limit)
84 .map_err(|e| {
85 WasmError::Other(format!("cannot inject the stack limiter: {:?}", e))
86 })?;
87
88 Ok(Self(BlobKind::WebAssembly(injected_module)))
89 }
90
91 pub fn convert_memory_import_into_export(&mut self) -> Result<(), WasmError> {
99 let raw_module = self.as_webassembly_blob_mut()?;
100 let import_section = match raw_module.import_section_mut() {
101 Some(import_section) => import_section,
102 None => return Ok(()),
103 };
104
105 let import_entries = import_section.entries_mut();
106 for index in 0..import_entries.len() {
107 let entry = &import_entries[index];
108 let memory_ty = match entry.external() {
109 External::Memory(memory_ty) => *memory_ty,
110 _ => continue,
111 };
112
113 let memory_name = entry.field().to_owned();
114 import_entries.remove(index);
115
116 raw_module
117 .insert_section(Section::Memory(MemorySection::with_entries(vec![memory_ty])))
118 .map_err(|error| {
119 WasmError::Other(format!(
120 "can't convert a memory import into an export: failed to insert a new memory section: {}",
121 error
122 ))
123 })?;
124
125 if raw_module.export_section_mut().is_none() {
126 raw_module
129 .insert_section(Section::Export(Default::default()))
130 .expect("an export section can be always inserted if it doesn't exist; qed");
131 }
132 raw_module
133 .export_section_mut()
134 .expect("export section already existed or we just added it above, so it always exists; qed")
135 .entries_mut()
136 .push(ExportEntry::new(memory_name, Internal::Memory(0)));
137
138 break;
139 }
140
141 Ok(())
142 }
143
144 pub fn clear_memory_max_limit(&mut self) -> Result<(), WasmError> {
148 let raw_module = self.as_webassembly_blob_mut()?;
149 let memory_section = match raw_module.memory_section_mut() {
150 Some(section) => section,
151 None => return Ok(()),
152 };
153 for memory_ty in memory_section.entries_mut() {
154 *memory_ty = MemoryType::new(memory_ty.limits().initial(), None);
155 }
156 Ok(())
157 }
158
159 #[deprecated]
166 pub fn setup_memory_according_to_heap_alloc_strategy(
167 &mut self,
168 heap_alloc_strategy: HeapAllocStrategy,
169 ) -> Result<(), WasmError> {
170 let raw_module = self.as_webassembly_blob_mut()?;
171 let memory_section = raw_module
172 .memory_section_mut()
173 .ok_or_else(|| WasmError::Other("no memory section found".into()))?;
174
175 if memory_section.entries().is_empty() {
176 return Err(WasmError::Other("memory section is empty".into()));
177 }
178 for memory_ty in memory_section.entries_mut() {
179 let initial = memory_ty.limits().initial();
180 let (min, max) = match heap_alloc_strategy {
181 HeapAllocStrategy::Dynamic { maximum_pages } => {
182 (maximum_pages.map(|m| m.min(initial)).unwrap_or(initial), maximum_pages)
184 },
185 HeapAllocStrategy::Static { extra_pages } => {
186 let pages = initial.saturating_add(extra_pages);
187 (pages, Some(pages))
188 },
189 };
190 *memory_ty = MemoryType::new(min, max);
191 }
192 Ok(())
193 }
194
195 pub fn custom_section_contents(&self, section_name: &str) -> Option<&[u8]> {
200 self.as_webassembly_blob()
201 .ok()?
202 .custom_sections()
203 .find(|cs| cs.name() == section_name)
204 .map(|cs| cs.payload())
205 }
206
207 pub fn serialize(self) -> Vec<u8> {
209 match self.0 {
210 BlobKind::WebAssembly(raw_module) => {
211 serialize(raw_module).expect("serializing into a vec should succeed; qed")
212 },
213 BlobKind::PolkaVM(ref blob) => blob.1.to_vec(),
214 }
215 }
216
217 fn as_webassembly_blob(&self) -> Result<&Module, WasmError> {
218 match self.0 {
219 BlobKind::WebAssembly(ref raw_module) => Ok(raw_module),
220 BlobKind::PolkaVM(..) => Err(WasmError::Other(
221 "expected a WebAssembly program; found a PolkaVM program blob".into(),
222 )),
223 }
224 }
225
226 fn as_webassembly_blob_mut(&mut self) -> Result<&mut Module, WasmError> {
227 match self.0 {
228 BlobKind::WebAssembly(ref mut raw_module) => Ok(raw_module),
229 BlobKind::PolkaVM(..) => Err(WasmError::Other(
230 "expected a WebAssembly program; found a PolkaVM program blob".into(),
231 )),
232 }
233 }
234
235 fn into_webassembly_blob(self) -> Result<Module, WasmError> {
236 match self.0 {
237 BlobKind::WebAssembly(raw_module) => Ok(raw_module),
238 BlobKind::PolkaVM(..) => Err(WasmError::Other(
239 "expected a WebAssembly program; found a PolkaVM program blob".into(),
240 )),
241 }
242 }
243
244 pub fn as_polkavm_blob(&self) -> Option<&polkavm::ProgramBlob> {
246 match self.0 {
247 BlobKind::WebAssembly(..) => None,
248 BlobKind::PolkaVM((ref blob, _)) => Some(blob),
249 }
250 }
251}