// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
use parking_lot::Mutex;
use sc_client_api::{
execution_extensions::ExecutionExtensions, BlockBackend, BlockImportNotification,
BlockchainEvents, CallExecutor, ChildInfo, ExecutorProvider, FinalityNotification,
FinalityNotifications, FinalizeSummary, ImportNotifications, KeysIter, MerkleValue, PairsIter,
StorageData, StorageEventStream, StorageKey, StorageProvider,
};
use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender};
use sp_api::{CallApiAt, CallApiAtParams};
use sp_blockchain::{BlockStatus, CachedHeaderMetadata, HeaderBackend, HeaderMetadata, Info};
use sp_consensus::BlockOrigin;
use sp_runtime::{
generic::SignedBlock,
traits::{Block as BlockT, Header as HeaderT, NumberFor},
Justifications,
};
use sp_version::RuntimeVersion;
use std::sync::Arc;
use substrate_test_runtime::{Block, Hash, Header};
pub struct ChainHeadMockClient {
client: Arc,
import_sinks: Mutex>>>,
finality_sinks: Mutex>>>,
}
impl ChainHeadMockClient {
pub fn new(client: Arc) -> Self {
ChainHeadMockClient {
client,
import_sinks: Default::default(),
finality_sinks: Default::default(),
}
}
pub async fn trigger_import_stream(&self, header: Header) {
// Ensure the client called the `import_notification_stream`.
while self.import_sinks.lock().is_empty() {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
}
// Build the notification.
let (sink, _stream) = tracing_unbounded("test_sink", 100_000);
let notification =
BlockImportNotification::new(header.hash(), BlockOrigin::Own, header, true, None, sink);
for sink in self.import_sinks.lock().iter_mut() {
let _ = sink.unbounded_send(notification.clone());
}
}
pub async fn trigger_finality_stream(&self, header: Header) {
// Ensure the client called the `finality_notification_stream`.
while self.finality_sinks.lock().is_empty() {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
}
// Build the notification.
let (sink, _stream) = tracing_unbounded("test_sink", 100_000);
let summary = FinalizeSummary {
header: header.clone(),
finalized: vec![header.hash()],
stale_heads: vec![],
};
let notification = FinalityNotification::from_summary(summary, sink);
for sink in self.finality_sinks.lock().iter_mut() {
let _ = sink.unbounded_send(notification.clone());
}
}
}
// ChainHead calls `import_notification_stream` and `finality_notification_stream` in order to
// subscribe to block events.
impl BlockchainEvents for ChainHeadMockClient {
fn import_notification_stream(&self) -> ImportNotifications {
let (sink, stream) = tracing_unbounded("import_notification_stream", 1024);
self.import_sinks.lock().push(sink);
stream
}
fn every_import_notification_stream(&self) -> ImportNotifications {
unimplemented!()
}
fn finality_notification_stream(&self) -> FinalityNotifications {
let (sink, stream) = tracing_unbounded("finality_notification_stream", 1024);
self.finality_sinks.lock().push(sink);
stream
}
fn storage_changes_notification_stream(
&self,
_filter_keys: Option<&[StorageKey]>,
_child_filter_keys: Option<&[(StorageKey, Option>)]>,
) -> sp_blockchain::Result> {
unimplemented!()
}
}
// The following implementations are imposed by the `chainHead` trait bounds.
impl, Client: ExecutorProvider>
ExecutorProvider for ChainHeadMockClient
{
type Executor = >::Executor;
fn executor(&self) -> &Self::Executor {
self.client.executor()
}
fn execution_extensions(&self) -> &ExecutionExtensions {
self.client.execution_extensions()
}
}
impl<
BE: sc_client_api::backend::Backend + Send + Sync + 'static,
Block: BlockT,
Client: StorageProvider,
> StorageProvider for ChainHeadMockClient
{
fn storage(
&self,
hash: Block::Hash,
key: &StorageKey,
) -> sp_blockchain::Result