1use 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
25pub 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 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 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}