referrerpolicy=no-referrer-when-downgrade

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