referrerpolicy=no-referrer-when-downgrade

sc_executor_wasmtime/
host.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! This module defines `HostState` and `HostContext` structs which provide logic and state
20//! required for execution of host.
21
22use wasmtime::Caller;
23
24use sc_allocator::{AllocationStats, FreeingBumpHeapAllocator};
25use sp_wasm_interface::{Pointer, WordSize};
26
27use crate::{instance_wrapper::MemoryWrapper, runtime::StoreData, util};
28
29/// The state required to construct a HostContext context. The context only lasts for one host
30/// call, whereas the state is maintained for the duration of a Wasm runtime call, which may make
31/// many different host calls that must share state.
32pub struct HostState {
33	/// The allocator instance to keep track of allocated memory.
34	///
35	/// This is stored as an `Option` as we need to temporarily set this to `None` when we are
36	/// allocating/deallocating memory. The problem being that we can only mutable access `caller`
37	/// once.
38	allocator: Option<FreeingBumpHeapAllocator>,
39	panic_message: Option<String>,
40}
41
42impl HostState {
43	/// Constructs a new `HostState`.
44	pub fn new(allocator: FreeingBumpHeapAllocator) -> Self {
45		HostState { allocator: Some(allocator), panic_message: None }
46	}
47
48	/// Takes the error message out of the host state, leaving a `None` in its place.
49	pub fn take_panic_message(&mut self) -> Option<String> {
50		self.panic_message.take()
51	}
52
53	pub(crate) fn allocation_stats(&self) -> AllocationStats {
54		self.allocator.as_ref()
55			.expect("Allocator is always set and only unavailable when doing an allocation/deallocation; qed")
56			.stats()
57	}
58}
59
60/// A `HostContext` implements `FunctionContext` for making host calls from a Wasmtime
61/// runtime. The `HostContext` exists only for the lifetime of the call and borrows state from
62/// a longer-living `HostState`.
63pub(crate) struct HostContext<'a> {
64	pub(crate) caller: Caller<'a, StoreData>,
65}
66
67impl<'a> HostContext<'a> {
68	fn host_state_mut(&mut self) -> &mut HostState {
69		self.caller
70			.data_mut()
71			.host_state_mut()
72			.expect("host state is not empty when calling a function in wasm; qed")
73	}
74}
75
76impl<'a> sp_wasm_interface::FunctionContext for HostContext<'a> {
77	fn read_memory_into(
78		&self,
79		address: Pointer<u8>,
80		dest: &mut [u8],
81	) -> sp_wasm_interface::Result<()> {
82		util::read_memory_into(&self.caller, address, dest).map_err(|e| e.to_string())
83	}
84
85	fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> sp_wasm_interface::Result<()> {
86		util::write_memory_from(&mut self.caller, address, data).map_err(|e| e.to_string())
87	}
88
89	fn allocate_memory(&mut self, size: WordSize) -> sp_wasm_interface::Result<Pointer<u8>> {
90		let memory = self.caller.data().memory();
91		let mut allocator = self
92			.host_state_mut()
93			.allocator
94			.take()
95			.expect("allocator is not empty when calling a function in wasm; qed");
96
97		// We can not return on error early, as we need to store back allocator.
98		let res = allocator
99			.allocate(&mut MemoryWrapper(&memory, &mut self.caller), size)
100			.map_err(|e| e.to_string());
101
102		self.host_state_mut().allocator = Some(allocator);
103
104		res
105	}
106
107	fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> sp_wasm_interface::Result<()> {
108		let memory = self.caller.data().memory();
109		let mut allocator = self
110			.host_state_mut()
111			.allocator
112			.take()
113			.expect("allocator is not empty when calling a function in wasm; qed");
114
115		// We can not return on error early, as we need to store back allocator.
116		let res = allocator
117			.deallocate(&mut MemoryWrapper(&memory, &mut self.caller), ptr)
118			.map_err(|e| e.to_string());
119
120		self.host_state_mut().allocator = Some(allocator);
121
122		res
123	}
124
125	fn register_panic_error_message(&mut self, message: &str) {
126		self.host_state_mut().panic_message = Some(message.to_owned());
127	}
128}