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