Skip to main content

sc_client_api/
client.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! A set of APIs supported by the client along with their primitives.
20
21use sp_consensus::BlockOrigin;
22use sp_core::{storage::StorageKey, H256};
23use sp_runtime::{
24	generic::SignedBlock,
25	traits::{Block as BlockT, NumberFor},
26	Justifications,
27};
28use std::{
29	collections::HashSet,
30	fmt::{self, Debug},
31	sync::Arc,
32};
33
34use crate::{
35	blockchain::Info, notifications::StorageEventStream, FinalizeSummary, ImportSummary, StaleBlock,
36};
37
38use sc_transaction_pool_api::ChainEvent;
39use sc_utils::mpsc::{TracingUnboundedReceiver, TracingUnboundedSender};
40use sp_blockchain;
41
42/// Type that implements `futures::Stream` of block import events.
43pub type ImportNotifications<Block> = TracingUnboundedReceiver<BlockImportNotification<Block>>;
44
45/// A stream of block finality notifications.
46pub type FinalityNotifications<Block> = TracingUnboundedReceiver<FinalityNotification<Block>>;
47
48/// Expected hashes of blocks at given heights.
49///
50/// This may be used as chain spec extension to set trusted checkpoints, i.e.
51/// the client will refuse to import a block with a different hash at the given
52/// height.
53pub type ForkBlocks<Block> = Option<Vec<(NumberFor<Block>, <Block as BlockT>::Hash)>>;
54
55/// Known bad block hashes.
56///
57/// This may be used as chain spec extension to filter out known, unwanted forks.
58pub type BadBlocks<Block> = Option<HashSet<<Block as BlockT>::Hash>>;
59
60/// Figure out the block type for a given type (for now, just a `Client`).
61pub trait BlockOf {
62	/// The type of the block.
63	type Type: BlockT;
64}
65
66/// A source of blockchain events.
67pub trait BlockchainEvents<Block: BlockT> {
68	/// Get block import event stream.
69	///
70	/// Not guaranteed to be fired for every imported block. Use
71	/// `every_import_notification_stream()` if you want a notification of every imported block
72	/// regardless.
73	///
74	/// The events for this notification stream are emitted:
75	/// - During initial sync process: if there is a re-org while importing blocks. See
76	/// [here](https://github.com/paritytech/substrate/pull/7118#issuecomment-694091901) for the
77	/// rationale behind this.
78	/// - After initial sync process: on every imported block, regardless of whether it is
79	/// the new best block or not, causes a re-org or not.
80	fn import_notification_stream(&self) -> ImportNotifications<Block>;
81
82	/// Get a stream of every imported block.
83	fn every_import_notification_stream(&self) -> ImportNotifications<Block>;
84
85	/// Get a stream of finality notifications. Not guaranteed to be fired for every
86	/// finalized block.
87	fn finality_notification_stream(&self) -> FinalityNotifications<Block>;
88
89	/// Get storage changes event stream.
90	///
91	/// Passing `None` as `filter_keys` subscribes to all storage changes.
92	fn storage_changes_notification_stream(
93		&self,
94		filter_keys: Option<&[StorageKey]>,
95		child_filter_keys: Option<&[(StorageKey, Option<Vec<StorageKey>>)]>,
96	) -> sp_blockchain::Result<StorageEventStream<Block::Hash>>;
97}
98
99/// List of operations to be performed on storage aux data.
100/// First tuple element is the encoded data key.
101/// Second tuple element is the encoded optional data to write.
102/// If `None`, the key and the associated data are deleted from storage.
103pub type AuxDataOperations = Vec<(Vec<u8>, Option<Vec<u8>>)>;
104
105/// Callback invoked before committing the operations created during block import.
106/// This gives the opportunity to perform auxiliary pre-commit actions and optionally
107/// enqueue further storage write operations to be atomically performed on commit.
108pub type OnImportAction<Block> =
109	Box<dyn (Fn(&BlockImportNotification<Block>) -> AuxDataOperations) + Send>;
110
111/// Callback invoked before committing the operations created during block finalization.
112/// This gives the opportunity to perform auxiliary pre-commit actions and optionally
113/// enqueue further storage write operations to be atomically performed on commit.
114pub type OnFinalityAction<Block> =
115	Box<dyn (Fn(&FinalityNotification<Block>) -> AuxDataOperations) + Send>;
116
117/// Interface to perform auxiliary actions before committing a block import or
118/// finality operation.
119pub trait PreCommitActions<Block: BlockT> {
120	/// Actions to be performed on block import.
121	fn register_import_action(&self, op: OnImportAction<Block>);
122
123	/// Actions to be performed on block finalization.
124	fn register_finality_action(&self, op: OnFinalityAction<Block>);
125}
126
127/// Interface for fetching block data.
128pub trait BlockBackend<Block: BlockT> {
129	/// Get block body by ID. Returns `None` if the body is not stored.
130	fn block_body(
131		&self,
132		hash: Block::Hash,
133	) -> sp_blockchain::Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
134
135	/// Get all indexed transactions for a block, including renewed transactions.
136	///
137	/// Note that this will only fetch transactions that are indexed by the runtime with
138	/// `storage_index_transaction`.
139	fn block_indexed_body(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<Vec<Vec<u8>>>>;
140
141	/// Get the BLAKE2b-256 hashes of all indexed transactions in a block, including renewed
142	/// transactions.
143	///
144	/// Note that this will only fetch transactions that are indexed by the runtime with
145	/// `storage_index_transaction`.
146	fn block_indexed_hashes(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<Vec<H256>>>;
147
148	/// Get full block by hash.
149	fn block(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<SignedBlock<Block>>>;
150
151	/// Get block status by block hash.
152	fn block_status(&self, hash: Block::Hash) -> sp_blockchain::Result<sp_consensus::BlockStatus>;
153
154	/// Get block justifications for the block with the given hash.
155	fn justifications(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<Justifications>>;
156
157	/// Get block hash by number.
158	fn block_hash(&self, number: NumberFor<Block>) -> sp_blockchain::Result<Option<Block::Hash>>;
159
160	/// Get single indexed transaction by content hash (BLAKE2b-256).
161	///
162	/// Note that this will only fetch transactions
163	/// that are indexed by the runtime with `storage_index_transaction`.
164	fn indexed_transaction(&self, hash: H256) -> sp_blockchain::Result<Option<Vec<u8>>>;
165
166	/// Check if transaction index exists given its BLAKE2b-256 hash.
167	fn has_indexed_transaction(&self, hash: H256) -> sp_blockchain::Result<bool> {
168		Ok(self.indexed_transaction(hash)?.is_some())
169	}
170
171	/// Tells whether the current client configuration requires full-sync mode.
172	fn requires_full_sync(&self) -> bool;
173}
174
175/// Provide a list of potential uncle headers for a given block.
176pub trait ProvideUncles<Block: BlockT> {
177	/// Gets the uncles of the block with `target_hash` going back `max_generation` ancestors.
178	fn uncles(
179		&self,
180		target_hash: Block::Hash,
181		max_generation: NumberFor<Block>,
182	) -> sp_blockchain::Result<Vec<Block::Header>>;
183}
184
185/// Client info
186#[derive(Debug, Clone)]
187pub struct ClientInfo<Block: BlockT> {
188	/// Best block hash.
189	pub chain: Info<Block>,
190	/// Usage info, if backend supports this.
191	pub usage: Option<UsageInfo>,
192}
193
194/// A wrapper to store the size of some memory.
195#[derive(Default, Clone, Debug, Copy)]
196pub struct MemorySize(usize);
197
198impl MemorySize {
199	/// Creates `Self` from the given `bytes` size.
200	pub fn from_bytes(bytes: usize) -> Self {
201		Self(bytes)
202	}
203
204	/// Returns the memory size as bytes.
205	pub fn as_bytes(self) -> usize {
206		self.0
207	}
208}
209
210impl fmt::Display for MemorySize {
211	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212		if self.0 < 1024 {
213			write!(f, "{} bytes", self.0)
214		} else if self.0 < 1024 * 1024 {
215			write!(f, "{:.2} KiB", self.0 as f64 / 1024f64)
216		} else if self.0 < 1024 * 1024 * 1024 {
217			write!(f, "{:.2} MiB", self.0 as f64 / (1024f64 * 1024f64))
218		} else {
219			write!(f, "{:.2} GiB", self.0 as f64 / (1024f64 * 1024f64 * 1024f64))
220		}
221	}
222}
223
224/// Memory statistics for client instance.
225#[derive(Default, Clone, Debug)]
226pub struct MemoryInfo {
227	/// Size of state cache.
228	pub state_cache: MemorySize,
229	/// Size of backend database cache.
230	pub database_cache: MemorySize,
231}
232
233/// I/O statistics for client instance.
234#[derive(Default, Clone, Debug)]
235pub struct IoInfo {
236	/// Number of transactions.
237	pub transactions: u64,
238	/// Total bytes read from disk.
239	pub bytes_read: u64,
240	/// Total bytes written to disk.
241	pub bytes_written: u64,
242	/// Total key writes to disk.
243	pub writes: u64,
244	/// Total key reads from disk.
245	pub reads: u64,
246	/// Average size of the transaction.
247	pub average_transaction_size: u64,
248	/// State reads (keys)
249	pub state_reads: u64,
250	/// State reads (keys) from cache.
251	pub state_reads_cache: u64,
252	/// State reads (keys)
253	pub state_writes: u64,
254	/// State write (keys) already cached.
255	pub state_writes_cache: u64,
256	/// State write (trie nodes) to backend db.
257	pub state_writes_nodes: u64,
258}
259
260/// Usage statistics for running client instance.
261///
262/// Returning backend determines the scope of these stats,
263/// but usually it is either from service start or from previous
264/// gathering of the statistics.
265#[derive(Default, Clone, Debug)]
266pub struct UsageInfo {
267	/// Memory statistics.
268	pub memory: MemoryInfo,
269	/// I/O statistics.
270	pub io: IoInfo,
271}
272
273impl fmt::Display for UsageInfo {
274	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275		write!(
276			f,
277			"caches: ({} state, {} db overlay), \
278			 i/o: ({} tx, {} write, {} read, {} avg tx, {}/{} key cache reads/total, {} trie nodes writes)",
279			self.memory.state_cache,
280			self.memory.database_cache,
281			self.io.transactions,
282			self.io.bytes_written,
283			self.io.bytes_read,
284			self.io.average_transaction_size,
285			self.io.state_reads_cache,
286			self.io.state_reads,
287			self.io.state_writes_nodes,
288		)
289	}
290}
291
292/// Sends a message to the pinning-worker once dropped to unpin a block in the backend.
293pub struct UnpinHandleInner<Block: BlockT> {
294	/// Hash of the block pinned by this handle
295	hash: Block::Hash,
296	unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
297}
298
299impl<Block: BlockT> Debug for UnpinHandleInner<Block> {
300	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301		f.debug_struct("UnpinHandleInner").field("pinned_block", &self.hash).finish()
302	}
303}
304
305impl<Block: BlockT> UnpinHandleInner<Block> {
306	/// Create a new [`UnpinHandleInner`]
307	pub fn new(
308		hash: Block::Hash,
309		unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
310	) -> Self {
311		Self { hash, unpin_worker_sender }
312	}
313}
314
315impl<Block: BlockT> Drop for UnpinHandleInner<Block> {
316	fn drop(&mut self) {
317		if let Err(err) =
318			self.unpin_worker_sender.unbounded_send(UnpinWorkerMessage::Unpin(self.hash))
319		{
320			log::debug!(target: "db", "Unable to unpin block with hash: {}, error: {:?}", self.hash, err);
321		};
322	}
323}
324
325/// Message that signals notification-based pinning actions to the pinning-worker.
326///
327/// When the notification is dropped, an `Unpin` message should be sent to the worker.
328#[derive(Debug)]
329pub enum UnpinWorkerMessage<Block: BlockT> {
330	/// Should be sent when a import or finality notification is created.
331	AnnouncePin(Block::Hash),
332	/// Should be sent when a import or finality notification is dropped.
333	Unpin(Block::Hash),
334}
335
336/// Keeps a specific block pinned while the handle is alive.
337/// Once the last handle instance for a given block is dropped, the
338/// block is unpinned in the [`Backend`](crate::backend::Backend::unpin_block).
339#[derive(Debug, Clone)]
340pub struct UnpinHandle<Block: BlockT>(Arc<UnpinHandleInner<Block>>);
341
342impl<Block: BlockT> UnpinHandle<Block> {
343	/// Create a new [`UnpinHandle`]
344	pub fn new(
345		hash: Block::Hash,
346		unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
347	) -> UnpinHandle<Block> {
348		UnpinHandle(Arc::new(UnpinHandleInner::new(hash, unpin_worker_sender)))
349	}
350
351	/// Hash of the block this handle is unpinning on drop
352	pub fn hash(&self) -> Block::Hash {
353		self.0.hash
354	}
355}
356
357/// Summary of an imported block
358#[derive(Clone, Debug)]
359pub struct BlockImportNotification<Block: BlockT> {
360	/// Imported block header hash.
361	pub hash: Block::Hash,
362	/// Imported block origin.
363	pub origin: BlockOrigin,
364	/// Imported block header.
365	pub header: Block::Header,
366	/// Is this the new best block.
367	pub is_new_best: bool,
368	/// Tree route from old best to new best parent.
369	///
370	/// If `None`, there was no re-org while importing.
371	pub tree_route: Option<Arc<sp_blockchain::TreeRoute<Block>>>,
372	/// Handle to unpin the block this notification is for
373	unpin_handle: UnpinHandle<Block>,
374}
375
376impl<Block: BlockT> BlockImportNotification<Block> {
377	/// Create new notification
378	pub fn new(
379		hash: Block::Hash,
380		origin: BlockOrigin,
381		header: Block::Header,
382		is_new_best: bool,
383		tree_route: Option<Arc<sp_blockchain::TreeRoute<Block>>>,
384		unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
385	) -> Self {
386		Self {
387			hash,
388			origin,
389			header,
390			is_new_best,
391			tree_route,
392			unpin_handle: UnpinHandle::new(hash, unpin_worker_sender),
393		}
394	}
395
396	/// Consume this notification and extract the unpin handle.
397	///
398	/// Note: Only use this if you want to keep the block pinned in the backend.
399	pub fn into_unpin_handle(self) -> UnpinHandle<Block> {
400		self.unpin_handle
401	}
402}
403
404/// Summary of a finalized block.
405#[derive(Clone, Debug)]
406pub struct FinalityNotification<Block: BlockT> {
407	/// Finalized block header hash.
408	pub hash: Block::Hash,
409	/// Finalized block header.
410	pub header: Block::Header,
411	/// Path from the old finalized to new finalized parent (implicitly finalized blocks).
412	///
413	/// This maps to the range `(old_finalized, new_finalized)`.
414	pub tree_route: Arc<[Block::Hash]>,
415	/// Stale blocks.
416	pub stale_blocks: Arc<[Arc<StaleBlock<Block>>]>,
417	/// Handle to unpin the block this notification is for
418	unpin_handle: UnpinHandle<Block>,
419}
420
421impl<B: BlockT> TryFrom<BlockImportNotification<B>> for ChainEvent<B> {
422	type Error = ();
423
424	fn try_from(n: BlockImportNotification<B>) -> Result<Self, ()> {
425		if n.is_new_best {
426			Ok(Self::NewBestBlock { hash: n.hash, tree_route: n.tree_route })
427		} else {
428			Err(())
429		}
430	}
431}
432
433impl<B: BlockT> From<FinalityNotification<B>> for ChainEvent<B> {
434	fn from(n: FinalityNotification<B>) -> Self {
435		Self::Finalized { hash: n.hash, tree_route: n.tree_route }
436	}
437}
438
439impl<Block: BlockT> FinalityNotification<Block> {
440	/// Create finality notification from finality summary.
441	pub fn from_summary(
442		mut summary: FinalizeSummary<Block>,
443		unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
444	) -> FinalityNotification<Block> {
445		let hash = summary.finalized.pop().unwrap_or_default();
446		FinalityNotification {
447			hash,
448			header: summary.header,
449			tree_route: Arc::from(summary.finalized),
450			stale_blocks: Arc::from(
451				summary.stale_blocks.into_iter().map(Arc::from).collect::<Vec<_>>(),
452			),
453			unpin_handle: UnpinHandle::new(hash, unpin_worker_sender),
454		}
455	}
456
457	/// Consume this notification and extract the unpin handle.
458	///
459	/// Note: Only use this if you want to keep the block pinned in the backend.
460	pub fn into_unpin_handle(self) -> UnpinHandle<Block> {
461		self.unpin_handle
462	}
463}
464
465impl<Block: BlockT> BlockImportNotification<Block> {
466	/// Create finality notification from finality summary.
467	pub fn from_summary(
468		summary: ImportSummary<Block>,
469		unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
470	) -> BlockImportNotification<Block> {
471		let hash = summary.hash;
472		BlockImportNotification {
473			hash,
474			origin: summary.origin,
475			header: summary.header,
476			is_new_best: summary.is_new_best,
477			tree_route: summary.tree_route.map(Arc::new),
478			unpin_handle: UnpinHandle::new(hash, unpin_worker_sender),
479		}
480	}
481}