use sp_consensus::BlockOrigin;
use sp_core::storage::StorageKey;
use sp_runtime::{
generic::SignedBlock,
traits::{Block as BlockT, NumberFor},
Justifications,
};
use std::{
collections::HashSet,
fmt::{self, Debug},
sync::Arc,
};
use crate::{
blockchain::Info, notifications::StorageEventStream, FinalizeSummary, ImportSummary, StaleBlock,
};
use sc_transaction_pool_api::ChainEvent;
use sc_utils::mpsc::{TracingUnboundedReceiver, TracingUnboundedSender};
use sp_blockchain;
pub type ImportNotifications<Block> = TracingUnboundedReceiver<BlockImportNotification<Block>>;
pub type FinalityNotifications<Block> = TracingUnboundedReceiver<FinalityNotification<Block>>;
pub type ForkBlocks<Block> = Option<Vec<(NumberFor<Block>, <Block as BlockT>::Hash)>>;
pub type BadBlocks<Block> = Option<HashSet<<Block as BlockT>::Hash>>;
pub trait BlockOf {
type Type: BlockT;
}
pub trait BlockchainEvents<Block: BlockT> {
fn import_notification_stream(&self) -> ImportNotifications<Block>;
fn every_import_notification_stream(&self) -> ImportNotifications<Block>;
fn finality_notification_stream(&self) -> FinalityNotifications<Block>;
fn storage_changes_notification_stream(
&self,
filter_keys: Option<&[StorageKey]>,
child_filter_keys: Option<&[(StorageKey, Option<Vec<StorageKey>>)]>,
) -> sp_blockchain::Result<StorageEventStream<Block::Hash>>;
}
pub type AuxDataOperations = Vec<(Vec<u8>, Option<Vec<u8>>)>;
pub type OnImportAction<Block> =
Box<dyn (Fn(&BlockImportNotification<Block>) -> AuxDataOperations) + Send>;
pub type OnFinalityAction<Block> =
Box<dyn (Fn(&FinalityNotification<Block>) -> AuxDataOperations) + Send>;
pub trait PreCommitActions<Block: BlockT> {
fn register_import_action(&self, op: OnImportAction<Block>);
fn register_finality_action(&self, op: OnFinalityAction<Block>);
}
pub trait BlockBackend<Block: BlockT> {
fn block_body(
&self,
hash: Block::Hash,
) -> sp_blockchain::Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
fn block_indexed_body(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<Vec<Vec<u8>>>>;
fn block(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<SignedBlock<Block>>>;
fn block_status(&self, hash: Block::Hash) -> sp_blockchain::Result<sp_consensus::BlockStatus>;
fn justifications(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<Justifications>>;
fn block_hash(&self, number: NumberFor<Block>) -> sp_blockchain::Result<Option<Block::Hash>>;
fn indexed_transaction(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<Vec<u8>>>;
fn has_indexed_transaction(&self, hash: Block::Hash) -> sp_blockchain::Result<bool> {
Ok(self.indexed_transaction(hash)?.is_some())
}
fn requires_full_sync(&self) -> bool;
}
pub trait ProvideUncles<Block: BlockT> {
fn uncles(
&self,
target_hash: Block::Hash,
max_generation: NumberFor<Block>,
) -> sp_blockchain::Result<Vec<Block::Header>>;
}
#[derive(Debug, Clone)]
pub struct ClientInfo<Block: BlockT> {
pub chain: Info<Block>,
pub usage: Option<UsageInfo>,
}
#[derive(Default, Clone, Debug, Copy)]
pub struct MemorySize(usize);
impl MemorySize {
pub fn from_bytes(bytes: usize) -> Self {
Self(bytes)
}
pub fn as_bytes(self) -> usize {
self.0
}
}
impl fmt::Display for MemorySize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0 < 1024 {
write!(f, "{} bytes", self.0)
} else if self.0 < 1024 * 1024 {
write!(f, "{:.2} KiB", self.0 as f64 / 1024f64)
} else if self.0 < 1024 * 1024 * 1024 {
write!(f, "{:.2} MiB", self.0 as f64 / (1024f64 * 1024f64))
} else {
write!(f, "{:.2} GiB", self.0 as f64 / (1024f64 * 1024f64 * 1024f64))
}
}
}
#[derive(Default, Clone, Debug)]
pub struct MemoryInfo {
pub state_cache: MemorySize,
pub database_cache: MemorySize,
}
#[derive(Default, Clone, Debug)]
pub struct IoInfo {
pub transactions: u64,
pub bytes_read: u64,
pub bytes_written: u64,
pub writes: u64,
pub reads: u64,
pub average_transaction_size: u64,
pub state_reads: u64,
pub state_reads_cache: u64,
pub state_writes: u64,
pub state_writes_cache: u64,
pub state_writes_nodes: u64,
}
#[derive(Default, Clone, Debug)]
pub struct UsageInfo {
pub memory: MemoryInfo,
pub io: IoInfo,
}
impl fmt::Display for UsageInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"caches: ({} state, {} db overlay), \
i/o: ({} tx, {} write, {} read, {} avg tx, {}/{} key cache reads/total, {} trie nodes writes)",
self.memory.state_cache,
self.memory.database_cache,
self.io.transactions,
self.io.bytes_written,
self.io.bytes_read,
self.io.average_transaction_size,
self.io.state_reads_cache,
self.io.state_reads,
self.io.state_writes_nodes,
)
}
}
pub struct UnpinHandleInner<Block: BlockT> {
hash: Block::Hash,
unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
}
impl<Block: BlockT> Debug for UnpinHandleInner<Block> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UnpinHandleInner").field("pinned_block", &self.hash).finish()
}
}
impl<Block: BlockT> UnpinHandleInner<Block> {
pub fn new(
hash: Block::Hash,
unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
) -> Self {
Self { hash, unpin_worker_sender }
}
}
impl<Block: BlockT> Drop for UnpinHandleInner<Block> {
fn drop(&mut self) {
if let Err(err) =
self.unpin_worker_sender.unbounded_send(UnpinWorkerMessage::Unpin(self.hash))
{
log::debug!(target: "db", "Unable to unpin block with hash: {}, error: {:?}", self.hash, err);
};
}
}
#[derive(Debug)]
pub enum UnpinWorkerMessage<Block: BlockT> {
AnnouncePin(Block::Hash),
Unpin(Block::Hash),
}
#[derive(Debug, Clone)]
pub struct UnpinHandle<Block: BlockT>(Arc<UnpinHandleInner<Block>>);
impl<Block: BlockT> UnpinHandle<Block> {
pub fn new(
hash: Block::Hash,
unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
) -> UnpinHandle<Block> {
UnpinHandle(Arc::new(UnpinHandleInner::new(hash, unpin_worker_sender)))
}
pub fn hash(&self) -> Block::Hash {
self.0.hash
}
}
#[derive(Clone, Debug)]
pub struct BlockImportNotification<Block: BlockT> {
pub hash: Block::Hash,
pub origin: BlockOrigin,
pub header: Block::Header,
pub is_new_best: bool,
pub tree_route: Option<Arc<sp_blockchain::TreeRoute<Block>>>,
unpin_handle: UnpinHandle<Block>,
}
impl<Block: BlockT> BlockImportNotification<Block> {
pub fn new(
hash: Block::Hash,
origin: BlockOrigin,
header: Block::Header,
is_new_best: bool,
tree_route: Option<Arc<sp_blockchain::TreeRoute<Block>>>,
unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
) -> Self {
Self {
hash,
origin,
header,
is_new_best,
tree_route,
unpin_handle: UnpinHandle::new(hash, unpin_worker_sender),
}
}
pub fn into_unpin_handle(self) -> UnpinHandle<Block> {
self.unpin_handle
}
}
#[derive(Clone, Debug)]
pub struct FinalityNotification<Block: BlockT> {
pub hash: Block::Hash,
pub header: Block::Header,
pub tree_route: Arc<[Block::Hash]>,
pub stale_blocks: Arc<[Arc<StaleBlock<Block>>]>,
unpin_handle: UnpinHandle<Block>,
}
impl<B: BlockT> TryFrom<BlockImportNotification<B>> for ChainEvent<B> {
type Error = ();
fn try_from(n: BlockImportNotification<B>) -> Result<Self, ()> {
if n.is_new_best {
Ok(Self::NewBestBlock { hash: n.hash, tree_route: n.tree_route })
} else {
Err(())
}
}
}
impl<B: BlockT> From<FinalityNotification<B>> for ChainEvent<B> {
fn from(n: FinalityNotification<B>) -> Self {
Self::Finalized { hash: n.hash, tree_route: n.tree_route }
}
}
impl<Block: BlockT> FinalityNotification<Block> {
pub fn from_summary(
mut summary: FinalizeSummary<Block>,
unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
) -> FinalityNotification<Block> {
let hash = summary.finalized.pop().unwrap_or_default();
FinalityNotification {
hash,
header: summary.header,
tree_route: Arc::from(summary.finalized),
stale_blocks: Arc::from(
summary.stale_blocks.into_iter().map(Arc::from).collect::<Vec<_>>(),
),
unpin_handle: UnpinHandle::new(hash, unpin_worker_sender),
}
}
pub fn into_unpin_handle(self) -> UnpinHandle<Block> {
self.unpin_handle
}
}
impl<Block: BlockT> BlockImportNotification<Block> {
pub fn from_summary(
summary: ImportSummary<Block>,
unpin_worker_sender: TracingUnboundedSender<UnpinWorkerMessage<Block>>,
) -> BlockImportNotification<Block> {
let hash = summary.hash;
BlockImportNotification {
hash,
origin: summary.origin,
header: summary.header,
is_new_best: summary.is_new_best,
tree_route: summary.tree_route.map(Arc::new),
unpin_handle: UnpinHandle::new(hash, unpin_worker_sender),
}
}
}