referrerpolicy=no-referrer-when-downgrade

sp_virtualization/
forwarder.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use crate::{
19	host_functions::{virtualization as host_fn, CompiledModule, ExecBuffer, ExecStatus},
20	CompileStatus, ExecError, ExecResult, InstanceId, InstantiateError, MemoryError, ModuleError,
21	ModuleId,
22};
23
24/// A compiled module handle.
25pub struct Module(ModuleId);
26
27impl Module {
28	/// Compile a module from raw bytes.
29	///
30	/// If `identifier` is `Some` and a module is already cached under it, no compilation
31	/// occurs and [`CompileStatus::Cached`] is returned. Otherwise the bytes are compiled,
32	/// cached under `identifier` if supplied, and [`CompileStatus::Compiled`] is returned.
33	///
34	/// A `Cached` result means the call was cheap; a `Compiled` result means real work was
35	/// done. Callers should weight accordingly.
36	pub fn from_bytes(
37		bytes: &[u8],
38		identifier: Option<&[u8]>,
39	) -> Result<(Self, CompileStatus), ModuleError> {
40		let CompiledModule { id, status } = host_fn::compile_from_bytes(bytes, identifier)?;
41		Ok((Self(id), status))
42	}
43
44	/// Look up a previously compiled module by `identifier`.
45	///
46	/// Returns `Err(ModuleError::NotCached)` if no module is cached under `identifier`.
47	/// This is a pure cache lookup — no storage access, no compilation — so the call is
48	/// cheap and never reads from the trie. Use [`Module::from_storage_key`] if you want
49	/// the host to fall back to a storage read on miss.
50	pub fn lookup(identifier: &[u8]) -> Result<Self, ModuleError> {
51		Ok(Self(host_fn::lookup(identifier)?))
52	}
53
54	/// Compile (or fetch from cache) a module whose program bytes live at `storage_key`.
55	///
56	/// The `storage_key` is also used as the cache identifier. Pass an empty `child_trie` to
57	/// read from the main state trie. On a cache hit returns [`CompileStatus::Cached`]
58	/// (cheap); on a miss the host reads from storage and compiles, returning
59	/// [`CompileStatus::Compiled`].
60	pub fn from_storage_key(
61		storage_key: &[u8],
62		child_trie: &[u8],
63	) -> Result<(Self, CompileStatus), ModuleError> {
64		let CompiledModule { id, status } =
65			host_fn::compile_from_storage_key(storage_key, child_trie)?;
66		Ok((Self(id), status))
67	}
68
69	pub fn instantiate(&self) -> Result<Instance, InstantiateError> {
70		Ok(Instance(host_fn::instantiate(self.0)?))
71	}
72}
73
74/// An idle virtualization instance.
75pub struct Instance(InstanceId);
76
77impl Drop for Instance {
78	fn drop(&mut self) {
79		host_fn::destroy(self.0).ok();
80	}
81}
82
83impl core::fmt::Debug for Instance {
84	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
85		f.debug_tuple("Instance").field(&self.0).finish()
86	}
87}
88
89impl Instance {
90	pub fn prepare(self, function: &[u8]) -> Result<Execution, (Self, ExecError)> {
91		match host_fn::prepare(self.0, function) {
92			Ok(()) => {
93				let instance_id = self.0;
94				core::mem::forget(self);
95				Ok(Execution(instance_id))
96			},
97			Err(err) => Err((self, err)),
98		}
99	}
100}
101
102/// A prepared or suspended virtualization execution.
103pub struct Execution(InstanceId);
104
105impl Drop for Execution {
106	fn drop(&mut self) {
107		host_fn::destroy(self.0).ok();
108	}
109}
110
111impl Execution {
112	pub fn run(self, gas_left: i64, a0: u64) -> ExecResult<Instance, Self> {
113		let mut buf = ExecBuffer::default();
114		let status_byte = match host_fn::run(self.0, gas_left, a0, &mut buf) {
115			Ok(s) => s,
116			Err(err) => {
117				let instance_id = self.0;
118				core::mem::forget(self);
119				return ExecResult::Error { instance: Instance(instance_id), error: err };
120			},
121		};
122		let status: ExecStatus = status_byte.try_into().expect("invalid status from host; qed");
123		match status {
124			ExecStatus::Finished => {
125				let instance_id = self.0;
126				core::mem::forget(self);
127				ExecResult::Finished { instance: Instance(instance_id), gas_left: buf.gas_left }
128			},
129			ExecStatus::Syscall => ExecResult::Syscall {
130				execution: self,
131				gas_left: buf.gas_left,
132				syscall_symbol: buf.syscall_symbol,
133				a0: buf.a0,
134				a1: buf.a1,
135				a2: buf.a2,
136				a3: buf.a3,
137				a4: buf.a4,
138				a5: buf.a5,
139			},
140		}
141	}
142
143	pub fn read_memory(&mut self, offset: u32, dest: &mut [u8]) -> Result<(), MemoryError> {
144		host_fn::read_memory(self.0, offset, dest)
145	}
146
147	pub fn write_memory(&mut self, offset: u32, src: &[u8]) -> Result<(), MemoryError> {
148		host_fn::write_memory(self.0, offset, src)
149	}
150}