|
@@ -22,11 +22,12 @@ use std::{path::StripPrefixError, sync::Arc};
|
|
|
use tracing::{debug, error, info};
|
|
use tracing::{debug, error, info};
|
|
|
|
|
|
|
|
use darkfi::{
|
|
use darkfi::{
|
|
|
- dht::DhtHandler,
|
|
|
|
|
|
|
+ dht::{event::DhtEvent, DhtHandler},
|
|
|
geode::hash_to_string,
|
|
geode::hash_to_string,
|
|
|
impl_p2p_message,
|
|
impl_p2p_message,
|
|
|
net::{
|
|
net::{
|
|
|
metering::{MeteringConfiguration, DEFAULT_METERING_CONFIGURATION},
|
|
metering::{MeteringConfiguration, DEFAULT_METERING_CONFIGURATION},
|
|
|
|
|
+ session::SESSION_INBOUND,
|
|
|
ChannelPtr, Message, MessageSubscription, P2pPtr, ProtocolBase, ProtocolBasePtr,
|
|
ChannelPtr, Message, MessageSubscription, P2pPtr, ProtocolBase, ProtocolBasePtr,
|
|
|
ProtocolJobsManager, ProtocolJobsManagerPtr,
|
|
ProtocolJobsManager, ProtocolJobsManagerPtr,
|
|
|
},
|
|
},
|
|
@@ -40,20 +41,42 @@ use crate::{
|
|
|
Fud,
|
|
Fud,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+/// Trait for resource-specific messages.
|
|
|
|
|
+/// Adds a method to get the resource's hash from the message.
|
|
|
|
|
+pub trait ResourceMessage {
|
|
|
|
|
+ fn resource_hash(&self) -> blake3::Hash;
|
|
|
|
|
+}
|
|
|
|
|
+macro_rules! impl_resource_msg {
|
|
|
|
|
+ ($msg:ty, $field:ident) => {
|
|
|
|
|
+ impl ResourceMessage for $msg {
|
|
|
|
|
+ fn resource_hash(&self) -> blake3::Hash {
|
|
|
|
|
+ self.$field
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ ($msg:ty) => {
|
|
|
|
|
+ impl_resource_msg!($msg, resource);
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/// Message representing a file reply from the network
|
|
/// Message representing a file reply from the network
|
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
|
pub struct FudFileReply {
|
|
pub struct FudFileReply {
|
|
|
|
|
+ pub resource: blake3::Hash,
|
|
|
pub chunk_hashes: Vec<blake3::Hash>,
|
|
pub chunk_hashes: Vec<blake3::Hash>,
|
|
|
}
|
|
}
|
|
|
impl_p2p_message!(FudFileReply, "FudFileReply", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
impl_p2p_message!(FudFileReply, "FudFileReply", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
+impl_resource_msg!(FudFileReply);
|
|
|
|
|
|
|
|
/// Message representing a directory reply from the network
|
|
/// Message representing a directory reply from the network
|
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
|
pub struct FudDirectoryReply {
|
|
pub struct FudDirectoryReply {
|
|
|
|
|
+ pub resource: blake3::Hash,
|
|
|
pub chunk_hashes: Vec<blake3::Hash>,
|
|
pub chunk_hashes: Vec<blake3::Hash>,
|
|
|
pub files: Vec<(String, u64)>, // Vec of (file path, file size)
|
|
pub files: Vec<(String, u64)>, // Vec of (file path, file size)
|
|
|
}
|
|
}
|
|
|
impl_p2p_message!(FudDirectoryReply, "FudDirectoryReply", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
impl_p2p_message!(FudDirectoryReply, "FudDirectoryReply", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
+impl_resource_msg!(FudDirectoryReply);
|
|
|
|
|
|
|
|
/// Message representing a node announcing a key on the network
|
|
/// Message representing a node announcing a key on the network
|
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
@@ -66,15 +89,29 @@ impl_p2p_message!(FudAnnounce, "FudAnnounce", 0, 0, DEFAULT_METERING_CONFIGURATI
|
|
|
/// Message representing a chunk reply from the network
|
|
/// Message representing a chunk reply from the network
|
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
|
pub struct FudChunkReply {
|
|
pub struct FudChunkReply {
|
|
|
|
|
+ pub resource: blake3::Hash,
|
|
|
// TODO: This should be a chunk-sized array, but then we need padding?
|
|
// TODO: This should be a chunk-sized array, but then we need padding?
|
|
|
pub chunk: Vec<u8>,
|
|
pub chunk: Vec<u8>,
|
|
|
}
|
|
}
|
|
|
impl_p2p_message!(FudChunkReply, "FudChunkReply", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
impl_p2p_message!(FudChunkReply, "FudChunkReply", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
+impl_resource_msg!(FudChunkReply);
|
|
|
|
|
|
|
|
-/// Message representing a chunk reply when a file is not found
|
|
|
|
|
|
|
+/// Message representing a reply when a metadata is not found
|
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
|
-pub struct FudNotFound;
|
|
|
|
|
-impl_p2p_message!(FudNotFound, "FudNotFound", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
|
|
+pub struct FudMetadataNotFound {
|
|
|
|
|
+ pub resource: blake3::Hash,
|
|
|
|
|
+}
|
|
|
|
|
+impl_p2p_message!(FudMetadataNotFound, "FudMetadataNotFound", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
+impl_resource_msg!(FudMetadataNotFound);
|
|
|
|
|
+
|
|
|
|
|
+/// Message representing a reply when a chunk is not found
|
|
|
|
|
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
|
|
|
+pub struct FudChunkNotFound {
|
|
|
|
|
+ pub resource: blake3::Hash,
|
|
|
|
|
+ pub chunk: blake3::Hash,
|
|
|
|
|
+}
|
|
|
|
|
+impl_p2p_message!(FudChunkNotFound, "FudChunkNotFound", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
+impl_resource_msg!(FudChunkNotFound);
|
|
|
|
|
|
|
|
/// Message representing a ping request on the network
|
|
/// Message representing a ping request on the network
|
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
@@ -87,61 +124,70 @@ impl_p2p_message!(FudPingRequest, "FudPingRequest", 0, 0, DEFAULT_METERING_CONFI
|
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
|
pub struct FudPingReply {
|
|
pub struct FudPingReply {
|
|
|
pub node: FudNode,
|
|
pub node: FudNode,
|
|
|
|
|
+ pub random: u64,
|
|
|
/// Signature of the random u64 from the ping request
|
|
/// Signature of the random u64 from the ping request
|
|
|
pub sig: Signature,
|
|
pub sig: Signature,
|
|
|
}
|
|
}
|
|
|
impl_p2p_message!(FudPingReply, "FudPingReply", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
impl_p2p_message!(FudPingReply, "FudPingReply", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
|
|
|
-/// Message representing a find file/chunk request from the network
|
|
|
|
|
|
|
+/// Message representing a find file/directory request from the network
|
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
|
-pub struct FudFindRequest {
|
|
|
|
|
- pub info: Option<blake3::Hash>,
|
|
|
|
|
- pub key: blake3::Hash,
|
|
|
|
|
|
|
+pub struct FudMetadataRequest {
|
|
|
|
|
+ pub resource: blake3::Hash,
|
|
|
}
|
|
}
|
|
|
-impl_p2p_message!(FudFindRequest, "FudFindRequest", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
|
|
+impl_p2p_message!(FudMetadataRequest, "FudMetadataRequest", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
+impl_resource_msg!(FudMetadataRequest);
|
|
|
|
|
+
|
|
|
|
|
+/// Message representing a find chunk request from the network
|
|
|
|
|
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
|
|
|
+pub struct FudChunkRequest {
|
|
|
|
|
+ pub resource: blake3::Hash,
|
|
|
|
|
+ pub chunk: blake3::Hash,
|
|
|
|
|
+}
|
|
|
|
|
+impl_p2p_message!(FudChunkRequest, "FudChunkRequest", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
+impl_resource_msg!(FudChunkRequest);
|
|
|
|
|
|
|
|
/// Message representing a find nodes request on the network
|
|
/// Message representing a find nodes request on the network
|
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
|
-pub struct FudFindNodesRequest {
|
|
|
|
|
|
|
+pub struct FudNodesRequest {
|
|
|
pub key: blake3::Hash,
|
|
pub key: blake3::Hash,
|
|
|
}
|
|
}
|
|
|
-impl_p2p_message!(FudFindNodesRequest, "FudFindNodesRequest", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
|
|
+impl_p2p_message!(FudNodesRequest, "FudNodesRequest", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
|
|
|
/// Message representing a find nodes reply on the network
|
|
/// Message representing a find nodes reply on the network
|
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
|
-pub struct FudFindNodesReply {
|
|
|
|
|
|
|
+pub struct FudNodesReply {
|
|
|
|
|
+ pub key: blake3::Hash,
|
|
|
pub nodes: Vec<FudNode>,
|
|
pub nodes: Vec<FudNode>,
|
|
|
}
|
|
}
|
|
|
-impl_p2p_message!(FudFindNodesReply, "FudFindNodesReply", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
|
|
+impl_p2p_message!(FudNodesReply, "FudNodesReply", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
+impl_resource_msg!(FudNodesReply, key);
|
|
|
|
|
|
|
|
/// Message representing a find seeders request on the network
|
|
/// Message representing a find seeders request on the network
|
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
|
-pub struct FudFindSeedersRequest {
|
|
|
|
|
|
|
+pub struct FudSeedersRequest {
|
|
|
pub key: blake3::Hash,
|
|
pub key: blake3::Hash,
|
|
|
}
|
|
}
|
|
|
-impl_p2p_message!(
|
|
|
|
|
- FudFindSeedersRequest,
|
|
|
|
|
- "FudFindSeedersRequest",
|
|
|
|
|
- 0,
|
|
|
|
|
- 0,
|
|
|
|
|
- DEFAULT_METERING_CONFIGURATION
|
|
|
|
|
-);
|
|
|
|
|
|
|
+impl_p2p_message!(FudSeedersRequest, "FudSeedersRequest", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
|
|
|
/// Message representing a find seeders reply on the network
|
|
/// Message representing a find seeders reply on the network
|
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
|
|
|
-pub struct FudFindSeedersReply {
|
|
|
|
|
|
|
+pub struct FudSeedersReply {
|
|
|
|
|
+ pub key: blake3::Hash,
|
|
|
pub seeders: Vec<FudSeeder>,
|
|
pub seeders: Vec<FudSeeder>,
|
|
|
pub nodes: Vec<FudNode>,
|
|
pub nodes: Vec<FudNode>,
|
|
|
}
|
|
}
|
|
|
-impl_p2p_message!(FudFindSeedersReply, "FudFindSeedersReply", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
|
|
+impl_p2p_message!(FudSeedersReply, "FudSeedersReply", 0, 0, DEFAULT_METERING_CONFIGURATION);
|
|
|
|
|
+impl_resource_msg!(FudSeedersReply, key);
|
|
|
|
|
|
|
|
/// P2P protocol implementation for fud.
|
|
/// P2P protocol implementation for fud.
|
|
|
pub struct ProtocolFud {
|
|
pub struct ProtocolFud {
|
|
|
channel: ChannelPtr,
|
|
channel: ChannelPtr,
|
|
|
ping_request_sub: MessageSubscription<FudPingRequest>,
|
|
ping_request_sub: MessageSubscription<FudPingRequest>,
|
|
|
- find_request_sub: MessageSubscription<FudFindRequest>,
|
|
|
|
|
- find_nodes_request_sub: MessageSubscription<FudFindNodesRequest>,
|
|
|
|
|
- find_seeders_request_sub: MessageSubscription<FudFindSeedersRequest>,
|
|
|
|
|
|
|
+ find_metadata_request_sub: MessageSubscription<FudMetadataRequest>,
|
|
|
|
|
+ find_chunk_request_sub: MessageSubscription<FudChunkRequest>,
|
|
|
|
|
+ find_nodes_request_sub: MessageSubscription<FudNodesRequest>,
|
|
|
|
|
+ find_seeders_request_sub: MessageSubscription<FudSeedersRequest>,
|
|
|
announce_sub: MessageSubscription<FudAnnounce>,
|
|
announce_sub: MessageSubscription<FudAnnounce>,
|
|
|
fud: Arc<Fud>,
|
|
fud: Arc<Fud>,
|
|
|
jobsman: ProtocolJobsManagerPtr,
|
|
jobsman: ProtocolJobsManagerPtr,
|
|
@@ -156,21 +202,32 @@ impl ProtocolFud {
|
|
|
|
|
|
|
|
let msg_subsystem = channel.message_subsystem();
|
|
let msg_subsystem = channel.message_subsystem();
|
|
|
msg_subsystem.add_dispatch::<FudPingRequest>().await;
|
|
msg_subsystem.add_dispatch::<FudPingRequest>().await;
|
|
|
- msg_subsystem.add_dispatch::<FudFindRequest>().await;
|
|
|
|
|
- msg_subsystem.add_dispatch::<FudFindNodesRequest>().await;
|
|
|
|
|
- msg_subsystem.add_dispatch::<FudFindSeedersRequest>().await;
|
|
|
|
|
|
|
+ msg_subsystem.add_dispatch::<FudPingReply>().await;
|
|
|
|
|
+ msg_subsystem.add_dispatch::<FudMetadataRequest>().await;
|
|
|
|
|
+ msg_subsystem.add_dispatch::<FudChunkRequest>().await;
|
|
|
|
|
+ msg_subsystem.add_dispatch::<FudChunkReply>().await;
|
|
|
|
|
+ msg_subsystem.add_dispatch::<FudChunkNotFound>().await;
|
|
|
|
|
+ msg_subsystem.add_dispatch::<FudFileReply>().await;
|
|
|
|
|
+ msg_subsystem.add_dispatch::<FudDirectoryReply>().await;
|
|
|
|
|
+ msg_subsystem.add_dispatch::<FudMetadataNotFound>().await;
|
|
|
|
|
+ msg_subsystem.add_dispatch::<FudNodesRequest>().await;
|
|
|
|
|
+ msg_subsystem.add_dispatch::<FudNodesReply>().await;
|
|
|
|
|
+ msg_subsystem.add_dispatch::<FudSeedersRequest>().await;
|
|
|
|
|
+ msg_subsystem.add_dispatch::<FudSeedersReply>().await;
|
|
|
msg_subsystem.add_dispatch::<FudAnnounce>().await;
|
|
msg_subsystem.add_dispatch::<FudAnnounce>().await;
|
|
|
|
|
|
|
|
let ping_request_sub = channel.subscribe_msg::<FudPingRequest>().await?;
|
|
let ping_request_sub = channel.subscribe_msg::<FudPingRequest>().await?;
|
|
|
- let find_request_sub = channel.subscribe_msg::<FudFindRequest>().await?;
|
|
|
|
|
- let find_nodes_request_sub = channel.subscribe_msg::<FudFindNodesRequest>().await?;
|
|
|
|
|
- let find_seeders_request_sub = channel.subscribe_msg::<FudFindSeedersRequest>().await?;
|
|
|
|
|
|
|
+ let find_metadata_request_sub = channel.subscribe_msg::<FudMetadataRequest>().await?;
|
|
|
|
|
+ let find_chunk_request_sub = channel.subscribe_msg::<FudChunkRequest>().await?;
|
|
|
|
|
+ let find_nodes_request_sub = channel.subscribe_msg::<FudNodesRequest>().await?;
|
|
|
|
|
+ let find_seeders_request_sub = channel.subscribe_msg::<FudSeedersRequest>().await?;
|
|
|
let announce_sub = channel.subscribe_msg::<FudAnnounce>().await?;
|
|
let announce_sub = channel.subscribe_msg::<FudAnnounce>().await?;
|
|
|
|
|
|
|
|
Ok(Arc::new(Self {
|
|
Ok(Arc::new(Self {
|
|
|
channel: channel.clone(),
|
|
channel: channel.clone(),
|
|
|
ping_request_sub,
|
|
ping_request_sub,
|
|
|
- find_request_sub,
|
|
|
|
|
|
|
+ find_metadata_request_sub,
|
|
|
|
|
+ find_chunk_request_sub,
|
|
|
find_nodes_request_sub,
|
|
find_nodes_request_sub,
|
|
|
find_seeders_request_sub,
|
|
find_seeders_request_sub,
|
|
|
announce_sub,
|
|
announce_sub,
|
|
@@ -186,187 +243,193 @@ impl ProtocolFud {
|
|
|
let ping_req = match self.ping_request_sub.receive().await {
|
|
let ping_req = match self.ping_request_sub.receive().await {
|
|
|
Ok(v) => v,
|
|
Ok(v) => v,
|
|
|
Err(Error::ChannelStopped) => continue,
|
|
Err(Error::ChannelStopped) => continue,
|
|
|
- Err(e) => {
|
|
|
|
|
- error!("{e}");
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Err(_) => continue,
|
|
|
};
|
|
};
|
|
|
- info!(target: "fud::ProtocolFud::handle_fud_ping_request()", "Received PING REQUEST");
|
|
|
|
|
|
|
+ info!(target: "fud::ProtocolFud::handle_fud_ping_request()", "Received PING REQUEST from {}", self.channel.address());
|
|
|
|
|
+ self.fud.dht.update_channel(self.channel.info.id).await;
|
|
|
|
|
|
|
|
let reply = FudPingReply {
|
|
let reply = FudPingReply {
|
|
|
node: self.fud.node().await,
|
|
node: self.fud.node().await,
|
|
|
|
|
+ random: ping_req.random,
|
|
|
sig: self.fud.secret_key.read().await.sign(&ping_req.random.to_be_bytes()),
|
|
sig: self.fud.secret_key.read().await.sign(&ping_req.random.to_be_bytes()),
|
|
|
};
|
|
};
|
|
|
- match self.channel.send(&reply).await {
|
|
|
|
|
- Ok(()) => continue,
|
|
|
|
|
- Err(_e) => continue,
|
|
|
|
|
|
|
+ if let Err(e) = self.channel.send(&reply).await {
|
|
|
|
|
+ self.fud
|
|
|
|
|
+ .dht
|
|
|
|
|
+ .event_publisher
|
|
|
|
|
+ .notify(DhtEvent::PingSent { to: self.channel.clone(), result: Err(e) })
|
|
|
|
|
+ .await;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ self.fud
|
|
|
|
|
+ .dht
|
|
|
|
|
+ .event_publisher
|
|
|
|
|
+ .notify(DhtEvent::PingSent { to: self.channel.clone(), result: Ok(()) })
|
|
|
|
|
+ .await;
|
|
|
|
|
+
|
|
|
|
|
+ // Ping the peer if this is an inbound connection
|
|
|
|
|
+ if self.channel.session_type_id() & SESSION_INBOUND != 0 {
|
|
|
|
|
+ let _ = self.fud.ping(self.channel.clone()).await;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- async fn handle_fud_find_request(self: Arc<Self>) -> Result<()> {
|
|
|
|
|
- debug!(target: "fud::ProtocolFud::handle_fud_find_request()", "START");
|
|
|
|
|
|
|
+ async fn handle_fud_metadata_request(self: Arc<Self>) -> Result<()> {
|
|
|
|
|
+ debug!(target: "fud::ProtocolFud::handle_fud_metadata_request()", "START");
|
|
|
|
|
|
|
|
loop {
|
|
loop {
|
|
|
- let request = match self.find_request_sub.receive().await {
|
|
|
|
|
|
|
+ let request = match self.find_metadata_request_sub.receive().await {
|
|
|
Ok(v) => v,
|
|
Ok(v) => v,
|
|
|
Err(Error::ChannelStopped) => continue,
|
|
Err(Error::ChannelStopped) => continue,
|
|
|
- Err(e) => {
|
|
|
|
|
- error!("{e}");
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Err(_) => continue,
|
|
|
};
|
|
};
|
|
|
- info!(target: "fud::ProtocolFud::handle_fud_find_request()", "Received FIND for {}", hash_to_string(&request.key));
|
|
|
|
|
|
|
+ info!(target: "fud::ProtocolFud::handle_fud_request()", "Received METADATA REQUEST for {}", hash_to_string(&request.resource));
|
|
|
|
|
+ self.fud.dht.update_channel(self.channel.info.id).await;
|
|
|
|
|
|
|
|
- let node = self.fud.dht().get_node_from_channel(self.channel.info.id).await;
|
|
|
|
|
- if let Some(node) = node {
|
|
|
|
|
- self.fud.dht.update_node(&node).await;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ let notfound = async || {
|
|
|
|
|
+ let reply = FudMetadataNotFound { resource: request.resource };
|
|
|
|
|
+ info!(target: "fud::ProtocolFud::handle_fud_metadata_request()", "We do not have the metadata of {}", hash_to_string(&request.resource));
|
|
|
|
|
+ let _ = self.channel.send(&reply).await;
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- if self.handle_fud_chunk_request(&request).await {
|
|
|
|
|
- continue;
|
|
|
|
|
|
|
+ let path = self.fud.hash_to_path(&request.resource).ok().flatten();
|
|
|
|
|
+ if path.is_none() {
|
|
|
|
|
+ notfound().await;
|
|
|
|
|
+ continue
|
|
|
}
|
|
}
|
|
|
|
|
+ let path = path.unwrap();
|
|
|
|
|
|
|
|
- if self.handle_fud_metadata_request(&request).await {
|
|
|
|
|
- continue;
|
|
|
|
|
|
|
+ let chunked_file = self.fud.geode.get(&request.resource, &path).await.ok();
|
|
|
|
|
+ if chunked_file.is_none() {
|
|
|
|
|
+ notfound().await;
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ let mut chunked_file = chunked_file.unwrap();
|
|
|
|
|
+
|
|
|
|
|
+ // If it's a file with a single chunk, just reply with the chunk
|
|
|
|
|
+ if chunked_file.len() == 1 && !chunked_file.is_dir() {
|
|
|
|
|
+ let chunk_hash = chunked_file.get_chunks()[0].0;
|
|
|
|
|
+ let chunk = self.fud.geode.get_chunk(&mut chunked_file, &chunk_hash).await;
|
|
|
|
|
+ if let Ok(chunk) = chunk {
|
|
|
|
|
+ if blake3::hash(blake3::hash(&chunk).as_bytes()) != request.resource {
|
|
|
|
|
+ // TODO: Run geode GC
|
|
|
|
|
+ notfound().await;
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ let reply = FudChunkReply { resource: request.resource, chunk };
|
|
|
|
|
+ info!(target: "fud::ProtocolFud::handle_fud_metadata_request()", "Sending chunk (file has a single chunk) {}", hash_to_string(&chunk_hash));
|
|
|
|
|
+ let _ = self.channel.send(&reply).await;
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ // We don't have the chunk, but we can still reply with the metadata
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Request did not match anything we have
|
|
|
|
|
- let reply = FudNotFound {};
|
|
|
|
|
- info!(target: "fud::ProtocolFud::handle_fud_find_request()", "We do not have {}", hash_to_string(&request.key));
|
|
|
|
|
- let _ = self.channel.send(&reply).await;
|
|
|
|
|
|
|
+ // Reply with the metadata
|
|
|
|
|
+ match chunked_file.is_dir() {
|
|
|
|
|
+ false => {
|
|
|
|
|
+ let reply = FudFileReply {
|
|
|
|
|
+ resource: request.resource,
|
|
|
|
|
+ chunk_hashes: chunked_file
|
|
|
|
|
+ .get_chunks()
|
|
|
|
|
+ .iter()
|
|
|
|
|
+ .map(|(chunk, _)| *chunk)
|
|
|
|
|
+ .collect(),
|
|
|
|
|
+ };
|
|
|
|
|
+ info!(target: "fud::ProtocolFud::handle_fud_metadata_request()", "Sending file metadata {}", hash_to_string(&request.resource));
|
|
|
|
|
+ let _ = self.channel.send(&reply).await;
|
|
|
|
|
+ }
|
|
|
|
|
+ true => {
|
|
|
|
|
+ let files = chunked_file
|
|
|
|
|
+ .get_files()
|
|
|
|
|
+ .iter()
|
|
|
|
|
+ .map(|(file_path, size)| match file_path.strip_prefix(path.clone()) {
|
|
|
|
|
+ Ok(rel_path) => Ok((rel_path.to_string_lossy().to_string(), *size)),
|
|
|
|
|
+ Err(e) => Err(e),
|
|
|
|
|
+ })
|
|
|
|
|
+ .collect::<std::result::Result<Vec<_>, StripPrefixError>>();
|
|
|
|
|
+ if let Err(e) = files {
|
|
|
|
|
+ error!(target: "fud::ProtocolFud::handle_fud_metadata_request()", "Error parsing file paths before sending directory metadata: {e}");
|
|
|
|
|
+ notfound().await;
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ let reply = FudDirectoryReply {
|
|
|
|
|
+ resource: request.resource,
|
|
|
|
|
+ chunk_hashes: chunked_file
|
|
|
|
|
+ .get_chunks()
|
|
|
|
|
+ .iter()
|
|
|
|
|
+ .map(|(chunk, _)| *chunk)
|
|
|
|
|
+ .collect(),
|
|
|
|
|
+ files: files.unwrap(),
|
|
|
|
|
+ };
|
|
|
|
|
+ info!(target: "fud::ProtocolFud::handle_fud_metadata_request()", "Sending directory metadata {}", hash_to_string(&request.resource));
|
|
|
|
|
+ let _ = self.channel.send(&reply).await;
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /// If the FudFindRequest matches a chunk we have, handle it.
|
|
|
|
|
- /// Returns true if the chunk was found.
|
|
|
|
|
- async fn handle_fud_chunk_request(&self, request: &FudFindRequest) -> bool {
|
|
|
|
|
- let hash = request.info;
|
|
|
|
|
- if hash.is_none() {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- let hash = hash.unwrap();
|
|
|
|
|
|
|
+ async fn handle_fud_chunk_request(self: Arc<Self>) -> Result<()> {
|
|
|
|
|
+ debug!(target: "fud::ProtocolFud::handle_fud_chunk_request()", "START");
|
|
|
|
|
|
|
|
- let path = self.fud.hash_to_path(&hash).ok().flatten();
|
|
|
|
|
- if path.is_none() {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- let path = path.unwrap();
|
|
|
|
|
|
|
+ loop {
|
|
|
|
|
+ let request = match self.find_chunk_request_sub.receive().await {
|
|
|
|
|
+ Ok(v) => v,
|
|
|
|
|
+ Err(Error::ChannelStopped) => continue,
|
|
|
|
|
+ Err(_) => continue,
|
|
|
|
|
+ };
|
|
|
|
|
+ info!(target: "fud::ProtocolFud::handle_fud_chunk_request()", "Received CHUNK REQUEST for {}", hash_to_string(&request.resource));
|
|
|
|
|
+ self.fud.dht.update_channel(self.channel.info.id).await;
|
|
|
|
|
|
|
|
- let chunked = self.fud.geode.get(&hash, &path).await;
|
|
|
|
|
- if chunked.is_err() {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ let notfound = async || {
|
|
|
|
|
+ let reply = FudChunkNotFound { resource: request.resource, chunk: request.chunk };
|
|
|
|
|
+ info!(target: "fud::ProtocolFud::handle_fud_chunk_request()", "We do not have chunk {} of resource {}", hash_to_string(&request.resource), hash_to_string(&request.chunk));
|
|
|
|
|
+ let _ = self.channel.send(&reply).await;
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- let chunk = self.fud.geode.get_chunk(&mut chunked.unwrap(), &request.key).await;
|
|
|
|
|
- if let Ok(chunk) = chunk {
|
|
|
|
|
- if !self.fud.geode.verify_chunk(&request.key, &chunk) {
|
|
|
|
|
- // TODO: Run geode GC
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+ let path = self.fud.hash_to_path(&request.resource).ok().flatten();
|
|
|
|
|
+ if path.is_none() {
|
|
|
|
|
+ notfound().await;
|
|
|
|
|
+ continue
|
|
|
}
|
|
}
|
|
|
- let reply = FudChunkReply { chunk };
|
|
|
|
|
- info!(target: "fud::ProtocolFud::handle_fud_chunk_request()", "Sending chunk {}", hash_to_string(&request.key));
|
|
|
|
|
- let _ = self.channel.send(&reply).await;
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- false
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// If the FudFindRequest matches a file we have, handle it
|
|
|
|
|
- /// Returns true if the file was found.
|
|
|
|
|
- async fn handle_fud_metadata_request(&self, request: &FudFindRequest) -> bool {
|
|
|
|
|
- let path = self.fud.hash_to_path(&request.key).ok().flatten();
|
|
|
|
|
- if path.is_none() {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- let path = path.unwrap();
|
|
|
|
|
|
|
+ let path = path.unwrap();
|
|
|
|
|
|
|
|
- let chunked_file = self.fud.geode.get(&request.key, &path).await.ok();
|
|
|
|
|
- if chunked_file.is_none() {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- let mut chunked_file = chunked_file.unwrap();
|
|
|
|
|
|
|
+ let chunked = self.fud.geode.get(&request.resource, &path).await;
|
|
|
|
|
+ if chunked.is_err() {
|
|
|
|
|
+ notfound().await;
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // If it's a file with a single chunk, just reply with the chunk
|
|
|
|
|
- if chunked_file.len() == 1 && !chunked_file.is_dir() {
|
|
|
|
|
- let chunk_hash = chunked_file.get_chunks()[0].0;
|
|
|
|
|
- let chunk = self.fud.geode.get_chunk(&mut chunked_file, &chunk_hash).await;
|
|
|
|
|
|
|
+ let chunk = self.fud.geode.get_chunk(&mut chunked.unwrap(), &request.chunk).await;
|
|
|
if let Ok(chunk) = chunk {
|
|
if let Ok(chunk) = chunk {
|
|
|
- if blake3::hash(blake3::hash(&chunk).as_bytes()) != request.key {
|
|
|
|
|
|
|
+ if !self.fud.geode.verify_chunk(&request.chunk, &chunk) {
|
|
|
// TODO: Run geode GC
|
|
// TODO: Run geode GC
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- let reply = FudChunkReply { chunk };
|
|
|
|
|
- info!(target: "fud::ProtocolFud::handle_fud_metadata_request()", "Sending chunk (file has a single chunk) {}", hash_to_string(&chunk_hash));
|
|
|
|
|
- let _ = self.channel.send(&reply).await;
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Otherwise reply with the metadata
|
|
|
|
|
- match chunked_file.is_dir() {
|
|
|
|
|
- false => {
|
|
|
|
|
- let reply = FudFileReply {
|
|
|
|
|
- chunk_hashes: chunked_file
|
|
|
|
|
- .get_chunks()
|
|
|
|
|
- .iter()
|
|
|
|
|
- .map(|(chunk, _)| *chunk)
|
|
|
|
|
- .collect(),
|
|
|
|
|
- };
|
|
|
|
|
- info!(target: "fud::ProtocolFud::handle_fud_metadata_request()", "Sending file metadata {}", hash_to_string(&request.key));
|
|
|
|
|
- let _ = self.channel.send(&reply).await;
|
|
|
|
|
- }
|
|
|
|
|
- true => {
|
|
|
|
|
- let files = chunked_file
|
|
|
|
|
- .get_files()
|
|
|
|
|
- .iter()
|
|
|
|
|
- .map(|(file_path, size)| match file_path.strip_prefix(path.clone()) {
|
|
|
|
|
- Ok(rel_path) => Ok((rel_path.to_string_lossy().to_string(), *size)),
|
|
|
|
|
- Err(e) => Err(e),
|
|
|
|
|
- })
|
|
|
|
|
- .collect::<std::result::Result<Vec<_>, StripPrefixError>>();
|
|
|
|
|
- if let Err(e) = files {
|
|
|
|
|
- error!(target: "fud::ProtocolFud::handle_fud_metadata_request()", "Error parsing file paths before sending directory metadata: {e}");
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+ notfound().await;
|
|
|
|
|
+ continue
|
|
|
}
|
|
}
|
|
|
- let reply = FudDirectoryReply {
|
|
|
|
|
- chunk_hashes: chunked_file
|
|
|
|
|
- .get_chunks()
|
|
|
|
|
- .iter()
|
|
|
|
|
- .map(|(chunk, _)| *chunk)
|
|
|
|
|
- .collect(),
|
|
|
|
|
- files: files.unwrap(),
|
|
|
|
|
- };
|
|
|
|
|
- info!(target: "fud::ProtocolFud::handle_fud_metadata_request()", "Sending directory metadata {}", hash_to_string(&request.key));
|
|
|
|
|
|
|
+ let reply = FudChunkReply { resource: request.resource, chunk };
|
|
|
|
|
+ info!(target: "fud::ProtocolFud::handle_fud_chunk_request()", "Sending chunk {}", hash_to_string(&request.chunk));
|
|
|
let _ = self.channel.send(&reply).await;
|
|
let _ = self.channel.send(&reply).await;
|
|
|
|
|
+ continue
|
|
|
}
|
|
}
|
|
|
- };
|
|
|
|
|
|
|
|
|
|
- true
|
|
|
|
|
|
|
+ notfound().await;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- async fn handle_fud_find_nodes_request(self: Arc<Self>) -> Result<()> {
|
|
|
|
|
- debug!(target: "fud::ProtocolFud::handle_fud_find_nodes_request()", "START");
|
|
|
|
|
|
|
+ async fn handle_fud_nodes_request(self: Arc<Self>) -> Result<()> {
|
|
|
|
|
+ debug!(target: "fud::ProtocolFud::handle_fud_nodes_request()", "START");
|
|
|
|
|
|
|
|
loop {
|
|
loop {
|
|
|
let request = match self.find_nodes_request_sub.receive().await {
|
|
let request = match self.find_nodes_request_sub.receive().await {
|
|
|
Ok(v) => v,
|
|
Ok(v) => v,
|
|
|
Err(Error::ChannelStopped) => continue,
|
|
Err(Error::ChannelStopped) => continue,
|
|
|
- Err(e) => {
|
|
|
|
|
- error!("{e}");
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Err(_) => continue,
|
|
|
};
|
|
};
|
|
|
- info!(target: "fud::ProtocolFud::handle_fud_find_nodes_request()", "Received FIND NODES for {}", hash_to_string(&request.key));
|
|
|
|
|
-
|
|
|
|
|
- let node = self.fud.dht().get_node_from_channel(self.channel.info.id).await;
|
|
|
|
|
- if let Some(node) = node {
|
|
|
|
|
- self.fud.dht.update_node(&node).await;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ info!(target: "fud::ProtocolFud::handle_fud_nodes_request()", "Received FIND NODES for {}", hash_to_string(&request.key));
|
|
|
|
|
+ self.fud.dht.update_channel(self.channel.info.id).await;
|
|
|
|
|
|
|
|
- let reply = FudFindNodesReply {
|
|
|
|
|
|
|
+ let reply = FudNodesReply {
|
|
|
|
|
+ key: request.key,
|
|
|
nodes: self.fud.dht().find_neighbors(&request.key, self.fud.dht().settings.k).await,
|
|
nodes: self.fud.dht().find_neighbors(&request.key, self.fud.dht().settings.k).await,
|
|
|
};
|
|
};
|
|
|
match self.channel.send(&reply).await {
|
|
match self.channel.send(&reply).await {
|
|
@@ -376,24 +439,17 @@ impl ProtocolFud {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- async fn handle_fud_find_seeders_request(self: Arc<Self>) -> Result<()> {
|
|
|
|
|
- debug!(target: "fud::ProtocolFud::handle_fud_find_seeders_request()", "START");
|
|
|
|
|
|
|
+ async fn handle_fud_seeders_request(self: Arc<Self>) -> Result<()> {
|
|
|
|
|
+ debug!(target: "fud::ProtocolFud::handle_fud_seeders_request()", "START");
|
|
|
|
|
|
|
|
loop {
|
|
loop {
|
|
|
let request = match self.find_seeders_request_sub.receive().await {
|
|
let request = match self.find_seeders_request_sub.receive().await {
|
|
|
Ok(v) => v,
|
|
Ok(v) => v,
|
|
|
Err(Error::ChannelStopped) => continue,
|
|
Err(Error::ChannelStopped) => continue,
|
|
|
- Err(e) => {
|
|
|
|
|
- error!("{e}");
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Err(_) => continue,
|
|
|
};
|
|
};
|
|
|
- info!(target: "fud::ProtocolFud::handle_fud_find_seeders_request()", "Received FIND SEEDERS for {}", hash_to_string(&request.key));
|
|
|
|
|
-
|
|
|
|
|
- let node = self.fud.dht().get_node_from_channel(self.channel.info.id).await;
|
|
|
|
|
- if let Some(node) = node {
|
|
|
|
|
- self.fud.dht.update_node(&node).await;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ info!(target: "fud::ProtocolFud::handle_fud_seeders_request()", "Received FIND SEEDERS for {} from {:?}", hash_to_string(&request.key), self.channel);
|
|
|
|
|
+ self.fud.dht.update_channel(self.channel.info.id).await;
|
|
|
|
|
|
|
|
let router = self.fud.dht.hash_table.read().await;
|
|
let router = self.fud.dht.hash_table.read().await;
|
|
|
let peers = router.get(&request.key);
|
|
let peers = router.get(&request.key);
|
|
@@ -402,7 +458,8 @@ impl ProtocolFud {
|
|
|
Some(seeders) => {
|
|
Some(seeders) => {
|
|
|
let _ = self
|
|
let _ = self
|
|
|
.channel
|
|
.channel
|
|
|
- .send(&FudFindSeedersReply {
|
|
|
|
|
|
|
+ .send(&FudSeedersReply {
|
|
|
|
|
+ key: request.key,
|
|
|
seeders: seeders.to_vec(),
|
|
seeders: seeders.to_vec(),
|
|
|
nodes: self
|
|
nodes: self
|
|
|
.fud
|
|
.fud
|
|
@@ -415,7 +472,8 @@ impl ProtocolFud {
|
|
|
None => {
|
|
None => {
|
|
|
let _ = self
|
|
let _ = self
|
|
|
.channel
|
|
.channel
|
|
|
- .send(&FudFindSeedersReply {
|
|
|
|
|
|
|
+ .send(&FudSeedersReply {
|
|
|
|
|
+ key: request.key,
|
|
|
seeders: vec![],
|
|
seeders: vec![],
|
|
|
nodes: self
|
|
nodes: self
|
|
|
.fud
|
|
.fud
|
|
@@ -436,17 +494,10 @@ impl ProtocolFud {
|
|
|
let request = match self.announce_sub.receive().await {
|
|
let request = match self.announce_sub.receive().await {
|
|
|
Ok(v) => v,
|
|
Ok(v) => v,
|
|
|
Err(Error::ChannelStopped) => continue,
|
|
Err(Error::ChannelStopped) => continue,
|
|
|
- Err(e) => {
|
|
|
|
|
- error!("{e}");
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Err(_) => continue,
|
|
|
};
|
|
};
|
|
|
info!(target: "fud::ProtocolFud::handle_fud_announce()", "Received ANNOUNCE for {}", hash_to_string(&request.key));
|
|
info!(target: "fud::ProtocolFud::handle_fud_announce()", "Received ANNOUNCE for {}", hash_to_string(&request.key));
|
|
|
-
|
|
|
|
|
- let node = self.fud.dht().get_node_from_channel(self.channel.info.id).await;
|
|
|
|
|
- if let Some(node) = node {
|
|
|
|
|
- self.fud.dht.update_node(&node).await;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ self.fud.dht.update_channel(self.channel.info.id).await;
|
|
|
|
|
|
|
|
let mut seeders = vec![];
|
|
let mut seeders = vec![];
|
|
|
|
|
|
|
@@ -454,6 +505,8 @@ impl ProtocolFud {
|
|
|
if seeder.node.addresses.is_empty() {
|
|
if seeder.node.addresses.is_empty() {
|
|
|
continue
|
|
continue
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: Limit the number of addresses
|
|
|
// TODO: Verify each address
|
|
// TODO: Verify each address
|
|
|
seeders.push(seeder);
|
|
seeders.push(seeder);
|
|
|
}
|
|
}
|
|
@@ -469,14 +522,15 @@ impl ProtocolBase for ProtocolFud {
|
|
|
debug!(target: "fud::ProtocolFud::start()", "START");
|
|
debug!(target: "fud::ProtocolFud::start()", "START");
|
|
|
self.jobsman.clone().start(executor.clone());
|
|
self.jobsman.clone().start(executor.clone());
|
|
|
self.jobsman.clone().spawn(self.clone().handle_fud_ping_request(), executor.clone()).await;
|
|
self.jobsman.clone().spawn(self.clone().handle_fud_ping_request(), executor.clone()).await;
|
|
|
- self.jobsman.clone().spawn(self.clone().handle_fud_find_request(), executor.clone()).await;
|
|
|
|
|
self.jobsman
|
|
self.jobsman
|
|
|
.clone()
|
|
.clone()
|
|
|
- .spawn(self.clone().handle_fud_find_nodes_request(), executor.clone())
|
|
|
|
|
|
|
+ .spawn(self.clone().handle_fud_metadata_request(), executor.clone())
|
|
|
.await;
|
|
.await;
|
|
|
|
|
+ self.jobsman.clone().spawn(self.clone().handle_fud_chunk_request(), executor.clone()).await;
|
|
|
|
|
+ self.jobsman.clone().spawn(self.clone().handle_fud_nodes_request(), executor.clone()).await;
|
|
|
self.jobsman
|
|
self.jobsman
|
|
|
.clone()
|
|
.clone()
|
|
|
- .spawn(self.clone().handle_fud_find_seeders_request(), executor.clone())
|
|
|
|
|
|
|
+ .spawn(self.clone().handle_fud_seeders_request(), executor.clone())
|
|
|
.await;
|
|
.await;
|
|
|
self.jobsman.clone().spawn(self.clone().handle_fud_announce(), executor.clone()).await;
|
|
self.jobsman.clone().spawn(self.clone().handle_fud_announce(), executor.clone()).await;
|
|
|
debug!(target: "fud::ProtocolFud::start()", "END");
|
|
debug!(target: "fud::ProtocolFud::start()", "END");
|