use std::{marker::PhantomData, sync::Arc};
use debug_logger::DebugLogger;
use dkg_runtime_primitives::{crypto::AuthorityId, DKGApi, MaxAuthorities, MaxProposalLength};
use parking_lot::RwLock;
use prometheus::Registry;
use sc_client_api::{Backend, BlockchainEvents};
use sc_keystore::LocalKeystore;
use sc_network::{config::ExHashT, NetworkService, ProtocolName};
use sc_network_sync::SyncingService;
use sp_api::{NumberFor, ProvideRuntimeApi};
use sp_blockchain::HeaderBackend;
use sp_keystore::KeystorePtr;
use sp_runtime::traits::Block;
mod error;
pub mod keyring;
pub mod keystore;
pub mod gossip_engine;
mod keygen_manager;
pub mod signing_manager;
pub mod db;
mod metrics;
mod utils;
pub mod worker;
pub mod async_protocols;
pub use dkg_logging::debug_logger;
pub mod constants;
pub mod dkg_modules;
pub mod gossip_messages;
pub mod storage;
pub use constants::{DKG_KEYGEN_PROTOCOL_NAME, DKG_SIGNING_PROTOCOL_NAME};
pub use debug_logger::RoundsEventType;
use gossip_engine::NetworkGossipEngineBuilder;
pub use keystore::DKGKeystore;
pub fn dkg_peers_set_config(
protocol_name: ProtocolName,
) -> sc_network::config::NonDefaultSetConfig {
NetworkGossipEngineBuilder::set_config(protocol_name)
}
pub trait Client<B, BE>:
BlockchainEvents<B> + HeaderBackend<B> + ProvideRuntimeApi<B> + Send + Sync
where
B: Block,
BE: Backend<B>,
{
}
impl<B, BE, T> Client<B, BE> for T
where
B: Block,
BE: Backend<B>,
T: BlockchainEvents<B> + HeaderBackend<B> + ProvideRuntimeApi<B> + Send + Sync,
{
}
pub struct DKGParams<B, BE, C>
where
B: Block,
<B as Block>::Hash: ExHashT,
BE: Backend<B>,
C: Client<B, BE>,
C::Api: DKGApi<B, AuthorityId, NumberFor<B>, MaxProposalLength, MaxAuthorities>,
{
pub client: Arc<C>,
pub backend: Arc<BE>,
pub key_store: Option<KeystorePtr>,
pub local_keystore: Option<Arc<LocalKeystore>>,
pub network: Arc<NetworkService<B, B::Hash>>,
pub sync_service: Arc<SyncingService<B>>,
pub prometheus_registry: Option<Registry>,
pub debug_logger: DebugLogger,
pub _block: PhantomData<B>,
}
pub async fn start_dkg_gadget<B, BE, C>(dkg_params: DKGParams<B, BE, C>)
where
B: Block,
BE: Backend<B> + Unpin + 'static,
C: Client<B, BE> + 'static,
C::Api: DKGApi<B, AuthorityId, NumberFor<B>, MaxProposalLength, MaxAuthorities>,
{
dkg_logging::setup_log();
let DKGParams {
client,
backend,
key_store,
network,
sync_service,
prometheus_registry,
local_keystore,
_block,
debug_logger,
} = dkg_params;
let dkg_keystore: DKGKeystore = DKGKeystore::new(key_store, debug_logger.clone());
let keygen_gossip_protocol = NetworkGossipEngineBuilder::new(
DKG_KEYGEN_PROTOCOL_NAME.to_string().into(),
dkg_keystore.clone(),
);
let logger_prometheus = debug_logger.clone();
let metrics =
prometheus_registry.as_ref().map(metrics::Metrics::register).and_then(
|result| match result {
Ok(metrics) => {
logger_prometheus.debug("🕸️ Registered metrics");
Some(metrics)
},
Err(err) => {
logger_prometheus.debug(format!("🕸️ Failed to register metrics: {err:?}"));
None
},
},
);
let latest_header = Arc::new(RwLock::new(None));
let (gossip_handler, gossip_engine) = keygen_gossip_protocol
.build(
network.clone(),
sync_service.clone(),
metrics.clone(),
latest_header.clone(),
debug_logger.clone(),
)
.expect("Keygen : Failed to build gossip engine");
gossip_engine.set_gossip_enabled(true);
let gossip_handle = crate::utils::ExplicitPanicFuture::new(tokio::spawn(gossip_handler.run()));
let offchain_db_backend = db::DKGOffchainStorageDb::new(
backend.clone(),
dkg_keystore.clone(),
local_keystore.clone(),
debug_logger.clone(),
);
let db_backend = Arc::new(offchain_db_backend);
let worker_params = worker::WorkerParams {
latest_header,
client,
backend,
key_store: dkg_keystore,
gossip_engine,
db_backend,
metrics,
local_keystore,
network: Some(network),
sync_service: Some(sync_service),
test_bundle: None,
_marker: PhantomData,
};
let worker = worker::DKGWorker::<_, _, _, _>::new(worker_params, debug_logger);
worker.run().await;
gossip_handle.abort();
}
pub mod deadlock_detection {
#[cfg(not(feature = "testing"))]
pub fn deadlock_detect() {}
#[cfg(feature = "testing")]
pub fn deadlock_detect() {
static HAS_STARTED: AtomicBool = AtomicBool::new(false);
use parking_lot::deadlock;
use std::{sync::atomic::AtomicBool, thread, time::Duration};
thread::spawn(move || {
if HAS_STARTED
.compare_exchange(
false,
true,
std::sync::atomic::Ordering::SeqCst,
std::sync::atomic::Ordering::SeqCst,
)
.unwrap_or(true)
{
println!("Deadlock detector already started");
return
}
println!("Deadlock detector started");
loop {
thread::sleep(Duration::from_secs(5));
let deadlocks = deadlock::check_deadlock();
if deadlocks.is_empty() {
continue
}
println!("{} deadlocks detected", deadlocks.len());
for (i, threads) in deadlocks.iter().enumerate() {
println!("Deadlock #{i}");
for t in threads {
println!("Thread Id {:#?}", t.thread_id());
println!("{:#?}", t.backtrace());
}
}
}
});
}
}