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