// 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> { self.client.storage(hash, key) } fn storage_hash( &self, hash: Block::Hash, key: &StorageKey, ) -> sp_blockchain::Result> { self.client.storage_hash(hash, key) } fn storage_keys( &self, hash: Block::Hash, prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, ) -> sp_blockchain::Result> { self.client.storage_keys(hash, prefix, start_key) } fn storage_pairs( &self, hash: ::Hash, prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, ) -> sp_blockchain::Result> { self.client.storage_pairs(hash, prefix, start_key) } fn child_storage( &self, hash: Block::Hash, child_info: &ChildInfo, key: &StorageKey, ) -> sp_blockchain::Result> { self.client.child_storage(hash, child_info, key) } fn child_storage_keys( &self, hash: Block::Hash, child_info: ChildInfo, prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, ) -> sp_blockchain::Result> { self.client.child_storage_keys(hash, child_info, prefix, start_key) } fn child_storage_hash( &self, hash: Block::Hash, child_info: &ChildInfo, key: &StorageKey, ) -> sp_blockchain::Result> { self.client.child_storage_hash(hash, child_info, key) } fn closest_merkle_value( &self, hash: Block::Hash, key: &StorageKey, ) -> sp_blockchain::Result>> { self.client.closest_merkle_value(hash, key) } fn child_closest_merkle_value( &self, hash: Block::Hash, child_info: &ChildInfo, key: &StorageKey, ) -> sp_blockchain::Result>> { self.client.child_closest_merkle_value(hash, child_info, key) } } impl> CallApiAt for ChainHeadMockClient { type StateBackend = >::StateBackend; fn call_api_at(&self, params: CallApiAtParams) -> Result, sp_api::ApiError> { self.client.call_api_at(params) } fn runtime_version_at(&self, hash: Block::Hash) -> Result { self.client.runtime_version_at(hash) } fn state_at(&self, at: Block::Hash) -> Result { self.client.state_at(at) } fn initialize_extensions( &self, at: ::Hash, extensions: &mut sp_externalities::Extensions, ) -> Result<(), sp_api::ApiError> { self.client.initialize_extensions(at, extensions) } } impl> BlockBackend for ChainHeadMockClient { fn block_body( &self, hash: Block::Hash, ) -> sp_blockchain::Result::Extrinsic>>> { self.client.block_body(hash) } fn block(&self, hash: Block::Hash) -> sp_blockchain::Result>> { self.client.block(hash) } fn block_status(&self, hash: Block::Hash) -> sp_blockchain::Result { self.client.block_status(hash) } fn justifications(&self, hash: Block::Hash) -> sp_blockchain::Result> { self.client.justifications(hash) } fn block_hash(&self, number: NumberFor) -> sp_blockchain::Result> { self.client.block_hash(number) } fn indexed_transaction(&self, hash: Block::Hash) -> sp_blockchain::Result>> { self.client.indexed_transaction(hash) } fn has_indexed_transaction(&self, hash: Block::Hash) -> sp_blockchain::Result { self.client.has_indexed_transaction(hash) } fn block_indexed_body(&self, hash: Block::Hash) -> sp_blockchain::Result>>> { self.client.block_indexed_body(hash) } fn requires_full_sync(&self) -> bool { self.client.requires_full_sync() } } impl + Send + Sync> HeaderMetadata for ChainHeadMockClient { type Error = >::Error; fn header_metadata( &self, hash: Block::Hash, ) -> Result, Self::Error> { self.client.header_metadata(hash) } fn insert_header_metadata( &self, hash: Block::Hash, header_metadata: CachedHeaderMetadata, ) { self.client.insert_header_metadata(hash, header_metadata) } fn remove_header_metadata(&self, hash: Block::Hash) { self.client.remove_header_metadata(hash) } } impl + Send + Sync> HeaderBackend for ChainHeadMockClient { fn header( &self, hash: Block::Hash, ) -> sp_blockchain::Result::Header>> { self.client.header(hash) } fn info(&self) -> Info { self.client.info() } fn status(&self, hash: Block::Hash) -> sc_client_api::blockchain::Result { self.client.status(hash) } fn number( &self, hash: Block::Hash, ) -> sc_client_api::blockchain::Result::Header as HeaderT>::Number>> { self.client.number(hash) } fn hash( &self, number: <::Header as HeaderT>::Number, ) -> sp_blockchain::Result> { self.client.hash(number) } }