Skip to main content

soil_runtime_utilities/
lib.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! Substrate client runtime utilities.
8//!
9//! Provides convenient APIs to ease calling functions contained by a FRAME
10//! runtime WASM blob.
11#![warn(missing_docs)]
12
13use codec::{Decode, Encode};
14use error::{Error, Result};
15use soil_client::executor::WasmExecutor;
16use std::borrow::Cow;
17use subsoil::core::{
18	traits::{CallContext, CodeExecutor, FetchRuntimeCode, RuntimeCode},
19	OpaqueMetadata,
20};
21use subsoil::state_machine::BasicExternalities;
22use subsoil::wasm_interface::HostFunctions;
23
24pub mod error;
25
26/// Fetches the latest metadata from the given runtime blob.
27pub fn fetch_latest_metadata_from_code_blob<HF: HostFunctions>(
28	executor: &WasmExecutor<HF>,
29	code_bytes: Cow<[u8]>,
30) -> Result<OpaqueMetadata> {
31	let runtime_caller = RuntimeCaller::new(executor, code_bytes);
32	let version_result = runtime_caller.call("Metadata_metadata_versions", ());
33
34	match version_result {
35		Ok(supported_versions) => {
36			let supported_versions = Vec::<u32>::decode(&mut supported_versions.as_slice())?;
37			let latest_stable = supported_versions
38				.into_iter()
39				// TODO: Subxt doesn't support V16 metadata until v0.42.0, so don't try
40				// to fetch it here until we update to that version.
41				.filter(|v| *v != u32::MAX && *v < 16)
42				.max()
43				.ok_or(Error::StableMetadataVersionNotFound)?;
44
45			let encoded = runtime_caller.call("Metadata_metadata_at_version", latest_stable)?;
46
47			Option::<OpaqueMetadata>::decode(&mut encoded.as_slice())?
48				.ok_or(Error::OpaqueMetadataNotFound)
49		},
50		Err(_) => {
51			let encoded = runtime_caller.call("Metadata_metadata", ())?;
52			Decode::decode(&mut encoded.as_slice()).map_err(Into::into)
53		},
54	}
55}
56
57struct BasicCodeFetcher<'a> {
58	code: Cow<'a, [u8]>,
59	hash: Vec<u8>,
60}
61
62impl<'a> FetchRuntimeCode for BasicCodeFetcher<'a> {
63	fn fetch_runtime_code(&self) -> Option<Cow<'_, [u8]>> {
64		Some(self.code.as_ref().into())
65	}
66}
67
68impl<'a> BasicCodeFetcher<'a> {
69	fn new(code: Cow<'a, [u8]>) -> Self {
70		Self { hash: subsoil_crypto_hashing::blake2_256(&code).to_vec(), code }
71	}
72
73	fn runtime_code(&'a self) -> RuntimeCode<'a> {
74		RuntimeCode {
75			code_fetcher: self as &'a dyn FetchRuntimeCode,
76			heap_pages: None,
77			hash: self.hash.clone(),
78		}
79	}
80}
81
82/// Simple utility that is used to call into the runtime.
83pub struct RuntimeCaller<'a, 'b, HF: HostFunctions> {
84	executor: &'b WasmExecutor<HF>,
85	code_fetcher: BasicCodeFetcher<'a>,
86}
87
88impl<'a, 'b, HF: HostFunctions> RuntimeCaller<'a, 'b, HF> {
89	/// Instantiate a new runtime caller.
90	pub fn new(executor: &'b WasmExecutor<HF>, code_bytes: Cow<'a, [u8]>) -> Self {
91		Self { executor, code_fetcher: BasicCodeFetcher::new(code_bytes) }
92	}
93
94	/// Calls a runtime function represented by a `method` name and `parity-scale-codec`
95	/// encodable arguments that will be passed to it.
96	pub fn call(&self, method: &str, data: impl Encode) -> Result<Vec<u8>> {
97		let mut ext = BasicExternalities::default();
98		self.executor
99			.call(
100				&mut ext,
101				&self.code_fetcher.runtime_code(),
102				method,
103				&data.encode(),
104				CallContext::Offchain,
105			)
106			.0
107			.map_err(Into::into)
108	}
109}