sc_runtime_utilities/
lib.rs1#![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
38pub 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
92pub 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 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 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}