/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2023 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
use std::collections::{HashMap, HashSet};
use async_std::sync::{Arc, RwLock};
use chrono::Utc;
use darkfi_serial::serialize;
use futures::{select, FutureExt};
use log::{debug, error, warn};
use rand::Rng;
use smol::Executor;
use crate::{
net,
net::P2pPtr,
util::async_util::sleep,
Error::{NetworkNotConnected, UnknownKey},
Result,
};
mod messages;
use messages::{KeyRequest, KeyResponse, LookupMapRequest, LookupMapResponse, LookupRequest};
mod protocol;
use protocol::Protocol;
// Constants configuration
const SEEN_DURATION: i64 = 120;
/// Atomic pointer to DHT state
pub type DhtPtr = Arc>;
// TODO: proper errors
// TODO: lookup table to be based on directly connected peers, not broadcast based
// Using string in structures because we are at an external crate
// and cant use blake3 serialization. To be replaced once merged with core src.
/// Struct representing DHT state.
pub struct Dht {
/// Daemon id
pub id: blake3::Hash,
/// Daemon hasmap
pub map: HashMap>,
/// Network lookup map, containing nodes that holds each key
pub lookup: HashMap>,
/// P2P network pointer
pub p2p: P2pPtr,
/// Channel to receive responses from P2P
p2p_recv_channel: smol::channel::Receiver,
/// Stop signal channel to terminate background processes
stop_signal: smol::channel::Receiver<()>,
/// Daemon seen requests/responses ids and timestamp,
/// to prevent rebroadcasting and loops
pub seen: HashMap,
}
impl Dht {
pub async fn new(
initial: Option>>,
p2p_ptr: P2pPtr,
stop_signal: smol::channel::Receiver<()>,
ex: Arc>,
) -> Result {
// Generate a random id
let mut rng = rand::thread_rng();
let n: u16 = rng.gen();
let id = blake3::hash(&serialize(&n));
let map = HashMap::default();
let lookup = match initial {
Some(l) => l,
None => HashMap::default(),
};
let p2p = p2p_ptr.clone();
let (p2p_send_channel, p2p_recv_channel) = smol::channel::unbounded::();
let seen = HashMap::default();
let dht = Arc::new(RwLock::new(Dht {
id,
map,
lookup,
p2p,
p2p_recv_channel,
stop_signal,
seen,
}));
// Registering P2P protocols
let registry = p2p_ptr.protocol_registry();
let _dht = dht.clone();
registry
.register(net::SESSION_ALL, move |channel, p2p_ptr| {
let sender = p2p_send_channel.clone();
let dht = _dht.clone();
async move { Protocol::init(channel, sender, dht, p2p_ptr).await.unwrap() }
})
.await;
// Task to periodically clean up daemon seen messages
ex.spawn(prune_seen_messages(dht.clone())).detach();
Ok(dht)
}
/// Store provided key value pair, update lookup map and broadcast new insert to network
pub async fn insert(
&mut self,
key: blake3::Hash,
value: Vec,
) -> Result