sc_service/client/
wasm_substitutes.rs1use sc_client_api::backend;
22use sc_executor::RuntimeVersionOf;
23use sp_blockchain::{HeaderBackend, Result};
24use sp_core::traits::{FetchRuntimeCode, RuntimeCode, WrappedRuntimeCode};
25use sp_runtime::traits::{Block as BlockT, NumberFor};
26use sp_state_machine::BasicExternalities;
27use sp_version::RuntimeVersion;
28use std::{
29 collections::{hash_map::DefaultHasher, HashMap},
30 hash::Hasher as _,
31 sync::Arc,
32};
33
34#[derive(Debug)]
36struct WasmSubstitute<Block: BlockT> {
37 code: Vec<u8>,
38 hash: Vec<u8>,
39 block_number: NumberFor<Block>,
41 version: RuntimeVersion,
42}
43
44impl<Block: BlockT> WasmSubstitute<Block> {
45 fn new(code: Vec<u8>, block_number: NumberFor<Block>, version: RuntimeVersion) -> Self {
46 let hash = make_hash(&code);
47 Self { code, hash, block_number, version }
48 }
49
50 fn runtime_code(&self, heap_pages: Option<u64>) -> RuntimeCode {
51 RuntimeCode { code_fetcher: self, hash: self.hash.clone(), heap_pages }
52 }
53
54 fn matches(
56 &self,
57 hash: <Block as BlockT>::Hash,
58 backend: &impl backend::Backend<Block>,
59 ) -> bool {
60 let requested_block_number = backend.blockchain().number(hash).ok().flatten();
61
62 Some(self.block_number) <= requested_block_number
63 }
64}
65
66fn make_hash<K: std::hash::Hash + ?Sized>(val: &K) -> Vec<u8> {
68 let mut state = DefaultHasher::new();
69 val.hash(&mut state);
70 state.finish().to_le_bytes().to_vec()
71}
72
73impl<Block: BlockT> FetchRuntimeCode for WasmSubstitute<Block> {
74 fn fetch_runtime_code(&self) -> Option<std::borrow::Cow<[u8]>> {
75 Some(self.code.as_slice().into())
76 }
77}
78
79#[derive(Debug, thiserror::Error)]
80#[allow(missing_docs)]
81pub enum WasmSubstituteError {
82 #[error("Failed to get runtime version: {0}")]
83 VersionInvalid(String),
84}
85
86impl From<WasmSubstituteError> for sp_blockchain::Error {
87 fn from(err: WasmSubstituteError) -> Self {
88 Self::Application(Box::new(err))
89 }
90}
91
92#[derive(Debug)]
94pub struct WasmSubstitutes<Block: BlockT, Executor, Backend> {
95 substitutes: Arc<HashMap<u32, WasmSubstitute<Block>>>,
97 executor: Arc<Executor>,
98 backend: Arc<Backend>,
99}
100
101impl<Block: BlockT, Executor: Clone, Backend> Clone for WasmSubstitutes<Block, Executor, Backend> {
102 fn clone(&self) -> Self {
103 Self {
104 substitutes: self.substitutes.clone(),
105 executor: self.executor.clone(),
106 backend: self.backend.clone(),
107 }
108 }
109}
110
111impl<Executor, Backend, Block> WasmSubstitutes<Block, Executor, Backend>
112where
113 Executor: RuntimeVersionOf,
114 Backend: backend::Backend<Block>,
115 Block: BlockT,
116{
117 pub fn new(
119 substitutes: HashMap<NumberFor<Block>, Vec<u8>>,
120 executor: Arc<Executor>,
121 backend: Arc<Backend>,
122 ) -> Result<Self> {
123 let substitutes = substitutes
124 .into_iter()
125 .map(|(block_number, code)| {
126 let runtime_code = RuntimeCode {
127 code_fetcher: &WrappedRuntimeCode((&code).into()),
128 heap_pages: None,
129 hash: make_hash(&code),
130 };
131 let version = Self::runtime_version(&executor, &runtime_code)?;
132 let spec_version = version.spec_version;
133
134 let substitute = WasmSubstitute::new(code, block_number, version);
135
136 Ok((spec_version, substitute))
137 })
138 .collect::<Result<HashMap<_, _>>>()?;
139
140 Ok(Self { executor, substitutes: Arc::new(substitutes), backend })
141 }
142
143 pub fn get(
147 &self,
148 spec: u32,
149 pages: Option<u64>,
150 hash: Block::Hash,
151 ) -> Option<(RuntimeCode<'_>, RuntimeVersion)> {
152 let s = self.substitutes.get(&spec)?;
153 s.matches(hash, &*self.backend)
154 .then(|| (s.runtime_code(pages), s.version.clone()))
155 }
156
157 fn runtime_version(executor: &Executor, code: &RuntimeCode) -> Result<RuntimeVersion> {
158 let mut ext = BasicExternalities::default();
159 executor
160 .runtime_version(&mut ext, code)
161 .map_err(|e| WasmSubstituteError::VersionInvalid(e.to_string()).into())
162 }
163}