referrerpolicy=no-referrer-when-downgrade

sc_service/client/
code_provider.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// 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.
10
11// 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.
15
16// 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/>.
18
19use 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
27/// Provider for fetching `:code` of a block.
28///
29/// As a node can run with code overrides or substitutes, this will ensure that these are taken into
30/// account before returning the actual `code` for a block.
31pub 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	/// Create a new instance.
56	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	/// Returns the `:code` for the given `block`.
79	///
80	/// This takes into account potential overrides/substitutes.
81	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	/// Maybe override the given `onchain_code`.
97	///
98	/// This takes into account potential overrides/substitutes.
99	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	/// Maybe override the given `onchain_code`.
109	///
110	/// This takes into account potential overrides(depending on `ignore_overrides`)/substitutes.
111	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	/// Returns the on chain runtime version.
151	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		// wasm_runtime_overrides is `None` here because we construct the
193		// LocalCallExecutor directly later on
194		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		// client is used for the convenience of creating and inserting the genesis block.
205		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		// wasm_runtime_overrides is `None` here because we construct the
256		// LocalCallExecutor directly later on
257		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		// client is used for the convenience of creating and inserting the genesis block.
268		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's only override the `spec_name` for our testing purposes.
313		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		// client is used for the convenience of creating and inserting the genesis block.
336		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}