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