sc_runtime_utilities/
lib.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//! Substrate client runtime utilities.
20//!
21//! Provides convenient APIs to ease calling functions contained by a FRAME
22//! runtime WASM blob.
23#![warn(missing_docs)]
24
25use codec::{Decode, Encode};
26use error::{Error, Result};
27use sc_executor::WasmExecutor;
28use sp_core::{
29	traits::{CallContext, CodeExecutor, FetchRuntimeCode, RuntimeCode},
30	OpaqueMetadata,
31};
32use sp_state_machine::BasicExternalities;
33use sp_wasm_interface::HostFunctions;
34use std::borrow::Cow;
35
36pub mod error;
37
38/// Fetches the latest metadata from the given runtime blob.
39pub fn fetch_latest_metadata_from_code_blob<HF: HostFunctions>(
40	executor: &WasmExecutor<HF>,
41	code_bytes: Cow<[u8]>,
42) -> Result<OpaqueMetadata> {
43	let runtime_caller = RuntimeCaller::new(executor, code_bytes);
44	let version_result = runtime_caller.call("Metadata_metadata_versions", ());
45
46	match version_result {
47		Ok(supported_versions) => {
48			let supported_versions = Vec::<u32>::decode(&mut supported_versions.as_slice())?;
49			let latest_stable = supported_versions
50				.into_iter()
51				.filter(|v| *v != u32::MAX)
52				.max()
53				.ok_or(Error::StableMetadataVersionNotFound)?;
54
55			let encoded = runtime_caller.call("Metadata_metadata_at_version", latest_stable)?;
56
57			Option::<OpaqueMetadata>::decode(&mut encoded.as_slice())?
58				.ok_or(Error::OpaqueMetadataNotFound)
59		},
60		Err(_) => {
61			let encoded = runtime_caller.call("Metadata_metadata", ())?;
62			Decode::decode(&mut encoded.as_slice()).map_err(Into::into)
63		},
64	}
65}
66
67struct BasicCodeFetcher<'a> {
68	code: Cow<'a, [u8]>,
69	hash: Vec<u8>,
70}
71
72impl<'a> FetchRuntimeCode for BasicCodeFetcher<'a> {
73	fn fetch_runtime_code(&self) -> Option<Cow<[u8]>> {
74		Some(self.code.as_ref().into())
75	}
76}
77
78impl<'a> BasicCodeFetcher<'a> {
79	fn new(code: Cow<'a, [u8]>) -> Self {
80		Self { hash: sp_crypto_hashing::blake2_256(&code).to_vec(), code }
81	}
82
83	fn runtime_code(&'a self) -> RuntimeCode<'a> {
84		RuntimeCode {
85			code_fetcher: self as &'a dyn FetchRuntimeCode,
86			heap_pages: None,
87			hash: self.hash.clone(),
88		}
89	}
90}
91
92/// Simple utility that is used to call into the runtime.
93pub struct RuntimeCaller<'a, 'b, HF: HostFunctions> {
94	executor: &'b WasmExecutor<HF>,
95	code_fetcher: BasicCodeFetcher<'a>,
96}
97
98impl<'a, 'b, HF: HostFunctions> RuntimeCaller<'a, 'b, HF> {
99	/// Instantiate a new runtime caller.
100	pub fn new(executor: &'b WasmExecutor<HF>, code_bytes: Cow<'a, [u8]>) -> Self {
101		Self { executor, code_fetcher: BasicCodeFetcher::new(code_bytes) }
102	}
103
104	/// Calls a runtime function represented by a `method` name and `parity-scale-codec`
105	/// encodable arguments that will be passed to it.
106	pub fn call(&self, method: &str, data: impl Encode) -> Result<Vec<u8>> {
107		let mut ext = BasicExternalities::default();
108		self.executor
109			.call(
110				&mut ext,
111				&self.code_fetcher.runtime_code(),
112				method,
113				&data.encode(),
114				CallContext::Offchain,
115			)
116			.0
117			.map_err(Into::into)
118	}
119}
120
121#[cfg(test)]
122mod tests {
123	use codec::Decode;
124	use sc_executor::WasmExecutor;
125	use sp_version::RuntimeVersion;
126
127	type ParachainHostFunctions = (
128		cumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions,
129		sp_io::SubstrateHostFunctions,
130	);
131
132	#[test]
133	fn test_fetch_latest_metadata_from_blob_fetches_metadata() {
134		let executor: WasmExecutor<ParachainHostFunctions> = WasmExecutor::builder().build();
135		let code_bytes = cumulus_test_runtime::WASM_BINARY
136			.expect("To run this test, build the wasm binary of cumulus-test-runtime")
137			.to_vec();
138		let metadata = subxt::Metadata::decode(
139			&mut (*super::fetch_latest_metadata_from_code_blob(&executor, code_bytes.into())
140				.unwrap())
141			.as_slice(),
142		)
143		.unwrap();
144		assert!(metadata.pallet_by_name("ParachainInfo").is_some());
145	}
146
147	#[test]
148	fn test_runtime_caller_can_call_into_runtime() {
149		let executor: WasmExecutor<ParachainHostFunctions> = WasmExecutor::builder().build();
150		let code_bytes = cumulus_test_runtime::WASM_BINARY
151			.expect("To run this test, build the wasm binary of cumulus-test-runtime")
152			.to_vec();
153		let runtime_caller = super::RuntimeCaller::new(&executor, code_bytes.into());
154		let runtime_version = runtime_caller
155			.call("Core_version", ())
156			.expect("Should be able to call runtime_version");
157		let _runtime_version: RuntimeVersion = Decode::decode(&mut runtime_version.as_slice())
158			.expect("Should be able to decode runtime version");
159	}
160}