Skip to main content

smoldot/executor/
host.rs

1// Smoldot
2// Copyright (C) 2019-2022  Parity Technologies (UK) Ltd.
3// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14
15// You should have received a copy of the GNU General Public License
16// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18//! Wasm virtual machine specific to the Substrate/Polkadot Runtime Environment.
19//!
20//! Contrary to [`VirtualMachine`](super::vm::VirtualMachine), this code is not just a generic
21//! Wasm virtual machine, but is aware of the Substrate/Polkadot runtime environment. The host
22//! functions that the Wasm code calls are automatically resolved and either handled or notified
23//! to the user of this module.
24//!
25//! Any host function that requires pure CPU computations (for example building or verifying
26//! a cryptographic signature) is directly handled by the code in this module. Other host
27//! functions (for example accessing the state or printing a message) are instead handled by
28//! interrupting the virtual machine and waiting for the user of this module to handle the call.
29//!
30//! > **Note**: The `ext_offchain_random_seed_version_1` and `ext_offchain_timestamp_version_1`
31//! >           functions, which requires the host to respectively produce a random seed and
32//! >           return the current time, must also be handled by the user. While these functions
33//! >           could theoretically be handled directly by this module, it might be useful for
34//! >           testing purposes to have the possibility to return a deterministic value.
35//!
36//! Contrary to most programs, runtime code doesn't have a singe `main` or `start` function.
37//! Instead, it exposes several entry points. Which one to call indicates which action it has to
38//! perform. Not all entry points are necessarily available on all runtimes.
39//!
40//! # Runtime requirements
41//!
42//! See the [documentation of the `vm` module](super::vm) for details about the requirements a
43//! runtime must adhere to.
44//!
45//! In addition to the requirements described there, the WebAssembly runtime code can also be
46//! zstandard-compressed and must also export a global symbol named `__heap_base`.
47//! More details below.
48//!
49//! ## `Zstandard` compression
50//!
51//! The runtime code passed as parameter to [`HostVmPrototype::new`] can be compressed using the
52//! [`zstd`](https://en.wikipedia.org/wiki/Zstandard) algorithm.
53//!
54//! If the code starts with the magic bytes `[82, 188, 83, 118, 70, 219, 142, 5]`, then it is
55//! assumed that the rest of the data is a zstandard-compressed WebAssembly module.
56//!
57//! ## Runtime version
58//!
59//! Wasm files can contain so-called custom sections. A runtime can contain two custom sections
60//! whose names are `"runtime_version"` and `"runtime_apis"`, in which case they must contain a
61//! so-called runtime version.
62//!
63//! The runtime version contains important field that identifies a runtime.
64//!
65//! If no `"runtime_version"` and `"runtime_apis"` custom sections can be found, the
66//! `Core_version` entry point is used as a fallback in order to obtain the runtime version. This
67//! fallback mechanism is maintained for backwards compatibility purposes, but is considered
68//! deprecated.
69//!
70//! ## Memory allocations
71//!
72//! One of the instructions available in WebAssembly code is
73//! [the `memory.grow` instruction](https://webassembly.github.io/spec/core/bikeshed/#-hrefsyntax-instr-memorymathsfmemorygrow),
74//! which allows increasing the size of the memory.
75//!
76//! WebAssembly code is normally intended to perform its own heap-management logic internally, and
77//! use the `memory.grow` instruction if more memory is needed.
78//!
79//! In order to minimize the size of the runtime binary, and in order to accommodate for the API of
80//! the host functions that return a buffer of variable length, the Substrate/Polkadot runtimes,
81//! however, do not perform their heap management internally. Instead, they use the
82//! `ext_allocator_malloc_version_1` and `ext_allocator_free_version_1` host functions for this
83//! purpose. Calling `memory.grow` is forbidden.
84//!
85//! The runtime code must export a global symbol named `__heap_base` of type `i32`. Any memory
86//! whose offset is below the value of `__heap_base` can be used at will by the program, while
87//! any memory above `__heap_base` but below `__heap_base + heap_pages` (where `heap_pages` is
88//! the value passed as parameter to [`HostVmPrototype::new`]) is available for use by the
89//! implementation of `ext_allocator_malloc_version_1`.
90//!
91//! ## Entry points
92//!
93//! All entry points that can be called from the host (using, for example,
94//! [`HostVmPrototype::run`]) have the same signature:
95//!
96//! ```ignore
97//! (func $runtime_entry(param $data i32) (param $len i32) (result i64))
98//! ```
99//!
100//! In order to call into the runtime, one must write a buffer of data containing the input
101//! parameters into the Wasm virtual machine's memory, then pass a pointer and length of this
102//! buffer as the parameters of the entry point.
103//!
104//! The function returns a 64 bits number. The 32 less significant bits represent a pointer to the
105//! Wasm virtual machine's memory, and the 32 most significant bits a length. This pointer and
106//! length designate a buffer containing the actual return value.
107//!
108//! ## Host functions
109//!
110//! The list of host functions available to the runtime is long and isn't documented here. See
111//! the official specification for details.
112//!
113//! # Usage
114//!
115//! The first step is to create a [`HostVmPrototype`] object from the WebAssembly code. Creating
116//! this object performs some initial steps, such as parsing and compiling the WebAssembly code.
117//! You are encouraged to maintain a cache of [`HostVmPrototype`] objects (one instance per
118//! WebAssembly byte code) in order to avoid performing these operations too often.
119//!
120//! To start calling the runtime, create a [`HostVm`] by calling [`HostVmPrototype::run`].
121//!
122//! While the Wasm runtime code has side-effects (such as storing values in the storage), the
123//! [`HostVm`] itself is a pure state machine with no side effects.
124//!
125//! At any given point, you can examine the [`HostVm`] in order to know in which state the
126//! execution currently is.
127//! In case of a [`HostVm::ReadyToRun`] (which initially is the case when you create the
128//! [`HostVm`]), you can execute the Wasm code by calling [`ReadyToRun::run`].
129//! No background thread of any kind is used, and calling [`ReadyToRun::run`] directly performs
130//! the execution of the Wasm code. If you need parallelism, you are encouraged to spawn a
131//! background thread yourself and call this function from there.
132//! [`ReadyToRun::run`] tries to make the execution progress as much as possible, and returns
133//! the new state of the virtual machine once that is done.
134//!
135//! If the runtime has finished, or has crashed, or wants to perform an operation with side
136//! effects, then the [`HostVm`] determines what to do next. For example, for
137//! [`HostVm::ExternalStorageGet`], you must load a value from the storage and pass it back by
138//! calling [`ExternalStorageGet::resume`].
139//!
140//! The Wasm execution is fully deterministic, and the outcome of the execution only depends on
141//! the inputs. There is, for example, no implicit injection of randomness or of the current time.
142//!
143//! ## Example
144//!
145//! ```
146//! use smoldot::executor::host::{
147//!     Config, HeapPages, HostVm, HostVmPrototype, StorageProofSizeBehavior
148//! };
149//!
150//! # let wasm_binary_code: &[u8] = return;
151//!
152//! // Start executing a function on the runtime.
153//! let mut vm: HostVm = {
154//!     let prototype = HostVmPrototype::new(Config {
155//!         module: &wasm_binary_code,
156//!         heap_pages: HeapPages::from(2048),
157//!         exec_hint: smoldot::executor::vm::ExecHint::ValidateAndExecuteOnce,
158//!         allow_unresolved_imports: false
159//!     }).unwrap();
160//!     prototype.run_no_param(
161//!         "Core_version",
162//!         StorageProofSizeBehavior::proof_recording_disabled()
163//!     ).unwrap().into()
164//! };
165//!
166//! // We need to answer the calls that the runtime might perform.
167//! loop {
168//!     match vm {
169//!         // Calling `runner.run()` is what actually executes WebAssembly code and updates
170//!         // the state.
171//!         HostVm::ReadyToRun(runner) => vm = runner.run(),
172//!
173//!         HostVm::Finished(finished) => {
174//!             // `finished.value()` here is an opaque blob of bytes returned by the runtime.
175//!             // In the case of a call to `"Core_version"`, we know that it must be empty.
176//!             assert!(finished.value().as_ref().is_empty());
177//!             println!("Success!");
178//!             break;
179//!         },
180//!
181//!         // Errors can happen if the WebAssembly code panics or does something wrong.
182//!         // In a real-life situation, the host should obviously not panic in these situations.
183//!         HostVm::Error { .. } => {
184//!             panic!("Error while executing code")
185//!         },
186//!
187//!         // All the other variants correspond to function calls that the runtime might perform.
188//!         // `ExternalStorageGet` is shown here as an example.
189//!         HostVm::ExternalStorageGet(req) => {
190//!             println!("Runtime requires the storage value at {:?}", req.key().as_ref());
191//!             // Injects the value into the virtual machine and updates the state.
192//!             vm = req.resume(None); // Just a stub
193//!         }
194//!         _ => unimplemented!()
195//!     }
196//! }
197//! ```
198
199use super::{allocator, vm};
200use crate::{trie, util};
201
202use alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec, vec::Vec};
203use core::{fmt, iter, str};
204use functions::HostFunction;
205
206pub mod runtime_version;
207
208pub use runtime_version::{
209    CoreVersion, CoreVersionApisFromSliceErr, CoreVersionError, CoreVersionRef,
210    FindEncodedEmbeddedRuntimeVersionApisError,
211};
212pub use trie::TrieEntryVersion;
213pub use vm::HeapPages;
214pub use zstd::Error as ModuleFormatError;
215
216mod functions;
217mod tests;
218mod zstd;
219
220/// Configuration for [`HostVmPrototype::new`].
221pub struct Config<TModule> {
222    /// Bytes of the WebAssembly module.
223    ///
224    /// The module can be either directly Wasm bytecode, or zstandard-compressed.
225    pub module: TModule,
226
227    /// Number of pages of heap available to the virtual machine.
228    ///
229    /// See the module-level documentation for an explanation.
230    pub heap_pages: HeapPages,
231
232    /// Hint used by the implementation to decide which kind of virtual machine to use.
233    pub exec_hint: vm::ExecHint,
234
235    /// If `true`, no [`vm::NewErr::UnresolvedFunctionImport`] error will be returned if the
236    /// module trying to import functions that aren't recognized by the implementation. Instead,
237    /// a [`Error::UnresolvedFunctionCalled`] error will be generated if the module tries to call
238    /// an unresolved function.
239    pub allow_unresolved_imports: bool,
240}
241
242/// Behavior if the `ext_storage_proof_size_storage_proof_size_version_1` host function is called.
243///
244/// When authoring a block or executing a block, this host function is expected to return the
245/// current size of the proof. Smoldot unfortunately can't implement this due to the fact that
246/// the proof generation algorithm is completely unspecified. For this reason, you should
247/// use [`StorageProofSizeBehavior::Unimplemented`]. However, for testing purposes, using
248/// `StorageProofSizeBehavior::ConstantReturnValue(0)` is acceptable.
249///
250/// In situations other than authoring or executing a block, use the value returned by
251/// [`StorageProofSizeBehavior::proof_recording_disabled`].
252///
253#[derive(Debug, Clone, PartialEq, Eq)]
254pub enum StorageProofSizeBehavior {
255    /// The host function is unimplemented. An error is returned if it is called.
256    Unimplemented,
257    /// The host function returns the given value.
258    ConstantReturnValue(u64),
259}
260
261impl StorageProofSizeBehavior {
262    /// Returns the behavior to employ when proof recording is disabled, as defined in the
263    /// specification.
264    pub fn proof_recording_disabled() -> Self {
265        StorageProofSizeBehavior::ConstantReturnValue(u64::MAX)
266    }
267}
268
269/// Prototype for an [`HostVm`].
270///
271/// > **Note**: This struct implements `Clone`. Cloning a [`HostVmPrototype`] allocates memory
272/// >           necessary for the clone to run.
273#[derive(Clone)]
274pub struct HostVmPrototype {
275    /// Fields that are kept as is even during the execution.
276    common: Box<VmCommon>,
277
278    /// Inner virtual machine prototype.
279    vm_proto: vm::VirtualMachinePrototype,
280}
281
282/// Fields that are kept as is even during the execution.
283#[derive(Clone)]
284struct VmCommon {
285    /// Runtime version of this runtime.
286    ///
287    /// Always `Some`, except at initialization.
288    runtime_version: Option<CoreVersion>,
289
290    /// Initial value of the `__heap_base` global in the Wasm module. Used to initialize the memory
291    /// allocator.
292    heap_base: u32,
293
294    /// List of functions that the Wasm code imports.
295    ///
296    /// The keys of this list (i.e. the `usize` indices) have been passed to the virtual machine
297    /// executor. Whenever the Wasm code invokes a host function, we obtain its index, and look
298    /// within this list to know what to do.
299    registered_functions: Arc<[FunctionImport]>,
300
301    /// Value of `heap_pages` passed to [`HostVmPrototype::new`].
302    heap_pages: HeapPages,
303
304    /// Total number of pages of Wasm memory. This is equal to `heap_base / 64k` (rounded up) plus
305    /// `heap_pages`.
306    memory_total_pages: HeapPages,
307}
308
309impl HostVmPrototype {
310    /// Creates a new [`HostVmPrototype`]. Parses and potentially JITs the module.
311    pub fn new(config: Config<impl AsRef<[u8]>>) -> Result<Self, NewErr> {
312        // The maximum allowed size for the decompressed Wasm code needs to be the same amongst
313        // all implementations.
314        // See <https://github.com/paritytech/substrate/blob/f9d10fabe04d598d68f8b097cc4905adbb1ad630/primitives/maybe-compressed-blob/src/lib.rs#L37>.
315        // Hopefully, this value doesn't get changed without the Substrate team informing everyone.
316        let module_bytes = zstd::zstd_decode_if_necessary(config.module.as_ref(), 50 * 1024 * 1024)
317            .map_err(NewErr::BadFormat)?;
318
319        // Try to find the runtime version as Wasm custom sections.
320        // An error is returned if the sections have a wrong format, in which case we fail the
321        // initialization. `Ok(None)` can also be returned, in which case the sections are
322        // missing, and we will instead try to retrieve the version through a runtime call later
323        // down this function.
324        // In the case of `CustomSectionsPresenceMismatch`, indicating that one section is present
325        // but not the other, we must ignore the custom sections. This is necessary due to some
326        // historical accidents.
327        let runtime_version = match runtime_version::find_embedded_runtime_version(&module_bytes) {
328            Ok(Some(r)) => Some(r),
329            Ok(None) => None,
330            Err(
331                runtime_version::FindEmbeddedRuntimeVersionError::CustomSectionsPresenceMismatch,
332            ) => None,
333            Err(runtime_version::FindEmbeddedRuntimeVersionError::FindSections(err)) => {
334                return Err(NewErr::RuntimeVersion(
335                    FindEmbeddedRuntimeVersionError::FindSections(err),
336                ));
337            }
338            Err(runtime_version::FindEmbeddedRuntimeVersionError::RuntimeApisDecode(err)) => {
339                return Err(NewErr::RuntimeVersion(
340                    FindEmbeddedRuntimeVersionError::RuntimeApisDecode(err),
341                ));
342            }
343            Err(runtime_version::FindEmbeddedRuntimeVersionError::RuntimeVersionDecode) => {
344                return Err(NewErr::RuntimeVersion(
345                    FindEmbeddedRuntimeVersionError::RuntimeVersionDecode,
346                ));
347            }
348        };
349
350        // Initialize the virtual machine.
351        // Each symbol requested by the Wasm runtime will be put in `registered_functions`. Later,
352        // when a function is invoked, the Wasm virtual machine will pass indices within that
353        // array.
354        let (mut vm_proto, registered_functions) = {
355            let mut registered_functions = Vec::new();
356            let vm_proto = vm::VirtualMachinePrototype::new(vm::Config {
357                module_bytes: &module_bytes[..],
358                exec_hint: config.exec_hint,
359                // This closure is called back for each function that the runtime imports.
360                symbols: &mut |mod_name, f_name, signature| {
361                    if mod_name != "env" {
362                        return Err(());
363                    }
364
365                    let id = registered_functions.len();
366                    registered_functions.push(match HostFunction::by_name(f_name) {
367                        Some(f) if f.signature() == *signature => FunctionImport::Resolved(f),
368                        Some(_) | None if !config.allow_unresolved_imports => {
369                            // TODO: return a better error if there is a signature mismatch
370                            return Err(());
371                        }
372                        Some(_) | None => FunctionImport::Unresolved {
373                            name: f_name.to_owned(),
374                            module: mod_name.to_owned(),
375                        },
376                    });
377                    Ok(id)
378                },
379            })?;
380            (vm_proto, registered_functions.into())
381        };
382
383        // In the runtime environment, Wasm blobs must export a global symbol named
384        // `__heap_base` indicating where the memory allocator is allowed to allocate memory.
385        let heap_base = vm_proto
386            .global_value("__heap_base")
387            .map_err(|_| NewErr::HeapBaseNotFound)?;
388
389        let memory_total_pages = if heap_base == 0 {
390            config.heap_pages
391        } else {
392            HeapPages::new((heap_base - 1) / (64 * 1024)) + config.heap_pages + HeapPages::new(1)
393        };
394
395        if vm_proto
396            .memory_max_pages()
397            .map_or(false, |max| max < memory_total_pages)
398        {
399            return Err(NewErr::MemoryMaxSizeTooLow);
400        }
401
402        let mut host_vm_prototype = HostVmPrototype {
403            vm_proto,
404            common: Box::new(VmCommon {
405                runtime_version,
406                heap_base,
407                registered_functions,
408                heap_pages: config.heap_pages,
409                memory_total_pages,
410            }),
411        };
412
413        // Call `Core_version` if no runtime version is known yet.
414        if host_vm_prototype.common.runtime_version.is_none() {
415            let mut vm: HostVm = match host_vm_prototype.run_no_param(
416                "Core_version",
417                StorageProofSizeBehavior::proof_recording_disabled(),
418            ) {
419                Ok(vm) => vm.into(),
420                Err((err, _)) => return Err(NewErr::CoreVersion(CoreVersionError::Start(err))),
421            };
422
423            loop {
424                match vm {
425                    HostVm::ReadyToRun(r) => vm = r.run(),
426                    HostVm::Finished(finished) => {
427                        let version =
428                            match CoreVersion::from_slice(finished.value().as_ref().to_vec()) {
429                                Ok(v) => v,
430                                Err(_) => {
431                                    return Err(NewErr::CoreVersion(CoreVersionError::Decode));
432                                }
433                            };
434
435                        host_vm_prototype = finished.into_prototype();
436                        host_vm_prototype.common.runtime_version = Some(version);
437                        break;
438                    }
439
440                    // Emitted log lines are ignored.
441                    HostVm::GetMaxLogLevel(resume) => {
442                        vm = resume.resume(0); // Off
443                    }
444                    HostVm::LogEmit(log) => vm = log.resume(),
445
446                    HostVm::Error { error, .. } => {
447                        return Err(NewErr::CoreVersion(CoreVersionError::Run(error)));
448                    }
449
450                    // Getting the runtime version is a very core operation, and very few
451                    // external calls are allowed.
452                    _ => return Err(NewErr::CoreVersion(CoreVersionError::ForbiddenHostFunction)),
453                }
454            }
455        }
456
457        // Success!
458        debug_assert!(host_vm_prototype.common.runtime_version.is_some());
459        Ok(host_vm_prototype)
460    }
461
462    /// Returns the number of heap pages that were passed to [`HostVmPrototype::new`].
463    pub fn heap_pages(&self) -> HeapPages {
464        self.common.heap_pages
465    }
466
467    /// Returns the runtime version found in the module.
468    pub fn runtime_version(&self) -> &CoreVersion {
469        self.common
470            .runtime_version
471            .as_ref()
472            .unwrap_or_else(|| unreachable!())
473    }
474
475    /// Starts the VM, calling the function passed as parameter.
476    ///
477    /// See the documentation of [`StorageProofSizeBehavior`] for an explanation of
478    /// the `storage_proof_size_behavior` parameter.
479    pub fn run(
480        self,
481        function_to_call: &str,
482        storage_proof_size_behavior: StorageProofSizeBehavior,
483        data: &[u8],
484    ) -> Result<ReadyToRun, (StartErr, Self)> {
485        self.run_vectored(
486            function_to_call,
487            storage_proof_size_behavior,
488            iter::once(data),
489        )
490    }
491
492    /// Same as [`HostVmPrototype::run`], except that the function doesn't need any parameter.
493    ///
494    /// See the documentation of [`StorageProofSizeBehavior`] for an explanation of
495    /// the `storage_proof_size_behavior` parameter.
496    pub fn run_no_param(
497        self,
498        function_to_call: &str,
499        storage_proof_size_behavior: StorageProofSizeBehavior,
500    ) -> Result<ReadyToRun, (StartErr, Self)> {
501        self.run_vectored(
502            function_to_call,
503            storage_proof_size_behavior,
504            iter::empty::<Vec<u8>>(),
505        )
506    }
507
508    /// Same as [`HostVmPrototype::run`], except that the function parameter can be passed as
509    /// a list of buffers. All the buffers will be concatenated in memory.
510    ///
511    /// See the documentation of [`StorageProofSizeBehavior`] for an explanation of
512    /// the `storage_proof_size_behavior` parameter.
513    pub fn run_vectored(
514        mut self,
515        function_to_call: &str,
516        storage_proof_size_behavior: StorageProofSizeBehavior,
517        data: impl Iterator<Item = impl AsRef<[u8]>> + Clone,
518    ) -> Result<ReadyToRun, (StartErr, Self)> {
519        // Determine the total length of `data`.
520        let mut data_len_u32: u32 = 0;
521        for data in data.clone() {
522            let len = match u32::try_from(data.as_ref().len()) {
523                Ok(v) => v,
524                Err(_) => return Err((StartErr::DataSizeOverflow, self)),
525            };
526            data_len_u32 = match data_len_u32.checked_add(len) {
527                Some(v) => v,
528                None => return Err((StartErr::DataSizeOverflow, self)),
529            };
530        }
531
532        // Initialize the state of the memory allocator. This is the allocator that is used in
533        // order to allocate space for the input data, and also later used when the Wasm code
534        // requests variable-length data.
535        let mut allocator = allocator::FreeingBumpHeapAllocator::new(self.common.heap_base);
536
537        // Prepare the virtual machine for execution.
538        let mut vm = self.vm_proto.prepare();
539
540        // Write the input data in the VM's memory using the allocator.
541        let data_ptr = match allocator.allocate(
542            &mut MemAccess {
543                vm: MemAccessVm::Prepare(&mut vm),
544                memory_total_pages: self.common.memory_total_pages,
545            },
546            data_len_u32,
547        ) {
548            Ok(p) => p,
549            Err(_) => {
550                self.vm_proto = vm.into_prototype();
551                return Err((StartErr::DataSizeOverflow, self));
552            }
553        };
554
555        // While the allocator has reserved memory, it might have reserved more memory than its
556        // current size.
557        if let Some(to_grow) = ((data_ptr + data_len_u32).saturating_sub(1) / (64 * 1024) + 1)
558            .checked_sub(u32::from(vm.memory_size()))
559        {
560            // If the memory can't be grown, it indicates a bug in the allocator.
561            vm.grow_memory(HeapPages::from(to_grow))
562                .unwrap_or_else(|_| unreachable!());
563        }
564
565        // Writing the input data into the VM.
566        let mut data_ptr_iter = data_ptr;
567        for data in data {
568            let data = data.as_ref();
569            vm.write_memory(data_ptr_iter, data)
570                .unwrap_or_else(|_| unreachable!());
571            data_ptr_iter = data_ptr_iter
572                .checked_add(u32::try_from(data.len()).unwrap_or_else(|_| unreachable!()))
573                .unwrap_or_else(|| unreachable!());
574        }
575
576        // Now start executing the function. We pass as parameter the location and size of the
577        // input data.
578        let vm = match vm.start(
579            function_to_call,
580            &[
581                vm::WasmValue::I32(i32::from_ne_bytes(data_ptr.to_ne_bytes())),
582                vm::WasmValue::I32(i32::from_ne_bytes(data_len_u32.to_ne_bytes())),
583            ],
584        ) {
585            Ok(vm) => vm,
586            Err((error, vm_proto)) => {
587                self.vm_proto = vm_proto;
588                return Err((error.into(), self));
589            }
590        };
591
592        Ok(ReadyToRun {
593            resume_value: None,
594            inner: Box::new(Inner {
595                common: self.common,
596                vm,
597                storage_transaction_depth: 0,
598                signatures_batch_verification: None,
599                allocator,
600                storage_proof_size_behavior,
601            }),
602        })
603    }
604}
605
606impl fmt::Debug for HostVmPrototype {
607    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
608        f.debug_tuple("HostVmPrototype").finish()
609    }
610}
611
612/// Running virtual machine.
613#[must_use]
614#[derive(derive_more::From, Debug)]
615pub enum HostVm {
616    /// Wasm virtual machine is ready to be run. Call [`ReadyToRun::run`] to make progress.
617    #[from]
618    ReadyToRun(ReadyToRun),
619    /// Function execution has succeeded. Contains the return value of the call.
620    ///
621    /// The trie root hash of all the child tries must be recalculated and written to the main trie
622    /// similar to when a [`ExternalStorageRoot`] with a `child_trie` of `None` is generated. See
623    /// the documentation of [`ExternalStorageRoot`].
624    #[from]
625    Finished(Finished),
626    /// The Wasm blob did something that doesn't conform to the runtime environment.
627    Error {
628        /// Virtual machine ready to be used again.
629        prototype: HostVmPrototype,
630        /// Error that happened.
631        error: Error,
632    },
633    /// Must load an storage value.
634    #[from]
635    ExternalStorageGet(ExternalStorageGet),
636    /// Must set an storage value.
637    #[from]
638    ExternalStorageSet(ExternalStorageSet),
639    /// See documentation of [`ExternalStorageAppend`].
640    #[from]
641    ExternalStorageAppend(ExternalStorageAppend),
642    /// Must remove all the storage values starting with a certain prefix.
643    #[from]
644    ExternalStorageClearPrefix(ExternalStorageClearPrefix),
645    /// Must provide the trie root hash of the storage and write the trie root hash of child tries
646    /// to the main trie.
647    #[from]
648    ExternalStorageRoot(ExternalStorageRoot),
649    /// Need to provide the storage key that follows a specific one.
650    #[from]
651    ExternalStorageNextKey(ExternalStorageNextKey),
652    /// Must set off-chain index value.
653    #[from]
654    ExternalOffchainIndexSet(ExternalOffchainIndexSet),
655    /// Must load an offchain storage value.
656    #[from]
657    ExternalOffchainStorageGet(ExternalOffchainStorageGet),
658    /// Must set value of an off-chain storage entry.
659    #[from]
660    ExternalOffchainStorageSet(ExternalOffchainStorageSet),
661    /// Need to provide the current timestamp.
662    #[from]
663    OffchainTimestamp(OffchainTimestamp),
664    /// Must return random seed.
665    #[from]
666    OffchainRandomSeed(OffchainRandomSeed),
667    /// Submit a transaction from offchain worker.
668    #[from]
669    OffchainSubmitTransaction(OffchainSubmitTransaction),
670    /// Need to verify whether a signature is valid.
671    #[from]
672    SignatureVerification(SignatureVerification),
673    /// Need to call `Core_version` on the given Wasm code and return the raw output (i.e.
674    /// still SCALE-encoded), or an error if the call has failed.
675    #[from]
676    CallRuntimeVersion(CallRuntimeVersion),
677    /// Declares the start of a storage transaction. See [`HostVm::EndStorageTransaction`].
678    #[from]
679    StartStorageTransaction(StartStorageTransaction),
680    /// Ends a storage transaction. All changes made to the storage (e.g. through a
681    /// [`HostVm::ExternalStorageSet`]) since the previous
682    /// [`HostVm::StartStorageTransaction`] must be rolled back if `rollback` is true.
683    ///
684    /// Guaranteed by the code in this module to never happen if no transaction is in progress.
685    /// If the runtime attempts to end a non-existing transaction, an [`HostVm::Error`] is
686    /// generated instead.
687    EndStorageTransaction {
688        /// Object used to resume execution.
689        resume: EndStorageTransaction,
690        /// If true, changes must be rolled back.
691        rollback: bool,
692    },
693    /// Need to provide the maximum log level.
694    #[from]
695    GetMaxLogLevel(GetMaxLogLevel),
696    /// Runtime has emitted a log entry.
697    #[from]
698    LogEmit(LogEmit),
699}
700
701impl HostVm {
702    /// Cancels execution of the virtual machine and returns back the prototype.
703    pub fn into_prototype(self) -> HostVmPrototype {
704        match self {
705            HostVm::ReadyToRun(inner) => inner.inner.into_prototype(),
706            HostVm::Finished(inner) => inner.inner.into_prototype(),
707            HostVm::Error { prototype, .. } => prototype,
708            HostVm::ExternalStorageGet(inner) => inner.inner.into_prototype(),
709            HostVm::ExternalStorageSet(inner) => inner.inner.into_prototype(),
710            HostVm::ExternalStorageAppend(inner) => inner.inner.into_prototype(),
711            HostVm::ExternalStorageClearPrefix(inner) => inner.inner.into_prototype(),
712            HostVm::ExternalStorageRoot(inner) => inner.inner.into_prototype(),
713            HostVm::ExternalStorageNextKey(inner) => inner.inner.into_prototype(),
714            HostVm::ExternalOffchainIndexSet(inner) => inner.inner.into_prototype(),
715            HostVm::ExternalOffchainStorageGet(inner) => inner.inner.into_prototype(),
716            HostVm::ExternalOffchainStorageSet(inner) => inner.inner.into_prototype(),
717            HostVm::OffchainTimestamp(inner) => inner.inner.into_prototype(),
718            HostVm::OffchainRandomSeed(inner) => inner.inner.into_prototype(),
719            HostVm::OffchainSubmitTransaction(inner) => inner.inner.into_prototype(),
720            HostVm::SignatureVerification(inner) => inner.inner.into_prototype(),
721            HostVm::CallRuntimeVersion(inner) => inner.inner.into_prototype(),
722            HostVm::StartStorageTransaction(inner) => inner.inner.into_prototype(),
723            HostVm::EndStorageTransaction { resume, .. } => resume.inner.into_prototype(),
724            HostVm::GetMaxLogLevel(inner) => inner.inner.into_prototype(),
725            HostVm::LogEmit(inner) => inner.inner.into_prototype(),
726        }
727    }
728}
729
730/// Virtual machine is ready to run.
731pub struct ReadyToRun {
732    inner: Box<Inner>,
733    resume_value: Option<vm::WasmValue>,
734}
735
736impl ReadyToRun {
737    /// Runs the virtual machine until something important happens.
738    ///
739    /// > **Note**: This is when the actual CPU-heavy computation happens.
740    pub fn run(mut self) -> HostVm {
741        loop {
742            match self.run_once() {
743                HostVm::ReadyToRun(r) => self = r,
744                other => return other,
745            }
746        }
747    }
748
749    fn run_once(mut self) -> HostVm {
750        // `vm::ExecOutcome::Interrupted` is by far the variant that requires the most
751        // handling code. As such, special-case all other variants before.
752        let (id, params) = match self.inner.vm.run(self.resume_value) {
753            Ok(vm::ExecOutcome::Interrupted { id, params }) => (id, params),
754
755            Ok(vm::ExecOutcome::Finished {
756                return_value: Ok(Some(vm::WasmValue::I64(ret))),
757            }) => {
758                // Wasm virtual machine has successfully returned.
759
760                if self.inner.storage_transaction_depth > 0 {
761                    return HostVm::Error {
762                        prototype: self.inner.into_prototype(),
763                        error: Error::FinishedWithPendingTransaction,
764                    };
765                }
766
767                // Turn the `i64` into a `u64`, not changing any bit.
768                let ret = u64::from_ne_bytes(ret.to_ne_bytes());
769
770                // According to the runtime environment specification, the return value is two
771                // consecutive I32s representing the length and size of the SCALE-encoded
772                // return value.
773                let value_size = u32::try_from(ret >> 32).unwrap_or_else(|_| unreachable!());
774                let value_ptr = u32::try_from(ret & 0xffff_ffff).unwrap_or_else(|_| unreachable!());
775
776                if value_size.saturating_add(value_ptr)
777                    <= u32::from(self.inner.vm.memory_size()) * 64 * 1024
778                {
779                    return HostVm::Finished(Finished {
780                        inner: self.inner,
781                        value_ptr,
782                        value_size,
783                    });
784                }
785                let error = Error::ReturnedPtrOutOfRange {
786                    pointer: value_ptr,
787                    size: value_size,
788                    memory_size: u32::from(self.inner.vm.memory_size()) * 64 * 1024,
789                };
790
791                return HostVm::Error {
792                    prototype: self.inner.into_prototype(),
793                    error,
794                };
795            }
796
797            Ok(vm::ExecOutcome::Finished {
798                return_value: Ok(return_value),
799            }) => {
800                // The Wasm function has successfully returned, but the specs require that it
801                // returns a `i64`.
802                return HostVm::Error {
803                    prototype: self.inner.into_prototype(),
804                    error: Error::BadReturnValue {
805                        actual: return_value.map(|v| v.ty()),
806                    },
807                };
808            }
809
810            Ok(vm::ExecOutcome::Finished {
811                return_value: Err(err),
812            }) => {
813                return HostVm::Error {
814                    error: Error::Trap(err),
815                    prototype: self.inner.into_prototype(),
816                };
817            }
818
819            Err(vm::RunErr::BadValueTy { .. }) => {
820                // Tried to inject back the value returned by a host function, but it doesn't
821                // match what the Wasm code expects. Given that we check the host function
822                // signatures at initialization, this indicates a bug in this implementation.
823                unreachable!()
824            }
825
826            Err(vm::RunErr::Poisoned) => {
827                // Can only happen if there's a bug somewhere.
828                unreachable!()
829            }
830        };
831
832        // The Wasm code has called an host_fn. The `id` is a value that we passed
833        // at initialization, and corresponds to an index in `registered_functions`.
834        let host_fn = match self.inner.common.registered_functions.get(id) {
835            Some(FunctionImport::Resolved(f)) => *f,
836            Some(FunctionImport::Unresolved { name, module }) => {
837                return HostVm::Error {
838                    error: Error::UnresolvedFunctionCalled {
839                        function: name.clone(),
840                        module_name: module.clone(),
841                    },
842                    prototype: self.inner.into_prototype(),
843                };
844            }
845            None => unreachable!(),
846        };
847
848        // Passed a parameter index. Produces an `impl AsRef<[u8]>`.
849        macro_rules! expect_pointer_size {
850            ($num:expr) => {{
851                let val = match &params[$num] {
852                    vm::WasmValue::I64(v) => u64::from_ne_bytes(v.to_ne_bytes()),
853                    // The signatures are checked at initialization and the Wasm VM ensures that
854                    // the proper parameter types are provided.
855                    _ => unreachable!(),
856                };
857
858                let len = u32::try_from(val >> 32).unwrap_or_else(|_| unreachable!());
859                let ptr = u32::try_from(val & 0xffffffff).unwrap_or_else(|_| unreachable!());
860
861                let result = self.inner.vm.read_memory(ptr, len);
862                match result {
863                    Ok(v) => v,
864                    Err(vm::OutOfBoundsError) => {
865                        drop(result);
866                        return HostVm::Error {
867                            error: Error::ParamOutOfRange {
868                                function: host_fn.name(),
869                                param_num: $num,
870                                pointer: ptr,
871                                length: len,
872                            },
873                            prototype: self.inner.into_prototype(),
874                        };
875                    }
876                }
877            }};
878        }
879
880        macro_rules! expect_pointer_size_raw {
881            ($num:expr) => {{
882                let val = match &params[$num] {
883                    vm::WasmValue::I64(v) => u64::from_ne_bytes(v.to_ne_bytes()),
884                    // The signatures are checked at initialization and the Wasm VM ensures that
885                    // the proper parameter types are provided.
886                    _ => unreachable!(),
887                };
888
889                let len = u32::try_from(val >> 32).unwrap_or_else(|_| unreachable!());
890                let ptr = u32::try_from(val & 0xffffffff).unwrap_or_else(|_| unreachable!());
891
892                if len.saturating_add(ptr) > u32::from(self.inner.vm.memory_size()) * 64 * 1024 {
893                    return HostVm::Error {
894                        error: Error::ParamOutOfRange {
895                            function: host_fn.name(),
896                            param_num: $num,
897                            pointer: ptr,
898                            length: len,
899                        },
900                        prototype: self.inner.into_prototype(),
901                    };
902                }
903
904                (ptr, len)
905            }};
906        }
907
908        macro_rules! expect_pointer_constant_size {
909            ($num:expr, $size:expr) => {{
910                let ptr = match params[$num] {
911                    vm::WasmValue::I32(v) => u32::from_ne_bytes(v.to_ne_bytes()),
912                    // The signatures are checked at initialization and the Wasm VM ensures that
913                    // the proper parameter types are provided.
914                    _ => unreachable!(),
915                };
916
917                let result = self.inner.vm.read_memory(ptr, $size);
918                match result {
919                    Ok(v) => {
920                        *<&[u8; $size]>::try_from(v.as_ref()).unwrap_or_else(|_| unreachable!())
921                    }
922                    Err(vm::OutOfBoundsError) => {
923                        drop(result);
924                        return HostVm::Error {
925                            error: Error::ParamOutOfRange {
926                                function: host_fn.name(),
927                                param_num: $num,
928                                pointer: ptr,
929                                length: $size,
930                            },
931                            prototype: self.inner.into_prototype(),
932                        };
933                    }
934                }
935            }};
936        }
937
938        macro_rules! expect_pointer_constant_size_raw {
939            ($num:expr, $size:expr) => {{
940                let ptr = match params[$num] {
941                    vm::WasmValue::I32(v) => u32::from_ne_bytes(v.to_ne_bytes()),
942                    // The signatures are checked at initialization and the Wasm VM ensures that
943                    // the proper parameter types are provided.
944                    _ => unreachable!(),
945                };
946
947                if u32::saturating_add($size, ptr)
948                    > u32::from(self.inner.vm.memory_size()) * 64 * 1024
949                {
950                    return HostVm::Error {
951                        error: Error::ParamOutOfRange {
952                            function: host_fn.name(),
953                            param_num: $num,
954                            pointer: ptr,
955                            length: $size,
956                        },
957                        prototype: self.inner.into_prototype(),
958                    };
959                }
960
961                ptr
962            }};
963        }
964
965        macro_rules! expect_u32 {
966            ($num:expr) => {{
967                match &params[$num] {
968                    vm::WasmValue::I32(v) => u32::from_ne_bytes(v.to_ne_bytes()),
969                    // The signatures are checked at initialization and the Wasm VM ensures that
970                    // the proper parameter types are provided.
971                    _ => unreachable!(),
972                }
973            }};
974        }
975
976        macro_rules! expect_offchain_storage_kind {
977            ($num:expr) => {{
978                match &params[$num] {
979                    // `0` indicates `StorageKind::PERSISTENT`, the only kind of offchain
980                    // storage that is available.
981                    vm::WasmValue::I32(0) => true,
982                    // `1` indicates `StorageKind::LOCAL`, which is valid but has never been
983                    // implemented in Substrate.
984                    vm::WasmValue::I32(1) => false,
985                    vm::WasmValue::I32(_) => {
986                        return HostVm::Error {
987                            error: Error::ParamDecodeError,
988                            prototype: self.inner.into_prototype(),
989                        };
990                    }
991                    // The signatures are checked at initialization and the Wasm VM ensures that
992                    // the proper parameter types are provided.
993                    _ => unreachable!(),
994                }
995            }};
996        }
997
998        macro_rules! expect_state_version {
999            ($num:expr) => {{
1000                match &params[$num] {
1001                    vm::WasmValue::I32(0) => TrieEntryVersion::V0,
1002                    vm::WasmValue::I32(1) => TrieEntryVersion::V1,
1003                    vm::WasmValue::I32(_) => {
1004                        return HostVm::Error {
1005                            error: Error::ParamDecodeError,
1006                            prototype: self.inner.into_prototype(),
1007                        };
1008                    }
1009                    // The signatures are checked at initialization and the Wasm VM ensures that
1010                    // the proper parameter types are provided.
1011                    _ => unreachable!(),
1012                }
1013            }};
1014        }
1015
1016        // TODO: implement all functions and remove this macro
1017        macro_rules! host_fn_not_implemented {
1018            () => {{
1019                return HostVm::Error {
1020                    error: Error::HostFunctionNotImplemented {
1021                        function: host_fn.name(),
1022                    },
1023                    prototype: self.inner.into_prototype(),
1024                };
1025            }};
1026        }
1027
1028        // Handle the function calls.
1029        // Some of these enum variants simply change the state of `self`, while most of them
1030        // instead return an `ExternalVm` to the user.
1031        match host_fn {
1032            HostFunction::ext_storage_set_version_1 => {
1033                let (key_ptr, key_size) = expect_pointer_size_raw!(0);
1034                let (value_ptr, value_size) = expect_pointer_size_raw!(1);
1035                HostVm::ExternalStorageSet(ExternalStorageSet {
1036                    key_ptr,
1037                    key_size,
1038                    child_trie_ptr_size: None,
1039                    value: Some((value_ptr, value_size)),
1040                    inner: self.inner,
1041                })
1042            }
1043            HostFunction::ext_storage_get_version_1 => {
1044                let (key_ptr, key_size) = expect_pointer_size_raw!(0);
1045                HostVm::ExternalStorageGet(ExternalStorageGet {
1046                    key_ptr,
1047                    key_size,
1048                    child_trie_ptr_size: None,
1049                    calling: id,
1050                    value_out_ptr: None,
1051                    offset: 0,
1052                    max_size: u32::MAX,
1053                    inner: self.inner,
1054                })
1055            }
1056            HostFunction::ext_storage_read_version_1 => {
1057                let (key_ptr, key_size) = expect_pointer_size_raw!(0);
1058                let (value_out_ptr, value_out_size) = expect_pointer_size_raw!(1);
1059                let offset = expect_u32!(2);
1060                HostVm::ExternalStorageGet(ExternalStorageGet {
1061                    key_ptr,
1062                    key_size,
1063                    child_trie_ptr_size: None,
1064                    calling: id,
1065                    value_out_ptr: Some(value_out_ptr),
1066                    offset,
1067                    max_size: value_out_size,
1068                    inner: self.inner,
1069                })
1070            }
1071            HostFunction::ext_storage_clear_version_1 => {
1072                let (key_ptr, key_size) = expect_pointer_size_raw!(0);
1073                HostVm::ExternalStorageSet(ExternalStorageSet {
1074                    key_ptr,
1075                    key_size,
1076                    child_trie_ptr_size: None,
1077                    value: None,
1078                    inner: self.inner,
1079                })
1080            }
1081            HostFunction::ext_storage_exists_version_1 => {
1082                let (key_ptr, key_size) = expect_pointer_size_raw!(0);
1083                HostVm::ExternalStorageGet(ExternalStorageGet {
1084                    key_ptr,
1085                    key_size,
1086                    child_trie_ptr_size: None,
1087                    calling: id,
1088                    value_out_ptr: None,
1089                    offset: 0,
1090                    max_size: 0,
1091                    inner: self.inner,
1092                })
1093            }
1094            HostFunction::ext_storage_clear_prefix_version_1 => {
1095                let (prefix_ptr, prefix_size) = expect_pointer_size_raw!(0);
1096                HostVm::ExternalStorageClearPrefix(ExternalStorageClearPrefix {
1097                    prefix_ptr_size: Some((prefix_ptr, prefix_size)),
1098                    child_trie_ptr_size: None,
1099                    inner: self.inner,
1100                    max_keys_to_remove: None,
1101                    calling: id,
1102                })
1103            }
1104            HostFunction::ext_storage_clear_prefix_version_2 => {
1105                let (prefix_ptr, prefix_size) = expect_pointer_size_raw!(0);
1106
1107                let max_keys_to_remove = {
1108                    let input = expect_pointer_size!(1);
1109                    let parsing_result: Result<_, nom::Err<(&[u8], nom::error::ErrorKind)>> =
1110                        nom::Parser::parse(
1111                            &mut nom::combinator::all_consuming(util::nom_option_decode(
1112                                nom::number::streaming::le_u32,
1113                            )),
1114                            input.as_ref(),
1115                        )
1116                        .map(|(_, parse_result)| parse_result);
1117
1118                    match parsing_result {
1119                        Ok(val) => Ok(val),
1120                        Err(_) => Err(()),
1121                    }
1122                };
1123
1124                let max_keys_to_remove = match max_keys_to_remove {
1125                    Ok(l) => l,
1126                    Err(()) => {
1127                        return HostVm::Error {
1128                            error: Error::ParamDecodeError,
1129                            prototype: self.inner.into_prototype(),
1130                        };
1131                    }
1132                };
1133
1134                HostVm::ExternalStorageClearPrefix(ExternalStorageClearPrefix {
1135                    prefix_ptr_size: Some((prefix_ptr, prefix_size)),
1136                    child_trie_ptr_size: None,
1137                    inner: self.inner,
1138                    max_keys_to_remove,
1139                    calling: id,
1140                })
1141            }
1142            HostFunction::ext_storage_root_version_1 => {
1143                HostVm::ExternalStorageRoot(ExternalStorageRoot {
1144                    inner: self.inner,
1145                    calling: id,
1146                    child_trie_ptr_size: None,
1147                })
1148            }
1149            HostFunction::ext_storage_root_version_2 => {
1150                // The `ext_storage_root_version_2` host function gets passed as parameter the
1151                // state version of the runtime. This is in fact completely unnecessary as the
1152                // same information is found in the runtime specification, and this parameter
1153                // should be considered as a historical accident. We verify that the version
1154                // provided as parameter is the same as the one in the specification.
1155                let version_param = expect_state_version!(0);
1156                let version_spec = self
1157                    .inner
1158                    .common
1159                    .runtime_version
1160                    .as_ref()
1161                    .unwrap_or_else(|| unreachable!())
1162                    .decode()
1163                    .state_version
1164                    .unwrap_or(TrieEntryVersion::V0);
1165
1166                if version_param != version_spec {
1167                    return HostVm::Error {
1168                        error: Error::StateVersionMismatch {
1169                            parameter: version_param,
1170                            specification: version_spec,
1171                        },
1172                        prototype: self.inner.into_prototype(),
1173                    };
1174                }
1175
1176                HostVm::ExternalStorageRoot(ExternalStorageRoot {
1177                    inner: self.inner,
1178                    calling: id,
1179                    child_trie_ptr_size: None,
1180                })
1181            }
1182            HostFunction::ext_storage_changes_root_version_1 => {
1183                // The changes trie is an obsolete attempt at having a second trie containing, for
1184                // each storage item, the latest block height where this item has been modified.
1185                // When this function returns `None`, it indicates that the changes trie is
1186                // disabled. While this function used to be called by the runtimes of
1187                // Westend/Polkadot/Kusama (and maybe others), it has never returned anything else
1188                // but `None`. The entire changes trie mechanism was ultimately removed in
1189                // October 2021.
1190                // This function is no longer called by recent runtimes, but must be preserved for
1191                // backwards compatibility.
1192                self.inner.alloc_write_and_return_pointer_size(
1193                    HostFunction::ext_storage_changes_root_version_1.name(),
1194                    iter::once(&[0][..]),
1195                )
1196            }
1197            HostFunction::ext_storage_next_key_version_1 => {
1198                let (key_ptr, key_size) = expect_pointer_size_raw!(0);
1199                HostVm::ExternalStorageNextKey(ExternalStorageNextKey {
1200                    key_ptr,
1201                    key_size,
1202                    child_trie_ptr_size: None,
1203                    inner: self.inner,
1204                })
1205            }
1206            HostFunction::ext_storage_append_version_1 => {
1207                let (key_ptr, key_size) = expect_pointer_size_raw!(0);
1208                let (value_ptr, value_size) = expect_pointer_size_raw!(1);
1209                HostVm::ExternalStorageAppend(ExternalStorageAppend {
1210                    key_ptr,
1211                    key_size,
1212                    value_ptr,
1213                    value_size,
1214                    inner: self.inner,
1215                })
1216            }
1217            HostFunction::ext_storage_start_transaction_version_1 => {
1218                // TODO: a maximum depth is important in order to prevent a malicious runtime from crashing the client, but the depth needs to be the same as in Substrate; figure out
1219                self.inner.storage_transaction_depth += 1;
1220                HostVm::StartStorageTransaction(StartStorageTransaction { inner: self.inner })
1221            }
1222            HostFunction::ext_storage_rollback_transaction_version_1 => {
1223                if self.inner.storage_transaction_depth == 0 {
1224                    return HostVm::Error {
1225                        error: Error::NoActiveTransaction,
1226                        prototype: self.inner.into_prototype(),
1227                    };
1228                }
1229
1230                self.inner.storage_transaction_depth -= 1;
1231                HostVm::EndStorageTransaction {
1232                    resume: EndStorageTransaction { inner: self.inner },
1233                    rollback: true,
1234                }
1235            }
1236            HostFunction::ext_storage_commit_transaction_version_1 => {
1237                if self.inner.storage_transaction_depth == 0 {
1238                    return HostVm::Error {
1239                        error: Error::NoActiveTransaction,
1240                        prototype: self.inner.into_prototype(),
1241                    };
1242                }
1243
1244                self.inner.storage_transaction_depth -= 1;
1245                HostVm::EndStorageTransaction {
1246                    resume: EndStorageTransaction { inner: self.inner },
1247                    rollback: false,
1248                }
1249            }
1250            HostFunction::ext_storage_proof_size_storage_proof_size_version_1 => {
1251                match self.inner.storage_proof_size_behavior {
1252                    StorageProofSizeBehavior::ConstantReturnValue(value) => {
1253                        HostVm::ReadyToRun(ReadyToRun {
1254                            inner: self.inner,
1255                            resume_value: Some(vm::WasmValue::I64(i64::from_ne_bytes(
1256                                value.to_ne_bytes(),
1257                            ))),
1258                        })
1259                    }
1260                    StorageProofSizeBehavior::Unimplemented => HostVm::Error {
1261                        error: Error::HostFunctionNotImplemented {
1262                            function: host_fn.name(),
1263                        },
1264                        prototype: self.inner.into_prototype(),
1265                    },
1266                }
1267            }
1268            HostFunction::ext_default_child_storage_get_version_1 => {
1269                let (child_trie_ptr, child_trie_size) = expect_pointer_size_raw!(0);
1270                let (key_ptr, key_size) = expect_pointer_size_raw!(1);
1271                HostVm::ExternalStorageGet(ExternalStorageGet {
1272                    key_ptr,
1273                    key_size,
1274                    child_trie_ptr_size: Some((child_trie_ptr, child_trie_size)),
1275                    calling: id,
1276                    value_out_ptr: None,
1277                    offset: 0,
1278                    max_size: u32::MAX,
1279                    inner: self.inner,
1280                })
1281            }
1282            HostFunction::ext_default_child_storage_read_version_1 => {
1283                let (child_trie_ptr, child_trie_size) = expect_pointer_size_raw!(0);
1284                let (key_ptr, key_size) = expect_pointer_size_raw!(1);
1285                let (value_out_ptr, value_out_size) = expect_pointer_size_raw!(2);
1286                let offset = expect_u32!(3);
1287                HostVm::ExternalStorageGet(ExternalStorageGet {
1288                    key_ptr,
1289                    key_size,
1290                    child_trie_ptr_size: Some((child_trie_ptr, child_trie_size)),
1291                    calling: id,
1292                    value_out_ptr: Some(value_out_ptr),
1293                    offset,
1294                    max_size: value_out_size,
1295                    inner: self.inner,
1296                })
1297            }
1298            HostFunction::ext_default_child_storage_storage_kill_version_1 => {
1299                let (child_trie_ptr, child_trie_size) = expect_pointer_size_raw!(0);
1300                HostVm::ExternalStorageClearPrefix(ExternalStorageClearPrefix {
1301                    prefix_ptr_size: None,
1302                    child_trie_ptr_size: Some((child_trie_ptr, child_trie_size)),
1303                    inner: self.inner,
1304                    max_keys_to_remove: None,
1305                    calling: id,
1306                })
1307            }
1308            HostFunction::ext_default_child_storage_storage_kill_version_2
1309            | HostFunction::ext_default_child_storage_storage_kill_version_3 => {
1310                let (child_trie_ptr, child_trie_size) = expect_pointer_size_raw!(0);
1311
1312                let max_keys_to_remove = {
1313                    let input = expect_pointer_size!(1);
1314                    let parsing_result: Result<_, nom::Err<(&[u8], nom::error::ErrorKind)>> =
1315                        nom::Parser::parse(
1316                            &mut nom::combinator::all_consuming(util::nom_option_decode(
1317                                nom::number::streaming::le_u32,
1318                            )),
1319                            input.as_ref(),
1320                        )
1321                        .map(|(_, parse_result)| parse_result);
1322
1323                    match parsing_result {
1324                        Ok(val) => Ok(val),
1325                        Err(_) => Err(()),
1326                    }
1327                };
1328
1329                let max_keys_to_remove = match max_keys_to_remove {
1330                    Ok(l) => l,
1331                    Err(()) => {
1332                        return HostVm::Error {
1333                            error: Error::ParamDecodeError,
1334                            prototype: self.inner.into_prototype(),
1335                        };
1336                    }
1337                };
1338
1339                HostVm::ExternalStorageClearPrefix(ExternalStorageClearPrefix {
1340                    prefix_ptr_size: None,
1341                    child_trie_ptr_size: Some((child_trie_ptr, child_trie_size)),
1342                    inner: self.inner,
1343                    max_keys_to_remove,
1344                    calling: id,
1345                })
1346            }
1347            HostFunction::ext_default_child_storage_clear_prefix_version_1 => {
1348                let (child_trie_ptr, child_trie_size) = expect_pointer_size_raw!(0);
1349                let (prefix_ptr, prefix_size) = expect_pointer_size_raw!(1);
1350                HostVm::ExternalStorageClearPrefix(ExternalStorageClearPrefix {
1351                    prefix_ptr_size: Some((prefix_ptr, prefix_size)),
1352                    child_trie_ptr_size: Some((child_trie_ptr, child_trie_size)),
1353                    inner: self.inner,
1354                    max_keys_to_remove: None,
1355                    calling: id,
1356                })
1357            }
1358            HostFunction::ext_default_child_storage_clear_prefix_version_2 => {
1359                let (child_trie_ptr, child_trie_size) = expect_pointer_size_raw!(0);
1360                let (prefix_ptr, prefix_size) = expect_pointer_size_raw!(1);
1361
1362                let max_keys_to_remove = {
1363                    let input = expect_pointer_size!(2);
1364                    let parsing_result: Result<_, nom::Err<(&[u8], nom::error::ErrorKind)>> =
1365                        nom::Parser::parse(
1366                            &mut nom::combinator::all_consuming(util::nom_option_decode(
1367                                nom::number::streaming::le_u32,
1368                            )),
1369                            input.as_ref(),
1370                        )
1371                        .map(|(_, parse_result)| parse_result);
1372
1373                    match parsing_result {
1374                        Ok(val) => Ok(val),
1375                        Err(_) => Err(()),
1376                    }
1377                };
1378
1379                let max_keys_to_remove = match max_keys_to_remove {
1380                    Ok(l) => l,
1381                    Err(()) => {
1382                        return HostVm::Error {
1383                            error: Error::ParamDecodeError,
1384                            prototype: self.inner.into_prototype(),
1385                        };
1386                    }
1387                };
1388
1389                HostVm::ExternalStorageClearPrefix(ExternalStorageClearPrefix {
1390                    prefix_ptr_size: Some((prefix_ptr, prefix_size)),
1391                    child_trie_ptr_size: Some((child_trie_ptr, child_trie_size)),
1392                    inner: self.inner,
1393                    max_keys_to_remove,
1394                    calling: id,
1395                })
1396            }
1397            HostFunction::ext_default_child_storage_set_version_1 => {
1398                let (child_trie_ptr, child_trie_size) = expect_pointer_size_raw!(0);
1399                let (key_ptr, key_size) = expect_pointer_size_raw!(1);
1400                let (value_ptr, value_size) = expect_pointer_size_raw!(2);
1401                HostVm::ExternalStorageSet(ExternalStorageSet {
1402                    key_ptr,
1403                    key_size,
1404                    child_trie_ptr_size: Some((child_trie_ptr, child_trie_size)),
1405                    value: Some((value_ptr, value_size)),
1406                    inner: self.inner,
1407                })
1408            }
1409            HostFunction::ext_default_child_storage_clear_version_1 => {
1410                let (child_trie_ptr, child_trie_size) = expect_pointer_size_raw!(0);
1411                let (key_ptr, key_size) = expect_pointer_size_raw!(1);
1412                HostVm::ExternalStorageSet(ExternalStorageSet {
1413                    key_ptr,
1414                    key_size,
1415                    child_trie_ptr_size: Some((child_trie_ptr, child_trie_size)),
1416                    value: None,
1417                    inner: self.inner,
1418                })
1419            }
1420            HostFunction::ext_default_child_storage_exists_version_1 => {
1421                let (child_trie_ptr, child_trie_size) = expect_pointer_size_raw!(0);
1422                let (key_ptr, key_size) = expect_pointer_size_raw!(1);
1423                HostVm::ExternalStorageGet(ExternalStorageGet {
1424                    key_ptr,
1425                    key_size,
1426                    child_trie_ptr_size: Some((child_trie_ptr, child_trie_size)),
1427                    calling: id,
1428                    value_out_ptr: None,
1429                    offset: 0,
1430                    max_size: 0,
1431                    inner: self.inner,
1432                })
1433            }
1434            HostFunction::ext_default_child_storage_next_key_version_1 => {
1435                let (child_trie_ptr, child_trie_size) = expect_pointer_size_raw!(0);
1436                let (key_ptr, key_size) = expect_pointer_size_raw!(1);
1437                HostVm::ExternalStorageNextKey(ExternalStorageNextKey {
1438                    key_ptr,
1439                    key_size,
1440                    child_trie_ptr_size: Some((child_trie_ptr, child_trie_size)),
1441                    inner: self.inner,
1442                })
1443            }
1444            HostFunction::ext_default_child_storage_root_version_1 => {
1445                let (child_trie_ptr, child_trie_size) = expect_pointer_size_raw!(0);
1446                HostVm::ExternalStorageRoot(ExternalStorageRoot {
1447                    inner: self.inner,
1448                    calling: id,
1449                    child_trie_ptr_size: Some((child_trie_ptr, child_trie_size)),
1450                })
1451            }
1452            HostFunction::ext_default_child_storage_root_version_2 => {
1453                let (child_trie_ptr, child_trie_size) = expect_pointer_size_raw!(0);
1454
1455                // The `ext_default_child_storage_root_version_2` host function gets passed as
1456                // parameter the state version of the runtime. This is in fact completely
1457                // unnecessary as the same information is found in the runtime specification, and
1458                // this parameter should be considered as a historical accident. We verify that the
1459                // version provided as parameter is the same as the one in the specification.
1460                let version_param = expect_state_version!(1);
1461                let version_spec = self
1462                    .inner
1463                    .common
1464                    .runtime_version
1465                    .as_ref()
1466                    .unwrap_or_else(|| unreachable!())
1467                    .decode()
1468                    .state_version
1469                    .unwrap_or(TrieEntryVersion::V0);
1470
1471                if version_param != version_spec {
1472                    return HostVm::Error {
1473                        error: Error::StateVersionMismatch {
1474                            parameter: version_param,
1475                            specification: version_spec,
1476                        },
1477                        prototype: self.inner.into_prototype(),
1478                    };
1479                }
1480
1481                HostVm::ExternalStorageRoot(ExternalStorageRoot {
1482                    inner: self.inner,
1483                    calling: id,
1484                    child_trie_ptr_size: Some((child_trie_ptr, child_trie_size)),
1485                })
1486            }
1487            HostFunction::ext_crypto_ed25519_public_keys_version_1 => host_fn_not_implemented!(),
1488            HostFunction::ext_crypto_ed25519_generate_version_1 => host_fn_not_implemented!(),
1489            HostFunction::ext_crypto_ed25519_sign_version_1 => host_fn_not_implemented!(),
1490            HostFunction::ext_crypto_ed25519_verify_version_1
1491            | HostFunction::ext_crypto_ed25519_batch_verify_version_1 => {
1492                let is_batch_verification = matches!(
1493                    host_fn,
1494                    HostFunction::ext_crypto_ed25519_batch_verify_version_1
1495                );
1496
1497                if is_batch_verification && self.inner.signatures_batch_verification.is_none() {
1498                    return HostVm::Error {
1499                        error: Error::BatchVerifyWithoutStarting,
1500                        prototype: self.inner.into_prototype(),
1501                    };
1502                }
1503
1504                let (message_ptr, message_size) = expect_pointer_size_raw!(1);
1505                HostVm::SignatureVerification(SignatureVerification {
1506                    algorithm: SignatureVerificationAlgorithm::Ed25519,
1507                    signature_ptr: expect_pointer_constant_size_raw!(0, 64),
1508                    public_key_ptr: expect_pointer_constant_size_raw!(2, 32),
1509                    message_ptr,
1510                    message_size,
1511                    inner: self.inner,
1512                    is_batch_verification,
1513                })
1514            }
1515            HostFunction::ext_crypto_sr25519_public_keys_version_1 => host_fn_not_implemented!(),
1516            HostFunction::ext_crypto_sr25519_generate_version_1 => host_fn_not_implemented!(),
1517            HostFunction::ext_crypto_sr25519_sign_version_1 => host_fn_not_implemented!(),
1518            HostFunction::ext_crypto_sr25519_verify_version_1
1519            | HostFunction::ext_crypto_sr25519_batch_verify_version_1 => {
1520                let is_batch_verification = matches!(
1521                    host_fn,
1522                    HostFunction::ext_crypto_sr25519_batch_verify_version_1
1523                );
1524
1525                if is_batch_verification && self.inner.signatures_batch_verification.is_none() {
1526                    return HostVm::Error {
1527                        error: Error::BatchVerifyWithoutStarting,
1528                        prototype: self.inner.into_prototype(),
1529                    };
1530                }
1531
1532                let (message_ptr, message_size) = expect_pointer_size_raw!(1);
1533                HostVm::SignatureVerification(SignatureVerification {
1534                    algorithm: SignatureVerificationAlgorithm::Sr25519V1,
1535                    signature_ptr: expect_pointer_constant_size_raw!(0, 64),
1536                    public_key_ptr: expect_pointer_constant_size_raw!(2, 32),
1537                    message_ptr,
1538                    message_size,
1539                    inner: self.inner,
1540                    is_batch_verification,
1541                })
1542            }
1543            HostFunction::ext_crypto_sr25519_verify_version_2 => {
1544                let (message_ptr, message_size) = expect_pointer_size_raw!(1);
1545                HostVm::SignatureVerification(SignatureVerification {
1546                    algorithm: SignatureVerificationAlgorithm::Sr25519V2,
1547                    signature_ptr: expect_pointer_constant_size_raw!(0, 64),
1548                    public_key_ptr: expect_pointer_constant_size_raw!(2, 32),
1549                    message_ptr,
1550                    message_size,
1551                    inner: self.inner,
1552                    is_batch_verification: false,
1553                })
1554            }
1555            HostFunction::ext_crypto_ecdsa_generate_version_1 => host_fn_not_implemented!(),
1556            HostFunction::ext_crypto_ecdsa_sign_version_1 => {
1557                // NOTE: safe to unwrap here because we supply the nn to blake2b fn
1558                let data = <[u8; 32]>::try_from(
1559                    blake2_rfc::blake2b::blake2b(32, &[], expect_pointer_size!(0).as_ref())
1560                        .as_bytes(),
1561                )
1562                .unwrap_or_else(|_| unreachable!());
1563                let message = libsecp256k1::Message::parse(&data);
1564
1565                if let Ok(sc) =
1566                    libsecp256k1::SecretKey::parse(&expect_pointer_constant_size!(1, 32))
1567                {
1568                    let (sig, ri) = libsecp256k1::sign(&message, &sc);
1569
1570                    // NOTE: the function returns 2 slices: signature (64 bytes) and recovery ID (1 byte; AS A SLICE)
1571                    self.inner.alloc_write_and_return_pointer(
1572                        host_fn.name(),
1573                        [&sig.serialize()[..], &[ri.serialize()]].into_iter(),
1574                    )
1575                } else {
1576                    HostVm::Error {
1577                        error: Error::ParamDecodeError,
1578                        prototype: self.inner.into_prototype(),
1579                    }
1580                }
1581            }
1582            HostFunction::ext_crypto_ecdsa_public_keys_version_1 => host_fn_not_implemented!(),
1583            HostFunction::ext_crypto_ecdsa_verify_version_1
1584            | HostFunction::ext_crypto_ecdsa_batch_verify_version_1 => {
1585                let is_batch_verification = matches!(
1586                    host_fn,
1587                    HostFunction::ext_crypto_ecdsa_batch_verify_version_1
1588                );
1589
1590                if is_batch_verification && self.inner.signatures_batch_verification.is_none() {
1591                    return HostVm::Error {
1592                        error: Error::BatchVerifyWithoutStarting,
1593                        prototype: self.inner.into_prototype(),
1594                    };
1595                }
1596
1597                let (message_ptr, message_size) = expect_pointer_size_raw!(1);
1598                HostVm::SignatureVerification(SignatureVerification {
1599                    algorithm: SignatureVerificationAlgorithm::Ecdsa,
1600                    signature_ptr: expect_pointer_constant_size_raw!(0, 65),
1601                    public_key_ptr: expect_pointer_constant_size_raw!(2, 33),
1602                    message_ptr,
1603                    message_size,
1604                    inner: self.inner,
1605                    is_batch_verification,
1606                })
1607            }
1608            HostFunction::ext_crypto_ecdsa_verify_version_2 => host_fn_not_implemented!(),
1609            HostFunction::ext_crypto_ecdsa_sign_prehashed_version_1 => {
1610                // TODO: seems misimplemented, see https://spec.polkadot.network/#id-ext_crypto_ecdsa_sign_prehashed
1611                let message = libsecp256k1::Message::parse(&expect_pointer_constant_size!(0, 32));
1612
1613                if let Ok(sc) =
1614                    libsecp256k1::SecretKey::parse(&expect_pointer_constant_size!(1, 32))
1615                {
1616                    let (sig, ri) = libsecp256k1::sign(&message, &sc);
1617
1618                    // NOTE: the function returns 2 slices: signature (64 bytes) and recovery ID (1 byte; AS A SLICE)
1619                    self.inner.alloc_write_and_return_pointer(
1620                        host_fn.name(),
1621                        [&sig.serialize()[..], &[ri.serialize()]].into_iter(),
1622                    )
1623                } else {
1624                    HostVm::Error {
1625                        error: Error::ParamDecodeError,
1626                        prototype: self.inner.into_prototype(),
1627                    }
1628                }
1629            }
1630            HostFunction::ext_crypto_ecdsa_verify_prehashed_version_1 => {
1631                HostVm::SignatureVerification(SignatureVerification {
1632                    algorithm: SignatureVerificationAlgorithm::EcdsaPrehashed,
1633                    signature_ptr: expect_pointer_constant_size_raw!(0, 65),
1634                    public_key_ptr: expect_pointer_constant_size_raw!(2, 33),
1635                    message_ptr: expect_pointer_constant_size_raw!(1, 32),
1636                    message_size: 32,
1637                    inner: self.inner,
1638                    is_batch_verification: false,
1639                })
1640            }
1641
1642            HostFunction::ext_crypto_secp256k1_ecdsa_recover_version_1
1643            | HostFunction::ext_crypto_secp256k1_ecdsa_recover_version_2 => {
1644                let sig = expect_pointer_constant_size!(0, 65);
1645                let msg = expect_pointer_constant_size!(1, 32);
1646                let is_v2 = matches!(
1647                    host_fn,
1648                    HostFunction::ext_crypto_secp256k1_ecdsa_recover_version_2
1649                );
1650
1651                let result = {
1652                    let rs = if is_v2 {
1653                        libsecp256k1::Signature::parse_standard_slice(&sig[0..64])
1654                    } else {
1655                        libsecp256k1::Signature::parse_overflowing_slice(&sig[0..64])
1656                    };
1657
1658                    if let Ok(rs) = rs {
1659                        let v = libsecp256k1::RecoveryId::parse(if sig[64] > 26 {
1660                            sig[64] - 27
1661                        } else {
1662                            sig[64]
1663                        });
1664
1665                        if let Ok(v) = v {
1666                            let pubkey = libsecp256k1::recover(
1667                                &libsecp256k1::Message::parse_slice(&msg)
1668                                    .unwrap_or_else(|_| unreachable!()),
1669                                &rs,
1670                                &v,
1671                            );
1672
1673                            if let Ok(pubkey) = pubkey {
1674                                let mut res = Vec::with_capacity(65);
1675                                res.push(0);
1676                                res.extend_from_slice(&pubkey.serialize()[1..65]);
1677                                res
1678                            } else {
1679                                vec![1, 2]
1680                            }
1681                        } else {
1682                            vec![1, 1]
1683                        }
1684                    } else {
1685                        vec![1, 0]
1686                    }
1687                };
1688
1689                self.inner
1690                    .alloc_write_and_return_pointer_size(host_fn.name(), iter::once(&result))
1691            }
1692            HostFunction::ext_crypto_secp256k1_ecdsa_recover_compressed_version_1
1693            | HostFunction::ext_crypto_secp256k1_ecdsa_recover_compressed_version_2 => {
1694                let sig = expect_pointer_constant_size!(0, 65);
1695                let msg = expect_pointer_constant_size!(1, 32);
1696                let is_v2 = matches!(
1697                    host_fn,
1698                    HostFunction::ext_crypto_secp256k1_ecdsa_recover_compressed_version_2
1699                );
1700
1701                let result = {
1702                    let rs = if is_v2 {
1703                        libsecp256k1::Signature::parse_standard_slice(&sig[0..64])
1704                    } else {
1705                        libsecp256k1::Signature::parse_overflowing_slice(&sig[0..64])
1706                    };
1707
1708                    if let Ok(rs) = rs {
1709                        let v = libsecp256k1::RecoveryId::parse(if sig[64] > 26 {
1710                            sig[64] - 27
1711                        } else {
1712                            sig[64]
1713                        });
1714
1715                        if let Ok(v) = v {
1716                            let pubkey = libsecp256k1::recover(
1717                                &libsecp256k1::Message::parse_slice(&msg)
1718                                    .unwrap_or_else(|_| unreachable!()),
1719                                &rs,
1720                                &v,
1721                            );
1722
1723                            if let Ok(pubkey) = pubkey {
1724                                let mut res = Vec::with_capacity(34);
1725                                res.push(0);
1726                                res.extend_from_slice(&pubkey.serialize_compressed());
1727                                res
1728                            } else {
1729                                vec![1, 2]
1730                            }
1731                        } else {
1732                            vec![1, 1]
1733                        }
1734                    } else {
1735                        vec![1, 0]
1736                    }
1737                };
1738
1739                self.inner
1740                    .alloc_write_and_return_pointer_size(host_fn.name(), iter::once(&result))
1741            }
1742            HostFunction::ext_crypto_start_batch_verify_version_1 => {
1743                if self.inner.signatures_batch_verification.is_some() {
1744                    return HostVm::Error {
1745                        error: Error::AlreadyBatchVerify,
1746                        prototype: self.inner.into_prototype(),
1747                    };
1748                }
1749
1750                self.inner.signatures_batch_verification = Some(true);
1751
1752                HostVm::ReadyToRun(ReadyToRun {
1753                    resume_value: None,
1754                    inner: self.inner,
1755                })
1756            }
1757            HostFunction::ext_crypto_finish_batch_verify_version_1 => {
1758                let Some(outcome) = self.inner.signatures_batch_verification.take() else {
1759                    return HostVm::Error {
1760                        error: Error::NoBatchVerify,
1761                        prototype: self.inner.into_prototype(),
1762                    };
1763                };
1764
1765                HostVm::ReadyToRun(ReadyToRun {
1766                    resume_value: Some(vm::WasmValue::I32(if outcome { 1 } else { 0 })),
1767                    inner: self.inner,
1768                })
1769            }
1770            HostFunction::ext_hashing_keccak_256_version_1 => {
1771                let hash =
1772                    <sha3::Keccak256 as sha3::Digest>::digest(expect_pointer_size!(0).as_ref());
1773                self.inner
1774                    .alloc_write_and_return_pointer(host_fn.name(), iter::once(&hash))
1775            }
1776            HostFunction::ext_hashing_keccak_512_version_1 => {
1777                let hash =
1778                    <sha3::Keccak512 as sha3::Digest>::digest(expect_pointer_size!(0).as_ref());
1779                self.inner
1780                    .alloc_write_and_return_pointer(host_fn.name(), iter::once(&hash))
1781            }
1782            HostFunction::ext_hashing_sha2_256_version_1 => {
1783                let hash = <sha2::Sha256 as sha2::Digest>::digest(expect_pointer_size!(0).as_ref());
1784                self.inner
1785                    .alloc_write_and_return_pointer(host_fn.name(), iter::once(&hash))
1786            }
1787            HostFunction::ext_hashing_blake2_128_version_1 => {
1788                let out = {
1789                    let data = expect_pointer_size!(0);
1790                    blake2_rfc::blake2b::blake2b(16, &[], data.as_ref())
1791                };
1792
1793                self.inner
1794                    .alloc_write_and_return_pointer(host_fn.name(), iter::once(out.as_bytes()))
1795            }
1796            HostFunction::ext_hashing_blake2_256_version_1 => {
1797                let out = {
1798                    let data = expect_pointer_size!(0);
1799                    blake2_rfc::blake2b::blake2b(32, &[], data.as_ref())
1800                };
1801
1802                self.inner
1803                    .alloc_write_and_return_pointer(host_fn.name(), iter::once(out.as_bytes()))
1804            }
1805            HostFunction::ext_hashing_twox_64_version_1 => {
1806                let r0 = {
1807                    let data = expect_pointer_size!(0);
1808                    twox_hash::XxHash64::oneshot(0, data.as_ref())
1809                };
1810
1811                self.inner
1812                    .alloc_write_and_return_pointer(host_fn.name(), iter::once(&r0.to_le_bytes()))
1813            }
1814            HostFunction::ext_hashing_twox_128_version_1 => {
1815                let [r0, r1] = {
1816                    let data = expect_pointer_size!(0);
1817                    let data = data.as_ref();
1818                    [
1819                        twox_hash::XxHash64::oneshot(0, data),
1820                        twox_hash::XxHash64::oneshot(1, data),
1821                    ]
1822                };
1823
1824                self.inner.alloc_write_and_return_pointer(
1825                    host_fn.name(),
1826                    iter::once(&r0.to_le_bytes()).chain(iter::once(&r1.to_le_bytes())),
1827                )
1828            }
1829            HostFunction::ext_hashing_twox_256_version_1 => {
1830                let [r0, r1, r2, r3] = {
1831                    let data = expect_pointer_size!(0);
1832                    let data = data.as_ref();
1833                    [
1834                        twox_hash::XxHash64::oneshot(0, data),
1835                        twox_hash::XxHash64::oneshot(1, data),
1836                        twox_hash::XxHash64::oneshot(2, data),
1837                        twox_hash::XxHash64::oneshot(3, data),
1838                    ]
1839                };
1840
1841                self.inner.alloc_write_and_return_pointer(
1842                    host_fn.name(),
1843                    iter::once(&r0.to_le_bytes())
1844                        .chain(iter::once(&r1.to_le_bytes()))
1845                        .chain(iter::once(&r2.to_le_bytes()))
1846                        .chain(iter::once(&r3.to_le_bytes())),
1847                )
1848            }
1849            HostFunction::ext_offchain_index_set_version_1 => {
1850                let (key_ptr, key_size) = expect_pointer_size_raw!(0);
1851                let (value_ptr, value_size) = expect_pointer_size_raw!(1);
1852                HostVm::ExternalOffchainIndexSet(ExternalOffchainIndexSet {
1853                    key_ptr,
1854                    key_size,
1855                    value: Some((value_ptr, value_size)),
1856                    inner: self.inner,
1857                })
1858            }
1859            HostFunction::ext_offchain_index_clear_version_1 => {
1860                let (key_ptr, key_size) = expect_pointer_size_raw!(0);
1861                HostVm::ExternalOffchainIndexSet(ExternalOffchainIndexSet {
1862                    key_ptr,
1863                    key_size,
1864                    value: None,
1865                    inner: self.inner,
1866                })
1867            }
1868            HostFunction::ext_offchain_is_validator_version_1 => HostVm::ReadyToRun(ReadyToRun {
1869                inner: self.inner,
1870                resume_value: Some(vm::WasmValue::I32(1)), // TODO: ask the API user
1871            }),
1872            HostFunction::ext_offchain_submit_transaction_version_1 => {
1873                let (tx_ptr, tx_size) = expect_pointer_size_raw!(0);
1874                HostVm::OffchainSubmitTransaction(OffchainSubmitTransaction {
1875                    inner: self.inner,
1876                    calling: id,
1877                    tx_ptr,
1878                    tx_size,
1879                })
1880            }
1881            HostFunction::ext_offchain_network_state_version_1 => {
1882                host_fn_not_implemented!()
1883            }
1884            HostFunction::ext_offchain_timestamp_version_1 => {
1885                HostVm::OffchainTimestamp(OffchainTimestamp { inner: self.inner })
1886            }
1887            HostFunction::ext_offchain_sleep_until_version_1 => {
1888                host_fn_not_implemented!()
1889            }
1890            HostFunction::ext_offchain_random_seed_version_1 => {
1891                HostVm::OffchainRandomSeed(OffchainRandomSeed {
1892                    inner: self.inner,
1893                    calling: id,
1894                })
1895            }
1896            HostFunction::ext_offchain_local_storage_set_version_1 => {
1897                if expect_offchain_storage_kind!(0) {
1898                    let (key_ptr, key_size) = expect_pointer_size_raw!(1);
1899                    let (value_ptr, value_size) = expect_pointer_size_raw!(2);
1900                    HostVm::ExternalOffchainStorageSet(ExternalOffchainStorageSet {
1901                        key_ptr,
1902                        key_size,
1903                        value: Some((value_ptr, value_size)),
1904                        old_value: None,
1905                        inner: self.inner,
1906                    })
1907                } else {
1908                    HostVm::ReadyToRun(ReadyToRun {
1909                        inner: self.inner,
1910                        resume_value: None,
1911                    })
1912                }
1913            }
1914            HostFunction::ext_offchain_local_storage_compare_and_set_version_1 => {
1915                if expect_offchain_storage_kind!(0) {
1916                    let (key_ptr, key_size) = expect_pointer_size_raw!(1);
1917                    let (old_value_ptr, old_value_size) = expect_pointer_size_raw!(2);
1918                    let (value_ptr, value_size) = expect_pointer_size_raw!(3);
1919                    HostVm::ExternalOffchainStorageSet(ExternalOffchainStorageSet {
1920                        key_ptr,
1921                        key_size,
1922                        value: Some((value_ptr, value_size)),
1923                        old_value: Some((old_value_ptr, old_value_size)),
1924                        inner: self.inner,
1925                    })
1926                } else {
1927                    HostVm::ReadyToRun(ReadyToRun {
1928                        inner: self.inner,
1929                        resume_value: Some(vm::WasmValue::I32(0)),
1930                    })
1931                }
1932            }
1933            HostFunction::ext_offchain_local_storage_get_version_1 => {
1934                if expect_offchain_storage_kind!(0) {
1935                    let (key_ptr, key_size) = expect_pointer_size_raw!(1);
1936                    HostVm::ExternalOffchainStorageGet(ExternalOffchainStorageGet {
1937                        key_ptr,
1938                        key_size,
1939                        calling: id,
1940                        inner: self.inner,
1941                    })
1942                } else {
1943                    // Write a SCALE-encoded `None`.
1944                    self.inner
1945                        .alloc_write_and_return_pointer_size(host_fn.name(), iter::once(&[0]))
1946                }
1947            }
1948            HostFunction::ext_offchain_local_storage_clear_version_1 => {
1949                if expect_offchain_storage_kind!(0) {
1950                    let (key_ptr, key_size) = expect_pointer_size_raw!(1);
1951                    HostVm::ExternalOffchainStorageSet(ExternalOffchainStorageSet {
1952                        key_ptr,
1953                        key_size,
1954                        value: None,
1955                        old_value: None,
1956                        inner: self.inner,
1957                    })
1958                } else {
1959                    HostVm::ReadyToRun(ReadyToRun {
1960                        inner: self.inner,
1961                        resume_value: None,
1962                    })
1963                }
1964            }
1965            HostFunction::ext_offchain_http_request_start_version_1 => host_fn_not_implemented!(),
1966            HostFunction::ext_offchain_http_request_add_header_version_1 => {
1967                host_fn_not_implemented!()
1968            }
1969            HostFunction::ext_offchain_http_request_write_body_version_1 => {
1970                host_fn_not_implemented!()
1971            }
1972            HostFunction::ext_offchain_http_response_wait_version_1 => host_fn_not_implemented!(),
1973            HostFunction::ext_offchain_http_response_headers_version_1 => {
1974                host_fn_not_implemented!()
1975            }
1976            HostFunction::ext_offchain_http_response_read_body_version_1 => {
1977                host_fn_not_implemented!()
1978            }
1979            HostFunction::ext_trie_blake2_256_root_version_1
1980            | HostFunction::ext_trie_blake2_256_root_version_2
1981            | HostFunction::ext_trie_keccak_256_root_version_1
1982            | HostFunction::ext_trie_keccak_256_root_version_2 => {
1983                let state_version = if matches!(
1984                    host_fn,
1985                    HostFunction::ext_trie_blake2_256_root_version_2
1986                        | HostFunction::ext_trie_keccak_256_root_version_2
1987                ) {
1988                    expect_state_version!(1)
1989                } else {
1990                    TrieEntryVersion::V0
1991                };
1992
1993                let result = {
1994                    let input = expect_pointer_size!(0);
1995                    let parsing_result: Result<_, nom::Err<(&[u8], nom::error::ErrorKind)>> =
1996                        nom::Parser::parse(
1997                            &mut nom::combinator::all_consuming(nom::combinator::flat_map(
1998                                crate::util::nom_scale_compact_usize,
1999                                |num_elems| {
2000                                    nom::multi::many_m_n(
2001                                        num_elems,
2002                                        num_elems,
2003                                        (
2004                                            nom::combinator::flat_map(
2005                                                crate::util::nom_scale_compact_usize,
2006                                                nom::bytes::streaming::take,
2007                                            ),
2008                                            nom::combinator::flat_map(
2009                                                crate::util::nom_scale_compact_usize,
2010                                                nom::bytes::streaming::take,
2011                                            ),
2012                                        ),
2013                                    )
2014                                },
2015                            )),
2016                            input.as_ref(),
2017                        )
2018                        .map(|(_, parse_result)| parse_result);
2019
2020                    match parsing_result {
2021                        Ok(elements) => Ok(trie::trie_root(
2022                            state_version,
2023                            if matches!(
2024                                host_fn,
2025                                HostFunction::ext_trie_blake2_256_root_version_1
2026                                    | HostFunction::ext_trie_blake2_256_root_version_2
2027                            ) {
2028                                trie::HashFunction::Blake2
2029                            } else {
2030                                trie::HashFunction::Keccak256
2031                            },
2032                            &elements[..],
2033                        )),
2034                        Err(_) => Err(()),
2035                    }
2036                };
2037
2038                match result {
2039                    Ok(out) => self
2040                        .inner
2041                        .alloc_write_and_return_pointer(host_fn.name(), iter::once(&out)),
2042                    Err(()) => HostVm::Error {
2043                        error: Error::ParamDecodeError,
2044                        prototype: self.inner.into_prototype(),
2045                    },
2046                }
2047            }
2048            HostFunction::ext_trie_blake2_256_ordered_root_version_1
2049            | HostFunction::ext_trie_blake2_256_ordered_root_version_2
2050            | HostFunction::ext_trie_keccak_256_ordered_root_version_1
2051            | HostFunction::ext_trie_keccak_256_ordered_root_version_2 => {
2052                let state_version = if matches!(
2053                    host_fn,
2054                    HostFunction::ext_trie_blake2_256_ordered_root_version_2
2055                        | HostFunction::ext_trie_keccak_256_ordered_root_version_2
2056                ) {
2057                    expect_state_version!(1)
2058                } else {
2059                    TrieEntryVersion::V0
2060                };
2061
2062                let result = {
2063                    let input = expect_pointer_size!(0);
2064                    let parsing_result: Result<_, nom::Err<(&[u8], nom::error::ErrorKind)>> =
2065                        nom::Parser::parse(
2066                            &mut nom::combinator::all_consuming(nom::combinator::flat_map(
2067                                crate::util::nom_scale_compact_usize,
2068                                |num_elems| {
2069                                    nom::multi::many_m_n(
2070                                        num_elems,
2071                                        num_elems,
2072                                        nom::combinator::flat_map(
2073                                            crate::util::nom_scale_compact_usize,
2074                                            nom::bytes::streaming::take,
2075                                        ),
2076                                    )
2077                                },
2078                            )),
2079                            input.as_ref(),
2080                        )
2081                        .map(|(_, parse_result)| parse_result);
2082
2083                    match parsing_result {
2084                        Ok(elements) => Ok(trie::ordered_root(
2085                            state_version,
2086                            if matches!(
2087                                host_fn,
2088                                HostFunction::ext_trie_blake2_256_ordered_root_version_1
2089                                    | HostFunction::ext_trie_blake2_256_ordered_root_version_2
2090                            ) {
2091                                trie::HashFunction::Blake2
2092                            } else {
2093                                trie::HashFunction::Keccak256
2094                            },
2095                            &elements[..],
2096                        )),
2097                        Err(_) => Err(()),
2098                    }
2099                };
2100
2101                match result {
2102                    Ok(out) => self
2103                        .inner
2104                        .alloc_write_and_return_pointer(host_fn.name(), iter::once(&out)),
2105                    Err(()) => HostVm::Error {
2106                        error: Error::ParamDecodeError,
2107                        prototype: self.inner.into_prototype(),
2108                    },
2109                }
2110            }
2111            HostFunction::ext_trie_blake2_256_verify_proof_version_1 => host_fn_not_implemented!(),
2112            HostFunction::ext_trie_blake2_256_verify_proof_version_2 => host_fn_not_implemented!(),
2113            HostFunction::ext_trie_keccak_256_verify_proof_version_1 => host_fn_not_implemented!(),
2114            HostFunction::ext_trie_keccak_256_verify_proof_version_2 => host_fn_not_implemented!(),
2115            HostFunction::ext_misc_print_num_version_1 => {
2116                let num = match params[0] {
2117                    vm::WasmValue::I64(v) => u64::from_ne_bytes(v.to_ne_bytes()),
2118                    // The signatures are checked at initialization and the Wasm VM ensures that
2119                    // the proper parameter types are provided.
2120                    _ => unreachable!(),
2121                };
2122
2123                HostVm::LogEmit(LogEmit {
2124                    inner: self.inner,
2125                    log_entry: LogEmitInner::Num(num),
2126                })
2127            }
2128            HostFunction::ext_misc_print_utf8_version_1 => {
2129                let (str_ptr, str_size) = expect_pointer_size_raw!(0);
2130
2131                let utf8_check = str::from_utf8(
2132                    self.inner
2133                        .vm
2134                        .read_memory(str_ptr, str_size)
2135                        .unwrap_or_else(|_| unreachable!())
2136                        .as_ref(),
2137                )
2138                .map(|_| ());
2139                if let Err(error) = utf8_check {
2140                    return HostVm::Error {
2141                        error: Error::Utf8Error {
2142                            function: host_fn.name(),
2143                            param_num: 2,
2144                            error,
2145                        },
2146                        prototype: self.inner.into_prototype(),
2147                    };
2148                }
2149
2150                HostVm::LogEmit(LogEmit {
2151                    inner: self.inner,
2152                    log_entry: LogEmitInner::Utf8 { str_ptr, str_size },
2153                })
2154            }
2155            HostFunction::ext_misc_print_hex_version_1 => {
2156                let (data_ptr, data_size) = expect_pointer_size_raw!(0);
2157                HostVm::LogEmit(LogEmit {
2158                    inner: self.inner,
2159                    log_entry: LogEmitInner::Hex {
2160                        data_ptr,
2161                        data_size,
2162                    },
2163                })
2164            }
2165            HostFunction::ext_misc_runtime_version_version_1 => {
2166                let (wasm_blob_ptr, wasm_blob_size) = expect_pointer_size_raw!(0);
2167                HostVm::CallRuntimeVersion(CallRuntimeVersion {
2168                    inner: self.inner,
2169                    wasm_blob_ptr,
2170                    wasm_blob_size,
2171                })
2172            }
2173            HostFunction::ext_allocator_malloc_version_1 => {
2174                let size = expect_u32!(0);
2175
2176                let ptr = match self.inner.alloc(host_fn.name(), size) {
2177                    Ok(p) => p,
2178                    Err(error) => {
2179                        return HostVm::Error {
2180                            error,
2181                            prototype: self.inner.into_prototype(),
2182                        };
2183                    }
2184                };
2185
2186                let ptr_i32 = i32::from_ne_bytes(ptr.to_ne_bytes());
2187                HostVm::ReadyToRun(ReadyToRun {
2188                    resume_value: Some(vm::WasmValue::I32(ptr_i32)),
2189                    inner: self.inner,
2190                })
2191            }
2192            HostFunction::ext_allocator_free_version_1 => {
2193                let pointer = expect_u32!(0);
2194                match self.inner.allocator.deallocate(
2195                    &mut MemAccess {
2196                        vm: MemAccessVm::Running(&mut self.inner.vm),
2197                        memory_total_pages: self.inner.common.memory_total_pages,
2198                    },
2199                    pointer,
2200                ) {
2201                    Ok(()) => {}
2202                    Err(_) => {
2203                        return HostVm::Error {
2204                            error: Error::FreeError { pointer },
2205                            prototype: self.inner.into_prototype(),
2206                        };
2207                    }
2208                };
2209
2210                HostVm::ReadyToRun(ReadyToRun {
2211                    resume_value: None,
2212                    inner: self.inner,
2213                })
2214            }
2215            HostFunction::ext_logging_log_version_1 => {
2216                let log_level = expect_u32!(0);
2217
2218                let (target_str_ptr, target_str_size) = expect_pointer_size_raw!(1);
2219                let target_utf8_check = str::from_utf8(
2220                    self.inner
2221                        .vm
2222                        .read_memory(target_str_ptr, target_str_size)
2223                        .unwrap_or_else(|_| unreachable!())
2224                        .as_ref(),
2225                )
2226                .map(|_| ());
2227                if let Err(error) = target_utf8_check {
2228                    return HostVm::Error {
2229                        error: Error::Utf8Error {
2230                            function: host_fn.name(),
2231                            param_num: 1,
2232                            error,
2233                        },
2234                        prototype: self.inner.into_prototype(),
2235                    };
2236                }
2237
2238                let (msg_str_ptr, msg_str_size) = expect_pointer_size_raw!(2);
2239                let msg_utf8_check = str::from_utf8(
2240                    self.inner
2241                        .vm
2242                        .read_memory(msg_str_ptr, msg_str_size)
2243                        .unwrap_or_else(|_| unreachable!())
2244                        .as_ref(),
2245                )
2246                .map(|_| ());
2247                if let Err(error) = msg_utf8_check {
2248                    return HostVm::Error {
2249                        error: Error::Utf8Error {
2250                            function: host_fn.name(),
2251                            param_num: 2,
2252                            error,
2253                        },
2254                        prototype: self.inner.into_prototype(),
2255                    };
2256                }
2257
2258                HostVm::LogEmit(LogEmit {
2259                    inner: self.inner,
2260                    log_entry: LogEmitInner::Log {
2261                        log_level,
2262                        target_str_ptr,
2263                        target_str_size,
2264                        msg_str_ptr,
2265                        msg_str_size,
2266                    },
2267                })
2268            }
2269            HostFunction::ext_logging_max_level_version_1 => {
2270                HostVm::GetMaxLogLevel(GetMaxLogLevel { inner: self.inner })
2271            }
2272            HostFunction::ext_panic_handler_abort_on_panic_version_1 => {
2273                let message = {
2274                    let message_bytes = expect_pointer_size!(0);
2275                    str::from_utf8(message_bytes.as_ref()).map(|msg| msg.to_owned())
2276                };
2277
2278                match message {
2279                    Ok(message) => HostVm::Error {
2280                        error: Error::AbortOnPanic { message },
2281                        prototype: self.inner.into_prototype(),
2282                    },
2283                    Err(error) => HostVm::Error {
2284                        error: Error::Utf8Error {
2285                            function: host_fn.name(),
2286                            param_num: 0,
2287                            error,
2288                        },
2289                        prototype: self.inner.into_prototype(),
2290                    },
2291                }
2292            }
2293            HostFunction::ext_transaction_index_index_version_1 => {
2294                // TODO: this is currently a no-op; because not all the parameters are verified, this might lead to consensus issues
2295                let _tx_ptr = expect_pointer_size_raw!(0);
2296                HostVm::ReadyToRun(ReadyToRun {
2297                    inner: self.inner,
2298                    resume_value: None,
2299                })
2300            }
2301            HostFunction::ext_transaction_index_renew_version_1 => {
2302                // TODO: this is currently a no-op; because not all the parameters are verified, this might lead to consensus issues
2303                let _tx_ptr = expect_pointer_size_raw!(0);
2304                HostVm::ReadyToRun(ReadyToRun {
2305                    inner: self.inner,
2306                    resume_value: None,
2307                })
2308            }
2309        }
2310    }
2311}
2312
2313impl fmt::Debug for ReadyToRun {
2314    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2315        f.debug_tuple("ReadyToRun").finish()
2316    }
2317}
2318
2319/// Function execution has succeeded. Contains the return value of the call.
2320///
2321/// The trie root hash of all the child tries must be recalculated and written to the main trie
2322/// similar to when a [`ExternalStorageRoot`] with a `child_trie` of `None` is generated. See the
2323/// documentation of [`ExternalStorageRoot`].
2324pub struct Finished {
2325    inner: Box<Inner>,
2326
2327    /// Pointer to the value returned by the VM. Guaranteed to be in range.
2328    value_ptr: u32,
2329    /// Size of the value returned by the VM. Guaranteed to be in range.
2330    value_size: u32,
2331}
2332
2333impl Finished {
2334    /// Returns the value the called function has returned.
2335    pub fn value(&self) -> impl AsRef<[u8]> {
2336        self.inner
2337            .vm
2338            .read_memory(self.value_ptr, self.value_size)
2339            .unwrap_or_else(|_| unreachable!())
2340    }
2341
2342    /// Turns the virtual machine back into a prototype.
2343    pub fn into_prototype(self) -> HostVmPrototype {
2344        self.inner.into_prototype()
2345    }
2346}
2347
2348impl fmt::Debug for Finished {
2349    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2350        f.debug_tuple("Finished").finish()
2351    }
2352}
2353
2354/// Must provide the value of a storage entry.
2355pub struct ExternalStorageGet {
2356    inner: Box<Inner>,
2357
2358    /// Function currently being called by the Wasm code. Refers to an index within
2359    /// [`VmCommon::registered_functions`]. Guaranteed to be [`FunctionImport::Resolved`].
2360    calling: usize,
2361
2362    /// Used only for the `ext_storage_read_version_1` function. Stores the pointer where the
2363    /// output should be stored.
2364    value_out_ptr: Option<u32>,
2365
2366    /// Pointer to the key whose value must be loaded. Guaranteed to be in range.
2367    key_ptr: u32,
2368    /// Size of the key whose value must be loaded. Guaranteed to be in range.
2369    key_size: u32,
2370    /// Pointer and size to the default child trie. `None` if main trie. Guaranteed to be in range.
2371    child_trie_ptr_size: Option<(u32, u32)>,
2372    /// Offset within the value that the Wasm VM requires.
2373    offset: u32,
2374    /// Maximum size that the Wasm VM would accept.
2375    max_size: u32,
2376}
2377
2378impl ExternalStorageGet {
2379    /// Returns the key whose value must be provided back with [`ExternalStorageGet::resume`].
2380    pub fn key(&self) -> impl AsRef<[u8]> {
2381        self.inner
2382            .vm
2383            .read_memory(self.key_ptr, self.key_size)
2384            .unwrap_or_else(|_| unreachable!())
2385    }
2386
2387    /// If `Some`, read from the given child trie. If `None`, read from the main trie.
2388    pub fn child_trie(&self) -> Option<impl AsRef<[u8]>> {
2389        if let Some((child_trie_ptr, child_trie_size)) = self.child_trie_ptr_size {
2390            let child_trie = self
2391                .inner
2392                .vm
2393                .read_memory(child_trie_ptr, child_trie_size)
2394                .unwrap_or_else(|_| unreachable!());
2395            Some(child_trie)
2396        } else {
2397            None
2398        }
2399    }
2400
2401    /// Offset within the value that is requested.
2402    pub fn offset(&self) -> u32 {
2403        self.offset
2404    }
2405
2406    /// Maximum size of the value to pass back.
2407    ///
2408    /// > **Note**: This can be 0 if we only want to know whether a value exists.
2409    pub fn max_size(&self) -> u32 {
2410        self.max_size
2411    }
2412
2413    /// Same as [`ExternalStorageGet::resume`], but passes the full value, without taking the
2414    /// offset and maximum size into account.
2415    ///
2416    /// This is a convenient function that automatically applies the offset and maximum size, to
2417    /// use when the full storage value is already present in memory.
2418    pub fn resume_full_value(self, value: Option<&[u8]>) -> HostVm {
2419        if let Some(value) = value {
2420            if usize::try_from(self.offset).unwrap_or_else(|_| unreachable!()) < value.len() {
2421                let value_slice =
2422                    &value[usize::try_from(self.offset).unwrap_or_else(|_| unreachable!())..];
2423                if usize::try_from(self.max_size).unwrap_or_else(|_| unreachable!())
2424                    < value_slice.len()
2425                {
2426                    let value_slice = &value_slice
2427                        [..usize::try_from(self.max_size).unwrap_or_else(|_| unreachable!())];
2428                    self.resume(Some((value_slice, value.len())))
2429                } else {
2430                    self.resume(Some((value_slice, value.len())))
2431                }
2432            } else {
2433                self.resume(Some((&[], value.len())))
2434            }
2435        } else {
2436            self.resume(None)
2437        }
2438    }
2439
2440    /// Writes the storage value in the Wasm VM's memory and prepares the virtual machine to
2441    /// resume execution.
2442    ///
2443    /// The value to provide must be the value of that key starting at the offset returned by
2444    /// [`ExternalStorageGet::offset`]. If the offset is out of range, an empty slice must be
2445    /// passed.
2446    ///
2447    /// If `Some`, the total size of the value, without taking [`ExternalStorageGet::offset`] or
2448    /// [`ExternalStorageGet::max_size`] into account, must additionally be provided.
2449    ///
2450    /// If [`ExternalStorageGet::child_trie`] returns `Some` but the child trie doesn't exist,
2451    /// then `None` must be provided.
2452    ///
2453    /// The value must not be longer than what [`ExternalStorageGet::max_size`] returns.
2454    ///
2455    /// # Panic
2456    ///
2457    /// Panics if the value is longer than what [`ExternalStorageGet::max_size`] returns.
2458    ///
2459    pub fn resume(self, value: Option<(&[u8], usize)>) -> HostVm {
2460        self.resume_vectored(
2461            value
2462                .as_ref()
2463                .map(|(value, size)| (iter::once(&value[..]), *size)),
2464        )
2465    }
2466
2467    /// Similar to [`ExternalStorageGet::resume`], but allows passing the value as a list of
2468    /// buffers whose concatenation forms the actual value.
2469    ///
2470    /// If `Some`, the total size of the value, without taking [`ExternalStorageGet::offset`] or
2471    /// [`ExternalStorageGet::max_size`] into account, must additionally be provided.
2472    ///
2473    /// # Panic
2474    ///
2475    /// See [`ExternalStorageGet::resume`].
2476    ///
2477    pub fn resume_vectored(
2478        mut self,
2479        value: Option<(impl Iterator<Item = impl AsRef<[u8]>> + Clone, usize)>,
2480    ) -> HostVm {
2481        let host_fn = match self.inner.common.registered_functions[self.calling] {
2482            FunctionImport::Resolved(f) => f,
2483            FunctionImport::Unresolved { .. } => unreachable!(),
2484        };
2485
2486        match host_fn {
2487            HostFunction::ext_storage_get_version_1
2488            | HostFunction::ext_default_child_storage_get_version_1 => {
2489                if let Some((value, value_total_len)) = value {
2490                    // Writing `Some(value)`.
2491                    debug_assert_eq!(
2492                        value.clone().fold(0, |a, b| a + b.as_ref().len()),
2493                        value_total_len
2494                    );
2495                    let value_len_enc = util::encode_scale_compact_usize(value_total_len);
2496                    self.inner.alloc_write_and_return_pointer_size(
2497                        host_fn.name(),
2498                        iter::once(&[1][..])
2499                            .chain(iter::once(value_len_enc.as_ref()))
2500                            .map(either::Left)
2501                            .chain(value.map(either::Right)),
2502                    )
2503                } else {
2504                    // Write a SCALE-encoded `None`.
2505                    self.inner
2506                        .alloc_write_and_return_pointer_size(host_fn.name(), iter::once(&[0]))
2507                }
2508            }
2509            HostFunction::ext_storage_read_version_1
2510            | HostFunction::ext_default_child_storage_read_version_1 => {
2511                let outcome = if let Some((value, value_total_len)) = value {
2512                    let mut remaining_max_allowed =
2513                        usize::try_from(self.max_size).unwrap_or_else(|_| unreachable!());
2514                    let mut offset = self.value_out_ptr.unwrap_or_else(|| unreachable!());
2515                    for value in value {
2516                        let value = value.as_ref();
2517                        assert!(value.len() <= remaining_max_allowed);
2518                        remaining_max_allowed -= value.len();
2519                        self.inner
2520                            .vm
2521                            .write_memory(offset, value)
2522                            .unwrap_or_else(|_| unreachable!());
2523                        offset += u32::try_from(value.len()).unwrap_or_else(|_| unreachable!());
2524                    }
2525
2526                    // Note: the https://github.com/paritytech/substrate/pull/7084 PR has changed
2527                    // the meaning of this return value.
2528                    Some(
2529                        u32::try_from(value_total_len).unwrap_or_else(|_| unreachable!())
2530                            - self.offset,
2531                    )
2532                } else {
2533                    None
2534                };
2535
2536                return self.inner.alloc_write_and_return_pointer_size(
2537                    host_fn.name(),
2538                    if let Some(outcome) = outcome {
2539                        either::Left(
2540                            iter::once(either::Left([1u8]))
2541                                .chain(iter::once(either::Right(outcome.to_le_bytes()))),
2542                        )
2543                    } else {
2544                        either::Right(iter::once(either::Left([0u8])))
2545                    },
2546                );
2547            }
2548            HostFunction::ext_storage_exists_version_1
2549            | HostFunction::ext_default_child_storage_exists_version_1 => {
2550                HostVm::ReadyToRun(ReadyToRun {
2551                    inner: self.inner,
2552                    resume_value: Some(if value.is_some() {
2553                        vm::WasmValue::I32(1)
2554                    } else {
2555                        vm::WasmValue::I32(0)
2556                    }),
2557                })
2558            }
2559            _ => unreachable!(),
2560        }
2561    }
2562}
2563
2564impl fmt::Debug for ExternalStorageGet {
2565    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2566        f.debug_tuple("ExternalStorageGet").finish()
2567    }
2568}
2569
2570/// Must set the value of a storage entry.
2571///
2572/// If [`ExternalStorageSet::child_trie`] return `None` and [`ExternalStorageSet::key`]
2573/// returns a key that starts with `:child_storage:`, then the write must be silently ignored.
2574///
2575/// If [`ExternalStorageSet::child_trie`] and [`ExternalStorageSet::value`] return `Some` and the
2576/// child trie doesn't exist, it must implicitly be created.
2577/// If [`ExternalStorageSet::child_trie`] returns `Some` and [`ExternalStorageSet::value`]
2578/// returns `None` and this is the last entry in the child trie, it must implicitly be destroyed.
2579pub struct ExternalStorageSet {
2580    inner: Box<Inner>,
2581
2582    /// Pointer to the key whose value must be set. Guaranteed to be in range.
2583    key_ptr: u32,
2584    /// Size of the key whose value must be set. Guaranteed to be in range.
2585    key_size: u32,
2586    /// Pointer and size to the default child trie key. `None` if main trie. Guaranteed to be
2587    /// in range.
2588    child_trie_ptr_size: Option<(u32, u32)>,
2589
2590    /// Pointer and size of the value to set. `None` for clearing. Guaranteed to be in range.
2591    value: Option<(u32, u32)>,
2592}
2593
2594impl ExternalStorageSet {
2595    /// Returns the key whose value must be set.
2596    pub fn key(&self) -> impl AsRef<[u8]> {
2597        self.inner
2598            .vm
2599            .read_memory(self.key_ptr, self.key_size)
2600            .unwrap_or_else(|_| unreachable!())
2601    }
2602
2603    /// If `Some`, write to the given child trie. If `None`, write to the main trie.
2604    ///
2605    /// If [`ExternalStorageSet::value`] returns `Some` and the child trie doesn't exist, it must
2606    /// implicitly be created.
2607    /// If [`ExternalStorageSet::value`] returns `None` and this is the last entry in the child
2608    /// trie, it must implicitly be destroyed.
2609    pub fn child_trie(&self) -> Option<impl AsRef<[u8]>> {
2610        match &self.child_trie_ptr_size {
2611            Some((ptr, size)) => {
2612                let child_trie = self
2613                    .inner
2614                    .vm
2615                    .read_memory(*ptr, *size)
2616                    .unwrap_or_else(|_| unreachable!());
2617                Some(child_trie)
2618            }
2619            None => None,
2620        }
2621    }
2622
2623    /// Returns the value to set.
2624    ///
2625    /// If `None` is returned, the key should be removed from the storage entirely.
2626    pub fn value(&self) -> Option<impl AsRef<[u8]>> {
2627        self.value.map(|(ptr, size)| {
2628            self.inner
2629                .vm
2630                .read_memory(ptr, size)
2631                .unwrap_or_else(|_| unreachable!())
2632        })
2633    }
2634
2635    /// Returns the state trie version indicated by the runtime.
2636    ///
2637    /// This information should be stored alongside with the storage value and is necessary in
2638    /// order to properly build the trie and thus the trie root node hash.
2639    pub fn state_trie_version(&self) -> TrieEntryVersion {
2640        self.inner
2641            .common
2642            .runtime_version
2643            .as_ref()
2644            .unwrap_or_else(|| unreachable!())
2645            .decode()
2646            .state_version
2647            .unwrap_or(TrieEntryVersion::V0)
2648    }
2649
2650    /// Resumes execution after having set the value.
2651    pub fn resume(self) -> HostVm {
2652        HostVm::ReadyToRun(ReadyToRun {
2653            inner: self.inner,
2654            resume_value: None,
2655        })
2656    }
2657}
2658
2659impl fmt::Debug for ExternalStorageSet {
2660    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2661        f.debug_tuple("ExternalStorageSet").finish()
2662    }
2663}
2664
2665/// Must load a storage value, treat it as if it was a SCALE-encoded container, and put `value`
2666/// at the end of the container, increasing the number of elements.
2667///
2668/// If [`ExternalStorageAppend::child_trie`] return `Some` and the child trie doesn't exist, it
2669/// must implicitly be created.
2670///
2671/// If [`ExternalStorageAppend::child_trie`] return `None` and [`ExternalStorageAppend::key`]
2672/// returns a key that starts with `:child_storage:`, then the write must be silently ignored.
2673///
2674/// If there isn't any existing value of if the existing value isn't actually a SCALE-encoded
2675/// container, store a 1-size container with the `value`.
2676///
2677/// # Details
2678///
2679/// The SCALE encoding encodes containers as a SCALE-compact-encoded length followed with the
2680/// SCALE-encoded items one after the other. For example, a container of two elements is stored
2681/// as the number `2` followed with the two items.
2682///
2683/// This change consists in taking an existing value and assuming that it is a SCALE-encoded
2684/// container. This can be done as decoding a SCALE-compact-encoded number at the start of
2685/// the existing encoded value. One most then increments that number and puts `value` at the
2686/// end of the encoded value.
2687///
2688/// It is not necessary to decode `value` as is assumed that is already encoded in the same
2689/// way as the other items in the container.
2690pub struct ExternalStorageAppend {
2691    inner: Box<Inner>,
2692
2693    /// Pointer to the key whose value must be set. Guaranteed to be in range.
2694    key_ptr: u32,
2695    /// Size of the key whose value must be set. Guaranteed to be in range.
2696    key_size: u32,
2697
2698    /// Pointer to the value to append. Guaranteed to be in range.
2699    value_ptr: u32,
2700    /// Size of the value to append. Guaranteed to be in range.
2701    value_size: u32,
2702}
2703
2704impl ExternalStorageAppend {
2705    /// Returns the key whose value must be set.
2706    pub fn key(&self) -> impl AsRef<[u8]> {
2707        self.inner
2708            .vm
2709            .read_memory(self.key_ptr, self.key_size)
2710            .unwrap_or_else(|_| unreachable!())
2711    }
2712
2713    /// If `Some`, write to the given child trie. If `None`, write to the main trie.
2714    ///
2715    /// If this returns `Some` and the child trie doesn't exist, it must implicitly be created.
2716    ///
2717    /// > **Note**: At the moment, this function always returns None, as there is no host function
2718    /// >           that appends to a child trie storage.
2719    pub fn child_trie(&self) -> Option<impl AsRef<[u8]>> {
2720        // Note that there is no equivalent of this host function for child tries.
2721        None::<&'static [u8]>
2722    }
2723
2724    /// Returns the value to append.
2725    pub fn value(&self) -> impl AsRef<[u8]> {
2726        self.inner
2727            .vm
2728            .read_memory(self.value_ptr, self.value_size)
2729            .unwrap_or_else(|_| unreachable!())
2730    }
2731
2732    /// Resumes execution after having set the value.
2733    pub fn resume(self) -> HostVm {
2734        HostVm::ReadyToRun(ReadyToRun {
2735            inner: self.inner,
2736            resume_value: None,
2737        })
2738    }
2739}
2740
2741impl fmt::Debug for ExternalStorageAppend {
2742    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2743        f.debug_tuple("ExternalStorageAppend").finish()
2744    }
2745}
2746
2747/// Must remove from the storage keys which start with a certain prefix. Use
2748/// [`ExternalStorageClearPrefix::max_keys_to_remove`] to determine the maximum number of keys
2749/// to remove.
2750///
2751/// If [`ExternalStorageClearPrefix::child_trie`] returns `Some` and all the entries of the child
2752/// trie are removed, the child trie must implicitly be destroyed.
2753///
2754/// If [`ExternalStorageClearPrefix::child_trie`] return `None` and the prefix returned by
2755/// [`ExternalStorageClearPrefix::prefix`] intersects with `:child_storage:`, then the clearing
2756/// must be silently ignored.
2757pub struct ExternalStorageClearPrefix {
2758    inner: Box<Inner>,
2759    /// Function currently being called by the Wasm code. Refers to an index within
2760    /// [`VmCommon::registered_functions`]. Guaranteed to be [`FunctionImport::Resolved`].
2761    calling: usize,
2762
2763    /// Pointer and size to the prefix. `None` if `&[]`. Guaranteed to be in range.
2764    prefix_ptr_size: Option<(u32, u32)>,
2765    /// Pointer and size to the default child trie. `None` if main trie. Guaranteed to be in range.
2766    child_trie_ptr_size: Option<(u32, u32)>,
2767
2768    /// Maximum number of keys to remove.
2769    max_keys_to_remove: Option<u32>,
2770}
2771
2772impl ExternalStorageClearPrefix {
2773    /// Returns the prefix whose keys must be removed.
2774    pub fn prefix(&self) -> impl AsRef<[u8]> {
2775        if let Some((prefix_ptr, prefix_size)) = self.prefix_ptr_size {
2776            either::Left(
2777                self.inner
2778                    .vm
2779                    .read_memory(prefix_ptr, prefix_size)
2780                    .unwrap_or_else(|_| unreachable!()),
2781            )
2782        } else {
2783            either::Right(&[][..])
2784        }
2785    }
2786
2787    /// If `Some`, write to the given child trie. If `None`, write to the main trie.
2788    ///
2789    /// If [`ExternalStorageClearPrefix::child_trie`] returns `Some` and all the entries of the
2790    /// child trie are removed, the child trie must implicitly be destroyed.
2791    pub fn child_trie(&self) -> Option<impl AsRef<[u8]>> {
2792        if let Some((child_trie_ptr, child_trie_size)) = self.child_trie_ptr_size {
2793            let child_trie = self
2794                .inner
2795                .vm
2796                .read_memory(child_trie_ptr, child_trie_size)
2797                .unwrap_or_else(|_| unreachable!());
2798            Some(child_trie)
2799        } else {
2800            None
2801        }
2802    }
2803
2804    /// Returns the maximum number of keys to remove. `None` means "infinity".
2805    pub fn max_keys_to_remove(&self) -> Option<u32> {
2806        self.max_keys_to_remove
2807    }
2808
2809    /// Resumes execution after having cleared the values.
2810    ///
2811    /// Must be passed how many keys have been cleared, and whether some keys remaining to be
2812    /// cleared.
2813    pub fn resume(self, num_cleared: u32, some_keys_remain: bool) -> HostVm {
2814        let host_fn = match self.inner.common.registered_functions[self.calling] {
2815            FunctionImport::Resolved(f) => f,
2816            FunctionImport::Unresolved { .. } => unreachable!(),
2817        };
2818
2819        match host_fn {
2820            HostFunction::ext_storage_clear_prefix_version_1
2821            | HostFunction::ext_default_child_storage_clear_prefix_version_1
2822            | HostFunction::ext_default_child_storage_storage_kill_version_1 => {
2823                HostVm::ReadyToRun(ReadyToRun {
2824                    inner: self.inner,
2825                    resume_value: None,
2826                })
2827            }
2828            HostFunction::ext_default_child_storage_storage_kill_version_2 => {
2829                HostVm::ReadyToRun(ReadyToRun {
2830                    inner: self.inner,
2831                    resume_value: Some(vm::WasmValue::I32(if some_keys_remain { 0 } else { 1 })),
2832                })
2833            }
2834            HostFunction::ext_storage_clear_prefix_version_2
2835            | HostFunction::ext_default_child_storage_clear_prefix_version_2
2836            | HostFunction::ext_default_child_storage_storage_kill_version_3 => {
2837                self.inner.alloc_write_and_return_pointer_size(
2838                    host_fn.name(),
2839                    [
2840                        either::Left(if some_keys_remain { [1u8] } else { [0u8] }),
2841                        either::Right(num_cleared.to_le_bytes()),
2842                    ]
2843                    .into_iter(),
2844                )
2845            }
2846            _ => unreachable!(),
2847        }
2848    }
2849}
2850
2851impl fmt::Debug for ExternalStorageClearPrefix {
2852    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2853        f.debug_tuple("ExternalStorageClearPrefix").finish()
2854    }
2855}
2856
2857/// Must provide the trie root hash of the storage and write the trie root hash of child tries
2858/// to the main trie.
2859///
2860/// If [`ExternalStorageRoot::child_trie`] returns `Some` and the child trie is non-empty, the
2861/// trie root hash of the child trie must also be written to the main trie at the key
2862/// `concat(":child_storage:default:", child_trie)`.
2863/// If [`ExternalStorageRoot::child_trie`] returns `Some` and the child trie is empty, the entry
2864/// in the main trie at the key `concat(":child_storage:default:", child_trie)` must be removed.
2865///
2866/// If [`ExternalStorageRoot::child_trie`] returns `None`, the same operation as above must be
2867/// done for every single child trie that has been modified in one way or the other during the
2868/// runtime call.
2869pub struct ExternalStorageRoot {
2870    inner: Box<Inner>,
2871
2872    /// Function currently being called by the Wasm code. Refers to an index within
2873    /// [`VmCommon::registered_functions`]. Guaranteed to be [`FunctionImport::Resolved`].
2874    calling: usize,
2875
2876    /// Pointer and size of the child trie, if any. Guaranteed to be in range.
2877    child_trie_ptr_size: Option<(u32, u32)>,
2878}
2879
2880impl ExternalStorageRoot {
2881    /// Returns the child trie whose root hash must be provided. `None` for the main trie.
2882    pub fn child_trie(&self) -> Option<impl AsRef<[u8]>> {
2883        if let Some((ptr, size)) = self.child_trie_ptr_size {
2884            let child_trie = self
2885                .inner
2886                .vm
2887                .read_memory(ptr, size)
2888                .unwrap_or_else(|_| unreachable!());
2889            Some(child_trie)
2890        } else {
2891            None
2892        }
2893    }
2894
2895    /// Writes the trie root hash to the Wasm VM and prepares it for resume.
2896    ///
2897    /// If [`ExternalStorageRoot::child_trie`] returns `Some` but the child trie doesn't exist,
2898    /// the root hash of an empty trie must be provided.
2899    pub fn resume(self, hash: &[u8; 32]) -> HostVm {
2900        let host_fn = match self.inner.common.registered_functions[self.calling] {
2901            FunctionImport::Resolved(f) => f,
2902            FunctionImport::Unresolved { .. } => unreachable!(),
2903        };
2904
2905        self.inner
2906            .alloc_write_and_return_pointer_size(host_fn.name(), iter::once(hash))
2907    }
2908}
2909
2910impl fmt::Debug for ExternalStorageRoot {
2911    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2912        f.debug_tuple("ExternalStorageRoot").finish()
2913    }
2914}
2915
2916/// Must provide the storage key that follows, in lexicographic order, a specific one.
2917pub struct ExternalStorageNextKey {
2918    inner: Box<Inner>,
2919
2920    /// Pointer to the key whose follow-up must be found. Guaranteed to be in range.
2921    key_ptr: u32,
2922    /// Size of the key whose follow-up must be found. Guaranteed to be in range.
2923    key_size: u32,
2924    /// Pointer and size of the child trie, if any. Guaranteed to be in range.
2925    child_trie_ptr_size: Option<(u32, u32)>,
2926}
2927
2928impl ExternalStorageNextKey {
2929    /// Returns the key whose following key must be returned.
2930    pub fn key(&self) -> impl AsRef<[u8]> {
2931        self.inner
2932            .vm
2933            .read_memory(self.key_ptr, self.key_size)
2934            .unwrap_or_else(|_| unreachable!())
2935    }
2936
2937    /// If `Some`, read from the given child trie. If `None`, read from the main trie.
2938    pub fn child_trie(&self) -> Option<impl AsRef<[u8]>> {
2939        if let Some((child_trie_ptr, child_trie_size)) = self.child_trie_ptr_size {
2940            let child_trie = self
2941                .inner
2942                .vm
2943                .read_memory(child_trie_ptr, child_trie_size)
2944                .unwrap_or_else(|_| unreachable!());
2945            Some(child_trie)
2946        } else {
2947            None
2948        }
2949    }
2950
2951    /// Writes the follow-up key in the Wasm VM memory and prepares it for execution.
2952    ///
2953    /// Must be passed `None` if the key is the last one in the storage or if
2954    /// [`ExternalStorageNextKey`] returns `Some` and the child trie doesn't exist.
2955    pub fn resume(self, follow_up: Option<&[u8]>) -> HostVm {
2956        let key = self
2957            .inner
2958            .vm
2959            .read_memory(self.key_ptr, self.key_size)
2960            .unwrap_or_else(|_| unreachable!());
2961
2962        match follow_up {
2963            Some(next) => {
2964                debug_assert!(key.as_ref() < next);
2965
2966                let value_len_enc = util::encode_scale_compact_usize(next.len());
2967                drop(key);
2968                self.inner.alloc_write_and_return_pointer_size(
2969                    HostFunction::ext_storage_next_key_version_1.name(), // TODO: no
2970                    iter::once(&[1][..])
2971                        .chain(iter::once(value_len_enc.as_ref()))
2972                        .chain(iter::once(next)),
2973                )
2974            }
2975            None => {
2976                // Write a SCALE-encoded `None`.
2977                drop(key);
2978                self.inner.alloc_write_and_return_pointer_size(
2979                    HostFunction::ext_storage_next_key_version_1.name(), // TODO: no
2980                    iter::once(&[0]),
2981                )
2982            }
2983        }
2984    }
2985}
2986
2987impl fmt::Debug for ExternalStorageNextKey {
2988    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2989        f.debug_tuple("ExternalStorageNextKey").finish()
2990    }
2991}
2992
2993/// Must verify whether a signature is correct.
2994pub struct SignatureVerification {
2995    inner: Box<Inner>,
2996    /// Which cryptographic algorithm.
2997    algorithm: SignatureVerificationAlgorithm,
2998    /// Pointer to the signature. The size of the signature depends on the algorithm. Guaranteed
2999    /// to be in range.
3000    signature_ptr: u32,
3001    /// Pointer to the public key. The size of the public key depends on the algorithm. Guaranteed
3002    /// to be in range.
3003    public_key_ptr: u32,
3004    /// Pointer to the message. Guaranteed to be in range.
3005    message_ptr: u32,
3006    /// Size of the message. Guaranteed to be in range.
3007    message_size: u32,
3008    /// `true` if the host function is a batch verification function.
3009    is_batch_verification: bool,
3010}
3011
3012enum SignatureVerificationAlgorithm {
3013    Ed25519,
3014    Sr25519V1,
3015    Sr25519V2,
3016    Ecdsa,
3017    EcdsaPrehashed,
3018}
3019
3020impl SignatureVerification {
3021    /// Returns the message that the signature is expected to sign.
3022    pub fn message(&self) -> impl AsRef<[u8]> {
3023        self.inner
3024            .vm
3025            .read_memory(self.message_ptr, self.message_size)
3026            .unwrap_or_else(|_| unreachable!())
3027    }
3028
3029    /// Returns the signature.
3030    ///
3031    /// > **Note**: Be aware that this signature is untrusted input and might not be part of the
3032    /// >           set of valid signatures.
3033    pub fn signature(&self) -> impl AsRef<[u8]> {
3034        let signature_size = match self.algorithm {
3035            SignatureVerificationAlgorithm::Ed25519 => 64,
3036            SignatureVerificationAlgorithm::Sr25519V1 => 64,
3037            SignatureVerificationAlgorithm::Sr25519V2 => 64,
3038            SignatureVerificationAlgorithm::Ecdsa => 65,
3039            SignatureVerificationAlgorithm::EcdsaPrehashed => 65,
3040        };
3041
3042        self.inner
3043            .vm
3044            .read_memory(self.signature_ptr, signature_size)
3045            .unwrap_or_else(|_| unreachable!())
3046    }
3047
3048    /// Returns the public key the signature is against.
3049    ///
3050    /// > **Note**: Be aware that this public key is untrusted input and might not be part of the
3051    /// >           set of valid public keys.
3052    pub fn public_key(&self) -> impl AsRef<[u8]> {
3053        let public_key_size = match self.algorithm {
3054            SignatureVerificationAlgorithm::Ed25519 => 32,
3055            SignatureVerificationAlgorithm::Sr25519V1 => 32,
3056            SignatureVerificationAlgorithm::Sr25519V2 => 32,
3057            SignatureVerificationAlgorithm::Ecdsa => 33,
3058            SignatureVerificationAlgorithm::EcdsaPrehashed => 33,
3059        };
3060
3061        self.inner
3062            .vm
3063            .read_memory(self.public_key_ptr, public_key_size)
3064            .unwrap_or_else(|_| unreachable!())
3065    }
3066
3067    /// Verify the signature. Returns `true` if it is valid.
3068    pub fn is_valid(&self) -> bool {
3069        match self.algorithm {
3070            SignatureVerificationAlgorithm::Ed25519 => {
3071                let public_key =
3072                    ed25519_zebra::VerificationKey::try_from(self.public_key().as_ref());
3073
3074                if let Ok(public_key) = public_key {
3075                    let signature = ed25519_zebra::Signature::from(
3076                        <[u8; 64]>::try_from(self.signature().as_ref())
3077                            .unwrap_or_else(|_| unreachable!()),
3078                    );
3079                    public_key
3080                        .verify(&signature, self.message().as_ref())
3081                        .is_ok()
3082                } else {
3083                    false
3084                }
3085            }
3086            SignatureVerificationAlgorithm::Sr25519V1 => {
3087                schnorrkel::PublicKey::from_bytes(self.public_key().as_ref()).map_or(false, |pk| {
3088                    pk.verify_simple_preaudit_deprecated(
3089                        b"substrate",
3090                        self.message().as_ref(),
3091                        self.signature().as_ref(),
3092                    )
3093                    .is_ok()
3094                })
3095            }
3096            SignatureVerificationAlgorithm::Sr25519V2 => {
3097                let Ok(signature) = schnorrkel::Signature::from_bytes(self.signature().as_ref())
3098                else {
3099                    return false;
3100                };
3101                schnorrkel::PublicKey::from_bytes(self.public_key().as_ref()).map_or(false, |pk| {
3102                    pk.verify_simple(b"substrate", self.message().as_ref(), &signature)
3103                        .is_ok()
3104                })
3105            }
3106            SignatureVerificationAlgorithm::Ecdsa => {
3107                // NOTE: safe to unwrap here because we supply the nn to blake2b fn
3108                let data = <[u8; 32]>::try_from(
3109                    blake2_rfc::blake2b::blake2b(32, &[], self.message().as_ref()).as_bytes(),
3110                )
3111                .unwrap_or_else(|_| unreachable!());
3112                let message = libsecp256k1::Message::parse(&data);
3113
3114                // signature (64 bytes) + recovery ID (1 byte)
3115                let sig_bytes = self.signature();
3116                libsecp256k1::Signature::parse_standard_slice(&sig_bytes.as_ref()[..64])
3117                    .and_then(|sig| {
3118                        libsecp256k1::RecoveryId::parse(sig_bytes.as_ref()[64])
3119                            .and_then(|ri| libsecp256k1::recover(&message, &sig, &ri))
3120                    })
3121                    .map_or(false, |actual| {
3122                        self.public_key().as_ref()[..] == actual.serialize_compressed()[..]
3123                    })
3124            }
3125            SignatureVerificationAlgorithm::EcdsaPrehashed => {
3126                // We can safely unwrap, as the size is checked when the `SignatureVerification`
3127                // is constructed.
3128                let message = libsecp256k1::Message::parse(
3129                    &<[u8; 32]>::try_from(self.message().as_ref())
3130                        .unwrap_or_else(|_| unreachable!()),
3131                );
3132
3133                // signature (64 bytes) + recovery ID (1 byte)
3134                let sig_bytes = self.signature();
3135                if let Ok(sig) =
3136                    libsecp256k1::Signature::parse_standard_slice(&sig_bytes.as_ref()[..64])
3137                {
3138                    if let Ok(ri) = libsecp256k1::RecoveryId::parse(sig_bytes.as_ref()[64]) {
3139                        if let Ok(actual) = libsecp256k1::recover(&message, &sig, &ri) {
3140                            self.public_key().as_ref()[..] == actual.serialize_compressed()[..]
3141                        } else {
3142                            false
3143                        }
3144                    } else {
3145                        false
3146                    }
3147                } else {
3148                    false
3149                }
3150            }
3151        }
3152    }
3153
3154    /// Verify the signature and resume execution.
3155    pub fn verify_and_resume(self) -> HostVm {
3156        let success = self.is_valid();
3157        self.resume(success)
3158    }
3159
3160    /// Resume the execution assuming that the signature is valid.
3161    ///
3162    /// > **Note**: You are strongly encouraged to call
3163    /// >           [`SignatureVerification::verify_and_resume`]. This function is meant to be
3164    /// >           used only in debugging situations.
3165    pub fn resume_success(self) -> HostVm {
3166        self.resume(true)
3167    }
3168
3169    /// Resume the execution assuming that the signature is invalid.
3170    ///
3171    /// > **Note**: You are strongly encouraged to call
3172    /// >           [`SignatureVerification::verify_and_resume`]. This function is meant to be
3173    /// >           used only in debugging situations.
3174    pub fn resume_failed(self) -> HostVm {
3175        self.resume(false)
3176    }
3177
3178    fn resume(mut self, success: bool) -> HostVm {
3179        debug_assert!(
3180            !self.is_batch_verification || self.inner.signatures_batch_verification.is_some()
3181        );
3182        if self.is_batch_verification && !success {
3183            self.inner.signatures_batch_verification = Some(false);
3184        }
3185
3186        // All signature-related host functions work the same way in terms of return value.
3187        HostVm::ReadyToRun(ReadyToRun {
3188            resume_value: Some(vm::WasmValue::I32(if success { 1 } else { 0 })),
3189            inner: self.inner,
3190        })
3191    }
3192}
3193
3194impl fmt::Debug for SignatureVerification {
3195    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3196        f.debug_struct("SignatureVerification")
3197            .field("message", &self.message().as_ref())
3198            .field("signature", &self.signature().as_ref())
3199            .field("public_key", &self.public_key().as_ref())
3200            .finish()
3201    }
3202}
3203
3204/// Must provide the runtime version obtained by calling the `Core_version` entry point of a Wasm
3205/// blob.
3206pub struct CallRuntimeVersion {
3207    inner: Box<Inner>,
3208
3209    /// Pointer to the wasm code whose runtime version must be provided. Guaranteed to be in range.
3210    wasm_blob_ptr: u32,
3211    /// Size of the wasm code whose runtime version must be provided. Guaranteed to be in range.
3212    wasm_blob_size: u32,
3213}
3214
3215impl CallRuntimeVersion {
3216    /// Returns the Wasm code whose runtime version must be provided.
3217    pub fn wasm_code(&self) -> impl AsRef<[u8]> {
3218        self.inner
3219            .vm
3220            .read_memory(self.wasm_blob_ptr, self.wasm_blob_size)
3221            .unwrap_or_else(|_| unreachable!())
3222    }
3223
3224    /// Writes the SCALE-encoded runtime version to the memory and prepares for execution.
3225    ///
3226    /// If an error happened during the execution (such as an invalid Wasm binary code), pass
3227    /// an `Err`.
3228    pub fn resume(self, scale_encoded_runtime_version: Result<&[u8], ()>) -> HostVm {
3229        if let Ok(scale_encoded_runtime_version) = scale_encoded_runtime_version {
3230            self.inner.alloc_write_and_return_pointer_size(
3231                HostFunction::ext_misc_runtime_version_version_1.name(),
3232                iter::once(either::Left([1]))
3233                    .chain(iter::once(either::Right(either::Left(
3234                        util::encode_scale_compact_usize(scale_encoded_runtime_version.len()),
3235                    ))))
3236                    .chain(iter::once(either::Right(either::Right(
3237                        scale_encoded_runtime_version,
3238                    )))),
3239            )
3240        } else {
3241            self.inner.alloc_write_and_return_pointer_size(
3242                HostFunction::ext_misc_runtime_version_version_1.name(),
3243                iter::once([0]),
3244            )
3245        }
3246    }
3247}
3248
3249impl fmt::Debug for CallRuntimeVersion {
3250    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3251        f.debug_tuple("CallRuntimeVersion").finish()
3252    }
3253}
3254
3255/// Must set off-chain index value.
3256pub struct ExternalOffchainIndexSet {
3257    inner: Box<Inner>,
3258
3259    /// Pointer to the key whose value must be set. Guaranteed to be in range.
3260    key_ptr: u32,
3261    /// Size of the key whose value must be set. Guaranteed to be in range.
3262    key_size: u32,
3263
3264    /// Pointer and size of the value to set. `None` for clearing. Guaranteed to be in range.
3265    value: Option<(u32, u32)>,
3266}
3267
3268impl ExternalOffchainIndexSet {
3269    /// Returns the key whose value must be set.
3270    pub fn key(&self) -> impl AsRef<[u8]> {
3271        self.inner
3272            .vm
3273            .read_memory(self.key_ptr, self.key_size)
3274            .unwrap_or_else(|_| unreachable!())
3275    }
3276
3277    /// Returns the value to set.
3278    ///
3279    /// If `None` is returned, the key should be removed from the storage entirely.
3280    pub fn value(&self) -> Option<impl AsRef<[u8]>> {
3281        if let Some((ptr, size)) = self.value {
3282            Some(
3283                self.inner
3284                    .vm
3285                    .read_memory(ptr, size)
3286                    .unwrap_or_else(|_| unreachable!()),
3287            )
3288        } else {
3289            None
3290        }
3291    }
3292
3293    /// Resumes execution after having set the value.
3294    pub fn resume(self) -> HostVm {
3295        HostVm::ReadyToRun(ReadyToRun {
3296            inner: self.inner,
3297            resume_value: None,
3298        })
3299    }
3300}
3301
3302impl fmt::Debug for ExternalOffchainIndexSet {
3303    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3304        f.debug_tuple("ExternalOffchainIndexSet").finish()
3305    }
3306}
3307
3308/// Must set the value of the off-chain storage.
3309pub struct ExternalOffchainStorageSet {
3310    inner: Box<Inner>,
3311
3312    /// Pointer to the key whose value must be set. Guaranteed to be in range.
3313    key_ptr: u32,
3314    /// Size of the key whose value must be set. Guaranteed to be in range.
3315    key_size: u32,
3316
3317    /// Pointer and size of the value to set. `None` for clearing. Guaranteed to be in range.
3318    value: Option<(u32, u32)>,
3319
3320    /// Pointer and size of the old value to compare. Guaranteed to be in range.
3321    old_value: Option<(u32, u32)>,
3322}
3323
3324impl ExternalOffchainStorageSet {
3325    /// Returns the key whose value must be set.
3326    pub fn key(&self) -> impl AsRef<[u8]> {
3327        self.inner
3328            .vm
3329            .read_memory(self.key_ptr, self.key_size)
3330            .unwrap_or_else(|_| unreachable!())
3331    }
3332
3333    /// Returns the value to set.
3334    ///
3335    /// If `None` is returned, the key should be removed from the storage entirely.
3336    pub fn value(&self) -> Option<impl AsRef<[u8]>> {
3337        if let Some((ptr, size)) = self.value {
3338            Some(
3339                self.inner
3340                    .vm
3341                    .read_memory(ptr, size)
3342                    .unwrap_or_else(|_| unreachable!()),
3343            )
3344        } else {
3345            None
3346        }
3347    }
3348
3349    /// Returns the value the current value should be compared against. The operation is a no-op if they don't compare equal.
3350    pub fn old_value(&self) -> Option<impl AsRef<[u8]>> {
3351        if let Some((ptr, size)) = self.old_value {
3352            Some(
3353                self.inner
3354                    .vm
3355                    .read_memory(ptr, size)
3356                    .unwrap_or_else(|_| unreachable!()),
3357            )
3358        } else {
3359            None
3360        }
3361    }
3362
3363    /// Resumes execution after having set the value. Must indicate whether a value was written.
3364    pub fn resume(self, replaced: bool) -> HostVm {
3365        if self.old_value.is_some() {
3366            HostVm::ReadyToRun(ReadyToRun {
3367                inner: self.inner,
3368                resume_value: Some(vm::WasmValue::I32(if replaced { 1 } else { 0 })),
3369            })
3370        } else {
3371            HostVm::ReadyToRun(ReadyToRun {
3372                inner: self.inner,
3373                resume_value: None,
3374            })
3375        }
3376    }
3377}
3378
3379impl fmt::Debug for ExternalOffchainStorageSet {
3380    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3381        f.debug_tuple("ExternalOffchainStorageSet").finish()
3382    }
3383}
3384
3385/// Must get the value of the off-chain storage.
3386pub struct ExternalOffchainStorageGet {
3387    inner: Box<Inner>,
3388
3389    /// Function currently being called by the Wasm code. Refers to an index within
3390    /// [`VmCommon::registered_functions`]. Guaranteed to be [`FunctionImport::Resolved`].
3391    calling: usize,
3392
3393    /// Pointer to the key whose value must be loaded. Guaranteed to be in range.
3394    key_ptr: u32,
3395    /// Size of the key whose value must be loaded. Guaranteed to be in range.
3396    key_size: u32,
3397}
3398
3399impl ExternalOffchainStorageGet {
3400    /// Returns the key whose value must be loaded.
3401    pub fn key(&self) -> impl AsRef<[u8]> {
3402        self.inner
3403            .vm
3404            .read_memory(self.key_ptr, self.key_size)
3405            .unwrap_or_else(|_| unreachable!())
3406    }
3407
3408    /// Resumes execution after having set the value.
3409    pub fn resume(self, value: Option<&[u8]>) -> HostVm {
3410        let host_fn = match self.inner.common.registered_functions[self.calling] {
3411            FunctionImport::Resolved(f) => f,
3412            FunctionImport::Unresolved { .. } => unreachable!(),
3413        };
3414
3415        if let Some(value) = value {
3416            let value_len_enc = util::encode_scale_compact_usize(value.len());
3417            self.inner.alloc_write_and_return_pointer_size(
3418                host_fn.name(),
3419                iter::once(&[1][..])
3420                    .chain(iter::once(value_len_enc.as_ref()))
3421                    .map(either::Left)
3422                    .chain(iter::once(value).map(either::Right)),
3423            )
3424        } else {
3425            // Write a SCALE-encoded `None`.
3426            self.inner
3427                .alloc_write_and_return_pointer_size(host_fn.name(), iter::once(&[0]))
3428        }
3429    }
3430}
3431
3432impl fmt::Debug for ExternalOffchainStorageGet {
3433    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3434        f.debug_tuple("ExternalOffchainStorageGet").finish()
3435    }
3436}
3437
3438/// Must return the current UNIX timestamp.
3439pub struct OffchainTimestamp {
3440    inner: Box<Inner>,
3441}
3442
3443impl OffchainTimestamp {
3444    /// Resumes execution after having set the value.
3445    pub fn resume(self, value: u64) -> HostVm {
3446        HostVm::ReadyToRun(ReadyToRun {
3447            inner: self.inner,
3448            resume_value: Some(vm::WasmValue::I64(i64::from_ne_bytes(value.to_ne_bytes()))),
3449        })
3450    }
3451}
3452
3453impl fmt::Debug for OffchainTimestamp {
3454    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3455        f.debug_tuple("OffchainTimestamp").finish()
3456    }
3457}
3458
3459/// Must provide a randomly-generate number.
3460pub struct OffchainRandomSeed {
3461    inner: Box<Inner>,
3462
3463    /// Function currently being called by the Wasm code. Refers to an index within
3464    /// [`VmCommon::registered_functions`]. Guaranteed to be [`FunctionImport::Resolved`].
3465    calling: usize,
3466}
3467
3468impl OffchainRandomSeed {
3469    /// Resumes execution after having set the value.
3470    pub fn resume(self, value: [u8; 32]) -> HostVm {
3471        let host_fn = match self.inner.common.registered_functions[self.calling] {
3472            FunctionImport::Resolved(f) => f,
3473            FunctionImport::Unresolved { .. } => unreachable!(),
3474        };
3475        self.inner
3476            .alloc_write_and_return_pointer(host_fn.name(), iter::once(value))
3477    }
3478}
3479
3480impl fmt::Debug for OffchainRandomSeed {
3481    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3482        f.debug_tuple("OffchainRandomSeed").finish()
3483    }
3484}
3485
3486/// Must submit an off-chain transaction.
3487pub struct OffchainSubmitTransaction {
3488    inner: Box<Inner>,
3489
3490    /// Function currently being called by the Wasm code. Refers to an index within
3491    /// [`VmCommon::registered_functions`]. Guaranteed to be [`FunctionImport::Resolved`].
3492    calling: usize,
3493
3494    /// Pointer to the transaction whose value must be set. Guaranteed to be in range.
3495    tx_ptr: u32,
3496
3497    /// Size of the transaction whose value must be set. Guaranteed to be in range.
3498    tx_size: u32,
3499}
3500
3501impl OffchainSubmitTransaction {
3502    /// Returns the SCALE-encoded transaction to submit to the chain.
3503    pub fn transaction(&self) -> impl AsRef<[u8]> {
3504        self.inner
3505            .vm
3506            .read_memory(self.tx_ptr, self.tx_size)
3507            .unwrap_or_else(|_| unreachable!())
3508    }
3509
3510    /// Resumes execution after having submitted the transaction.
3511    pub fn resume(self, success: bool) -> HostVm {
3512        let host_fn = match self.inner.common.registered_functions[self.calling] {
3513            FunctionImport::Resolved(f) => f,
3514            FunctionImport::Unresolved { .. } => unreachable!(),
3515        };
3516
3517        self.inner.alloc_write_and_return_pointer_size(
3518            host_fn.name(),
3519            if success {
3520                iter::once(&[0x00])
3521            } else {
3522                iter::once(&[0x01])
3523            },
3524        )
3525    }
3526}
3527
3528impl fmt::Debug for OffchainSubmitTransaction {
3529    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3530        f.debug_tuple("OffchainSubmitTransaction").finish()
3531    }
3532}
3533
3534/// Report about a log entry being emitted.
3535///
3536/// Use [`LogEmit::info`] to obtain what must be printed.
3537pub struct LogEmit {
3538    inner: Box<Inner>,
3539    log_entry: LogEmitInner,
3540}
3541
3542enum LogEmitInner {
3543    Num(u64),
3544    Utf8 {
3545        /// Pointer to the string. Guaranteed to be in range and to be UTF-8.
3546        str_ptr: u32,
3547        /// Size of the string. Guaranteed to be in range and to be UTF-8.
3548        str_size: u32,
3549    },
3550    Hex {
3551        /// Pointer to the data. Guaranteed to be in range.
3552        data_ptr: u32,
3553        /// Size of the data. Guaranteed to be in range.
3554        data_size: u32,
3555    },
3556    Log {
3557        /// Log level. Arbitrary number indicated by runtime, but typically in the `1..=5` range.
3558        log_level: u32,
3559        /// Pointer to the string of the log target. Guaranteed to be in range and to be UTF-8.
3560        target_str_ptr: u32,
3561        /// Size of the string of the log target. Guaranteed to be in range and to be UTF-8.
3562        target_str_size: u32,
3563        /// Pointer to the string of the log message. Guaranteed to be in range and to be UTF-8.
3564        msg_str_ptr: u32,
3565        /// Size of the string of the log message. Guaranteed to be in range and to be UTF-8.
3566        msg_str_size: u32,
3567    },
3568}
3569
3570impl LogEmit {
3571    /// Returns the data that the runtime would like to print.
3572    pub fn info(&'_ self) -> LogEmitInfo<'_> {
3573        match self.log_entry {
3574            LogEmitInner::Num(n) => LogEmitInfo::Num(n),
3575            LogEmitInner::Utf8 { str_ptr, str_size } => LogEmitInfo::Utf8(LogEmitInfoStr {
3576                data: Box::new(
3577                    self.inner
3578                        .vm
3579                        .read_memory(str_ptr, str_size)
3580                        .unwrap_or_else(|_| unreachable!()),
3581                ),
3582            }),
3583            LogEmitInner::Hex {
3584                data_ptr,
3585                data_size,
3586            } => LogEmitInfo::Hex(LogEmitInfoHex {
3587                data: Box::new(
3588                    self.inner
3589                        .vm
3590                        .read_memory(data_ptr, data_size)
3591                        .unwrap_or_else(|_| unreachable!()),
3592                ),
3593            }),
3594            LogEmitInner::Log {
3595                msg_str_ptr,
3596                msg_str_size,
3597                target_str_ptr,
3598                target_str_size,
3599                log_level,
3600            } => LogEmitInfo::Log {
3601                log_level,
3602                target: LogEmitInfoStr {
3603                    data: Box::new(
3604                        self.inner
3605                            .vm
3606                            .read_memory(target_str_ptr, target_str_size)
3607                            .unwrap_or_else(|_| unreachable!()),
3608                    ),
3609                },
3610                message: LogEmitInfoStr {
3611                    data: Box::new(
3612                        self.inner
3613                            .vm
3614                            .read_memory(msg_str_ptr, msg_str_size)
3615                            .unwrap_or_else(|_| unreachable!()),
3616                    ),
3617                },
3618            },
3619        }
3620    }
3621
3622    /// Resumes execution.
3623    pub fn resume(self) -> HostVm {
3624        HostVm::ReadyToRun(ReadyToRun {
3625            inner: self.inner,
3626            resume_value: None,
3627        })
3628    }
3629}
3630
3631impl fmt::Debug for LogEmit {
3632    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3633        f.debug_struct("LogEmit")
3634            .field("info", &self.info())
3635            .finish()
3636    }
3637}
3638
3639/// Detail about what a [`LogEmit`] should output. See [`LogEmit::info`].
3640#[derive(Debug)]
3641pub enum LogEmitInfo<'a> {
3642    /// Must output a single number.
3643    Num(u64),
3644    /// Must output a UTF-8 string.
3645    Utf8(LogEmitInfoStr<'a>),
3646    /// Must output the hexadecimal encoding of the given buffer.
3647    Hex(LogEmitInfoHex<'a>),
3648    /// Must output a log line.
3649    Log {
3650        /// Log level. Arbitrary number indicated by runtime, but typically in the `1..=5` range.
3651        log_level: u32,
3652        /// "Target" of the log. Arbitrary string indicated by the runtime. Typically indicates
3653        /// the subsystem which has emitted the log line.
3654        target: LogEmitInfoStr<'a>,
3655        /// Actual log message being emitted.
3656        message: LogEmitInfoStr<'a>,
3657    },
3658}
3659
3660/// See [`LogEmitInfo`]. Use the `AsRef` trait implementation to retrieve the buffer.
3661pub struct LogEmitInfoHex<'a> {
3662    // TODO: don't use a Box once Rust supports type Foo = impl Trait;
3663    data: Box<dyn AsRef<[u8]> + Send + Sync + 'a>,
3664}
3665
3666impl<'a> AsRef<[u8]> for LogEmitInfoHex<'a> {
3667    fn as_ref(&self) -> &[u8] {
3668        (*self.data).as_ref()
3669    }
3670}
3671
3672impl<'a> fmt::Debug for LogEmitInfoHex<'a> {
3673    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3674        fmt::Debug::fmt(self.as_ref(), f)
3675    }
3676}
3677
3678impl<'a> fmt::Display for LogEmitInfoHex<'a> {
3679    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3680        fmt::Debug::fmt(&hex::encode(self.as_ref()), f)
3681    }
3682}
3683
3684/// See [`LogEmitInfo`]. Use the `AsRef` trait implementation to retrieve the string.
3685pub struct LogEmitInfoStr<'a> {
3686    // TODO: don't use a Box once Rust supports type Foo = impl Trait;
3687    data: Box<dyn AsRef<[u8]> + Send + Sync + 'a>,
3688}
3689
3690impl<'a> AsRef<str> for LogEmitInfoStr<'a> {
3691    fn as_ref(&self) -> &str {
3692        let data = (*self.data).as_ref();
3693        // The creator of `LogEmitInfoStr` always makes sure that the string is indeed UTF-8
3694        // before creating it.
3695        str::from_utf8(data).unwrap_or_else(|_| unreachable!())
3696    }
3697}
3698
3699impl<'a> fmt::Debug for LogEmitInfoStr<'a> {
3700    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3701        fmt::Debug::fmt(self.as_ref(), f)
3702    }
3703}
3704
3705impl<'a> fmt::Display for LogEmitInfoStr<'a> {
3706    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3707        fmt::Display::fmt(self.as_ref(), f)
3708    }
3709}
3710
3711/// Queries the maximum log level.
3712pub struct GetMaxLogLevel {
3713    inner: Box<Inner>,
3714}
3715
3716impl GetMaxLogLevel {
3717    /// Resumes execution after indicating the maximum log level.
3718    ///
3719    /// 0 means off, 1 means error, 2 means warn, 3 means info, 4 means debug, 5 means trace.
3720    pub fn resume(self, max_level: u32) -> HostVm {
3721        HostVm::ReadyToRun(ReadyToRun {
3722            inner: self.inner,
3723            resume_value: Some(vm::WasmValue::I32(i32::from_ne_bytes(
3724                max_level.to_ne_bytes(),
3725            ))),
3726        })
3727    }
3728}
3729
3730impl fmt::Debug for GetMaxLogLevel {
3731    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3732        f.debug_tuple("GetMaxLogLevel").finish()
3733    }
3734}
3735
3736/// Declares the start of a transaction.
3737pub struct StartStorageTransaction {
3738    inner: Box<Inner>,
3739}
3740
3741impl StartStorageTransaction {
3742    /// Resumes execution after having acknowledged the event.
3743    pub fn resume(self) -> HostVm {
3744        HostVm::ReadyToRun(ReadyToRun {
3745            inner: self.inner,
3746            resume_value: None,
3747        })
3748    }
3749}
3750
3751impl fmt::Debug for StartStorageTransaction {
3752    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3753        f.debug_tuple("StartStorageTransaction").finish()
3754    }
3755}
3756
3757/// Declares the end of a transaction.
3758pub struct EndStorageTransaction {
3759    inner: Box<Inner>,
3760}
3761
3762impl EndStorageTransaction {
3763    /// Resumes execution after having acknowledged the event.
3764    pub fn resume(self) -> HostVm {
3765        HostVm::ReadyToRun(ReadyToRun {
3766            inner: self.inner,
3767            resume_value: None,
3768        })
3769    }
3770}
3771
3772impl fmt::Debug for EndStorageTransaction {
3773    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3774        f.debug_tuple("EndStorageTransaction").finish()
3775    }
3776}
3777
3778#[derive(Clone)]
3779enum FunctionImport {
3780    Resolved(HostFunction),
3781    Unresolved { module: String, name: String },
3782}
3783
3784/// Running virtual machine. Shared between all the variants in [`HostVm`].
3785struct Inner {
3786    /// Inner lower-level virtual machine.
3787    vm: vm::VirtualMachine,
3788
3789    /// The depth of storage transaction started with `ext_storage_start_transaction_version_1`.
3790    storage_transaction_depth: u32,
3791
3792    /// The host provides a "batch signature verification" mechanism, where the runtime can start
3793    /// verifying multiple signatures at once. This mechanism is deprecated, but is emulated
3794    /// through a simple field.
3795    ///
3796    /// Contains `Some` if and only if the runtime is currently within a batch signatures
3797    /// verification. If `Some`, contains `true` if all the signatures have been verified
3798    /// successfully so far.
3799    signatures_batch_verification: Option<bool>,
3800
3801    /// Memory allocator in order to answer the calls to `malloc` and `free`.
3802    allocator: allocator::FreeingBumpHeapAllocator,
3803
3804    /// Value passed as parameter.
3805    storage_proof_size_behavior: StorageProofSizeBehavior,
3806
3807    /// Fields that are kept as is even during the execution.
3808    common: Box<VmCommon>,
3809}
3810
3811impl Inner {
3812    /// Uses the memory allocator to allocate some memory for the given data, writes the data in
3813    /// memory, and returns an [`HostVm`] ready for the Wasm `host_fn` return.
3814    ///
3815    /// The data is passed as a list of chunks. These chunks will be laid out linearly in memory.
3816    ///
3817    /// The function name passed as parameter is used for error-reporting reasons.
3818    ///
3819    /// # Panic
3820    ///
3821    /// Must only be called while the Wasm is handling an `host_fn`.
3822    ///
3823    fn alloc_write_and_return_pointer_size(
3824        mut self: Box<Self>,
3825        function_name: &'static str,
3826        data: impl Iterator<Item = impl AsRef<[u8]>> + Clone,
3827    ) -> HostVm {
3828        let mut data_len = 0u32;
3829        for chunk in data.clone() {
3830            data_len =
3831                data_len.saturating_add(u32::try_from(chunk.as_ref().len()).unwrap_or(u32::MAX));
3832        }
3833
3834        let dest_ptr = match self.alloc(function_name, data_len) {
3835            Ok(p) => p,
3836            Err(error) => {
3837                return HostVm::Error {
3838                    error,
3839                    prototype: self.into_prototype(),
3840                };
3841            }
3842        };
3843
3844        let mut ptr_iter = dest_ptr;
3845        for chunk in data {
3846            let chunk = chunk.as_ref();
3847            self.vm
3848                .write_memory(ptr_iter, chunk)
3849                .unwrap_or_else(|_| unreachable!());
3850            ptr_iter += u32::try_from(chunk.len()).unwrap_or(u32::MAX);
3851        }
3852
3853        let ret_val = (u64::from(data_len) << 32) | u64::from(dest_ptr);
3854        let ret_val = i64::from_ne_bytes(ret_val.to_ne_bytes());
3855
3856        ReadyToRun {
3857            inner: self,
3858            resume_value: Some(vm::WasmValue::I64(ret_val)),
3859        }
3860        .into()
3861    }
3862
3863    /// Uses the memory allocator to allocate some memory for the given data, writes the data in
3864    /// memory, and returns an [`HostVm`] ready for the Wasm `host_fn` return.
3865    ///
3866    /// The data is passed as a list of chunks. These chunks will be laid out linearly in memory.
3867    ///
3868    /// The function name passed as parameter is used for error-reporting reasons.
3869    ///
3870    /// # Panic
3871    ///
3872    /// Must only be called while the Wasm is handling an `host_fn`.
3873    ///
3874    fn alloc_write_and_return_pointer(
3875        mut self: Box<Self>,
3876        function_name: &'static str,
3877        data: impl Iterator<Item = impl AsRef<[u8]>> + Clone,
3878    ) -> HostVm {
3879        let mut data_len = 0u32;
3880        for chunk in data.clone() {
3881            data_len =
3882                data_len.saturating_add(u32::try_from(chunk.as_ref().len()).unwrap_or(u32::MAX));
3883        }
3884
3885        let dest_ptr = match self.alloc(function_name, data_len) {
3886            Ok(p) => p,
3887            Err(error) => {
3888                return HostVm::Error {
3889                    error,
3890                    prototype: self.into_prototype(),
3891                };
3892            }
3893        };
3894
3895        let mut ptr_iter = dest_ptr;
3896        for chunk in data {
3897            let chunk = chunk.as_ref();
3898            self.vm
3899                .write_memory(ptr_iter, chunk)
3900                .unwrap_or_else(|_| unreachable!());
3901            ptr_iter += u32::try_from(chunk.len()).unwrap_or(u32::MAX);
3902        }
3903
3904        let ret_val = i32::from_ne_bytes(dest_ptr.to_ne_bytes());
3905        ReadyToRun {
3906            inner: self,
3907            resume_value: Some(vm::WasmValue::I32(ret_val)),
3908        }
3909        .into()
3910    }
3911
3912    /// Uses the memory allocator to allocate some memory.
3913    ///
3914    /// The function name passed as parameter is used for error-reporting reasons.
3915    ///
3916    /// # Panic
3917    ///
3918    /// Must only be called while the Wasm is handling an `host_fn`.
3919    ///
3920    fn alloc(&mut self, function_name: &'static str, size: u32) -> Result<u32, Error> {
3921        // Use the allocator to decide where the value will be written.
3922        let dest_ptr = match self.allocator.allocate(
3923            &mut MemAccess {
3924                vm: MemAccessVm::Running(&mut self.vm),
3925                memory_total_pages: self.common.memory_total_pages,
3926            },
3927            size,
3928        ) {
3929            Ok(p) => p,
3930            Err(_) => {
3931                return Err(Error::OutOfMemory {
3932                    function: function_name,
3933                    requested_size: size,
3934                });
3935            }
3936        };
3937
3938        // Unfortunately this function doesn't stop here.
3939        // We lie to the allocator. The allocator thinks that there are `self.memory_total_pages`
3940        // allocated and available, while in reality it can be less.
3941        // The allocator might thus allocate memory at a location above the current memory size.
3942        // To handle this, we grow the memory.
3943
3944        // Offset of the memory page where the last allocated byte is found.
3945        let last_byte_memory_page = HeapPages::new((dest_ptr + size - 1) / (64 * 1024));
3946
3947        // Grow the memory more if necessary.
3948        // Please note the `=`. For example if we write to page 0, we want to have at least 1 page
3949        // allocated.
3950        let current_num_pages = self.vm.memory_size();
3951        debug_assert!(current_num_pages <= self.common.memory_total_pages);
3952        if current_num_pages <= last_byte_memory_page {
3953            // For now, we grow the memory just enough to fit.
3954            // TODO: do better
3955            // Note the order of operations: we add 1 at the end to avoid a potential overflow
3956            // in case `last_byte_memory_page` is the maximum possible value.
3957            let to_grow = last_byte_memory_page - current_num_pages + HeapPages::new(1);
3958
3959            // We check at initialization that the virtual machine is capable of growing up to
3960            // `memory_total_pages`, meaning that this can't panic.
3961            self.vm
3962                .grow_memory(to_grow)
3963                .unwrap_or_else(|_| unreachable!());
3964        }
3965
3966        Ok(dest_ptr)
3967    }
3968
3969    /// Turns the virtual machine back into a prototype.
3970    fn into_prototype(self) -> HostVmPrototype {
3971        HostVmPrototype {
3972            vm_proto: self.vm.into_prototype(),
3973            common: self.common,
3974        }
3975    }
3976}
3977
3978/// Error that can happen when initializing a VM.
3979#[derive(Debug, derive_more::From, derive_more::Display, derive_more::Error, Clone)]
3980pub enum NewErr {
3981    /// Error in the format of the runtime code.
3982    #[display("{_0}")]
3983    BadFormat(ModuleFormatError),
3984    /// Error while initializing the virtual machine.
3985    #[display("{_0}")]
3986    VirtualMachine(vm::NewErr),
3987    /// Error while finding the runtime-version-related sections in the Wasm blob.
3988    #[display("Error in runtime spec Wasm sections: {_0}")]
3989    RuntimeVersion(FindEmbeddedRuntimeVersionError),
3990    /// Error while calling `Core_version` to determine the runtime version.
3991    #[display("Error while calling Core_version: {_0}")]
3992    CoreVersion(CoreVersionError),
3993    /// Couldn't find the `__heap_base` symbol in the Wasm code.
3994    HeapBaseNotFound,
3995    /// Maximum size of the Wasm memory found in the module is too low to provide the requested
3996    /// number of heap pages.
3997    MemoryMaxSizeTooLow,
3998}
3999
4000/// Error while determining .
4001#[derive(Debug, derive_more::Display, derive_more::Error, Clone)]
4002pub enum FindEmbeddedRuntimeVersionError {
4003    /// Error while finding the custom section.
4004    #[display("{_0}")]
4005    FindSections(FindEncodedEmbeddedRuntimeVersionApisError),
4006    /// Error while decoding the runtime version.
4007    RuntimeVersionDecode,
4008    /// Error while decoding the runtime APIs.
4009    #[display("{_0}")]
4010    RuntimeApisDecode(CoreVersionApisFromSliceErr),
4011}
4012
4013/// Error that can happen when starting a VM.
4014#[derive(Debug, Clone, derive_more::From, derive_more::Display, derive_more::Error)]
4015pub enum StartErr {
4016    /// Error while starting the virtual machine.
4017    #[display("{_0}")]
4018    VirtualMachine(vm::StartErr),
4019    /// The size of the input data is too large.
4020    DataSizeOverflow,
4021}
4022
4023/// Reason why the Wasm blob isn't conforming to the runtime environment.
4024#[derive(Debug, Clone, derive_more::Display, derive_more::Error)]
4025pub enum Error {
4026    /// Error in the Wasm code execution.
4027    #[display("{_0}")]
4028    Trap(vm::Trap),
4029    /// Runtime has called the `ext_panic_handler_abort_on_panic_version_1` host function.
4030    #[display("Runtime has aborted: {message:?}")]
4031    AbortOnPanic {
4032        /// Message generated by the runtime.
4033        message: String,
4034    },
4035    /// A non-`i64` value has been returned by the Wasm entry point.
4036    #[display("A non-I64 value has been returned: {actual:?}")]
4037    BadReturnValue {
4038        /// Type that has actually gotten returned. `None` for "void".
4039        actual: Option<vm::ValueType>,
4040    },
4041    /// The pointer and size returned by the Wasm entry point function are invalid.
4042    #[display("The pointer and size returned by the function are invalid")]
4043    ReturnedPtrOutOfRange {
4044        /// Pointer that got returned.
4045        pointer: u32,
4046        /// Size that got returned.
4047        size: u32,
4048        /// Size of the virtual memory.
4049        memory_size: u32,
4050    },
4051    /// Called a function that is unknown to the host.
4052    ///
4053    /// > **Note**: Can only happen if `allow_unresolved_imports` was `true`.
4054    #[display("Called unresolved function `{module_name}`:`{function}`")]
4055    UnresolvedFunctionCalled {
4056        /// Name of the function that was unresolved.
4057        function: String,
4058        /// Name of module associated with the unresolved function.
4059        module_name: String,
4060    },
4061    /// Failed to decode a SCALE-encoded parameter.
4062    ParamDecodeError,
4063    /// One parameter is expected to point to a buffer, but the pointer is out
4064    /// of range of the memory of the Wasm VM.
4065    #[display(
4066        "Bad pointer for parameter of index {param_num} of {function}: 0x{pointer:x}, \
4067        len = 0x{length:x}"
4068    )]
4069    ParamOutOfRange {
4070        /// Name of the function being called where a type mismatch happens.
4071        function: &'static str,
4072        /// Index of the invalid parameter. The first parameter has index 0.
4073        param_num: usize,
4074        /// Pointer passed as parameter.
4075        pointer: u32,
4076        /// Expected length of the buffer.
4077        ///
4078        /// Depending on the function, this can either be an implicit length
4079        /// or a length passed as parameter.
4080        length: u32,
4081    },
4082    /// One parameter is expected to point to a UTF-8 string, but the buffer
4083    /// isn't valid UTF-8.
4084    #[display("UTF-8 error for parameter of index {param_num} of {function}: {error}")]
4085    Utf8Error {
4086        /// Name of the function being called where a type mismatch happens.
4087        function: &'static str,
4088        /// Index of the invalid parameter. The first parameter has index 0.
4089        param_num: usize,
4090        /// Decoding error that happened.
4091        #[error(source)]
4092        error: core::str::Utf8Error,
4093    },
4094    /// Called `ext_storage_rollback_transaction_version_1` or
4095    /// `ext_storage_commit_transaction_version_1` but no transaction was in progress.
4096    #[display("Attempted to end a transaction while none is in progress")]
4097    NoActiveTransaction,
4098    /// Execution has finished while a transaction started with
4099    /// `ext_storage_start_transaction_version_1` was still in progress.
4100    #[display("Execution returned with a pending storage transaction")]
4101    FinishedWithPendingTransaction,
4102    /// Error when allocating memory for a return type.
4103    #[display("Out of memory allocating 0x{requested_size:x} bytes during {function}")]
4104    OutOfMemory {
4105        /// Name of the function being called.
4106        function: &'static str,
4107        /// Size of the requested allocation.
4108        requested_size: u32,
4109    },
4110    /// Called `ext_allocator_free_version_1` with an invalid pointer.
4111    #[display("Bad pointer passed to ext_allocator_free_version_1: 0x{pointer:x}")]
4112    FreeError {
4113        /// Pointer that was expected to be freed.
4114        pointer: u32,
4115    },
4116    /// Mismatch between the state trie version provided as parameter and the state trie version
4117    /// found in the runtime specification.
4118    #[display(
4119        "Mismatch between the state trie version provided as parameter ({parameter:?}) and \
4120        the state trie version found in the runtime specification ({specification:?})."
4121    )]
4122    StateVersionMismatch {
4123        /// The version passed as parameter.
4124        parameter: TrieEntryVersion,
4125        /// The version in the specification.
4126        specification: TrieEntryVersion,
4127    },
4128    /// Called `ext_default_child_storage_root_version_1` or
4129    /// `ext_default_child_storage_root_version_2` on a child trie that doesn't exist.
4130    #[display(
4131        "Called `ext_default_child_storage_root_version_1` or
4132        `ext_default_child_storage_root_version_2` on a child trie that doesn't exist."
4133    )]
4134    ChildStorageRootTrieDoesntExist,
4135    /// Runtime has tried to perform a signature batch verification before initiating a batch
4136    /// verification.
4137    BatchVerifyWithoutStarting,
4138    /// Runtime has tried to initiate a batch signatures verification while there was already one
4139    /// in progress.
4140    AlreadyBatchVerify,
4141    /// Runtime has tried to finish a batch signatures verification while none is in progress.
4142    NoBatchVerify,
4143    /// The host function isn't implemented.
4144    // TODO: this variant should eventually disappear as all functions are implemented
4145    #[display("Host function not implemented: {function}")]
4146    HostFunctionNotImplemented {
4147        /// Name of the function being called.
4148        function: &'static str,
4149    },
4150}
4151
4152// Glue between the `allocator` module and the `vm` module.
4153//
4154// The allocator believes that there are `memory_total_pages` pages available and allocated, where
4155// `memory_total_pages` is equal to `heap_base + heap_pages`, while in reality, because we grow
4156// memory lazily, there might be fewer.
4157struct MemAccess<'a> {
4158    vm: MemAccessVm<'a>,
4159    memory_total_pages: HeapPages,
4160}
4161
4162enum MemAccessVm<'a> {
4163    Prepare(&'a mut vm::Prepare),
4164    Running(&'a mut vm::VirtualMachine),
4165}
4166
4167impl<'a> allocator::Memory for MemAccess<'a> {
4168    fn read_le_u64(&self, ptr: u32) -> Result<u64, allocator::Error> {
4169        if (ptr + 8) > u32::from(self.memory_total_pages) * 64 * 1024 {
4170            return Err(allocator::Error::Other);
4171        }
4172
4173        // Note that this function (`read_le_u64`) really should take ̀`&mut self` but that is
4174        // unfortunately not the case, meaning that we can't "just" grow the memory if trying
4175        // to access an out of bound location.
4176        //
4177        // Additionally, the value being read can in theory overlap between an allocated and
4178        // non-allocated parts of the memory, making this more complicated.
4179
4180        // Offset of the memory page where the first byte of the value will be read.
4181        let accessed_memory_page_start = HeapPages::new(ptr / (64 * 1024));
4182        // Offset of the memory page where the last byte of the value will be read.
4183        let accessed_memory_page_end = HeapPages::new((ptr + 7) / (64 * 1024));
4184        // Number of pages currently allocated.
4185        let current_num_pages = match self.vm {
4186            MemAccessVm::Prepare(ref vm) => vm.memory_size(),
4187            MemAccessVm::Running(ref vm) => vm.memory_size(),
4188        };
4189        debug_assert!(current_num_pages <= self.memory_total_pages);
4190
4191        if accessed_memory_page_end < current_num_pages {
4192            // This is the simple case: the memory access is in bounds.
4193            match self.vm {
4194                MemAccessVm::Prepare(ref vm) => {
4195                    let bytes = vm.read_memory(ptr, 8).unwrap_or_else(|_| unreachable!());
4196                    Ok(u64::from_le_bytes(
4197                        <[u8; 8]>::try_from(bytes.as_ref()).unwrap_or_else(|_| unreachable!()),
4198                    ))
4199                }
4200                MemAccessVm::Running(ref vm) => {
4201                    let bytes = vm.read_memory(ptr, 8).unwrap_or_else(|_| unreachable!());
4202                    Ok(u64::from_le_bytes(
4203                        <[u8; 8]>::try_from(bytes.as_ref()).unwrap_or_else(|_| unreachable!()),
4204                    ))
4205                }
4206            }
4207        } else if accessed_memory_page_start < current_num_pages {
4208            // Memory access is partially in bounds. This is the most complicated situation.
4209            match self.vm {
4210                MemAccessVm::Prepare(ref vm) => {
4211                    let partial_bytes = vm
4212                        .read_memory(ptr, u32::from(current_num_pages) * 64 * 1024 - ptr)
4213                        .unwrap_or_else(|_| unreachable!());
4214                    let partial_bytes = partial_bytes.as_ref();
4215                    debug_assert!(partial_bytes.len() < 8);
4216
4217                    let mut out = [0; 8];
4218                    out[..partial_bytes.len()].copy_from_slice(partial_bytes);
4219                    Ok(u64::from_le_bytes(out))
4220                }
4221                MemAccessVm::Running(ref vm) => {
4222                    let partial_bytes = vm
4223                        .read_memory(ptr, u32::from(current_num_pages) * 64 * 1024 - ptr)
4224                        .unwrap_or_else(|_| unreachable!());
4225                    let partial_bytes = partial_bytes.as_ref();
4226                    debug_assert!(partial_bytes.len() < 8);
4227
4228                    let mut out = [0; 8];
4229                    out[..partial_bytes.len()].copy_from_slice(partial_bytes);
4230                    Ok(u64::from_le_bytes(out))
4231                }
4232            }
4233        } else {
4234            // Everything out bounds. Memory is zero.
4235            Ok(0)
4236        }
4237    }
4238
4239    fn write_le_u64(&mut self, ptr: u32, val: u64) -> Result<(), allocator::Error> {
4240        if (ptr + 8) > u32::from(self.memory_total_pages) * 64 * 1024 {
4241            return Err(allocator::Error::Other);
4242        }
4243
4244        let bytes = val.to_le_bytes();
4245
4246        // Offset of the memory page where the last byte of the value will be written.
4247        let written_memory_page = HeapPages::new((ptr + 7) / (64 * 1024));
4248
4249        // Grow the memory more if necessary.
4250        // Please note the `<=`. For example if we write to page 0, we want to have at least 1 page
4251        // allocated.
4252        let current_num_pages = match self.vm {
4253            MemAccessVm::Prepare(ref vm) => vm.memory_size(),
4254            MemAccessVm::Running(ref vm) => vm.memory_size(),
4255        };
4256        debug_assert!(current_num_pages <= self.memory_total_pages);
4257        if current_num_pages <= written_memory_page {
4258            // For now, we grow the memory just enough to fit.
4259            // TODO: do better
4260            // Note the order of operations: we add 1 at the end to avoid a potential overflow
4261            // in case `written_memory_page` is the maximum possible value.
4262            let to_grow = written_memory_page - current_num_pages + HeapPages::new(1);
4263
4264            // We check at initialization that the virtual machine is capable of growing up to
4265            // `memory_total_pages`, meaning that this can't panic.
4266            match self.vm {
4267                MemAccessVm::Prepare(ref mut vm) => {
4268                    vm.grow_memory(to_grow).unwrap_or_else(|_| unreachable!())
4269                }
4270                MemAccessVm::Running(ref mut vm) => {
4271                    vm.grow_memory(to_grow).unwrap_or_else(|_| unreachable!())
4272                }
4273            }
4274        }
4275
4276        match self.vm {
4277            MemAccessVm::Prepare(ref mut vm) => vm
4278                .write_memory(ptr, &bytes)
4279                .unwrap_or_else(|_| unreachable!()),
4280            MemAccessVm::Running(ref mut vm) => vm
4281                .write_memory(ptr, &bytes)
4282                .unwrap_or_else(|_| unreachable!()),
4283        }
4284        Ok(())
4285    }
4286
4287    fn size(&self) -> u32 {
4288        // Lie to the allocator to pretend that `memory_total_pages` are available.
4289        u32::from(self.memory_total_pages)
4290            .saturating_mul(64)
4291            .saturating_mul(1024)
4292    }
4293}