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;
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,
136	/// including renewed transactions.
137	///
138	/// Note that this will only fetch transactions
139	/// that are indexed by the runtime with `storage_index_transaction`.
140	fn block_indexed_body(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<Vec<Vec<u8>>>>;
141
142	/// Get full block by hash.
143	fn block(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<SignedBlock<Block>>>;
144
145	/// Get block status by block hash.
146	fn block_status(&self, hash: Block::Hash) -> sp_blockchain::Result<sp_consensus::BlockStatus>;
147
148	/// Get block justifications for the block with the given hash.
149	fn justifications(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<Justifications>>;
150
151	/// Get block hash by number.
152	fn block_hash(&self, number: NumberFor<Block>) -> sp_blockchain::Result<Option<Block::Hash>>;
153
154	/// Get single indexed transaction by content hash.
155	///
156	/// Note that this will only fetch transactions
157	/// that are indexed by the runtime with `storage_index_transaction`.
158	fn indexed_transaction(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<Vec<u8>>>;
159
160	/// Check if transaction index exists.
161	fn has_indexed_transaction(&self, hash: Block::Hash) -> sp_blockchain::Result<bool> {
162		Ok(self.indexed_transaction(hash)?.is_some())
163	}
164
165	/// Tells whether the current client configuration requires full-sync mode.
166	fn requires_full_sync(&self) -> bool;
167}
168
169/// Provide a list of potential uncle headers for a given block.
170pub trait ProvideUncles<Block: BlockT> {
171	/// Gets the uncles of the block with `target_hash` going back `max_generation` ancestors.
172	fn uncles(
173		&self,
174		target_hash: Block::Hash,
175		max_generation: NumberFor<Block>,
176	) -> sp_blockchain::Result<Vec<Block::Header>>;
177}
178
179/// Client info
180#[derive(Debug, Clone)]
181pub struct ClientInfo<Block: BlockT> {
182	/// Best block hash.
183	pub chain: Info<Block>,
184	/// Usage info, if backend supports this.
185	pub usage: Option<UsageInfo>,
186}
187
188/// A wrapper to store the size of some memory.
189#[derive(Default, Clone, Debug, Copy)]
190pub struct MemorySize(usize);
191
192impl MemorySize {
193	/// Creates `Self` from the given `bytes` size.
194	pub fn from_bytes(bytes: usize) -> Self {
195		Self(bytes)
196	}
197
198	/// Returns the memory size as bytes.
199	pub fn as_bytes(self) -> usize {
200		self.0
201	}
202}
203
204impl fmt::Display for MemorySize {
205	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206		if self.0 < 1024 {
207			write!(f, "{} bytes", self.0)
208		} else if self.0 < 1024 * 1024 {
209			write!(f, "{:.2} KiB", self.0 as f64 / 1024f64)
210		} else if self.0 < 1024 * 1024 * 1024 {
211			write!(f, "{:.2} MiB", self.0 as f64 / (1024f64 * 1024f64))
212		} else {
213			write!(f, "{:.2} GiB", self.0 as f64 / (1024f64 * 1024f64 * 1024f64))
214		}
215	}
216}
217
218/// Memory statistics for client instance.
219#[derive(Default, Clone, Debug)]
220pub struct MemoryInfo {
221	/// Size of state cache.
222	pub state_cache: MemorySize,
223	/// Size of backend database cache.
224	pub database_cache: MemorySize,
225}
226
227/// I/O statistics for client instance.
228#[derive(Default, Clone, Debug)]
229pub struct IoInfo {
230	/// Number of transactions.
231	pub transactions: u64,
232	/// Total bytes read from disk.
233	pub bytes_read: u64,
234	/// Total bytes written to disk.
235	pub bytes_written: u64,
236	/// Total key writes to disk.
237	pub writes: u64,
238	/// Total key reads from disk.
239	pub reads: u64,
240	/// Average size of the transaction.
241	pub average_transaction_size: u64,
242	/// State reads (keys)
243	pub state_reads: u64,
244	/// State reads (keys) from cache.
245	pub state_reads_cache: u64,
246	/// State reads (keys)
247	pub state_writes: u64,
248	/// State write (keys) already cached.
249	pub state_writes_cache: u64,
250	/// State write (trie nodes) to backend db.
251	pub state_writes_nodes: u64,
252}
253
254/// Usage statistics for running client instance.
255///
256/// Returning backend determines the scope of these stats,
257/// but usually it is either from service start or from previous
258/// gathering of the statistics.
259#[derive(Default, Clone, Debug)]
260pub struct UsageInfo {
261	/// Memory statistics.
262	pub memory: MemoryInfo,
263	/// I/O statistics.
264	pub io: IoInfo,
265}
266
267impl fmt::Display for UsageInfo {
268	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269		write!(
270			f,
271			"caches: ({} state, {} db overlay), \
272			 i/o: ({} tx, {} write, {} read, {} avg tx, {}/{} key cache reads/total, {} trie nodes writes)",
273			self.memory.state_cache,
274			self.memory.database_cache,
275			self.io.transactions,
276			self.io.bytes_written,
277			self.io.bytes_read,
278			self.io.average_transaction_size,
279			self.io.state_reads_cache,
280			self.io.state_reads,
281			self.io.state_writes_nodes,
282		)
283	}
284}
285
286/// Sends a message to the pinning-worker once dropped to unpin a block in the backend.
287pub struct UnpinHandleInner<Block: BlockT> {
288	/// Hash of the block pinned by this handle
289	hash: Block::Hash,
290	unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
291}
292
293impl<Block: BlockT> Debug for UnpinHandleInner<Block> {
294	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295		f.debug_struct("UnpinHandleInner").field("pinned_block", &self.hash).finish()
296	}
297}
298
299impl<Block: BlockT> UnpinHandleInner<Block> {
300	/// Create a new [`UnpinHandleInner`]
301	pub fn new(
302		hash: Block::Hash,
303		unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
304	) -> Self {
305		Self { hash, unpin_worker_sender }
306	}
307}
308
309impl<Block: BlockT> Drop for UnpinHandleInner<Block> {
310	fn drop(&mut self) {
311		if let Err(err) =
312			self.unpin_worker_sender.unbounded_send(UnpinWorkerMessage::Unpin(self.hash))
313		{
314			log::debug!(target: "db", "Unable to unpin block with hash: {}, error: {:?}", self.hash, err);
315		};
316	}
317}
318
319/// Message that signals notification-based pinning actions to the pinning-worker.
320///
321/// When the notification is dropped, an `Unpin` message should be sent to the worker.
322#[derive(Debug)]
323pub enum UnpinWorkerMessage<Block: BlockT> {
324	/// Should be sent when a import or finality notification is created.
325	AnnouncePin(Block::Hash),
326	/// Should be sent when a import or finality notification is dropped.
327	Unpin(Block::Hash),
328}
329
330/// Keeps a specific block pinned while the handle is alive.
331/// Once the last handle instance for a given block is dropped, the
332/// block is unpinned in the [`Backend`](crate::backend::Backend::unpin_block).
333#[derive(Debug, Clone)]
334pub struct UnpinHandle<Block: BlockT>(Arc<UnpinHandleInner<Block>>);
335
336impl<Block: BlockT> UnpinHandle<Block> {
337	/// Create a new [`UnpinHandle`]
338	pub fn new(
339		hash: Block::Hash,
340		unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
341	) -> UnpinHandle<Block> {
342		UnpinHandle(Arc::new(UnpinHandleInner::new(hash, unpin_worker_sender)))
343	}
344
345	/// Hash of the block this handle is unpinning on drop
346	pub fn hash(&self) -> Block::Hash {
347		self.0.hash
348	}
349}
350
351/// Summary of an imported block
352#[derive(Clone, Debug)]
353pub struct BlockImportNotification<Block: BlockT> {
354	/// Imported block header hash.
355	pub hash: Block::Hash,
356	/// Imported block origin.
357	pub origin: BlockOrigin,
358	/// Imported block header.
359	pub header: Block::Header,
360	/// Is this the new best block.
361	pub is_new_best: bool,
362	/// Tree route from old best to new best parent.
363	///
364	/// If `None`, there was no re-org while importing.
365	pub tree_route: Option<Arc<sp_blockchain::TreeRoute<Block>>>,
366	/// Handle to unpin the block this notification is for
367	unpin_handle: UnpinHandle<Block>,
368}
369
370impl<Block: BlockT> BlockImportNotification<Block> {
371	/// Create new notification
372	pub fn new(
373		hash: Block::Hash,
374		origin: BlockOrigin,
375		header: Block::Header,
376		is_new_best: bool,
377		tree_route: Option<Arc<sp_blockchain::TreeRoute<Block>>>,
378		unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
379	) -> Self {
380		Self {
381			hash,
382			origin,
383			header,
384			is_new_best,
385			tree_route,
386			unpin_handle: UnpinHandle::new(hash, unpin_worker_sender),
387		}
388	}
389
390	/// Consume this notification and extract the unpin handle.
391	///
392	/// Note: Only use this if you want to keep the block pinned in the backend.
393	pub fn into_unpin_handle(self) -> UnpinHandle<Block> {
394		self.unpin_handle
395	}
396}
397
398/// Summary of a finalized block.
399#[derive(Clone, Debug)]
400pub struct FinalityNotification<Block: BlockT> {
401	/// Finalized block header hash.
402	pub hash: Block::Hash,
403	/// Finalized block header.
404	pub header: Block::Header,
405	/// Path from the old finalized to new finalized parent (implicitly finalized blocks).
406	///
407	/// This maps to the range `(old_finalized, new_finalized)`.
408	pub tree_route: Arc<[Block::Hash]>,
409	/// Stale blocks.
410	pub stale_blocks: Arc<[Arc<StaleBlock<Block>>]>,
411	/// Handle to unpin the block this notification is for
412	unpin_handle: UnpinHandle<Block>,
413}
414
415impl<B: BlockT> TryFrom<BlockImportNotification<B>> for ChainEvent<B> {
416	type Error = ();
417
418	fn try_from(n: BlockImportNotification<B>) -> Result<Self, ()> {
419		if n.is_new_best {
420			Ok(Self::NewBestBlock { hash: n.hash, tree_route: n.tree_route })
421		} else {
422			Err(())
423		}
424	}
425}
426
427impl<B: BlockT> From<FinalityNotification<B>> for ChainEvent<B> {
428	fn from(n: FinalityNotification<B>) -> Self {
429		Self::Finalized { hash: n.hash, tree_route: n.tree_route }
430	}
431}
432
433impl<Block: BlockT> FinalityNotification<Block> {
434	/// Create finality notification from finality summary.
435	pub fn from_summary(
436		mut summary: FinalizeSummary<Block>,
437		unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
438	) -> FinalityNotification<Block> {
439		let hash = summary.finalized.pop().unwrap_or_default();
440		FinalityNotification {
441			hash,
442			header: summary.header,
443			tree_route: Arc::from(summary.finalized),
444			stale_blocks: Arc::from(
445				summary.stale_blocks.into_iter().map(Arc::from).collect::<Vec<_>>(),
446			),
447			unpin_handle: UnpinHandle::new(hash, unpin_worker_sender),
448		}
449	}
450
451	/// Consume this notification and extract the unpin handle.
452	///
453	/// Note: Only use this if you want to keep the block pinned in the backend.
454	pub fn into_unpin_handle(self) -> UnpinHandle<Block> {
455		self.unpin_handle
456	}
457}
458
459impl<Block: BlockT> BlockImportNotification<Block> {
460	/// Create finality notification from finality summary.
461	pub fn from_summary(
462		summary: ImportSummary<Block>,
463		unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
464	) -> BlockImportNotification<Block> {
465		let hash = summary.hash;
466		BlockImportNotification {
467			hash,
468			origin: summary.origin,
469			header: summary.header,
470			is_new_best: summary.is_new_best,
471			tree_route: summary.tree_route.map(Arc::new),
472			unpin_handle: UnpinHandle::new(hash, unpin_worker_sender),
473		}
474	}
475}