1use super::{client::ClientConfig, wasm_override::WasmOverride, wasm_substitutes::WasmSubstitutes};
20use sc_client_api::backend;
21use sc_executor::{RuntimeVersion, RuntimeVersionOf};
22use sp_core::traits::{FetchRuntimeCode, RuntimeCode};
23use sp_runtime::traits::Block as BlockT;
24use sp_state_machine::{Ext, OverlayedChanges};
25use std::sync::Arc;
26
27pub struct CodeProvider<Block: BlockT, Backend, Executor> {
32 backend: Arc<Backend>,
33 executor: Arc<Executor>,
34 wasm_override: Arc<Option<WasmOverride>>,
35 wasm_substitutes: WasmSubstitutes<Block, Executor, Backend>,
36}
37
38impl<Block: BlockT, Backend, Executor: Clone> Clone for CodeProvider<Block, Backend, Executor> {
39 fn clone(&self) -> Self {
40 Self {
41 backend: self.backend.clone(),
42 executor: self.executor.clone(),
43 wasm_override: self.wasm_override.clone(),
44 wasm_substitutes: self.wasm_substitutes.clone(),
45 }
46 }
47}
48
49impl<Block, Backend, Executor> CodeProvider<Block, Backend, Executor>
50where
51 Block: BlockT,
52 Backend: backend::Backend<Block>,
53 Executor: RuntimeVersionOf,
54{
55 pub fn new(
57 client_config: &ClientConfig<Block>,
58 executor: Executor,
59 backend: Arc<Backend>,
60 ) -> sp_blockchain::Result<Self> {
61 let wasm_override = client_config
62 .wasm_runtime_overrides
63 .as_ref()
64 .map(|p| WasmOverride::new(p.clone(), &executor))
65 .transpose()?;
66
67 let executor = Arc::new(executor);
68
69 let wasm_substitutes = WasmSubstitutes::new(
70 client_config.wasm_runtime_substitutes.clone(),
71 executor.clone(),
72 backend.clone(),
73 )?;
74
75 Ok(Self { backend, executor, wasm_override: Arc::new(wasm_override), wasm_substitutes })
76 }
77
78 pub fn code_at_ignoring_overrides(&self, block: Block::Hash) -> sp_blockchain::Result<Vec<u8>> {
82 let state = self.backend.state_at(block)?;
83
84 let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state);
85 let runtime_code =
86 state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?;
87
88 self.maybe_override_code_internal(runtime_code, &state, block, true)
89 .and_then(|r| {
90 r.0.fetch_runtime_code().map(Into::into).ok_or_else(|| {
91 sp_blockchain::Error::Backend("Could not find `:code` in backend.".into())
92 })
93 })
94 }
95
96 pub fn maybe_override_code<'a>(
100 &'a self,
101 onchain_code: RuntimeCode<'a>,
102 state: &Backend::State,
103 hash: Block::Hash,
104 ) -> sp_blockchain::Result<(RuntimeCode<'a>, RuntimeVersion)> {
105 self.maybe_override_code_internal(onchain_code, state, hash, false)
106 }
107
108 fn maybe_override_code_internal<'a>(
112 &'a self,
113 onchain_code: RuntimeCode<'a>,
114 state: &Backend::State,
115 hash: Block::Hash,
116 ignore_overrides: bool,
117 ) -> sp_blockchain::Result<(RuntimeCode<'a>, RuntimeVersion)> {
118 let on_chain_version = self.on_chain_runtime_version(&onchain_code, state)?;
119 let code_and_version = if let Some(d) = self.wasm_override.as_ref().as_ref().and_then(|o| {
120 if ignore_overrides {
121 return None
122 }
123
124 o.get(
125 &on_chain_version.spec_version,
126 onchain_code.heap_pages,
127 &on_chain_version.spec_name,
128 )
129 }) {
130 tracing::debug!(target: "code-provider::overrides", block = ?hash, "using WASM override");
131 d
132 } else if let Some(s) =
133 self.wasm_substitutes
134 .get(on_chain_version.spec_version, onchain_code.heap_pages, hash)
135 {
136 tracing::debug!(target: "code-provider::substitutes", block = ?hash, "Using WASM substitute");
137 s
138 } else {
139 tracing::debug!(
140 target: "code-provider",
141 block = ?hash,
142 "Neither WASM override nor substitute available, using onchain code",
143 );
144 (onchain_code, on_chain_version)
145 };
146
147 Ok(code_and_version)
148 }
149
150 fn on_chain_runtime_version(
152 &self,
153 code: &RuntimeCode,
154 state: &Backend::State,
155 ) -> sp_blockchain::Result<RuntimeVersion> {
156 let mut overlay = OverlayedChanges::default();
157
158 let mut ext = Ext::new(&mut overlay, state, None);
159
160 self.executor
161 .runtime_version(&mut ext, code)
162 .map_err(|e| sp_blockchain::Error::VersionInvalid(e.to_string()))
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 use backend::Backend;
170 use sc_client_api::{in_mem, HeaderBackend};
171 use sc_executor::WasmExecutor;
172 use sp_core::{
173 testing::TaskExecutor,
174 traits::{FetchRuntimeCode, WrappedRuntimeCode},
175 };
176 use std::collections::HashMap;
177 use substrate_test_runtime_client::{runtime, GenesisInit};
178
179 #[test]
180 fn no_override_no_substitutes_work() {
181 let executor = WasmExecutor::default();
182
183 let code_fetcher = WrappedRuntimeCode(substrate_test_runtime::wasm_binary_unwrap().into());
184 let onchain_code = RuntimeCode {
185 code_fetcher: &code_fetcher,
186 heap_pages: Some(128),
187 hash: vec![0, 0, 0, 0],
188 };
189
190 let backend = Arc::new(in_mem::Backend::<runtime::Block>::new());
191
192 let client_config = ClientConfig::default();
195
196 let genesis_block_builder = crate::GenesisBlockBuilder::new(
197 &substrate_test_runtime_client::GenesisParameters::default().genesis_storage(),
198 !client_config.no_genesis,
199 backend.clone(),
200 executor.clone(),
201 )
202 .expect("Creates genesis block builder");
203
204 let _client =
206 crate::client::new_with_backend::<_, _, runtime::Block, _, runtime::RuntimeApi>(
207 backend.clone(),
208 executor.clone(),
209 genesis_block_builder,
210 Box::new(TaskExecutor::new()),
211 None,
212 None,
213 client_config.clone(),
214 )
215 .expect("Creates a client");
216
217 let executor = Arc::new(executor);
218
219 let code_provider = CodeProvider {
220 backend: backend.clone(),
221 executor: executor.clone(),
222 wasm_override: Arc::new(None),
223 wasm_substitutes: WasmSubstitutes::new(Default::default(), executor, backend.clone())
224 .unwrap(),
225 };
226
227 let check = code_provider
228 .maybe_override_code(
229 onchain_code,
230 &backend.state_at(backend.blockchain().info().genesis_hash).unwrap(),
231 backend.blockchain().info().genesis_hash,
232 )
233 .expect("RuntimeCode override")
234 .0;
235
236 assert_eq!(code_fetcher.fetch_runtime_code(), check.fetch_runtime_code());
237 }
238
239 #[test]
240 fn should_get_override_if_exists() {
241 let executor = WasmExecutor::default();
242
243 let overrides = crate::client::wasm_override::dummy_overrides();
244 let onchain_code = WrappedRuntimeCode(substrate_test_runtime::wasm_binary_unwrap().into());
245 let onchain_code = RuntimeCode {
246 code_fetcher: &onchain_code,
247 heap_pages: Some(128),
248 hash: vec![0, 0, 0, 0],
249 };
250
251 let backend = Arc::new(in_mem::Backend::<runtime::Block>::new());
252
253 let client_config = ClientConfig::default();
256
257 let genesis_block_builder = crate::GenesisBlockBuilder::new(
258 &substrate_test_runtime_client::GenesisParameters::default().genesis_storage(),
259 !client_config.no_genesis,
260 backend.clone(),
261 executor.clone(),
262 )
263 .expect("Creates genesis block builder");
264
265 let _client =
267 crate::client::new_with_backend::<_, _, runtime::Block, _, runtime::RuntimeApi>(
268 backend.clone(),
269 executor.clone(),
270 genesis_block_builder,
271 Box::new(TaskExecutor::new()),
272 None,
273 None,
274 client_config.clone(),
275 )
276 .expect("Creates a client");
277
278 let executor = Arc::new(executor);
279
280 let code_provider = CodeProvider {
281 backend: backend.clone(),
282 executor: executor.clone(),
283 wasm_override: Arc::new(Some(overrides)),
284 wasm_substitutes: WasmSubstitutes::new(Default::default(), executor, backend.clone())
285 .unwrap(),
286 };
287
288 let check = code_provider
289 .maybe_override_code(
290 onchain_code,
291 &backend.state_at(backend.blockchain().info().genesis_hash).unwrap(),
292 backend.blockchain().info().genesis_hash,
293 )
294 .expect("RuntimeCode override")
295 .0;
296
297 assert_eq!(Some(vec![2, 2, 2, 2, 2, 2, 2, 2]), check.fetch_runtime_code().map(Into::into));
298 }
299
300 #[test]
301 fn returns_runtime_version_from_substitute() {
302 const SUBSTITUTE_SPEC_NAME: &str = "substitute-spec-name-cool";
303
304 let executor = WasmExecutor::default();
305
306 let backend = Arc::new(in_mem::Backend::<runtime::Block>::new());
307
308 let substitute = sp_version::embed::embed_runtime_version(
310 &substrate_test_runtime::WASM_BINARY_BLOATY.unwrap(),
311 sp_version::RuntimeVersion {
312 spec_name: SUBSTITUTE_SPEC_NAME.into(),
313 ..substrate_test_runtime::VERSION
314 },
315 )
316 .unwrap();
317
318 let client_config = crate::client::ClientConfig {
319 wasm_runtime_substitutes: vec![(0, substitute)].into_iter().collect::<HashMap<_, _>>(),
320 ..Default::default()
321 };
322
323 let genesis_block_builder = crate::GenesisBlockBuilder::new(
324 &substrate_test_runtime_client::GenesisParameters::default().genesis_storage(),
325 !client_config.no_genesis,
326 backend.clone(),
327 executor.clone(),
328 )
329 .expect("Creates genesis block builder");
330
331 let client =
333 crate::client::new_with_backend::<_, _, runtime::Block, _, runtime::RuntimeApi>(
334 backend.clone(),
335 executor.clone(),
336 genesis_block_builder,
337 Box::new(TaskExecutor::new()),
338 None,
339 None,
340 client_config,
341 )
342 .expect("Creates a client");
343
344 let version = client.runtime_version_at(client.chain_info().genesis_hash).unwrap();
345
346 assert_eq!(SUBSTITUTE_SPEC_NAME, &*version.spec_name);
347 }
348}