1use super::{client::ClientConfig, wasm_override::WasmOverride, wasm_substitutes::WasmSubstitutes};
20use sc_client_api::{backend, TrieCacheContext};
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, TrieCacheContext::Untrusted)?;
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
231 .state_at(backend.blockchain().info().genesis_hash, TrieCacheContext::Untrusted)
232 .unwrap(),
233 backend.blockchain().info().genesis_hash,
234 )
235 .expect("RuntimeCode override")
236 .0;
237
238 assert_eq!(code_fetcher.fetch_runtime_code(), check.fetch_runtime_code());
239 }
240
241 #[test]
242 fn should_get_override_if_exists() {
243 let executor = WasmExecutor::default();
244
245 let overrides = crate::client::wasm_override::dummy_overrides();
246 let onchain_code = WrappedRuntimeCode(substrate_test_runtime::wasm_binary_unwrap().into());
247 let onchain_code = RuntimeCode {
248 code_fetcher: &onchain_code,
249 heap_pages: Some(128),
250 hash: vec![0, 0, 0, 0],
251 };
252
253 let backend = Arc::new(in_mem::Backend::<runtime::Block>::new());
254
255 let client_config = ClientConfig::default();
258
259 let genesis_block_builder = crate::GenesisBlockBuilder::new(
260 &substrate_test_runtime_client::GenesisParameters::default().genesis_storage(),
261 !client_config.no_genesis,
262 backend.clone(),
263 executor.clone(),
264 )
265 .expect("Creates genesis block builder");
266
267 let _client =
269 crate::client::new_with_backend::<_, _, runtime::Block, _, runtime::RuntimeApi>(
270 backend.clone(),
271 executor.clone(),
272 genesis_block_builder,
273 Box::new(TaskExecutor::new()),
274 None,
275 None,
276 client_config.clone(),
277 )
278 .expect("Creates a client");
279
280 let executor = Arc::new(executor);
281
282 let code_provider = CodeProvider {
283 backend: backend.clone(),
284 executor: executor.clone(),
285 wasm_override: Arc::new(Some(overrides)),
286 wasm_substitutes: WasmSubstitutes::new(Default::default(), executor, backend.clone())
287 .unwrap(),
288 };
289
290 let check = code_provider
291 .maybe_override_code(
292 onchain_code,
293 &backend
294 .state_at(backend.blockchain().info().genesis_hash, TrieCacheContext::Untrusted)
295 .unwrap(),
296 backend.blockchain().info().genesis_hash,
297 )
298 .expect("RuntimeCode override")
299 .0;
300
301 assert_eq!(Some(vec![2, 2, 2, 2, 2, 2, 2, 2]), check.fetch_runtime_code().map(Into::into));
302 }
303
304 #[test]
305 fn returns_runtime_version_from_substitute() {
306 const SUBSTITUTE_SPEC_NAME: &str = "substitute-spec-name-cool";
307
308 let executor = WasmExecutor::default();
309
310 let backend = Arc::new(in_mem::Backend::<runtime::Block>::new());
311
312 let substitute = sp_version::embed::embed_runtime_version(
314 &substrate_test_runtime::WASM_BINARY_BLOATY.unwrap(),
315 sp_version::RuntimeVersion {
316 spec_name: SUBSTITUTE_SPEC_NAME.into(),
317 ..substrate_test_runtime::VERSION
318 },
319 )
320 .unwrap();
321
322 let client_config = crate::client::ClientConfig {
323 wasm_runtime_substitutes: vec![(0, substitute)].into_iter().collect::<HashMap<_, _>>(),
324 ..Default::default()
325 };
326
327 let genesis_block_builder = crate::GenesisBlockBuilder::new(
328 &substrate_test_runtime_client::GenesisParameters::default().genesis_storage(),
329 !client_config.no_genesis,
330 backend.clone(),
331 executor.clone(),
332 )
333 .expect("Creates genesis block builder");
334
335 let client =
337 crate::client::new_with_backend::<_, _, runtime::Block, _, runtime::RuntimeApi>(
338 backend.clone(),
339 executor.clone(),
340 genesis_block_builder,
341 Box::new(TaskExecutor::new()),
342 None,
343 None,
344 client_config,
345 )
346 .expect("Creates a client");
347
348 let version = client.runtime_version_at(client.chain_info().genesis_hash).unwrap();
349
350 assert_eq!(SUBSTITUTE_SPEC_NAME, &*version.spec_name);
351 }
352}