1use super::{
10 block_rules::{BlockRules, LookupResult as BlockLookupResult},
11 CodeProvider,
12};
13use crate::client::notification_pinning::NotificationPinningWorker;
14use log::{debug, info, trace, warn};
15use parking_lot::{Mutex, RwLock};
16use soil_prometheus::Registry;
17use rand::Rng;
18use soil_chain_spec::{resolve_state_version_from_wasm, BuildGenesisBlock};
19use soil_client::blockchain::{
20 self as blockchain, Backend as ChainBackend, CachedHeaderMetadata, Error,
21 HeaderBackend as ChainHeaderBackend, HeaderMetadata, Info as BlockchainInfo,
22};
23use soil_client::client_api::{
24 backend::{
25 self, apply_aux, BlockImportOperation, ClientImportOperation, FinalizeSummary, Finalizer,
26 ImportNotificationAction, ImportSummary, LockImportRun, NewBlockState, StorageProvider,
27 },
28 client::{
29 BadBlocks, BlockBackend, BlockImportNotification, BlockOf, BlockchainEvents, ClientInfo,
30 FinalityNotification, FinalityNotifications, ForkBlocks, ImportNotifications,
31 PreCommitActions, ProvideUncles,
32 },
33 execution_extensions::ExecutionExtensions,
34 notifications::{StorageEventStream, StorageNotifications},
35 CallExecutor, ExecutorProvider, KeysIter, OnFinalityAction, OnImportAction, PairsIter,
36 ProofProvider, StaleBlock, TrieCacheContext, UnpinWorkerMessage, UsageProvider,
37};
38use soil_client::consensus::{BlockOrigin, BlockStatus, Error as ConsensusError};
39use soil_client::executor::RuntimeVersion;
40use soil_client::import::{
41 BlockCheckParams, BlockImportParams, ForkChoiceStrategy, ImportResult, StateAction,
42};
43use soil_telemetry::{telemetry, TelemetryHandle, SUBSTRATE_INFO};
44use subsoil::api::{
45 ApiExt, ApiRef, CallApiAt, CallApiAtParams, ConstructRuntimeApi, Core as CoreApi,
46 ProvideRuntimeApi,
47};
48
49use soil_client::utils::mpsc::{tracing_unbounded, TracingUnboundedSender};
50use std::{
51 collections::{HashMap, HashSet},
52 marker::PhantomData,
53 path::PathBuf,
54 sync::Arc,
55};
56use subsoil::core::{
57 storage::{ChildInfo, ChildType, PrefixedStorageKey, StorageChild, StorageData, StorageKey},
58 traits::{CallContext, SpawnNamed},
59};
60use subsoil::runtime::{
61 generic::{BlockId, SignedBlock},
62 traits::{
63 Block as BlockT, BlockIdTo, HashingFor, Header as HeaderT, NumberFor, One,
64 SaturatedConversion, Zero,
65 },
66 Justification, Justifications, StateVersion,
67};
68use subsoil::state_machine::{
69 prove_child_read, prove_range_read_with_child_with_size, prove_read,
70 read_range_proof_check_with_child_on_proving_backend, Backend as StateBackend,
71 ChildStorageCollection, KeyValueStates, KeyValueStorageLevel, StorageCollection,
72 MAX_NESTED_TRIE_DEPTH,
73};
74use subsoil::trie::{proof_size_extension::ProofSizeExt, CompactProof, MerkleValue, StorageProof};
75
76use super::call_executor::LocalCallExecutor;
77use subsoil::core::traits::CodeExecutor;
78
79type NotificationSinks<T> = Mutex<Vec<TracingUnboundedSender<T>>>;
80
81pub struct Client<B, E, Block, RA>
83where
84 Block: BlockT,
85{
86 backend: Arc<B>,
87 executor: E,
88 storage_notifications: StorageNotifications<Block>,
89 import_notification_sinks: NotificationSinks<BlockImportNotification<Block>>,
90 every_import_notification_sinks: NotificationSinks<BlockImportNotification<Block>>,
91 finality_notification_sinks: NotificationSinks<FinalityNotification<Block>>,
92 import_actions: Mutex<Vec<OnImportAction<Block>>>,
95 finality_actions: Mutex<Vec<OnFinalityAction<Block>>>,
98 importing_block: RwLock<Option<Block::Hash>>,
100 block_rules: BlockRules<Block>,
101 config: ClientConfig<Block>,
102 telemetry: Option<TelemetryHandle>,
103 unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
104 code_provider: CodeProvider<Block, B, E>,
105 _phantom: PhantomData<RA>,
106}
107
108enum PrePostHeader<H> {
111 Same(H),
113 Different(H, H),
115}
116
117impl<H> PrePostHeader<H> {
118 fn post(&self) -> &H {
121 match *self {
122 PrePostHeader::Same(ref h) => h,
123 PrePostHeader::Different(_, ref h) => h,
124 }
125 }
126
127 fn into_post(self) -> H {
130 match self {
131 PrePostHeader::Same(h) => h,
132 PrePostHeader::Different(_, h) => h,
133 }
134 }
135}
136
137enum PrepareStorageChangesResult<Block: BlockT> {
138 Discard(ImportResult),
139 Import(Option<soil_client::import::StorageChanges<Block>>),
140}
141#[derive(Debug, Clone)]
143pub struct ClientConfig<Block: BlockT> {
144 pub offchain_worker_enabled: bool,
146 pub offchain_indexing_api: bool,
148 pub wasm_runtime_overrides: Option<PathBuf>,
150 pub no_genesis: bool,
152 pub wasm_runtime_substitutes: HashMap<NumberFor<Block>, Vec<u8>>,
155 pub enable_import_proof_recording: bool,
157}
158
159impl<Block: BlockT> Default for ClientConfig<Block> {
160 fn default() -> Self {
161 Self {
162 offchain_worker_enabled: false,
163 offchain_indexing_api: false,
164 wasm_runtime_overrides: None,
165 no_genesis: false,
166 wasm_runtime_substitutes: HashMap::new(),
167 enable_import_proof_recording: false,
168 }
169 }
170}
171
172pub fn new_with_backend<B, E, Block, G, RA>(
175 backend: Arc<B>,
176 executor: E,
177 genesis_block_builder: G,
178 spawn_handle: Box<dyn SpawnNamed>,
179 prometheus_registry: Option<Registry>,
180 telemetry: Option<TelemetryHandle>,
181 config: ClientConfig<Block>,
182) -> soil_client::blockchain::Result<Client<B, LocalCallExecutor<Block, B, E>, Block, RA>>
183where
184 E: CodeExecutor + soil_client::executor::RuntimeVersionOf,
185 G: BuildGenesisBlock<
186 Block,
187 BlockImportOperation = <B as backend::Backend<Block>>::BlockImportOperation,
188 >,
189 Block: BlockT,
190 B: backend::LocalBackend<Block> + 'static,
191{
192 let extensions = ExecutionExtensions::new(None, Arc::new(executor.clone()));
193
194 let call_executor =
195 LocalCallExecutor::new(backend.clone(), executor, config.clone(), extensions)?;
196
197 Client::new(
198 backend,
199 call_executor,
200 spawn_handle,
201 genesis_block_builder,
202 Default::default(),
203 Default::default(),
204 prometheus_registry,
205 telemetry,
206 config,
207 )
208}
209
210impl<B, E, Block, RA> BlockOf for Client<B, E, Block, RA>
211where
212 B: backend::Backend<Block>,
213 E: CallExecutor<Block>,
214 Block: BlockT,
215{
216 type Type = Block;
217}
218
219impl<B, E, Block, RA> LockImportRun<Block, B> for Client<B, E, Block, RA>
220where
221 B: backend::Backend<Block>,
222 E: CallExecutor<Block>,
223 Block: BlockT,
224{
225 fn lock_import_and_run<R, Err, F>(&self, f: F) -> Result<R, Err>
226 where
227 F: FnOnce(&mut ClientImportOperation<Block, B>) -> Result<R, Err>,
228 Err: From<soil_client::blockchain::Error>,
229 {
230 let inner = || {
231 let _import_lock = self.backend.get_import_lock().write();
232
233 let mut op = ClientImportOperation {
234 op: self.backend.begin_operation()?,
235 notify_imported: None,
236 notify_finalized: None,
237 };
238
239 let r = f(&mut op)?;
240
241 let ClientImportOperation { mut op, notify_imported, notify_finalized } = op;
242
243 let finality_notification = notify_finalized.map(|summary| {
244 FinalityNotification::from_summary(summary, self.unpin_worker_sender.clone())
245 });
246
247 let (import_notification, storage_changes, import_notification_action) =
248 match notify_imported {
249 Some(mut summary) => {
250 let import_notification_action = summary.import_notification_action;
251 let storage_changes = summary.storage_changes.take();
252 (
253 Some(BlockImportNotification::from_summary(
254 summary,
255 self.unpin_worker_sender.clone(),
256 )),
257 storage_changes,
258 import_notification_action,
259 )
260 },
261 None => (None, None, ImportNotificationAction::None),
262 };
263
264 if let Some(ref notification) = finality_notification {
265 for action in self.finality_actions.lock().iter_mut() {
266 op.insert_aux(action(notification))?;
267 }
268 }
269 if let Some(ref notification) = import_notification {
270 for action in self.import_actions.lock().iter_mut() {
271 op.insert_aux(action(notification))?;
272 }
273 }
274
275 self.backend.commit_operation(op)?;
276
277 if let Some(ref notification) = finality_notification {
281 if let Err(err) = self.backend.pin_block(notification.hash) {
282 debug!(
283 "Unable to pin block for finality notification. hash: {}, Error: {}",
284 notification.hash, err
285 );
286 } else {
287 let _ = self
288 .unpin_worker_sender
289 .unbounded_send(UnpinWorkerMessage::AnnouncePin(notification.hash))
290 .map_err(|e| {
291 log::error!(
292 "Unable to send AnnouncePin worker message for finality: {e}"
293 )
294 });
295 }
296 }
297
298 if let Some(ref notification) = import_notification {
299 if let Err(err) = self.backend.pin_block(notification.hash) {
300 debug!(
301 "Unable to pin block for import notification. hash: {}, Error: {}",
302 notification.hash, err
303 );
304 } else {
305 let _ = self
306 .unpin_worker_sender
307 .unbounded_send(UnpinWorkerMessage::AnnouncePin(notification.hash))
308 .map_err(|e| {
309 log::error!("Unable to send AnnouncePin worker message for import: {e}")
310 });
311 };
312 }
313
314 self.notify_finalized(finality_notification)?;
315 self.notify_imported(import_notification, import_notification_action, storage_changes)?;
316
317 Ok(r)
318 };
319
320 let result = inner();
321 *self.importing_block.write() = None;
322
323 result
324 }
325}
326
327impl<B, E, Block, RA> LockImportRun<Block, B> for &Client<B, E, Block, RA>
328where
329 Block: BlockT,
330 B: backend::Backend<Block>,
331 E: CallExecutor<Block>,
332{
333 fn lock_import_and_run<R, Err, F>(&self, f: F) -> Result<R, Err>
334 where
335 F: FnOnce(&mut ClientImportOperation<Block, B>) -> Result<R, Err>,
336 Err: From<soil_client::blockchain::Error>,
337 {
338 (**self).lock_import_and_run(f)
339 }
340}
341
342impl<B, E, Block, RA> Client<B, E, Block, RA>
343where
344 B: backend::Backend<Block>,
345 E: CallExecutor<Block>,
346 Block: BlockT,
347 Block::Header: Clone,
348{
349 pub fn new<G>(
351 backend: Arc<B>,
352 executor: E,
353 spawn_handle: Box<dyn SpawnNamed>,
354 genesis_block_builder: G,
355 fork_blocks: ForkBlocks<Block>,
356 bad_blocks: BadBlocks<Block>,
357 prometheus_registry: Option<Registry>,
358 telemetry: Option<TelemetryHandle>,
359 config: ClientConfig<Block>,
360 ) -> soil_client::blockchain::Result<Self>
361 where
362 G: BuildGenesisBlock<
363 Block,
364 BlockImportOperation = <B as backend::Backend<Block>>::BlockImportOperation,
365 >,
366 E: Clone,
367 B: 'static,
368 {
369 let info = backend.blockchain().info();
370 if info.finalized_state.is_none() {
371 let (genesis_block, mut op) = genesis_block_builder.build_genesis_block()?;
372 info!(
373 "🔨 Initializing Genesis block/state (state: {}, header-hash: {})",
374 genesis_block.header().state_root(),
375 genesis_block.header().hash()
376 );
377 let block_state = if info.best_hash == Default::default() {
380 NewBlockState::Final
381 } else {
382 NewBlockState::Normal
383 };
384 let (header, body) = genesis_block.deconstruct();
385 op.set_block_data(header, Some(body), None, None, block_state, true)?;
386 backend.commit_operation(op)?;
387 }
388
389 let (unpin_worker_sender, rx) = tracing_unbounded::<UnpinWorkerMessage<Block>>(
390 "notification-pinning-worker-channel",
391 10_000,
392 );
393 let unpin_worker = NotificationPinningWorker::new(rx, backend.clone());
394 spawn_handle.spawn("notification-pinning-worker", None, Box::pin(unpin_worker.run()));
395 let code_provider = CodeProvider::new(&config, executor.clone(), backend.clone())?;
396
397 Ok(Client {
398 backend,
399 executor,
400 storage_notifications: StorageNotifications::new(prometheus_registry),
401 import_notification_sinks: Default::default(),
402 every_import_notification_sinks: Default::default(),
403 finality_notification_sinks: Default::default(),
404 import_actions: Default::default(),
405 finality_actions: Default::default(),
406 importing_block: Default::default(),
407 block_rules: BlockRules::new(fork_blocks, bad_blocks),
408 config,
409 telemetry,
410 unpin_worker_sender,
411 code_provider,
412 _phantom: Default::default(),
413 })
414 }
415
416 pub fn import_notification_sinks(&self) -> &NotificationSinks<BlockImportNotification<Block>> {
419 &self.import_notification_sinks
420 }
421
422 pub fn finality_notification_sinks(&self) -> &NotificationSinks<FinalityNotification<Block>> {
425 &self.finality_notification_sinks
426 }
427
428 pub fn state_at(&self, hash: Block::Hash) -> soil_client::blockchain::Result<B::State> {
430 self.backend.state_at(hash, TrieCacheContext::Untrusted)
431 }
432
433 pub fn code_at(&self, hash: Block::Hash) -> soil_client::blockchain::Result<Vec<u8>> {
437 self.code_provider.code_at_ignoring_overrides(hash)
438 }
439
440 pub fn runtime_version_at(
442 &self,
443 hash: Block::Hash,
444 ) -> soil_client::blockchain::Result<RuntimeVersion> {
445 CallExecutor::runtime_version(&self.executor, hash)
446 }
447
448 fn apply_block(
450 &self,
451 operation: &mut ClientImportOperation<Block, B>,
452 import_block: BlockImportParams<Block>,
453 storage_changes: Option<soil_client::import::StorageChanges<Block>>,
454 ) -> soil_client::blockchain::Result<ImportResult>
455 where
456 Self: ProvideRuntimeApi<Block>,
457 <Self as ProvideRuntimeApi<Block>>::Api: CoreApi<Block> + ApiExt<Block>,
458 {
459 let BlockImportParams {
460 origin,
461 header,
462 justifications,
463 post_digests,
464 body,
465 indexed_body,
466 finalized,
467 auxiliary,
468 fork_choice,
469 intermediates,
470 import_existing,
471 create_gap,
472 ..
473 } = import_block;
474
475 if !intermediates.is_empty() {
476 return Err(Error::IncompletePipeline);
477 }
478
479 let fork_choice = fork_choice.ok_or(Error::IncompletePipeline)?;
480
481 let import_headers = if post_digests.is_empty() {
482 PrePostHeader::Same(header)
483 } else {
484 let mut post_header = header.clone();
485 for item in post_digests {
486 post_header.digest_mut().push(item);
487 }
488 PrePostHeader::Different(header, post_header)
489 };
490
491 let hash = import_headers.post().hash();
492 let height = (*import_headers.post().number()).saturated_into::<u64>();
493
494 *self.importing_block.write() = Some(hash);
495
496 operation.op.set_create_gap(create_gap);
497
498 let result = self.execute_and_import_block(
499 operation,
500 origin,
501 hash,
502 import_headers,
503 justifications,
504 body,
505 indexed_body,
506 storage_changes,
507 finalized,
508 auxiliary,
509 fork_choice,
510 import_existing,
511 );
512
513 if let Ok(ImportResult::Imported(ref aux)) = result {
514 if aux.is_new_best {
515 if origin != BlockOrigin::NetworkInitialSync || rand::thread_rng().gen_bool(0.1) {
519 telemetry!(
520 self.telemetry;
521 SUBSTRATE_INFO;
522 "block.import";
523 "height" => height,
524 "best" => ?hash,
525 "origin" => ?origin
526 );
527 }
528 }
529 }
530
531 result
532 }
533
534 fn execute_and_import_block(
535 &self,
536 operation: &mut ClientImportOperation<Block, B>,
537 origin: BlockOrigin,
538 hash: Block::Hash,
539 import_headers: PrePostHeader<Block::Header>,
540 justifications: Option<Justifications>,
541 body: Option<Vec<Block::Extrinsic>>,
542 indexed_body: Option<Vec<Vec<u8>>>,
543 storage_changes: Option<soil_client::import::StorageChanges<Block>>,
544 finalized: bool,
545 aux: Vec<(Vec<u8>, Option<Vec<u8>>)>,
546 fork_choice: ForkChoiceStrategy,
547 import_existing: bool,
548 ) -> soil_client::blockchain::Result<ImportResult>
549 where
550 Self: ProvideRuntimeApi<Block>,
551 <Self as ProvideRuntimeApi<Block>>::Api: CoreApi<Block> + ApiExt<Block>,
552 {
553 let parent_hash = *import_headers.post().parent_hash();
554 let status = self.backend.blockchain().status(hash)?;
555 let parent_exists =
556 self.backend.blockchain().status(parent_hash)? == blockchain::BlockStatus::InChain;
557
558 match (import_existing, status) {
559 (false, blockchain::BlockStatus::InChain) => return Ok(ImportResult::AlreadyInChain),
560 (false, blockchain::BlockStatus::Unknown) => {},
561 (true, blockchain::BlockStatus::InChain) => {},
562 (true, blockchain::BlockStatus::Unknown) => {},
563 }
564
565 let info = self.backend.blockchain().info();
566 let gap_block =
567 info.block_gap.map_or(false, |gap| *import_headers.post().number() == gap.start);
568
569 if status == blockchain::BlockStatus::Unknown
572 && *import_headers.post().number() <= info.finalized_number
573 && !gap_block
574 {
575 return Err(soil_client::blockchain::Error::NotInFinalizedChain);
576 }
577
578 let make_notifications = match origin {
582 BlockOrigin::NetworkBroadcast | BlockOrigin::Own | BlockOrigin::ConsensusBroadcast => {
583 true
584 },
585 BlockOrigin::Genesis
586 | BlockOrigin::NetworkInitialSync
587 | BlockOrigin::File
588 | BlockOrigin::WarpSync
589 | BlockOrigin::GapSync => false,
590 };
591
592 let storage_changes = match storage_changes {
593 Some(storage_changes) => {
594 let storage_changes = match storage_changes {
595 soil_client::import::StorageChanges::Changes(storage_changes) => {
596 self.backend.begin_state_operation(&mut operation.op, parent_hash)?;
597 let (main_sc, child_sc, offchain_sc, tx, _, tx_index) =
598 storage_changes.into_inner();
599
600 if self.config.offchain_indexing_api {
601 operation.op.update_offchain_storage(offchain_sc)?;
602 }
603
604 operation.op.update_db_storage(tx)?;
605 operation.op.update_storage(main_sc.clone(), child_sc.clone())?;
606 operation.op.update_transaction_index(tx_index)?;
607
608 Some((main_sc, child_sc))
609 },
610 soil_client::import::StorageChanges::Import(changes) => {
611 let mut storage = subsoil::storage::Storage::default();
612 for state in changes.state.0.into_iter() {
613 if state.parent_storage_keys.is_empty() && state.state_root.is_empty() {
614 for (key, value) in state.key_values.into_iter() {
615 storage.top.insert(key, value);
616 }
617 } else {
618 for parent_storage in state.parent_storage_keys {
619 let storage_key = PrefixedStorageKey::new_ref(&parent_storage);
620 let storage_key =
621 match ChildType::from_prefixed_key(storage_key) {
622 Some((ChildType::ParentKeyId, storage_key)) => {
623 storage_key
624 },
625 None => {
626 return Err(Error::Backend(
627 "Invalid child storage key.".to_string(),
628 ))
629 },
630 };
631 let entry = storage
632 .children_default
633 .entry(storage_key.to_vec())
634 .or_insert_with(|| StorageChild {
635 data: Default::default(),
636 child_info: ChildInfo::new_default(storage_key),
637 });
638 for (key, value) in state.key_values.iter() {
639 entry.data.insert(key.clone(), value.clone());
640 }
641 }
642 }
643 }
644
645 let state_version = resolve_state_version_from_wasm::<_, HashingFor<Block>>(
648 &storage,
649 &self.executor,
650 )?;
651 let state_root = operation.op.reset_storage(storage, state_version)?;
652 if state_root != *import_headers.post().state_root() {
653 warn!("Error importing state: State root mismatch.");
656 return Err(Error::InvalidStateRoot);
657 }
658 None
659 },
660 };
661
662 storage_changes
663 },
664 None => None,
665 };
666
667 if finalized && parent_exists && info.finalized_hash != parent_hash {
670 self.apply_finality_with_block_hash(
671 operation,
672 parent_hash,
673 None,
674 &info,
675 make_notifications,
676 )?;
677 }
678
679 let is_new_best = !gap_block
680 && (finalized
681 || match fork_choice {
682 ForkChoiceStrategy::LongestChain => {
683 import_headers.post().number() > &info.best_number
684 },
685 ForkChoiceStrategy::Custom(v) => v,
686 });
687
688 let leaf_state = if finalized {
689 NewBlockState::Final
690 } else if is_new_best {
691 NewBlockState::Best
692 } else {
693 NewBlockState::Normal
694 };
695
696 let register_as_leaf = origin != BlockOrigin::WarpSync;
699
700 let tree_route = if is_new_best && info.best_hash != parent_hash && parent_exists {
701 let route_from_best = soil_client::blockchain::tree_route(
702 self.backend.blockchain(),
703 info.best_hash,
704 parent_hash,
705 )?;
706 Some(route_from_best)
707 } else {
708 None
709 };
710
711 trace!(
712 "Imported {}, (#{}), best={}, origin={:?}",
713 hash,
714 import_headers.post().number(),
715 is_new_best,
716 origin,
717 );
718
719 operation.op.set_block_data(
720 import_headers.post().clone(),
721 body,
722 indexed_body,
723 justifications,
724 leaf_state,
725 register_as_leaf,
726 )?;
727
728 operation.op.insert_aux(aux)?;
729
730 let should_notify_every_block = !self.every_import_notification_sinks.lock().is_empty();
731
732 let should_notify_recent_block = make_notifications || tree_route.is_some();
735
736 if should_notify_every_block || should_notify_recent_block {
737 let header = import_headers.into_post();
738 if finalized && should_notify_recent_block {
739 let mut summary = match operation.notify_finalized.take() {
740 Some(mut summary) => {
741 summary.header = header.clone();
742 summary.finalized.push(hash);
743 summary
744 },
745 None => FinalizeSummary {
746 header: header.clone(),
747 finalized: vec![hash],
748 stale_blocks: Vec::new(),
749 },
750 };
751
752 if parent_exists {
753 let stale_heads = self.backend.blockchain().displaced_leaves_after_finalizing(
755 hash,
756 *header.number(),
757 parent_hash,
758 )?;
759
760 summary.stale_blocks.extend(stale_heads.displaced_blocks.into_iter().map(
761 |b| StaleBlock {
762 hash: b,
763 is_head: stale_heads.displaced_leaves.iter().any(|(_, h)| *h == b),
764 },
765 ));
766 }
767 operation.notify_finalized = Some(summary);
768 }
769
770 let import_notification_action = if should_notify_every_block {
771 if should_notify_recent_block {
772 ImportNotificationAction::Both
773 } else {
774 ImportNotificationAction::EveryBlock
775 }
776 } else {
777 ImportNotificationAction::RecentBlock
778 };
779
780 operation.notify_imported = Some(ImportSummary {
781 hash,
782 origin,
783 header,
784 is_new_best,
785 storage_changes,
786 tree_route,
787 import_notification_action,
788 })
789 }
790
791 Ok(ImportResult::imported(is_new_best))
792 }
793
794 fn prepare_block_storage_changes(
800 &self,
801 import_block: &mut BlockImportParams<Block>,
802 ) -> soil_client::blockchain::Result<PrepareStorageChangesResult<Block>>
803 where
804 Self: ProvideRuntimeApi<Block>,
805 <Self as ProvideRuntimeApi<Block>>::Api: CoreApi<Block> + ApiExt<Block>,
806 {
807 let parent_hash = import_block.header.parent_hash();
808 let state_action = std::mem::replace(&mut import_block.state_action, StateAction::Skip);
809 let (enact_state, storage_changes) = match (self.block_status(*parent_hash)?, state_action)
810 {
811 (BlockStatus::KnownBad, _) => {
812 return Ok(PrepareStorageChangesResult::Discard(ImportResult::KnownBad))
813 },
814 (
815 BlockStatus::InChainPruned,
816 StateAction::ApplyChanges(soil_client::import::StorageChanges::Changes(_)),
817 ) => return Ok(PrepareStorageChangesResult::Discard(ImportResult::MissingState)),
818 (_, StateAction::ApplyChanges(changes)) => (true, Some(changes)),
819 (_, StateAction::Skip) => (false, None),
820 (BlockStatus::Unknown, _) => {
821 return Ok(PrepareStorageChangesResult::Discard(ImportResult::UnknownParent))
822 },
823 (BlockStatus::InChainPruned, StateAction::Execute) => {
824 return Ok(PrepareStorageChangesResult::Discard(ImportResult::MissingState))
825 },
826 (BlockStatus::InChainPruned, StateAction::ExecuteIfPossible) => (false, None),
827 (_, StateAction::Execute) => (true, None),
828 (_, StateAction::ExecuteIfPossible) => (true, None),
829 };
830
831 let storage_changes = match (enact_state, storage_changes, &import_block.body) {
832 (true, changes @ Some(_), _) => changes,
835 (true, None, Some(ref body)) => {
838 let mut runtime_api = self.runtime_api();
839 let call_context = CallContext::Onchain;
840 runtime_api.set_call_context(call_context);
841
842 if self.config.enable_import_proof_recording {
843 runtime_api.record_proof();
844 let recorder = runtime_api
845 .proof_recorder()
846 .expect("Proof recording is enabled in the line above; qed.");
847 runtime_api.register_extension(ProofSizeExt::new(recorder));
848 }
849
850 runtime_api.execute_block(
851 *parent_hash,
852 Block::new(import_block.header.clone(), body.clone()).into(),
853 )?;
854
855 let state = self.backend.state_at(*parent_hash, call_context.into())?;
856 let gen_storage_changes = runtime_api
857 .into_storage_changes(&state, *parent_hash)
858 .map_err(soil_client::blockchain::Error::Storage)?;
859
860 if import_block.header.state_root() != &gen_storage_changes.transaction_storage_root
861 {
862 return Err(Error::InvalidStateRoot);
863 }
864 Some(soil_client::import::StorageChanges::Changes(gen_storage_changes))
865 },
866 (true, None, None) => None,
868 (false, _, _) => None,
870 };
871
872 Ok(PrepareStorageChangesResult::Import(storage_changes))
873 }
874
875 fn apply_finality_with_block_hash(
876 &self,
877 operation: &mut ClientImportOperation<Block, B>,
878 hash: Block::Hash,
879 justification: Option<Justification>,
880 info: &BlockchainInfo<Block>,
881 notify: bool,
882 ) -> soil_client::blockchain::Result<()> {
883 if hash == info.finalized_hash {
884 warn!(
885 "Possible safety violation: attempted to re-finalize last finalized block {:?} ",
886 hash,
887 );
888 return Ok(());
889 }
890
891 let route_from_finalized = soil_client::blockchain::tree_route(
893 self.backend.blockchain(),
894 info.finalized_hash,
895 hash,
896 )?;
897
898 if let Some(retracted) = route_from_finalized.retracted().get(0) {
899 warn!(
900 "Safety violation: attempted to revert finalized block {:?} which is not in the \
901 same chain as last finalized {:?}",
902 retracted, info.finalized_hash
903 );
904
905 return Err(soil_client::blockchain::Error::NotInFinalizedChain);
906 }
907
908 let block_number = self
913 .backend
914 .blockchain()
915 .number(hash)?
916 .ok_or(Error::MissingHeader(format!("{hash:?}")))?;
917 if self.backend.blockchain().leaves()?.len() > 1 || info.best_number < block_number {
918 let route_from_best = soil_client::blockchain::tree_route(
919 self.backend.blockchain(),
920 info.best_hash,
921 hash,
922 )?;
923
924 if route_from_best.common_block().hash != hash {
927 operation.op.mark_head(hash)?;
934 }
935 }
936
937 let enacted = route_from_finalized.enacted();
938 assert!(enacted.len() > 0);
939 for finalize_new in &enacted[..enacted.len() - 1] {
940 operation.op.mark_finalized(finalize_new.hash, None)?;
941 }
942
943 assert_eq!(enacted.last().map(|e| e.hash), Some(hash));
944 operation.op.mark_finalized(hash, justification)?;
945
946 if notify {
947 let finalized =
948 route_from_finalized.enacted().iter().map(|elem| elem.hash).collect::<Vec<_>>();
949
950 let header = self
951 .backend
952 .blockchain()
953 .header(hash)?
954 .expect("Block to finalize expected to be onchain; qed");
955 let block_number = *header.number();
956
957 let mut stale_blocks = Vec::new();
959
960 let stale_heads = self.backend.blockchain().displaced_leaves_after_finalizing(
961 hash,
962 block_number,
963 *header.parent_hash(),
964 )?;
965
966 stale_blocks.extend(stale_heads.displaced_blocks.into_iter().map(|b| StaleBlock {
967 hash: b,
968 is_head: stale_heads.displaced_leaves.iter().any(|(_, h)| *h == b),
969 }));
970
971 operation.notify_finalized = Some(FinalizeSummary { header, finalized, stale_blocks });
972 }
973
974 Ok(())
975 }
976
977 fn notify_finalized(
978 &self,
979 notification: Option<FinalityNotification<Block>>,
980 ) -> soil_client::blockchain::Result<()> {
981 let mut sinks = self.finality_notification_sinks.lock();
982
983 let notification = match notification {
984 Some(notify_finalized) => notify_finalized,
985 None => {
986 sinks.retain(|sink| !sink.is_closed());
990 return Ok(());
991 },
992 };
993
994 telemetry!(
995 self.telemetry;
996 SUBSTRATE_INFO;
997 "notify.finalized";
998 "height" => format!("{}", notification.header.number()),
999 "best" => ?notification.hash,
1000 );
1001
1002 sinks.retain(|sink| sink.unbounded_send(notification.clone()).is_ok());
1003
1004 Ok(())
1005 }
1006
1007 fn notify_imported(
1008 &self,
1009 notification: Option<BlockImportNotification<Block>>,
1010 import_notification_action: ImportNotificationAction,
1011 storage_changes: Option<(StorageCollection, ChildStorageCollection)>,
1012 ) -> soil_client::blockchain::Result<()> {
1013 let notification = match notification {
1014 Some(notify_import) => notify_import,
1015 None => {
1016 self.import_notification_sinks.lock().retain(|sink| !sink.is_closed());
1023
1024 self.every_import_notification_sinks.lock().retain(|sink| !sink.is_closed());
1025
1026 return Ok(());
1027 },
1028 };
1029
1030 let trigger_storage_changes_notification = || {
1031 if let Some(storage_changes) = storage_changes {
1032 self.storage_notifications.trigger(
1034 ¬ification.hash,
1035 storage_changes.0.into_iter(),
1036 storage_changes.1.into_iter().map(|(sk, v)| (sk, v.into_iter())),
1037 );
1038 }
1039 };
1040
1041 match import_notification_action {
1042 ImportNotificationAction::Both => {
1043 trigger_storage_changes_notification();
1044 self.import_notification_sinks
1045 .lock()
1046 .retain(|sink| sink.unbounded_send(notification.clone()).is_ok());
1047
1048 self.every_import_notification_sinks
1049 .lock()
1050 .retain(|sink| sink.unbounded_send(notification.clone()).is_ok());
1051 },
1052 ImportNotificationAction::RecentBlock => {
1053 trigger_storage_changes_notification();
1054 self.import_notification_sinks
1055 .lock()
1056 .retain(|sink| sink.unbounded_send(notification.clone()).is_ok());
1057
1058 self.every_import_notification_sinks.lock().retain(|sink| !sink.is_closed());
1059 },
1060 ImportNotificationAction::EveryBlock => {
1061 self.every_import_notification_sinks
1062 .lock()
1063 .retain(|sink| sink.unbounded_send(notification.clone()).is_ok());
1064
1065 self.import_notification_sinks.lock().retain(|sink| !sink.is_closed());
1066 },
1067 ImportNotificationAction::None => {
1068 self.import_notification_sinks.lock().retain(|sink| !sink.is_closed());
1072
1073 self.every_import_notification_sinks.lock().retain(|sink| !sink.is_closed());
1074 },
1075 }
1076
1077 Ok(())
1078 }
1079
1080 pub fn revert(&self, n: NumberFor<Block>) -> soil_client::blockchain::Result<NumberFor<Block>> {
1084 let (number, _) = self.backend.revert(n, false)?;
1085 Ok(number)
1086 }
1087
1088 pub fn unsafe_revert(
1098 &mut self,
1099 n: NumberFor<Block>,
1100 blacklist: bool,
1101 ) -> soil_client::blockchain::Result<NumberFor<Block>> {
1102 let (number, reverted) = self.backend.revert(n, true)?;
1103 if blacklist {
1104 for b in reverted {
1105 self.block_rules.mark_bad(b);
1106 }
1107 }
1108 Ok(number)
1109 }
1110
1111 pub fn chain_info(&self) -> BlockchainInfo<Block> {
1113 self.backend.blockchain().info()
1114 }
1115
1116 pub fn block_status(&self, hash: Block::Hash) -> soil_client::blockchain::Result<BlockStatus> {
1118 if self
1120 .importing_block
1121 .read()
1122 .as_ref()
1123 .map_or(false, |importing| &hash == importing)
1124 {
1125 return Ok(BlockStatus::Queued);
1126 }
1127
1128 let hash_and_number = self.backend.blockchain().number(hash)?.map(|n| (hash, n));
1129 match hash_and_number {
1130 Some((hash, number)) => {
1131 if self.backend.have_state_at(hash, number) {
1132 Ok(BlockStatus::InChainWithState)
1133 } else {
1134 Ok(BlockStatus::InChainPruned)
1135 }
1136 },
1137 None => Ok(BlockStatus::Unknown),
1138 }
1139 }
1140
1141 pub fn header(
1143 &self,
1144 hash: Block::Hash,
1145 ) -> soil_client::blockchain::Result<Option<<Block as BlockT>::Header>> {
1146 self.backend.blockchain().header(hash)
1147 }
1148
1149 pub fn body(
1151 &self,
1152 hash: Block::Hash,
1153 ) -> soil_client::blockchain::Result<Option<Vec<<Block as BlockT>::Extrinsic>>> {
1154 self.backend.blockchain().body(hash)
1155 }
1156
1157 pub fn uncles(
1159 &self,
1160 target_hash: Block::Hash,
1161 max_generation: NumberFor<Block>,
1162 ) -> soil_client::blockchain::Result<Vec<Block::Hash>> {
1163 let load_header = |hash: Block::Hash| -> soil_client::blockchain::Result<Block::Header> {
1164 self.backend
1165 .blockchain()
1166 .header(hash)?
1167 .ok_or_else(|| Error::UnknownBlock(format!("{:?}", hash)))
1168 };
1169
1170 let genesis_hash = self.backend.blockchain().info().genesis_hash;
1171 if genesis_hash == target_hash {
1172 return Ok(Vec::new());
1173 }
1174
1175 let mut current_hash = target_hash;
1176 let mut current = load_header(current_hash)?;
1177 let mut ancestor_hash = *current.parent_hash();
1178 let mut ancestor = load_header(ancestor_hash)?;
1179 let mut uncles = Vec::new();
1180
1181 let mut generation: NumberFor<Block> = Zero::zero();
1182 while generation < max_generation {
1183 let children = self.backend.blockchain().children(ancestor_hash)?;
1184 uncles.extend(children.into_iter().filter(|h| h != ¤t_hash));
1185 current_hash = ancestor_hash;
1186
1187 if genesis_hash == current_hash {
1188 break;
1189 }
1190
1191 current = ancestor;
1192 ancestor_hash = *current.parent_hash();
1193 ancestor = load_header(ancestor_hash)?;
1194 generation += One::one();
1195 }
1196 trace!("Collected {} uncles", uncles.len());
1197 Ok(uncles)
1198 }
1199}
1200
1201impl<B, E, Block, RA> UsageProvider<Block> for Client<B, E, Block, RA>
1202where
1203 B: backend::Backend<Block>,
1204 E: CallExecutor<Block>,
1205 Block: BlockT,
1206{
1207 fn usage_info(&self) -> ClientInfo<Block> {
1209 ClientInfo { chain: self.chain_info(), usage: self.backend.usage_info() }
1210 }
1211}
1212
1213impl<B, E, Block, RA> ProofProvider<Block> for Client<B, E, Block, RA>
1214where
1215 B: backend::Backend<Block>,
1216 E: CallExecutor<Block>,
1217 Block: BlockT,
1218{
1219 fn read_proof(
1220 &self,
1221 hash: Block::Hash,
1222 keys: &mut dyn Iterator<Item = &[u8]>,
1223 ) -> soil_client::blockchain::Result<StorageProof> {
1224 self.state_at(hash)
1225 .and_then(|state| prove_read(state, keys).map_err(Into::into))
1226 }
1227
1228 fn read_child_proof(
1229 &self,
1230 hash: Block::Hash,
1231 child_info: &ChildInfo,
1232 keys: &mut dyn Iterator<Item = &[u8]>,
1233 ) -> soil_client::blockchain::Result<StorageProof> {
1234 self.state_at(hash)
1235 .and_then(|state| prove_child_read(state, child_info, keys).map_err(Into::into))
1236 }
1237
1238 fn execution_proof(
1239 &self,
1240 hash: Block::Hash,
1241 method: &str,
1242 call_data: &[u8],
1243 ) -> soil_client::blockchain::Result<(Vec<u8>, StorageProof)> {
1244 self.executor.prove_execution(hash, method, call_data)
1245 }
1246
1247 fn read_proof_collection(
1248 &self,
1249 hash: Block::Hash,
1250 start_key: &[Vec<u8>],
1251 size_limit: usize,
1252 ) -> soil_client::blockchain::Result<(CompactProof, u32)> {
1253 let state = self.state_at(hash)?;
1254 let root = state.storage_root(std::iter::empty(), StateVersion::V0).0;
1256
1257 let (proof, count) = prove_range_read_with_child_with_size::<_, HashingFor<Block>>(
1258 state, size_limit, start_key,
1259 )?;
1260 let proof = proof
1261 .into_compact_proof::<HashingFor<Block>>(root)
1262 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))?;
1263 Ok((proof, count))
1264 }
1265
1266 fn storage_collection(
1267 &self,
1268 hash: Block::Hash,
1269 start_key: &[Vec<u8>],
1270 size_limit: usize,
1271 ) -> soil_client::blockchain::Result<Vec<(KeyValueStorageLevel, bool)>> {
1272 if start_key.len() > MAX_NESTED_TRIE_DEPTH {
1273 return Err(Error::Backend("Invalid start key.".to_string()));
1274 }
1275 let state = self.state_at(hash)?;
1276 let child_info = |storage_key: &Vec<u8>| -> soil_client::blockchain::Result<ChildInfo> {
1277 let storage_key = PrefixedStorageKey::new_ref(storage_key);
1278 match ChildType::from_prefixed_key(storage_key) {
1279 Some((ChildType::ParentKeyId, storage_key)) => {
1280 Ok(ChildInfo::new_default(storage_key))
1281 },
1282 None => Err(Error::Backend("Invalid child storage key.".to_string())),
1283 }
1284 };
1285 let mut current_child = if start_key.len() == 2 {
1286 let start_key = start_key.get(0).expect("checked len");
1287 if let Some(child_root) = state
1288 .storage(start_key)
1289 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))?
1290 {
1291 Some((child_info(start_key)?, child_root))
1292 } else {
1293 return Err(Error::Backend("Invalid root start key.".to_string()));
1294 }
1295 } else {
1296 None
1297 };
1298 let mut current_key = start_key.last().map(Clone::clone).unwrap_or_default();
1299 let mut total_size = 0;
1300 let mut result = vec![(
1301 KeyValueStorageLevel {
1302 state_root: Vec::new(),
1303 key_values: Vec::new(),
1304 parent_storage_keys: Vec::new(),
1305 },
1306 false,
1307 )];
1308
1309 let mut child_roots = HashSet::new();
1310 loop {
1311 let mut entries = Vec::new();
1312 let mut complete = true;
1313 let mut switch_child_key = None;
1314 while let Some(next_key) = if let Some(child) = current_child.as_ref() {
1315 state
1316 .next_child_storage_key(&child.0, ¤t_key)
1317 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))?
1318 } else {
1319 state
1320 .next_storage_key(¤t_key)
1321 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))?
1322 } {
1323 let value = if let Some(child) = current_child.as_ref() {
1324 state
1325 .child_storage(&child.0, next_key.as_ref())
1326 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))?
1327 .unwrap_or_default()
1328 } else {
1329 state
1330 .storage(next_key.as_ref())
1331 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))?
1332 .unwrap_or_default()
1333 };
1334 let size = value.len() + next_key.len();
1335 if total_size + size > size_limit && !entries.is_empty() {
1336 complete = false;
1337 break;
1338 }
1339 total_size += size;
1340
1341 if current_child.is_none()
1342 && subsoil::core::storage::well_known_keys::is_child_storage_key(
1343 next_key.as_slice(),
1344 ) && !child_roots.contains(value.as_slice())
1345 {
1346 child_roots.insert(value.clone());
1347 switch_child_key = Some((next_key.clone(), value.clone()));
1348 entries.push((next_key.clone(), value));
1349 break;
1350 }
1351 entries.push((next_key.clone(), value));
1352 current_key = next_key;
1353 }
1354 if let Some((child, child_root)) = switch_child_key.take() {
1355 result[0].0.key_values.extend(entries.into_iter());
1356 current_child = Some((child_info(&child)?, child_root));
1357 current_key = Vec::new();
1358 } else if let Some((child, child_root)) = current_child.take() {
1359 current_key = child.into_prefixed_storage_key().into_inner();
1360 result.push((
1361 KeyValueStorageLevel {
1362 state_root: child_root,
1363 key_values: entries,
1364 parent_storage_keys: Vec::new(),
1365 },
1366 complete,
1367 ));
1368 if !complete {
1369 break;
1370 }
1371 } else {
1372 result[0].0.key_values.extend(entries.into_iter());
1373 result[0].1 = complete;
1374 break;
1375 }
1376 }
1377 Ok(result)
1378 }
1379
1380 fn verify_range_proof(
1381 &self,
1382 root: Block::Hash,
1383 proof: CompactProof,
1384 start_key: &[Vec<u8>],
1385 ) -> soil_client::blockchain::Result<(KeyValueStates, usize)> {
1386 let mut db = subsoil::state_machine::MemoryDB::<HashingFor<Block>>::new(&[]);
1387 subsoil::trie::decode_compact::<subsoil::state_machine::LayoutV0<HashingFor<Block>>, _, _>(
1389 &mut db,
1390 proof.iter_compact_encoded_nodes(),
1391 Some(&root),
1392 )
1393 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))?;
1394 let proving_backend = subsoil::state_machine::TrieBackendBuilder::new(db, root).build();
1395 let state = read_range_proof_check_with_child_on_proving_backend::<HashingFor<Block>>(
1396 &proving_backend,
1397 start_key,
1398 )?;
1399
1400 Ok(state)
1401 }
1402}
1403
1404impl<B, E, Block, RA> ExecutorProvider<Block> for Client<B, E, Block, RA>
1405where
1406 B: backend::Backend<Block>,
1407 E: CallExecutor<Block>,
1408 Block: BlockT,
1409{
1410 type Executor = E;
1411
1412 fn executor(&self) -> &Self::Executor {
1413 &self.executor
1414 }
1415
1416 fn execution_extensions(&self) -> &ExecutionExtensions<Block> {
1417 self.executor.execution_extensions()
1418 }
1419}
1420
1421impl<B, E, Block, RA> StorageProvider<Block, B> for Client<B, E, Block, RA>
1422where
1423 B: backend::Backend<Block>,
1424 E: CallExecutor<Block>,
1425 Block: BlockT,
1426{
1427 fn storage_keys(
1428 &self,
1429 hash: <Block as BlockT>::Hash,
1430 prefix: Option<&StorageKey>,
1431 start_key: Option<&StorageKey>,
1432 ) -> soil_client::blockchain::Result<KeysIter<B::State, Block>> {
1433 let state = self.state_at(hash)?;
1434 KeysIter::new(state, prefix, start_key)
1435 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))
1436 }
1437
1438 fn child_storage_keys(
1439 &self,
1440 hash: <Block as BlockT>::Hash,
1441 child_info: ChildInfo,
1442 prefix: Option<&StorageKey>,
1443 start_key: Option<&StorageKey>,
1444 ) -> soil_client::blockchain::Result<KeysIter<B::State, Block>> {
1445 let state = self.state_at(hash)?;
1446 KeysIter::new_child(state, child_info, prefix, start_key)
1447 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))
1448 }
1449
1450 fn storage_pairs(
1451 &self,
1452 hash: <Block as BlockT>::Hash,
1453 prefix: Option<&StorageKey>,
1454 start_key: Option<&StorageKey>,
1455 ) -> soil_client::blockchain::Result<PairsIter<B::State, Block>> {
1456 let state = self.state_at(hash)?;
1457 PairsIter::new(state, prefix, start_key)
1458 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))
1459 }
1460
1461 fn storage(
1462 &self,
1463 hash: Block::Hash,
1464 key: &StorageKey,
1465 ) -> soil_client::blockchain::Result<Option<StorageData>> {
1466 Ok(self
1467 .state_at(hash)?
1468 .storage(&key.0)
1469 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))?
1470 .map(StorageData))
1471 }
1472
1473 fn storage_hash(
1474 &self,
1475 hash: <Block as BlockT>::Hash,
1476 key: &StorageKey,
1477 ) -> soil_client::blockchain::Result<Option<Block::Hash>> {
1478 self.state_at(hash)?
1479 .storage_hash(&key.0)
1480 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))
1481 }
1482
1483 fn child_storage(
1484 &self,
1485 hash: <Block as BlockT>::Hash,
1486 child_info: &ChildInfo,
1487 key: &StorageKey,
1488 ) -> soil_client::blockchain::Result<Option<StorageData>> {
1489 Ok(self
1490 .state_at(hash)?
1491 .child_storage(child_info, &key.0)
1492 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))?
1493 .map(StorageData))
1494 }
1495
1496 fn child_storage_hash(
1497 &self,
1498 hash: <Block as BlockT>::Hash,
1499 child_info: &ChildInfo,
1500 key: &StorageKey,
1501 ) -> soil_client::blockchain::Result<Option<Block::Hash>> {
1502 self.state_at(hash)?
1503 .child_storage_hash(child_info, &key.0)
1504 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))
1505 }
1506
1507 fn closest_merkle_value(
1508 &self,
1509 hash: <Block as BlockT>::Hash,
1510 key: &StorageKey,
1511 ) -> blockchain::Result<Option<MerkleValue<<Block as BlockT>::Hash>>> {
1512 self.state_at(hash)?
1513 .closest_merkle_value(&key.0)
1514 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))
1515 }
1516
1517 fn child_closest_merkle_value(
1518 &self,
1519 hash: <Block as BlockT>::Hash,
1520 child_info: &ChildInfo,
1521 key: &StorageKey,
1522 ) -> blockchain::Result<Option<MerkleValue<<Block as BlockT>::Hash>>> {
1523 self.state_at(hash)?
1524 .child_closest_merkle_value(child_info, &key.0)
1525 .map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))
1526 }
1527}
1528
1529impl<B, E, Block, RA> HeaderMetadata<Block> for Client<B, E, Block, RA>
1530where
1531 B: backend::Backend<Block>,
1532 E: CallExecutor<Block>,
1533 Block: BlockT,
1534{
1535 type Error = soil_client::blockchain::Error;
1536
1537 fn header_metadata(
1538 &self,
1539 hash: Block::Hash,
1540 ) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
1541 self.backend.blockchain().header_metadata(hash)
1542 }
1543
1544 fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
1545 self.backend.blockchain().insert_header_metadata(hash, metadata)
1546 }
1547
1548 fn remove_header_metadata(&self, hash: Block::Hash) {
1549 self.backend.blockchain().remove_header_metadata(hash)
1550 }
1551}
1552
1553impl<B, E, Block, RA> ProvideUncles<Block> for Client<B, E, Block, RA>
1554where
1555 B: backend::Backend<Block>,
1556 E: CallExecutor<Block>,
1557 Block: BlockT,
1558{
1559 fn uncles(
1560 &self,
1561 target_hash: Block::Hash,
1562 max_generation: NumberFor<Block>,
1563 ) -> soil_client::blockchain::Result<Vec<Block::Header>> {
1564 Ok(Client::uncles(self, target_hash, max_generation)?
1565 .into_iter()
1566 .filter_map(|hash| Client::header(self, hash).unwrap_or(None))
1567 .collect())
1568 }
1569}
1570
1571impl<B, E, Block, RA> ChainHeaderBackend<Block> for Client<B, E, Block, RA>
1572where
1573 B: backend::Backend<Block>,
1574 E: CallExecutor<Block> + Send + Sync,
1575 Block: BlockT,
1576 RA: Send + Sync,
1577{
1578 fn header(&self, hash: Block::Hash) -> soil_client::blockchain::Result<Option<Block::Header>> {
1579 self.backend.blockchain().header(hash)
1580 }
1581
1582 fn info(&self) -> blockchain::Info<Block> {
1583 self.backend.blockchain().info()
1584 }
1585
1586 fn status(
1587 &self,
1588 hash: Block::Hash,
1589 ) -> soil_client::blockchain::Result<blockchain::BlockStatus> {
1590 self.backend.blockchain().status(hash)
1591 }
1592
1593 fn number(
1594 &self,
1595 hash: Block::Hash,
1596 ) -> soil_client::blockchain::Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>> {
1597 self.backend.blockchain().number(hash)
1598 }
1599
1600 fn hash(
1601 &self,
1602 number: NumberFor<Block>,
1603 ) -> soil_client::blockchain::Result<Option<Block::Hash>> {
1604 self.backend.blockchain().hash(number)
1605 }
1606}
1607
1608impl<B, E, Block, RA> BlockIdTo<Block> for Client<B, E, Block, RA>
1609where
1610 B: backend::Backend<Block>,
1611 E: CallExecutor<Block> + Send + Sync,
1612 Block: BlockT,
1613 RA: Send + Sync,
1614{
1615 type Error = Error;
1616
1617 fn to_hash(
1618 &self,
1619 block_id: &BlockId<Block>,
1620 ) -> soil_client::blockchain::Result<Option<Block::Hash>> {
1621 self.block_hash_from_id(block_id)
1622 }
1623
1624 fn to_number(
1625 &self,
1626 block_id: &BlockId<Block>,
1627 ) -> soil_client::blockchain::Result<Option<NumberFor<Block>>> {
1628 self.block_number_from_id(block_id)
1629 }
1630}
1631
1632impl<B, E, Block, RA> ChainHeaderBackend<Block> for &Client<B, E, Block, RA>
1633where
1634 B: backend::Backend<Block>,
1635 E: CallExecutor<Block> + Send + Sync,
1636 Block: BlockT,
1637 RA: Send + Sync,
1638{
1639 fn header(&self, hash: Block::Hash) -> soil_client::blockchain::Result<Option<Block::Header>> {
1640 self.backend.blockchain().header(hash)
1641 }
1642
1643 fn info(&self) -> blockchain::Info<Block> {
1644 self.backend.blockchain().info()
1645 }
1646
1647 fn status(
1648 &self,
1649 hash: Block::Hash,
1650 ) -> soil_client::blockchain::Result<blockchain::BlockStatus> {
1651 (**self).status(hash)
1652 }
1653
1654 fn number(
1655 &self,
1656 hash: Block::Hash,
1657 ) -> soil_client::blockchain::Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>> {
1658 (**self).number(hash)
1659 }
1660
1661 fn hash(
1662 &self,
1663 number: NumberFor<Block>,
1664 ) -> soil_client::blockchain::Result<Option<Block::Hash>> {
1665 (**self).hash(number)
1666 }
1667}
1668
1669impl<B, E, Block, RA> ProvideRuntimeApi<Block> for Client<B, E, Block, RA>
1670where
1671 B: backend::Backend<Block>,
1672 E: CallExecutor<Block, Backend = B> + Send + Sync,
1673 Block: BlockT,
1674 RA: ConstructRuntimeApi<Block, Self> + Send + Sync,
1675{
1676 type Api = <RA as ConstructRuntimeApi<Block, Self>>::RuntimeApi;
1677
1678 fn runtime_api(&self) -> ApiRef<'_, Self::Api> {
1679 RA::construct_runtime_api(self)
1680 }
1681}
1682
1683impl<B, E, Block, RA> CallApiAt<Block> for Client<B, E, Block, RA>
1684where
1685 B: backend::Backend<Block>,
1686 E: CallExecutor<Block, Backend = B> + Send + Sync,
1687 Block: BlockT,
1688 RA: Send + Sync,
1689{
1690 type StateBackend = B::State;
1691
1692 fn call_api_at(
1693 &self,
1694 params: CallApiAtParams<Block>,
1695 ) -> Result<Vec<u8>, subsoil::api::ApiError> {
1696 self.executor
1697 .contextual_call(
1698 params.at,
1699 params.function,
1700 ¶ms.arguments,
1701 params.overlayed_changes,
1702 params.recorder,
1703 params.call_context,
1704 params.extensions,
1705 )
1706 .map_err(Into::into)
1707 }
1708
1709 fn runtime_version_at(
1710 &self,
1711 hash: Block::Hash,
1712 ) -> Result<RuntimeVersion, subsoil::api::ApiError> {
1713 CallExecutor::runtime_version(&self.executor, hash).map_err(Into::into)
1714 }
1715
1716 fn state_at(&self, at: Block::Hash) -> Result<Self::StateBackend, subsoil::api::ApiError> {
1717 self.state_at(at).map_err(Into::into)
1718 }
1719
1720 fn initialize_extensions(
1721 &self,
1722 at: Block::Hash,
1723 extensions: &mut subsoil::externalities::Extensions,
1724 ) -> Result<(), subsoil::api::ApiError> {
1725 let block_number = self.expect_block_number_from_id(&BlockId::Hash(at))?;
1726
1727 extensions.merge(self.executor.execution_extensions().extensions(at, block_number));
1728
1729 Ok(())
1730 }
1731}
1732
1733#[async_trait::async_trait]
1737impl<B, E, Block, RA> soil_client::import::BlockImport<Block> for &Client<B, E, Block, RA>
1738where
1739 B: backend::Backend<Block>,
1740 E: CallExecutor<Block> + Send + Sync,
1741 Block: BlockT,
1742 Client<B, E, Block, RA>: ProvideRuntimeApi<Block>,
1743 <Client<B, E, Block, RA> as ProvideRuntimeApi<Block>>::Api: CoreApi<Block> + ApiExt<Block>,
1744 RA: Sync + Send,
1745{
1746 type Error = ConsensusError;
1747
1748 async fn import_block(
1757 &self,
1758 mut import_block: BlockImportParams<Block>,
1759 ) -> Result<ImportResult, Self::Error> {
1760 let span = tracing::span!(tracing::Level::DEBUG, "import_block");
1761 let _enter = span.enter();
1762
1763 let storage_changes =
1764 match self.prepare_block_storage_changes(&mut import_block).map_err(|e| {
1765 warn!("Block prepare storage changes error: {}", e);
1766 ConsensusError::ClientImport(e.to_string())
1767 })? {
1768 PrepareStorageChangesResult::Discard(res) => return Ok(res),
1769 PrepareStorageChangesResult::Import(storage_changes) => storage_changes,
1770 };
1771
1772 self.lock_import_and_run(|operation| {
1773 self.apply_block(operation, import_block, storage_changes)
1774 })
1775 .map_err(|e| {
1776 warn!("Block import error: {}", e);
1777 ConsensusError::ClientImport(e.to_string())
1778 })
1779 }
1780
1781 async fn check_block(
1783 &self,
1784 block: BlockCheckParams<Block>,
1785 ) -> Result<ImportResult, Self::Error> {
1786 let BlockCheckParams {
1787 hash,
1788 number,
1789 parent_hash,
1790 allow_missing_state,
1791 import_existing,
1792 allow_missing_parent,
1793 } = block;
1794
1795 match self.block_rules.lookup(number, &hash) {
1798 BlockLookupResult::KnownBad => {
1799 trace!("Rejecting known bad block: #{} {:?}", number, hash);
1800 return Ok(ImportResult::KnownBad);
1801 },
1802 BlockLookupResult::Expected(expected_hash) => {
1803 trace!(
1804 "Rejecting block from known invalid fork. Got {:?}, expected: {:?} at height {}",
1805 hash,
1806 expected_hash,
1807 number
1808 );
1809 return Ok(ImportResult::KnownBad);
1810 },
1811 BlockLookupResult::NotSpecial => {},
1812 }
1813
1814 match self
1817 .block_status(hash)
1818 .map_err(|e| ConsensusError::ClientImport(e.to_string()))?
1819 {
1820 BlockStatus::InChainWithState | BlockStatus::Queued => {
1821 return Ok(ImportResult::AlreadyInChain)
1822 },
1823 BlockStatus::InChainPruned if !import_existing => {
1824 return Ok(ImportResult::AlreadyInChain)
1825 },
1826 BlockStatus::InChainPruned => {},
1827 BlockStatus::Unknown => {},
1828 BlockStatus::KnownBad => return Ok(ImportResult::KnownBad),
1829 }
1830
1831 match self
1832 .block_status(parent_hash)
1833 .map_err(|e| ConsensusError::ClientImport(e.to_string()))?
1834 {
1835 BlockStatus::InChainWithState | BlockStatus::Queued => {},
1836 BlockStatus::Unknown if allow_missing_parent => {},
1837 BlockStatus::Unknown => return Ok(ImportResult::UnknownParent),
1838 BlockStatus::InChainPruned if allow_missing_state => {},
1839 BlockStatus::InChainPruned => return Ok(ImportResult::MissingState),
1840 BlockStatus::KnownBad => return Ok(ImportResult::KnownBad),
1841 }
1842
1843 Ok(ImportResult::imported(false))
1844 }
1845}
1846
1847#[async_trait::async_trait]
1848impl<B, E, Block, RA> soil_client::import::BlockImport<Block> for Client<B, E, Block, RA>
1849where
1850 B: backend::Backend<Block>,
1851 E: CallExecutor<Block> + Send + Sync,
1852 Block: BlockT,
1853 Self: ProvideRuntimeApi<Block>,
1854 <Self as ProvideRuntimeApi<Block>>::Api: CoreApi<Block> + ApiExt<Block>,
1855 RA: Sync + Send,
1856{
1857 type Error = ConsensusError;
1858
1859 async fn check_block(
1860 &self,
1861 block: BlockCheckParams<Block>,
1862 ) -> Result<ImportResult, Self::Error> {
1863 (&self).check_block(block).await
1864 }
1865
1866 async fn import_block(
1867 &self,
1868 import_block: BlockImportParams<Block>,
1869 ) -> Result<ImportResult, Self::Error> {
1870 (&self).import_block(import_block).await
1871 }
1872}
1873
1874impl<B, E, Block, RA> Finalizer<Block, B> for Client<B, E, Block, RA>
1875where
1876 B: backend::Backend<Block>,
1877 E: CallExecutor<Block>,
1878 Block: BlockT,
1879{
1880 fn apply_finality(
1881 &self,
1882 operation: &mut ClientImportOperation<Block, B>,
1883 hash: Block::Hash,
1884 justification: Option<Justification>,
1885 notify: bool,
1886 ) -> soil_client::blockchain::Result<()> {
1887 let info = self.backend.blockchain().info();
1888 self.apply_finality_with_block_hash(operation, hash, justification, &info, notify)
1889 }
1890
1891 fn finalize_block(
1892 &self,
1893 hash: Block::Hash,
1894 justification: Option<Justification>,
1895 notify: bool,
1896 ) -> soil_client::blockchain::Result<()> {
1897 self.lock_import_and_run(|operation| {
1898 self.apply_finality(operation, hash, justification, notify)
1899 })
1900 }
1901}
1902
1903impl<B, E, Block, RA> Finalizer<Block, B> for &Client<B, E, Block, RA>
1904where
1905 B: backend::Backend<Block>,
1906 E: CallExecutor<Block>,
1907 Block: BlockT,
1908{
1909 fn apply_finality(
1910 &self,
1911 operation: &mut ClientImportOperation<Block, B>,
1912 hash: Block::Hash,
1913 justification: Option<Justification>,
1914 notify: bool,
1915 ) -> soil_client::blockchain::Result<()> {
1916 (**self).apply_finality(operation, hash, justification, notify)
1917 }
1918
1919 fn finalize_block(
1920 &self,
1921 hash: Block::Hash,
1922 justification: Option<Justification>,
1923 notify: bool,
1924 ) -> soil_client::blockchain::Result<()> {
1925 (**self).finalize_block(hash, justification, notify)
1926 }
1927}
1928
1929impl<B, E, Block, RA> PreCommitActions<Block> for Client<B, E, Block, RA>
1930where
1931 Block: BlockT,
1932{
1933 fn register_import_action(&self, action: OnImportAction<Block>) {
1934 self.import_actions.lock().push(action);
1935 }
1936
1937 fn register_finality_action(&self, action: OnFinalityAction<Block>) {
1938 self.finality_actions.lock().push(action);
1939 }
1940}
1941
1942impl<B, E, Block, RA> BlockchainEvents<Block> for Client<B, E, Block, RA>
1943where
1944 E: CallExecutor<Block>,
1945 Block: BlockT,
1946{
1947 fn import_notification_stream(&self) -> ImportNotifications<Block> {
1949 let (sink, stream) = tracing_unbounded("mpsc_import_notification_stream", 100_000);
1950 self.import_notification_sinks.lock().push(sink);
1951 stream
1952 }
1953
1954 fn every_import_notification_stream(&self) -> ImportNotifications<Block> {
1955 let (sink, stream) = tracing_unbounded("mpsc_every_import_notification_stream", 100_000);
1956 self.every_import_notification_sinks.lock().push(sink);
1957 stream
1958 }
1959
1960 fn finality_notification_stream(&self) -> FinalityNotifications<Block> {
1961 let (sink, stream) = tracing_unbounded("mpsc_finality_notification_stream", 100_000);
1962 self.finality_notification_sinks.lock().push(sink);
1963 stream
1964 }
1965
1966 fn storage_changes_notification_stream(
1968 &self,
1969 filter_keys: Option<&[StorageKey]>,
1970 child_filter_keys: Option<&[(StorageKey, Option<Vec<StorageKey>>)]>,
1971 ) -> soil_client::blockchain::Result<StorageEventStream<Block::Hash>> {
1972 Ok(self.storage_notifications.listen(filter_keys, child_filter_keys))
1973 }
1974}
1975
1976impl<B, E, Block, RA> BlockBackend<Block> for Client<B, E, Block, RA>
1977where
1978 B: backend::Backend<Block>,
1979 E: CallExecutor<Block>,
1980 Block: BlockT,
1981{
1982 fn block_body(
1983 &self,
1984 hash: Block::Hash,
1985 ) -> soil_client::blockchain::Result<Option<Vec<<Block as BlockT>::Extrinsic>>> {
1986 self.body(hash)
1987 }
1988
1989 fn block(
1990 &self,
1991 hash: Block::Hash,
1992 ) -> soil_client::blockchain::Result<Option<SignedBlock<Block>>> {
1993 Ok(match (self.header(hash)?, self.body(hash)?, self.justifications(hash)?) {
1994 (Some(header), Some(extrinsics), justifications) => {
1995 Some(SignedBlock { block: Block::new(header, extrinsics), justifications })
1996 },
1997 _ => None,
1998 })
1999 }
2000
2001 fn block_status(&self, hash: Block::Hash) -> soil_client::blockchain::Result<BlockStatus> {
2002 Client::block_status(self, hash)
2003 }
2004
2005 fn justifications(
2006 &self,
2007 hash: Block::Hash,
2008 ) -> soil_client::blockchain::Result<Option<Justifications>> {
2009 self.backend.blockchain().justifications(hash)
2010 }
2011
2012 fn block_hash(
2013 &self,
2014 number: NumberFor<Block>,
2015 ) -> soil_client::blockchain::Result<Option<Block::Hash>> {
2016 self.backend.blockchain().hash(number)
2017 }
2018
2019 fn indexed_transaction(
2020 &self,
2021 hash: Block::Hash,
2022 ) -> soil_client::blockchain::Result<Option<Vec<u8>>> {
2023 self.backend.blockchain().indexed_transaction(hash)
2024 }
2025
2026 fn has_indexed_transaction(&self, hash: Block::Hash) -> soil_client::blockchain::Result<bool> {
2027 self.backend.blockchain().has_indexed_transaction(hash)
2028 }
2029
2030 fn block_indexed_body(
2031 &self,
2032 hash: Block::Hash,
2033 ) -> soil_client::blockchain::Result<Option<Vec<Vec<u8>>>> {
2034 self.backend.blockchain().block_indexed_body(hash)
2035 }
2036
2037 fn requires_full_sync(&self) -> bool {
2038 self.backend.requires_full_sync()
2039 }
2040}
2041
2042impl<B, E, Block, RA> backend::AuxStore for Client<B, E, Block, RA>
2043where
2044 B: backend::Backend<Block>,
2045 E: CallExecutor<Block>,
2046 Block: BlockT,
2047 Self: ProvideRuntimeApi<Block>,
2048 <Self as ProvideRuntimeApi<Block>>::Api: CoreApi<Block>,
2049{
2050 fn insert_aux<
2052 'a,
2053 'b: 'a,
2054 'c: 'a,
2055 I: IntoIterator<Item = &'a (&'c [u8], &'c [u8])>,
2056 D: IntoIterator<Item = &'a &'b [u8]>,
2057 >(
2058 &self,
2059 insert: I,
2060 delete: D,
2061 ) -> soil_client::blockchain::Result<()> {
2062 self.lock_import_and_run(|operation| apply_aux(operation, insert, delete))
2067 }
2068 fn get_aux(&self, key: &[u8]) -> soil_client::blockchain::Result<Option<Vec<u8>>> {
2070 backend::AuxStore::get_aux(&*self.backend, key)
2071 }
2072}
2073
2074impl<B, E, Block, RA> backend::AuxStore for &Client<B, E, Block, RA>
2075where
2076 B: backend::Backend<Block>,
2077 E: CallExecutor<Block>,
2078 Block: BlockT,
2079 Client<B, E, Block, RA>: ProvideRuntimeApi<Block>,
2080 <Client<B, E, Block, RA> as ProvideRuntimeApi<Block>>::Api: CoreApi<Block>,
2081{
2082 fn insert_aux<
2083 'a,
2084 'b: 'a,
2085 'c: 'a,
2086 I: IntoIterator<Item = &'a (&'c [u8], &'c [u8])>,
2087 D: IntoIterator<Item = &'a &'b [u8]>,
2088 >(
2089 &self,
2090 insert: I,
2091 delete: D,
2092 ) -> soil_client::blockchain::Result<()> {
2093 (**self).insert_aux(insert, delete)
2094 }
2095
2096 fn get_aux(&self, key: &[u8]) -> soil_client::blockchain::Result<Option<Vec<u8>>> {
2097 (**self).get_aux(key)
2098 }
2099}
2100
2101impl<BE, E, B, RA> soil_client::consensus::block_validation::Chain<B> for Client<BE, E, B, RA>
2102where
2103 BE: backend::Backend<B>,
2104 E: CallExecutor<B>,
2105 B: BlockT,
2106{
2107 fn block_status(
2108 &self,
2109 hash: B::Hash,
2110 ) -> Result<BlockStatus, Box<dyn std::error::Error + Send>> {
2111 Client::block_status(self, hash).map_err(|e| Box::new(e) as Box<_>)
2112 }
2113}
2114
2115impl<BE, E, B, RA> soil_transaction_storage_proof::IndexedBody<B> for Client<BE, E, B, RA>
2116where
2117 BE: backend::Backend<B>,
2118 E: CallExecutor<B>,
2119 B: BlockT,
2120{
2121 fn block_indexed_body(
2122 &self,
2123 number: NumberFor<B>,
2124 ) -> Result<Option<Vec<Vec<u8>>>, soil_transaction_storage_proof::Error> {
2125 let hash = match self
2126 .backend
2127 .blockchain()
2128 .block_hash_from_id(&BlockId::Number(number))
2129 .map_err(|e| soil_transaction_storage_proof::Error::Application(Box::new(e)))?
2130 {
2131 Some(hash) => hash,
2132 None => return Ok(None),
2133 };
2134
2135 self.backend
2136 .blockchain()
2137 .block_indexed_body(hash)
2138 .map_err(|e| soil_transaction_storage_proof::Error::Application(Box::new(e)))
2139 }
2140
2141 fn number(
2142 &self,
2143 hash: B::Hash,
2144 ) -> Result<Option<NumberFor<B>>, soil_transaction_storage_proof::Error> {
2145 self.backend
2146 .blockchain()
2147 .number(hash)
2148 .map_err(|e| soil_transaction_storage_proof::Error::Application(Box::new(e)))
2149 }
2150}