Skip to main content

soil_service/client/
call_executor.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
6
7use super::{code_provider::CodeProvider, ClientConfig};
8use soil_client::client_api::{
9	backend, call_executor::CallExecutor, execution_extensions::ExecutionExtensions, HeaderBackend,
10	TrieCacheContext,
11};
12use soil_client::executor::{RuntimeVersion, RuntimeVersionOf};
13use std::{cell::RefCell, sync::Arc};
14use subsoil::api::ProofRecorder;
15use subsoil::core::traits::{CallContext, CodeExecutor};
16use subsoil::externalities::Extensions;
17use subsoil::runtime::{
18	generic::BlockId,
19	traits::{Block as BlockT, HashingFor},
20};
21use subsoil::state_machine::{
22	backend::AsTrieBackend, OverlayedChanges, StateMachine, StorageProof,
23};
24
25/// Call executor that executes methods locally, querying all required
26/// data from local backend.
27pub struct LocalCallExecutor<Block: BlockT, B, E> {
28	backend: Arc<B>,
29	executor: E,
30	code_provider: CodeProvider<Block, B, E>,
31	execution_extensions: Arc<ExecutionExtensions<Block>>,
32}
33
34impl<Block: BlockT, B, E> LocalCallExecutor<Block, B, E>
35where
36	E: CodeExecutor + RuntimeVersionOf + Clone + 'static,
37	B: backend::Backend<Block>,
38{
39	/// Creates new instance of local call executor.
40	pub fn new(
41		backend: Arc<B>,
42		executor: E,
43		client_config: ClientConfig<Block>,
44		execution_extensions: ExecutionExtensions<Block>,
45	) -> soil_client::blockchain::Result<Self> {
46		let code_provider = CodeProvider::new(&client_config, executor.clone(), backend.clone())?;
47
48		Ok(LocalCallExecutor {
49			backend,
50			executor,
51			code_provider,
52			execution_extensions: Arc::new(execution_extensions),
53		})
54	}
55}
56
57impl<Block: BlockT, B, E> Clone for LocalCallExecutor<Block, B, E>
58where
59	E: Clone,
60{
61	fn clone(&self) -> Self {
62		LocalCallExecutor {
63			backend: self.backend.clone(),
64			executor: self.executor.clone(),
65			code_provider: self.code_provider.clone(),
66			execution_extensions: self.execution_extensions.clone(),
67		}
68	}
69}
70
71impl<B, E, Block> CallExecutor<Block> for LocalCallExecutor<Block, B, E>
72where
73	B: backend::Backend<Block>,
74	E: CodeExecutor + RuntimeVersionOf + Clone + 'static,
75	Block: BlockT,
76{
77	type Error = E::Error;
78
79	type Backend = B;
80
81	fn execution_extensions(&self) -> &ExecutionExtensions<Block> {
82		&self.execution_extensions
83	}
84
85	fn call(
86		&self,
87		at_hash: Block::Hash,
88		method: &str,
89		call_data: &[u8],
90		context: CallContext,
91	) -> soil_client::blockchain::Result<Vec<u8>> {
92		let mut changes = OverlayedChanges::default();
93		let at_number =
94			self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(at_hash))?;
95		let state = self.backend.state_at(at_hash, context.into())?;
96
97		let state_runtime_code = subsoil::state_machine::backend::BackendRuntimeCode::new(&state);
98		let runtime_code = state_runtime_code
99			.runtime_code()
100			.map_err(soil_client::blockchain::Error::RuntimeCode)?;
101
102		let runtime_code = self.code_provider.maybe_override_code(runtime_code, &state, at_hash)?.0;
103
104		let mut extensions = self.execution_extensions.extensions(at_hash, at_number);
105
106		let mut sm = StateMachine::new(
107			&state,
108			&mut changes,
109			&self.executor,
110			method,
111			call_data,
112			&mut extensions,
113			&runtime_code,
114			context,
115		)
116		.set_parent_hash(at_hash);
117
118		sm.execute().map_err(Into::into)
119	}
120
121	fn contextual_call(
122		&self,
123		at_hash: Block::Hash,
124		method: &str,
125		call_data: &[u8],
126		changes: &RefCell<OverlayedChanges<HashingFor<Block>>>,
127		recorder: &Option<ProofRecorder<Block>>,
128		call_context: CallContext,
129		extensions: &RefCell<Extensions>,
130	) -> Result<Vec<u8>, soil_client::blockchain::Error> {
131		let state = self.backend.state_at(at_hash, call_context.into())?;
132
133		let changes = &mut *changes.borrow_mut();
134
135		// It is important to extract the runtime code here before we create the proof
136		// recorder to not record it. We also need to fetch the runtime code from `state` to
137		// make sure we use the caching layers.
138		let state_runtime_code = subsoil::state_machine::backend::BackendRuntimeCode::new(&state);
139
140		let runtime_code = state_runtime_code
141			.runtime_code()
142			.map_err(soil_client::blockchain::Error::RuntimeCode)?;
143		let runtime_code = self.code_provider.maybe_override_code(runtime_code, &state, at_hash)?.0;
144		let mut extensions = extensions.borrow_mut();
145
146		match recorder {
147			Some(recorder) => {
148				let trie_state = state.as_trie_backend();
149
150				let backend = subsoil::state_machine::TrieBackendBuilder::wrap(&trie_state)
151					.with_recorder(recorder.clone())
152					.build();
153
154				let mut state_machine = StateMachine::new(
155					&backend,
156					changes,
157					&self.executor,
158					method,
159					call_data,
160					&mut extensions,
161					&runtime_code,
162					call_context,
163				)
164				.set_parent_hash(at_hash);
165				state_machine.execute()
166			},
167			None => {
168				let mut state_machine = StateMachine::new(
169					&state,
170					changes,
171					&self.executor,
172					method,
173					call_data,
174					&mut extensions,
175					&runtime_code,
176					call_context,
177				)
178				.set_parent_hash(at_hash);
179				state_machine.execute()
180			},
181		}
182		.map_err(Into::into)
183	}
184
185	fn runtime_version(
186		&self,
187		at_hash: Block::Hash,
188	) -> soil_client::blockchain::Result<RuntimeVersion> {
189		let state = self.backend.state_at(at_hash, backend::TrieCacheContext::Untrusted)?;
190		let state_runtime_code = subsoil::state_machine::backend::BackendRuntimeCode::new(&state);
191
192		let runtime_code = state_runtime_code
193			.runtime_code()
194			.map_err(soil_client::blockchain::Error::RuntimeCode)?;
195		self.code_provider
196			.maybe_override_code(runtime_code, &state, at_hash)
197			.map(|(_, v)| v)
198	}
199
200	fn prove_execution(
201		&self,
202		at_hash: Block::Hash,
203		method: &str,
204		call_data: &[u8],
205	) -> soil_client::blockchain::Result<(Vec<u8>, StorageProof)> {
206		let at_number =
207			self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(at_hash))?;
208		let state = self.backend.state_at(at_hash, TrieCacheContext::Untrusted)?;
209
210		let trie_backend = state.as_trie_backend();
211
212		let state_runtime_code =
213			subsoil::state_machine::backend::BackendRuntimeCode::new(trie_backend);
214		let runtime_code = state_runtime_code
215			.runtime_code()
216			.map_err(soil_client::blockchain::Error::RuntimeCode)?;
217		let runtime_code = self.code_provider.maybe_override_code(runtime_code, &state, at_hash)?.0;
218
219		subsoil::state_machine::prove_execution_on_trie_backend(
220			trie_backend,
221			&mut Default::default(),
222			&self.executor,
223			method,
224			call_data,
225			&runtime_code,
226			&mut self.execution_extensions.extensions(at_hash, at_number),
227		)
228		.map_err(Into::into)
229	}
230}
231
232impl<B, E, Block> RuntimeVersionOf for LocalCallExecutor<Block, B, E>
233where
234	E: RuntimeVersionOf,
235	Block: BlockT,
236{
237	fn runtime_version(
238		&self,
239		ext: &mut dyn subsoil::externalities::Externalities,
240		runtime_code: &subsoil::core::traits::RuntimeCode,
241	) -> Result<subsoil::version::RuntimeVersion, soil_client::executor::error::Error> {
242		RuntimeVersionOf::runtime_version(&self.executor, ext, runtime_code)
243	}
244}
245
246impl<Block, B, E> subsoil::version::GetRuntimeVersionAt<Block> for LocalCallExecutor<Block, B, E>
247where
248	B: backend::Backend<Block>,
249	E: CodeExecutor + RuntimeVersionOf + Clone + 'static,
250	Block: BlockT,
251{
252	fn runtime_version(&self, at: Block::Hash) -> Result<subsoil::version::RuntimeVersion, String> {
253		CallExecutor::runtime_version(self, at).map_err(|e| e.to_string())
254	}
255}
256
257impl<Block, B, E> subsoil::version::GetNativeVersion for LocalCallExecutor<Block, B, E>
258where
259	B: backend::Backend<Block>,
260	E: CodeExecutor + subsoil::version::GetNativeVersion + Clone + 'static,
261	Block: BlockT,
262{
263	fn native_version(&self) -> &subsoil::version::NativeVersion {
264		self.executor.native_version()
265	}
266}