//! The domain protocol carried over `federation-net`. use std::fmt; use federation_net::EndpointId; use serde::{Deserialize, Serialize}; use crate::record::{DhtKey, StoredArtistRecord}; use crate::routing::{NodeContact, NodeId}; /// Version of the artist-dht protocol. pub const DHT_PROTOCOL_VERSION: u16 = 1; /// Maximum number of contacts in a single [`PeerExchange`]. pub const MAX_PEER_EXCHANGE_CONTACTS: usize = 32; /// Maximum number of records in a single [`FindValueResponse`]. pub const MAX_RECORDS_PER_RESPONSE: usize = 100; /// Correlates a response with its request. #[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct RequestId([u8; 16]); impl RequestId { /// Generates a random request id. pub fn random() -> Self { Self(rand::random()) } /// Returns the raw bytes. pub fn as_bytes(&self) -> &[u8; 16] { &self.0 } } impl fmt::Debug for RequestId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "RequestId(")?; for byte in &self.0 { write!(f, "{byte:02x}")?; } write!(f, ")") } } /// Wraps a request payload with its correlation id. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RequestEnvelope { /// Correlation id; echoed back in the response. pub request_id: RequestId, /// The request itself. pub payload: T, } /// Wraps a response payload with the correlation id of its request. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ResponseEnvelope { /// Correlation id of the request being answered. pub request_id: RequestId, /// The response itself. pub payload: T, } /// Introduction sent right after a connection is established. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Hello { /// DHT identifier of the sender. pub node_id: NodeId, /// Transport identifier of the sender (informational; the authenticated /// id always comes from the connection itself). pub peer_id: EndpointId, /// Ticket other peers can use to reach the sender. pub ticket: String, /// Protocol version of the sender. pub protocol_version: u16, } /// A batch of known contacts, shared after [`Hello`]. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PeerExchange { /// Up to [`MAX_PEER_EXCHANGE_CONTACTS`] contacts. pub peers: Vec, } /// Liveness probe. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PingRequest {} /// Reply to [`PingRequest`]. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PongResponse { /// DHT identifier of the responder. pub node_id: NodeId, } /// Asks for the closest known nodes to `target`. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FindNodeRequest { /// Point of the key space to search around. pub target: NodeId, } /// Reply to [`FindNodeRequest`]. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FindNodeResponse { /// Up to `K` known nodes closest to the target. pub nodes: Vec, } /// Asks for records stored under `key`, or the closest nodes to it. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FindValueRequest { /// The DHT key to look up. pub key: DhtKey, } /// Reply to [`FindValueRequest`]. #[derive(Debug, Clone, Serialize, Deserialize)] pub enum FindValueResponse { /// The responder stores records under the key. Records { /// Up to [`MAX_RECORDS_PER_RESPONSE`] non-expired records. records: Vec, }, /// The responder has nothing stored; here are closer nodes instead. CloserNodes { /// Up to `K` known nodes closest to the key. nodes: Vec, }, } /// Asks the receiver to store a replica of a record. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StoreRecordRequest { /// The key the record is published under. pub key: DhtKey, /// The record to store. pub record: StoredArtistRecord, } /// Reply to [`StoreRecordRequest`]. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StoreRecordResponse { /// `true` if the record was accepted and stored (or refreshed). pub stored: bool, } /// Every message exchanged between artist-dht peers. #[derive(Debug, Clone, Serialize, Deserialize)] #[allow(clippy::large_enum_variant)] pub enum ArtistDhtMessage { /// Introduction after connect. Hello(Hello), /// Contact gossip after `Hello`. PeerExchange(PeerExchange), /// Liveness probe. Ping(RequestEnvelope), /// Reply to `Ping`. Pong(ResponseEnvelope), /// Node lookup request. FindNode(RequestEnvelope), /// Reply to `FindNode`. FindNodeResult(ResponseEnvelope), /// Value lookup request. FindValue(RequestEnvelope), /// Reply to `FindValue`. FindValueResult(ResponseEnvelope), /// Replication request. StoreRecord(RequestEnvelope), /// Reply to `StoreRecord`. StoreRecordResult(ResponseEnvelope), }