1// This file is part of Substrate.
23// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
56// 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.
1011// 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.
1516// 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/>.
1819//! 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)]
2425use 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;
3536pub mod error;
3738/// 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> {
43let runtime_caller = RuntimeCaller::new(executor, code_bytes);
44let version_result = runtime_caller.call("Metadata_metadata_versions", ());
4546match version_result {
47Ok(supported_versions) => {
48let supported_versions = Vec::<u32>::decode(&mut supported_versions.as_slice())?;
49let latest_stable = supported_versions
50 .into_iter()
51// TODO: Subxt doesn't support V16 metadata until v0.42.0, so don't try
52 // to fetch it here until we update to that version.
53.filter(|v| *v != u32::MAX && *v < 16)
54 .max()
55 .ok_or(Error::StableMetadataVersionNotFound)?;
5657let encoded = runtime_caller.call("Metadata_metadata_at_version", latest_stable)?;
5859Option::<OpaqueMetadata>::decode(&mut encoded.as_slice())?
60.ok_or(Error::OpaqueMetadataNotFound)
61 },
62Err(_) => {
63let encoded = runtime_caller.call("Metadata_metadata", ())?;
64 Decode::decode(&mut encoded.as_slice()).map_err(Into::into)
65 },
66 }
67}
6869struct BasicCodeFetcher<'a> {
70 code: Cow<'a, [u8]>,
71 hash: Vec<u8>,
72}
7374impl<'a> FetchRuntimeCode for BasicCodeFetcher<'a> {
75fn fetch_runtime_code(&self) -> Option<Cow<[u8]>> {
76Some(self.code.as_ref().into())
77 }
78}
7980impl<'a> BasicCodeFetcher<'a> {
81fn new(code: Cow<'a, [u8]>) -> Self {
82Self { hash: sp_crypto_hashing::blake2_256(&code).to_vec(), code }
83 }
8485fn runtime_code(&'a self) -> RuntimeCode<'a> {
86 RuntimeCode {
87 code_fetcher: self as &'a dyn FetchRuntimeCode,
88 heap_pages: None,
89 hash: self.hash.clone(),
90 }
91 }
92}
9394/// Simple utility that is used to call into the runtime.
95pub struct RuntimeCaller<'a, 'b, HF: HostFunctions> {
96 executor: &'b WasmExecutor<HF>,
97 code_fetcher: BasicCodeFetcher<'a>,
98}
99100impl<'a, 'b, HF: HostFunctions> RuntimeCaller<'a, 'b, HF> {
101/// Instantiate a new runtime caller.
102pub fn new(executor: &'b WasmExecutor<HF>, code_bytes: Cow<'a, [u8]>) -> Self {
103Self { executor, code_fetcher: BasicCodeFetcher::new(code_bytes) }
104 }
105106/// Calls a runtime function represented by a `method` name and `parity-scale-codec`
107 /// encodable arguments that will be passed to it.
108pub fn call(&self, method: &str, data: impl Encode) -> Result<Vec<u8>> {
109let mut ext = BasicExternalities::default();
110self.executor
111 .call(
112&mut ext,
113&self.code_fetcher.runtime_code(),
114 method,
115&data.encode(),
116 CallContext::Offchain,
117 )
118 .0
119.map_err(Into::into)
120 }
121}
122123#[cfg(test)]
124mod tests {
125use codec::Decode;
126use sc_executor::WasmExecutor;
127use sp_version::RuntimeVersion;
128129type ParachainHostFunctions = (
130 cumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions,
131 sp_io::SubstrateHostFunctions,
132 );
133134#[test]
135fn test_fetch_latest_metadata_from_blob_fetches_metadata() {
136let executor: WasmExecutor<ParachainHostFunctions> = WasmExecutor::builder().build();
137let code_bytes = cumulus_test_runtime::WASM_BINARY
138 .expect("To run this test, build the wasm binary of cumulus-test-runtime")
139 .to_vec();
140let metadata = subxt::Metadata::decode(
141&mut (*super::fetch_latest_metadata_from_code_blob(&executor, code_bytes.into())
142 .unwrap())
143 .as_slice(),
144 )
145 .unwrap();
146assert!(metadata.pallet_by_name("ParachainInfo").is_some());
147 }
148149#[test]
150fn test_runtime_caller_can_call_into_runtime() {
151let executor: WasmExecutor<ParachainHostFunctions> = WasmExecutor::builder().build();
152let code_bytes = cumulus_test_runtime::WASM_BINARY
153 .expect("To run this test, build the wasm binary of cumulus-test-runtime")
154 .to_vec();
155let runtime_caller = super::RuntimeCaller::new(&executor, code_bytes.into());
156let runtime_version = runtime_caller
157 .call("Core_version", ())
158 .expect("Should be able to call runtime_version");
159let _runtime_version: RuntimeVersion = Decode::decode(&mut runtime_version.as_slice())
160 .expect("Should be able to decode runtime version");
161 }
162}