Skip to main content

soil_service/client/
client.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
7//! Substrate Client
8
9use 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
81/// Substrate Client
82pub 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	// Collects auxiliary operations to be performed atomically together with
93	// block import operations.
94	import_actions: Mutex<Vec<OnImportAction<Block>>>,
95	// Collects auxiliary operations to be performed atomically together with
96	// block finalization operations.
97	finality_actions: Mutex<Vec<OnFinalityAction<Block>>>,
98	// Holds the block hash currently being imported. TODO: replace this with block queue.
99	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
108/// Used in importing a block, where additional changes are made after the runtime
109/// executed.
110enum PrePostHeader<H> {
111	/// they are the same: no post-runtime digest items.
112	Same(H),
113	/// different headers (pre, post).
114	Different(H, H),
115}
116
117impl<H> PrePostHeader<H> {
118	/// get a reference to the "post-header" -- the header as it should be
119	/// after all changes are applied.
120	fn post(&self) -> &H {
121		match *self {
122			PrePostHeader::Same(ref h) => h,
123			PrePostHeader::Different(_, ref h) => h,
124		}
125	}
126
127	/// convert to the "post-header" -- the header as it should be after
128	/// all changes are applied.
129	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/// Client configuration items.
142#[derive(Debug, Clone)]
143pub struct ClientConfig<Block: BlockT> {
144	/// Enable the offchain worker db.
145	pub offchain_worker_enabled: bool,
146	/// If true, allows access from the runtime to write into offchain worker db.
147	pub offchain_indexing_api: bool,
148	/// Path where WASM files exist to override the on-chain WASM.
149	pub wasm_runtime_overrides: Option<PathBuf>,
150	/// Skip writing genesis state on first start.
151	pub no_genesis: bool,
152	/// Map of WASM runtime substitute starting at the child of the given block until the runtime
153	/// version doesn't match anymore.
154	pub wasm_runtime_substitutes: HashMap<NumberFor<Block>, Vec<u8>>,
155	/// Enable recording of storage proofs during block import
156	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
172/// Create a client with the explicitly provided backend.
173/// This is useful for testing backend implementations.
174pub 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			// We need to pin the block in the backend once
278			// for each notification. Once all notifications are
279			// dropped, the block will be unpinned automatically.
280			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	/// Creates new Substrate Client with given blockchain and code executor.
350	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			// Genesis may be written after some blocks have been imported and finalized.
378			// So we only finalize it when the database is empty.
379			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	/// returns a reference to the block import notification sinks
417	/// useful for test environments.
418	pub fn import_notification_sinks(&self) -> &NotificationSinks<BlockImportNotification<Block>> {
419		&self.import_notification_sinks
420	}
421
422	/// returns a reference to the finality notification sinks
423	/// useful for test environments.
424	pub fn finality_notification_sinks(&self) -> &NotificationSinks<FinalityNotification<Block>> {
425		&self.finality_notification_sinks
426	}
427
428	/// Get a reference to the state at a given block.
429	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	/// Get the code at a given block.
434	///
435	/// This takes any potential substitutes into account, but ignores overrides.
436	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	/// Get the RuntimeVersion at a given block.
441	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	/// Apply a checked and validated block to an operation.
449	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				// don't send telemetry block import events during initial sync for every
516				// block to avoid spamming the telemetry server, these events will be randomly
517				// sent at a rate of 1/10.
518				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		// the block is lower than our last finalized block so it must revert
570		// finality, refusing import.
571		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		// this is a fairly arbitrary choice of where to draw the line on making notifications,
579		// but the general goal is to only make notifications when we are already fully synced
580		// and get a new chain head.
581		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						// This is use by fast sync for runtime version to be resolvable from
646						// changes.
647						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							// State root mismatch when importing state. This should not happen in
654							// safe fast sync mode, but may happen in unsafe mode.
655							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		// Ensure parent chain is finalized to maintain invariant that finality is called
668		// sequentially.
669		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		// Warp sync imported blocks shall be stored in the DB, but they should not be registered
697		// as leaves.
698		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		// Notify when we are already synced to the tip of the chain
733		// or if this import triggers a re-org
734		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					// The stale blocks that will be displaced after the block is finalized.
754					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	/// Prepares the storage changes for a block.
795	///
796	/// It checks if the state should be enacted and if the `import_block` maybe already provides
797	/// the required storage changes. If the state should be enacted and the storage changes are not
798	/// provided, the block is re-executed to get the storage changes.
799	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			// We have storage changes and should enact the state, so we don't need to do anything
833			// here
834			(true, changes @ Some(_), _) => changes,
835			// We should enact state, but don't have any storage changes, so we need to execute the
836			// block.
837			(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			// No block body, no storage changes
867			(true, None, None) => None,
868			// We should not enact the state, so we set the storage changes to `None`.
869			(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		// Find tree route from last finalized to given block.
892		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		// We may need to coercively update the best block if there is more than one
909		// leaf or if the finalized block number is greater than last best number recorded
910		// by the backend. This last condition may apply in case of consensus implementations
911		// not always checking this condition.
912		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 the block is not a direct ancestor of the current best chain,
925			// then some other block is the common ancestor.
926			if route_from_best.common_block().hash != hash {
927				// NOTE: we're setting the finalized block as best block, this might
928				// be slightly inaccurate since we might have a "better" block
929				// further along this chain, but since best chain selection logic is
930				// plugable we cannot make a better choice here. usages that need
931				// an accurate "best" block need to go through `SelectChain`
932				// instead.
933				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			// The stale blocks that will be displaced after the block is finalized.
958			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				// Cleanup any closed finality notification sinks
987				// since we won't be running the loop below which
988				// would also remove any closed sinks.
989				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				// Cleanup any closed import notification sinks since we won't
1017				// be sending any notifications below which would remove any
1018				// closed sinks. this is necessary since during initial sync we
1019				// won't send any import notifications which could lead to a
1020				// temporary leak of closed/discarded notification sinks (e.g.
1021				// from consensus code).
1022				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				// TODO [ToDr] How to handle re-orgs? Should we re-emit all storage changes?
1033				self.storage_notifications.trigger(
1034					&notification.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				// This branch is unreachable in fact because the block import notification must be
1069				// Some(_) instead of None (it's already handled at the beginning of this function)
1070				// at this point.
1071				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	/// Attempts to revert the chain by `n` blocks guaranteeing that no block is
1081	/// reverted past the last finalized block. Returns the number of blocks
1082	/// that were successfully reverted.
1083	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	/// Attempts to revert the chain by `n` blocks disregarding finality. This method will revert
1089	/// any finalized blocks as requested and can potentially leave the node in an inconsistent
1090	/// state. Other modules in the system that persist data and that rely on finality
1091	/// (e.g. consensus parts) will be unaffected by the revert. Use this method with caution and
1092	/// making sure that no other data needs to be reverted for consistency aside from the block
1093	/// data. If `blacklist` is set to true, will also blacklist reverted blocks from finalizing
1094	/// again. The blacklist is reset upon client restart.
1095	///
1096	/// Returns the number of blocks that were successfully reverted.
1097	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	/// Get blockchain info.
1112	pub fn chain_info(&self) -> BlockchainInfo<Block> {
1113		self.backend.blockchain().info()
1114	}
1115
1116	/// Get block status.
1117	pub fn block_status(&self, hash: Block::Hash) -> soil_client::blockchain::Result<BlockStatus> {
1118		// this can probably be implemented more efficiently
1119		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	/// Get block header by id.
1142	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	/// Get block body by id.
1150	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	/// Gets the uncles of the block with `target_hash` going back `max_generation` ancestors.
1158	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 != &current_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	/// Get usage info about current client.
1208	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		// this is a read proof, using version V0 or V1 is equivalent.
1255		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, &current_key)
1317					.map_err(|e| soil_client::blockchain::Error::from_state(Box::new(e)))?
1318			} else {
1319				state
1320					.next_storage_key(&current_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		// Compact encoding
1388		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				&params.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/// NOTE: only use this implementation when you are sure there are NO consensus-level BlockImport
1734/// objects. Otherwise, importing blocks directly into the client would be bypassing
1735/// important verification work.
1736#[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	/// Import a checked and validated block.
1749	///
1750	/// NOTE: only use this implementation when there are NO consensus-level BlockImport
1751	/// objects. Otherwise, importing blocks directly into the client would be bypassing
1752	/// important verification work.
1753	///
1754	/// If you are not sure that there are no BlockImport objects provided by the consensus
1755	/// algorithm, don't use this function.
1756	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	/// Check block preconditions.
1782	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		// Check the block against white and black lists if any are defined
1796		// (i.e. fork blocks and bad blocks respectively)
1797		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		// Own status must be checked first. If the block and ancestry is pruned
1815		// this function must return `AlreadyInChain` rather than `MissingState`
1816		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	/// Get block import event stream.
1948	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	/// Get storage changes event stream.
1967	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	/// Insert auxiliary data into key-value store.
2051	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		// Import is locked here because we may have other block import
2063		// operations that tries to set aux data. Note that for consensus
2064		// layer, one can always use atomic operations to make sure
2065		// import is only locked once.
2066		self.lock_import_and_run(|operation| apply_aux(operation, insert, delete))
2067	}
2068	/// Query auxiliary data from key-value store.
2069	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}