use crate::dht::{
channel::message_channel,
errors::DhtError,
keys::DhtKeyMaterial,
processor::DhtProcessor,
rpc::{DhtMessageClient, DhtRequest, DhtResponse},
types::{DhtRecord, NetworkInfo, Peer},
DhtConfig, RecordValidator,
};
use libp2p::{Multiaddr, PeerId};
use std::time::Duration;
use tokio;
macro_rules! ensure_response {
($response:expr, $matcher:pat => $statement:expr) => {
match $response {
$matcher => $statement,
_ => Err(DhtError::Error("Unexpected".into())),
}
};
}
pub struct DhtNode {
config: DhtConfig,
client: DhtMessageClient,
thread_handle: tokio::task::JoinHandle<Result<(), DhtError>>,
peer_id: PeerId,
}
impl DhtNode {
pub fn new<K: DhtKeyMaterial, V: RecordValidator + 'static>(
key_material: &K,
config: DhtConfig,
validator: Option<V>,
) -> Result<Self, DhtError> {
let keypair = key_material.to_dht_keypair()?;
let peer_id = PeerId::from(keypair.public());
let channels = message_channel::<DhtRequest, DhtResponse, DhtError>();
let thread_handle =
DhtProcessor::spawn(&keypair, peer_id, validator, config.clone(), channels.1)?;
Ok(DhtNode {
peer_id,
config,
client: channels.0,
thread_handle,
})
}
pub fn config(&self) -> &DhtConfig {
&self.config
}
pub fn peer_id(&self) -> &PeerId {
&self.peer_id
}
pub async fn addresses(&self) -> Result<Vec<Multiaddr>, DhtError> {
let request = DhtRequest::GetAddresses { external: false };
let response = self.send_request(request).await?;
ensure_response!(response, DhtResponse::GetAddresses(addresses) => Ok(addresses))
}
pub async fn external_addresses(&self) -> Result<Vec<Multiaddr>, DhtError> {
let request = DhtRequest::GetAddresses { external: false };
let response = self.send_request(request).await?;
ensure_response!(response, DhtResponse::GetAddresses(addresses) => Ok(addresses))
}
pub async fn add_peers(&self, peers: Vec<Multiaddr>) -> Result<(), DhtError> {
let request = DhtRequest::AddPeers { peers };
let response = self.send_request(request).await?;
ensure_response!(response, DhtResponse::Success => Ok(()))
}
pub async fn listen(&self, listening_address: Multiaddr) -> Result<Multiaddr, DhtError> {
let request = DhtRequest::StartListening {
address: listening_address,
};
let response = self.send_request(request).await?;
ensure_response!(response, DhtResponse::Address(addr) => Ok(addr))
}
pub async fn stop_listening(&self) -> Result<(), DhtError> {
let request = DhtRequest::StopListening;
let response = self.send_request(request).await?;
ensure_response!(response, DhtResponse::Success => Ok(()))
}
pub async fn wait_for_peers(&self, requested_peers: usize) -> Result<(), DhtError> {
loop {
let info = self.network_info().await?;
if info.num_peers >= requested_peers {
return Ok(());
}
tokio::time::sleep(Duration::from_secs(1)).await;
}
}
pub async fn bootstrap(&self) -> Result<(), DhtError> {
let request = DhtRequest::Bootstrap;
let response = self.send_request(request).await?;
ensure_response!(response, DhtResponse::Success => Ok(()))
}
pub async fn network_info(&self) -> Result<NetworkInfo, DhtError> {
let request = DhtRequest::GetNetworkInfo;
let response = self.send_request(request).await?;
ensure_response!(response, DhtResponse::GetNetworkInfo(info) => Ok(info))
}
pub async fn peers(&self) -> Result<Vec<Peer>, DhtError> {
let request = DhtRequest::GetPeers;
let response = self.send_request(request).await?;
ensure_response!(response, DhtResponse::GetPeers(peers) => Ok(peers))
}
pub async fn put_record(&self, key: &[u8], value: &[u8]) -> Result<Vec<u8>, DhtError> {
let request = DhtRequest::PutRecord {
key: key.to_vec(),
value: value.to_vec(),
};
let response = self.send_request(request).await?;
ensure_response!(response, DhtResponse::PutRecord { key } => Ok(key))
}
pub async fn get_record(&self, key: &[u8]) -> Result<DhtRecord, DhtError> {
let request = DhtRequest::GetRecord { key: key.to_vec() };
let response = self.send_request(request).await?;
ensure_response!(response, DhtResponse::GetRecord(record) => Ok(record))
}
pub async fn start_providing(&self, key: &[u8]) -> Result<(), DhtError> {
let request = DhtRequest::StartProviding { key: key.to_vec() };
let response = self.send_request(request).await?;
ensure_response!(response, DhtResponse::Success => Ok(()))
}
pub async fn get_providers(&self, key: &[u8]) -> Result<Vec<PeerId>, DhtError> {
let request = DhtRequest::GetProviders { key: key.to_vec() };
let response = self.send_request(request).await?;
ensure_response!(response, DhtResponse::GetProviders { providers } => Ok(providers))
}
async fn send_request(&self, request: DhtRequest) -> Result<DhtResponse, DhtError> {
self.client
.send_request_async(request)
.await
.map_err(DhtError::from)
.and_then(|res| res)
}
}
impl Drop for DhtNode {
fn drop(&mut self) {
self.thread_handle.abort();
}
}