| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- use async_std::sync::{Arc, Mutex};
- use std::collections::VecDeque;
- use async_executor::Executor;
- use async_trait::async_trait;
- use chrono::Utc;
- use fxhash::FxHashMap;
- use log::debug;
- use rand::{rngs::OsRng, RngCore};
- use darkfi::{
- net,
- util::{
- serial::{SerialDecodable, SerialEncodable},
- sleep,
- },
- Result,
- };
- use crate::{
- chains::{Chains, Privmsg},
- settings,
- };
- #[derive(Clone)]
- pub struct RingBuffer<T> {
- pub items: VecDeque<T>,
- }
- impl<T: Eq + PartialEq + Clone> RingBuffer<T> {
- pub fn new(capacity: usize) -> Self {
- let items = VecDeque::with_capacity(capacity);
- Self { items }
- }
- pub fn push(&mut self, val: T) {
- if self.items.len() == self.items.capacity() {
- self.items.pop_front();
- }
- self.items.push_back(val);
- }
- pub fn contains(&self, val: &T) -> bool {
- self.items.contains(val)
- }
- }
- pub struct SeenIds {
- ids: Mutex<RingBuffer<String>>,
- }
- impl SeenIds {
- pub fn new() -> Self {
- Self { ids: Mutex::new(RingBuffer::new(settings::SIZE_OF_IDSS_BUFFER)) }
- }
- pub async fn push(&self, id: &String) -> bool {
- let ids = &mut self.ids.lock().await;
- if !ids.contains(id) {
- ids.push(id.clone());
- return true
- }
- false
- }
- }
- pub struct UnreadMsgs {
- msgs: Mutex<FxHashMap<String, Privmsg>>,
- }
- impl UnreadMsgs {
- pub fn new() -> Self {
- Self { msgs: Mutex::new(FxHashMap::default()) }
- }
- pub async fn contains(&self, key: &str) -> bool {
- self.msgs.lock().await.contains_key(key)
- }
- // Increase the read_confirms for a message, if it has exceeded the MAX_CONFIRM
- // then remove it from the hash_map and return Some(msg), otherwise return None
- pub async fn inc_read_confirms(&self, key: &str) -> Option<Privmsg> {
- let msgs = &mut self.msgs.lock().await;
- let mut result = None;
- if let Some(msg) = msgs.get_mut(key) {
- msg.read_confirms += 1;
- if msg.read_confirms >= settings::MAX_CONFIRM {
- result = Some(msg.clone())
- }
- }
- if result.is_some() {
- msgs.remove(key);
- }
- result
- }
- pub async fn insert(&self, msg: &Privmsg) {
- let msgs = &mut self.msgs.lock().await;
- // prune expired msgs
- let mut prune_ids = vec![];
- for (id, m) in msgs.iter() {
- if m.timestamp + settings::UNREAD_MSG_EXPIRE_TIME < Utc::now().timestamp() {
- prune_ids.push(id.clone());
- }
- }
- for id in prune_ids {
- msgs.remove(&id);
- }
- msgs.insert(msg.id.clone(), msg.clone());
- }
- }
- #[derive(SerialDecodable, SerialEncodable, Clone, Debug)]
- struct Inv {
- id: String,
- hash: String,
- target: String,
- }
- #[derive(SerialDecodable, SerialEncodable, Clone, Debug)]
- struct GetMsgs {
- invs: Vec<String>,
- target: String,
- }
- #[derive(SerialDecodable, SerialEncodable, Clone, Debug)]
- struct Hashes {
- hashes: Vec<String>,
- height: usize,
- target: String,
- }
- #[derive(SerialDecodable, SerialEncodable, Clone, Debug)]
- struct SyncHash {
- height: usize,
- target: String,
- }
- pub struct ProtocolPrivmsg {
- jobsman: net::ProtocolJobsManagerPtr,
- notify: async_channel::Sender<Privmsg>,
- msg_sub: net::MessageSubscription<Privmsg>,
- inv_sub: net::MessageSubscription<Inv>,
- getmsgs_sub: net::MessageSubscription<GetMsgs>,
- hashes_sub: net::MessageSubscription<Hashes>,
- synchash_sub: net::MessageSubscription<SyncHash>,
- p2p: net::P2pPtr,
- channel: net::ChannelPtr,
- chains: Chains,
- seen_ids: SeenIds,
- unread_msgs: UnreadMsgs,
- }
- impl ProtocolPrivmsg {
- pub async fn init(
- channel: net::ChannelPtr,
- notify: async_channel::Sender<Privmsg>,
- p2p: net::P2pPtr,
- chains: Chains,
- seen_ids: SeenIds,
- unread_msgs: UnreadMsgs,
- ) -> net::ProtocolBasePtr {
- let message_subsytem = channel.get_message_subsystem();
- message_subsytem.add_dispatch::<Privmsg>().await;
- message_subsytem.add_dispatch::<Inv>().await;
- message_subsytem.add_dispatch::<GetMsgs>().await;
- message_subsytem.add_dispatch::<Hashes>().await;
- message_subsytem.add_dispatch::<SyncHash>().await;
- let msg_sub =
- channel.clone().subscribe_msg::<Privmsg>().await.expect("Missing Privmsg dispatcher!");
- let inv_sub = channel.subscribe_msg::<Inv>().await.expect("Missing Inv dispatcher!");
- let getmsgs_sub =
- channel.clone().subscribe_msg::<GetMsgs>().await.expect("Missing GetMsgs dispatcher!");
- let hashes_sub =
- channel.clone().subscribe_msg::<Hashes>().await.expect("Missing Hashes dispatcher!");
- let synchash_sub = channel
- .clone()
- .subscribe_msg::<SyncHash>()
- .await
- .expect("Missing HashSync dispatcher!");
- Arc::new(Self {
- notify,
- msg_sub,
- inv_sub,
- getmsgs_sub,
- hashes_sub,
- synchash_sub,
- jobsman: net::ProtocolJobsManager::new("ProtocolPrivmsg", channel.clone()),
- p2p,
- channel,
- chains,
- seen_ids,
- unread_msgs,
- })
- }
- async fn handle_receive_inv(self: Arc<Self>) -> Result<()> {
- debug!(target: "ircd", "ProtocolPrivmsg::handle_receive_inv() [START]");
- let exclude_list = vec![self.channel.address()];
- loop {
- let inv = self.inv_sub.receive().await?;
- let inv = (*inv).to_owned();
- if !self.seen_ids.push(&inv.id).await {
- continue
- }
- // On receive inv message, if the unread_msgs buffer has the msg's hash then increase
- // the read_confirms, if not then send GetMsgs contain the msg's hash
- if !self.unread_msgs.contains(&inv.hash).await {
- self.send_getmsgs(&inv.target, vec![inv.hash.clone()]).await?;
- } else if let Some(msg) = self.unread_msgs.inc_read_confirms(&inv.hash).await {
- self.new_msg(&msg).await?;
- }
- // Either way, broadcast the inv msg
- self.p2p.broadcast_with_exclude(inv, &exclude_list).await?;
- }
- }
- async fn handle_receive_msg(self: Arc<Self>) -> Result<()> {
- debug!(target: "ircd", "ProtocolPrivmsg::handle_receive_msg() [START]");
- let exclude_list = vec![self.channel.address()];
- loop {
- let msg = self.msg_sub.receive().await?;
- let mut msg = (*msg).to_owned();
- if !self.seen_ids.push(&msg.id).await {
- continue
- }
- // If the msg has read_confirms greater or equal to MAX_CONFIRM, it will be added to
- // the chains, otherwise increase the msg's read_confirms, add it to unread_msgs, and
- // broadcast an Inv msg contain the hash of the message
- if msg.read_confirms >= settings::MAX_CONFIRM {
- self.new_msg(&msg).await?;
- } else {
- msg.read_confirms += 1;
- self.unread_msgs.insert(&msg).await;
- self.send_inv_msg(&msg).await?;
- }
- // Broadcast the msg
- self.p2p.broadcast_with_exclude(msg, &exclude_list).await?;
- }
- }
- async fn handle_receive_getmsgs(self: Arc<Self>) -> Result<()> {
- debug!(target: "ircd", "ProtocolPrivmsg::handle_receive_getmsgs() [START]");
- loop {
- let getmsgs = self.getmsgs_sub.receive().await?;
- // Load the msgs from the chains, and send them back to the sender
- let msgs = self.chains.get_msgs(&getmsgs.target, &getmsgs.invs).await;
- for msg in msgs {
- self.channel.send(msg.clone()).await?;
- }
- }
- }
- async fn handle_receive_hashes(self: Arc<Self>) -> Result<()> {
- debug!(target: "ircd", "ProtocolPrivmsg::handle_receive_hashes() [START]");
- loop {
- let hashmsg = self.hashes_sub.receive().await?;
- self.chains
- .push_hashes(hashmsg.target.clone(), hashmsg.height, hashmsg.hashes.clone())
- .await;
- }
- }
- async fn handle_receive_synchash(self: Arc<Self>) -> Result<()> {
- debug!(target: "ircd", "ProtocolPrivmsg::handle_receive_synchash() [START]");
- loop {
- let synchash = self.synchash_sub.receive().await?;
- if synchash.height < self.chains.get_height(&synchash.target).await {
- let hashes = self.chains.get_hashes(&synchash.target, synchash.height + 1).await;
- // send the hashes from the chain
- self.channel
- .send(Hashes {
- target: synchash.target.clone(),
- height: synchash.height + 1,
- hashes: hashes.clone(),
- })
- .await?;
- // send the msgs from the chain's buffer
- let msgs = self.chains.get_msgs(&synchash.target, &hashes).await;
- for msg in msgs {
- self.channel.send(msg).await?;
- }
- }
- }
- }
- // every 2 seconds send a SyncHash msg, contain the last_height for each chain
- async fn send_sync_hash_loop(self: Arc<Self>) -> Result<()> {
- loop {
- // TODO loop through preconfigured channels
- let height = self.chains.get_height("").await;
- self.channel.send(SyncHash { target: "".to_string(), height }).await?;
- sleep(2).await;
- }
- }
- async fn new_msg(&self, msg: &Privmsg) -> Result<()> {
- if self.chains.push_msg(msg).await {
- self.notify.send(msg.clone()).await?;
- }
- Ok(())
- }
- async fn send_inv_msg(&self, msg: &Privmsg) -> Result<()> {
- let inv_id = OsRng.next_u64().to_string();
- self.p2p
- .broadcast(Inv { id: inv_id, hash: msg.id.clone(), target: msg.target.clone() })
- .await?;
- Ok(())
- }
- async fn send_getmsgs(&self, target: &str, hashes: Vec<String>) -> Result<()> {
- self.channel.send(GetMsgs { target: target.to_string(), invs: hashes }).await?;
- Ok(())
- }
- }
- #[async_trait]
- impl net::ProtocolBase for ProtocolPrivmsg {
- /// Starts ping-pong keep-alive messages exchange. Runs ping-pong in the
- /// protocol task manager, then queues the reply. Sends out a ping and
- /// waits for pong reply. Waits for ping and replies with a pong.
- async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) -> Result<()> {
- debug!(target: "ircd", "ProtocolPrivmsg::start() [START]");
- self.jobsman.clone().start(executor.clone());
- self.jobsman.clone().spawn(self.clone().handle_receive_msg(), executor.clone()).await;
- self.jobsman.clone().spawn(self.clone().handle_receive_inv(), executor.clone()).await;
- self.jobsman.clone().spawn(self.clone().handle_receive_getmsgs(), executor.clone()).await;
- self.jobsman.clone().spawn(self.clone().handle_receive_hashes(), executor.clone()).await;
- self.jobsman.clone().spawn(self.clone().handle_receive_synchash(), executor.clone()).await;
- self.jobsman.clone().spawn(self.clone().send_sync_hash_loop(), executor.clone()).await;
- debug!(target: "ircd", "ProtocolPrivmsg::start() [END]");
- Ok(())
- }
- fn name(&self) -> &'static str {
- "ProtocolPrivmsg"
- }
- }
- impl net::Message for Privmsg {
- fn name() -> &'static str {
- "privmsg"
- }
- }
- impl net::Message for Inv {
- fn name() -> &'static str {
- "inv"
- }
- }
- impl net::Message for GetMsgs {
- fn name() -> &'static str {
- "getmsgs"
- }
- }
- impl net::Message for Hashes {
- fn name() -> &'static str {
- "hashes"
- }
- }
- impl net::Message for SyncHash {
- fn name() -> &'static str {
- "synchash"
- }
- }
|