1use crate::error::ExecuteError;
20use polkadot_primitives::{
21 executor_params::{DEFAULT_LOGICAL_STACK_MAX, DEFAULT_NATIVE_STACK_MAX},
22 ExecutorParam, ExecutorParams,
23};
24use sc_executor_common::{
25 error::WasmError,
26 runtime_blob::RuntimeBlob,
27 wasm_runtime::{HeapAllocStrategy, WasmModule as _},
28};
29use sc_executor_wasmtime::{Config, DeterministicStackLimit, Semantics, WasmtimeRuntime};
30use sp_core::storage::{ChildInfo, TrackedStorageKey};
31use sp_externalities::MultiRemovalResults;
32use std::any::{Any, TypeId};
33
34const DEFAULT_HEAP_PAGES_ESTIMATE: u32 = 32;
47const EXTRA_HEAP_PAGES: u32 = 2048;
48
49pub const DEFAULT_CONFIG: Config = Config {
54 allow_missing_func_imports: true,
55 cache_path: None,
56 semantics: Semantics {
57 heap_alloc_strategy: sc_executor_common::wasm_runtime::HeapAllocStrategy::Dynamic {
58 maximum_pages: Some(DEFAULT_HEAP_PAGES_ESTIMATE + EXTRA_HEAP_PAGES),
59 },
60
61 instantiation_strategy:
62 sc_executor_wasmtime::InstantiationStrategy::RecreateInstanceCopyOnWrite,
63
64 deterministic_stack_limit: Some(DeterministicStackLimit {
77 logical_max: DEFAULT_LOGICAL_STACK_MAX,
78 native_stack_max: DEFAULT_NATIVE_STACK_MAX,
79 }),
80 canonicalize_nans: true,
81 parallel_compilation: false,
88
89 wasm_reference_types: false,
93 wasm_simd: false,
94 wasm_bulk_memory: false,
95 wasm_multi_value: false,
96 },
97};
98
99pub unsafe fn execute_artifact(
110 compiled_artifact_blob: &[u8],
111 executor_params: &ExecutorParams,
112 params: &[u8],
113) -> Result<Vec<u8>, ExecuteError> {
114 let mut extensions = sp_externalities::Extensions::new();
115
116 extensions.register(sp_core::traits::ReadRuntimeVersionExt::new(ReadRuntimeVersion));
117
118 let mut ext = ValidationExternalities(extensions);
119
120 match sc_executor::with_externalities_safe(&mut ext, || {
121 let (semantics, _) = params_to_wasmtime_semantics(executor_params);
122 let runtime = create_runtime_from_artifact_bytes(compiled_artifact_blob, executor_params)?;
123 runtime
124 .new_instance(semantics.heap_alloc_strategy)?
125 .call("validate_block", params)
126 }) {
127 Ok(Ok(ok)) => Ok(ok),
128 Ok(Err(err)) | Err(err) => Err(err),
129 }
130}
131
132pub unsafe fn create_runtime_from_artifact_bytes(
142 compiled_artifact_blob: &[u8],
143 executor_params: &ExecutorParams,
144) -> Result<WasmtimeRuntime, WasmError> {
145 let mut config = DEFAULT_CONFIG.clone();
146 config.semantics = params_to_wasmtime_semantics(executor_params).0;
147
148 let ecc_hf_enabled = executor_params.iter().any(|p| {
149 p == &ExecutorParam::EnabledHostFunction(
150 polkadot_primitives::ExecutorHostFunction::EccRfc163,
151 )
152 });
153
154 if ecc_hf_enabled {
155 sc_executor_wasmtime::create_runtime_from_artifact_bytes::<HostFunctionsWithEcc>(
156 compiled_artifact_blob,
157 config,
158 )
159 } else {
160 sc_executor_wasmtime::create_runtime_from_artifact_bytes::<HostFunctions>(
161 compiled_artifact_blob,
162 config,
163 )
164 }
165}
166
167pub fn params_to_wasmtime_semantics(par: &ExecutorParams) -> (Semantics, DeterministicStackLimit) {
171 let mut sem = DEFAULT_CONFIG.semantics.clone();
172 let mut stack_limit = sem
173 .deterministic_stack_limit
174 .expect("There is a comment to not change the default stack limit; it should always be available; qed")
175 .clone();
176
177 for p in par.iter() {
178 match p {
179 ExecutorParam::MaxMemoryPages(max_pages) => {
180 sem.heap_alloc_strategy = HeapAllocStrategy::Dynamic {
181 maximum_pages: Some((*max_pages).saturating_add(DEFAULT_HEAP_PAGES_ESTIMATE)),
182 }
183 },
184 ExecutorParam::StackLogicalMax(slm) => stack_limit.logical_max = *slm,
185 ExecutorParam::StackNativeMax(snm) => stack_limit.native_stack_max = *snm,
186 ExecutorParam::WasmExtBulkMemory => sem.wasm_bulk_memory = true,
187 ExecutorParam::PrecheckingMaxMemory(_) |
188 ExecutorParam::PvfPrepTimeout(_, _) |
189 ExecutorParam::PvfExecTimeout(_, _) |
190 ExecutorParam::EnabledHostFunction(_) => (), }
192 }
193 sem.deterministic_stack_limit = Some(stack_limit.clone());
194 (sem, stack_limit)
195}
196
197pub fn prevalidate(code: &[u8]) -> Result<RuntimeBlob, sc_executor_common::error::WasmError> {
199 let blob = RuntimeBlob::new(code)?;
201 Ok(blob)
203}
204
205pub fn prepare(
208 blob: RuntimeBlob,
209 executor_params: &ExecutorParams,
210) -> Result<Vec<u8>, sc_executor_common::error::WasmError> {
211 let (semantics, _) = params_to_wasmtime_semantics(executor_params);
212 sc_executor_wasmtime::prepare_runtime_artifact(blob, &semantics)
213}
214
215type HostFunctions = (
223 sp_io::misc::HostFunctions,
224 sp_io::crypto::HostFunctions,
225 sp_io::hashing::HostFunctions,
226 sp_io::allocator::HostFunctions,
227 sp_io::logging::HostFunctions,
228 sp_io::trie::HostFunctions,
229);
230
231type HostFunctionsWithEcc = (HostFunctions, sp_crypto_ec_utils::HostFunctionsRfc163);
234
235struct ValidationExternalities(sp_externalities::Extensions);
238
239impl sp_externalities::Externalities for ValidationExternalities {
240 fn storage(&mut self, _: &[u8]) -> Option<Vec<u8>> {
241 panic!("storage: unsupported feature for parachain validation")
242 }
243
244 fn storage_hash(&mut self, _: &[u8]) -> Option<Vec<u8>> {
245 panic!("storage_hash: unsupported feature for parachain validation")
246 }
247
248 fn child_storage_hash(&mut self, _: &ChildInfo, _: &[u8]) -> Option<Vec<u8>> {
249 panic!("child_storage_hash: unsupported feature for parachain validation")
250 }
251
252 fn child_storage(&mut self, _: &ChildInfo, _: &[u8]) -> Option<Vec<u8>> {
253 panic!("child_storage: unsupported feature for parachain validation")
254 }
255
256 fn kill_child_storage(
257 &mut self,
258 _child_info: &ChildInfo,
259 _maybe_limit: Option<u32>,
260 _maybe_cursor: Option<&[u8]>,
261 ) -> MultiRemovalResults {
262 panic!("kill_child_storage: unsupported feature for parachain validation")
263 }
264
265 fn clear_prefix(
266 &mut self,
267 _prefix: &[u8],
268 _maybe_limit: Option<u32>,
269 _maybe_cursor: Option<&[u8]>,
270 ) -> MultiRemovalResults {
271 panic!("clear_prefix: unsupported feature for parachain validation")
272 }
273
274 fn clear_child_prefix(
275 &mut self,
276 _child_info: &ChildInfo,
277 _prefix: &[u8],
278 _maybe_limit: Option<u32>,
279 _maybe_cursor: Option<&[u8]>,
280 ) -> MultiRemovalResults {
281 panic!("clear_child_prefix: unsupported feature for parachain validation")
282 }
283
284 fn place_storage(&mut self, _: Vec<u8>, _: Option<Vec<u8>>) {
285 panic!("place_storage: unsupported feature for parachain validation")
286 }
287
288 fn place_child_storage(&mut self, _: &ChildInfo, _: Vec<u8>, _: Option<Vec<u8>>) {
289 panic!("place_child_storage: unsupported feature for parachain validation")
290 }
291
292 fn storage_root(&mut self, _: sp_core::storage::StateVersion) -> Vec<u8> {
293 panic!("storage_root: unsupported feature for parachain validation")
294 }
295
296 fn child_storage_root(&mut self, _: &ChildInfo, _: sp_core::storage::StateVersion) -> Vec<u8> {
297 panic!("child_storage_root: unsupported feature for parachain validation")
298 }
299
300 fn next_child_storage_key(&mut self, _: &ChildInfo, _: &[u8]) -> Option<Vec<u8>> {
301 panic!("next_child_storage_key: unsupported feature for parachain validation")
302 }
303
304 fn next_storage_key(&mut self, _: &[u8]) -> Option<Vec<u8>> {
305 panic!("next_storage_key: unsupported feature for parachain validation")
306 }
307
308 fn storage_append(&mut self, _key: Vec<u8>, _value: Vec<u8>) {
309 panic!("storage_append: unsupported feature for parachain validation")
310 }
311
312 fn storage_start_transaction(&mut self) {
313 panic!("storage_start_transaction: unsupported feature for parachain validation")
314 }
315
316 fn storage_rollback_transaction(&mut self) -> Result<(), ()> {
317 panic!("storage_rollback_transaction: unsupported feature for parachain validation")
318 }
319
320 fn storage_commit_transaction(&mut self) -> Result<(), ()> {
321 panic!("storage_commit_transaction: unsupported feature for parachain validation")
322 }
323
324 fn wipe(&mut self) {
325 panic!("wipe: unsupported feature for parachain validation")
326 }
327
328 fn commit(&mut self) {
329 panic!("commit: unsupported feature for parachain validation")
330 }
331
332 fn read_write_count(&self) -> (u32, u32, u32, u32) {
333 panic!("read_write_count: unsupported feature for parachain validation")
334 }
335
336 fn reset_read_write_count(&mut self) {
337 panic!("reset_read_write_count: unsupported feature for parachain validation")
338 }
339
340 fn get_whitelist(&self) -> Vec<TrackedStorageKey> {
341 panic!("get_whitelist: unsupported feature for parachain validation")
342 }
343
344 fn set_whitelist(&mut self, _: Vec<TrackedStorageKey>) {
345 panic!("set_whitelist: unsupported feature for parachain validation")
346 }
347
348 fn set_offchain_storage(&mut self, _: &[u8], _: std::option::Option<&[u8]>) {
349 panic!("set_offchain_storage: unsupported feature for parachain validation")
350 }
351
352 fn get_read_and_written_keys(&self) -> Vec<(Vec<u8>, u32, u32, bool)> {
353 panic!("get_read_and_written_keys: unsupported feature for parachain validation")
354 }
355}
356
357impl sp_externalities::ExtensionStore for ValidationExternalities {
358 fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any> {
359 self.0.get_mut(type_id)
360 }
361
362 fn register_extension_with_type_id(
363 &mut self,
364 type_id: TypeId,
365 extension: Box<dyn sp_externalities::Extension>,
366 ) -> Result<(), sp_externalities::Error> {
367 self.0.register_with_type_id(type_id, extension)
368 }
369
370 fn deregister_extension_by_type_id(
371 &mut self,
372 type_id: TypeId,
373 ) -> Result<(), sp_externalities::Error> {
374 if self.0.deregister(type_id) {
375 Ok(())
376 } else {
377 Err(sp_externalities::Error::ExtensionIsNotRegistered(type_id))
378 }
379 }
380}
381
382struct ReadRuntimeVersion;
383
384impl sp_core::traits::ReadRuntimeVersion for ReadRuntimeVersion {
385 fn read_runtime_version(
386 &self,
387 wasm_code: &[u8],
388 _ext: &mut dyn sp_externalities::Externalities,
389 ) -> Result<Vec<u8>, String> {
390 let blob = RuntimeBlob::uncompress_if_needed(wasm_code)
391 .map_err(|e| format!("Failed to read the PVF runtime blob: {:?}", e))?;
392
393 match sc_executor::read_embedded_version(&blob)
394 .map_err(|e| format!("Failed to read the static section from the PVF blob: {:?}", e))?
395 {
396 Some(version) => {
397 use codec::Encode;
398 Ok(version.encode())
399 },
400 None => Err("runtime version section is not found".to_string()),
401 }
402 }
403}
404
405#[cfg(test)]
406mod tests {
407 use super::*;
408
409 #[test]
410 fn prep_hash_matches_artifact_effect_of_executor_params() {
411 use ExecutorParam::*;
412
413 let _coverage_check = |param: &ExecutorParam| match param {
416 MaxMemoryPages(_) => true,
417 StackLogicalMax(_) => true,
418 StackNativeMax(_) => true,
419 PrecheckingMaxMemory(_) => true,
420 PvfPrepTimeout(_, _) => true,
421 PvfExecTimeout(_, _) => true,
422 WasmExtBulkMemory => true,
423 EnabledHostFunction(_) => true,
424 };
425
426 let wat = r#"(module
428 (memory 1)
429 (func (export "validate_block") (param i32 i32))
430 )"#;
431 let wasm = wat::parse_str(wat).expect("wat parsing failed");
432 let blob = prevalidate(&wasm).expect("valid runtime blob");
433
434 let base = ExecutorParams::default();
435
436 let prepare_with = |params: &ExecutorParams| -> Vec<u8> {
437 prepare(blob.clone(), params).expect("prepare should succeed")
438 };
439
440 let cases: Vec<(&str, ExecutorParams, ExecutorParams)> = vec![
442 (
443 "MaxMemoryPages",
444 base.clone(),
445 ExecutorParams::from(&[ExecutorParam::MaxMemoryPages(128)][..]),
446 ),
447 (
448 "StackLogicalMax",
449 base.clone(),
450 ExecutorParams::from(
451 &[ExecutorParam::StackLogicalMax(DEFAULT_LOGICAL_STACK_MAX + 1)][..],
452 ),
453 ),
454 (
455 "StackNativeMax",
456 base.clone(),
457 ExecutorParams::from(
458 &[ExecutorParam::StackNativeMax(DEFAULT_NATIVE_STACK_MAX + 1024)][..],
459 ),
460 ),
461 (
462 "PrecheckingMaxMemory",
463 base.clone(),
464 ExecutorParams::from(&[ExecutorParam::PrecheckingMaxMemory(300 * 1024 * 1024)][..]),
465 ),
466 (
467 "PvfPrepTimeout(Precheck)",
468 base.clone(),
469 ExecutorParams::from(
470 &[ExecutorParam::PvfPrepTimeout(polkadot_primitives::PvfPrepKind::Precheck, 1)]
471 [..],
472 ),
473 ),
474 (
475 "PvfPrepTimeout(Prepare)",
476 base.clone(),
477 ExecutorParams::from(
478 &[ExecutorParam::PvfPrepTimeout(polkadot_primitives::PvfPrepKind::Prepare, 2)]
479 [..],
480 ),
481 ),
482 (
483 "PvfExecTimeout(Backing)",
484 base.clone(),
485 ExecutorParams::from(
486 &[ExecutorParam::PvfExecTimeout(polkadot_primitives::PvfExecKind::Backing, 1)]
487 [..],
488 ),
489 ),
490 (
491 "PvfExecTimeout(Approval)",
492 base.clone(),
493 ExecutorParams::from(
494 &[ExecutorParam::PvfExecTimeout(polkadot_primitives::PvfExecKind::Approval, 2)]
495 [..],
496 ),
497 ),
498 (
499 "WasmExtBulkMemory",
500 base.clone(),
501 ExecutorParams::from(&[ExecutorParam::WasmExtBulkMemory][..]),
502 ),
503 (
504 "EnabledHostFunction(EccRfc163)",
505 base.clone(),
506 ExecutorParams::from(
507 &[ExecutorParam::EnabledHostFunction(
508 polkadot_primitives::ExecutorHostFunction::EccRfc163,
509 )][..],
510 ),
511 ),
512 ];
513
514 for (name, a, b) in cases.into_iter() {
515 let art_a = prepare_with(&a);
516 let art_b = prepare_with(&b);
517 let artifact_changed = art_a != art_b;
518 let prep_hash_changed = a.prep_hash() != b.prep_hash();
519 assert_eq!(
520 artifact_changed,
521 prep_hash_changed,
522 "ExecutorParam classification mismatch for {}: artifact_changed={}, prep_hash_changed={}",
523 name,
524 artifact_changed,
525 prep_hash_changed,
526 );
527 }
528 }
529}