protocol.rs
1 //! Relay protocol message types. 2 //! 3 //! This module defines the wire protocol for communicating with relay servers. 4 //! All messages are serialized using bincode for efficient binary encoding. 5 6 use serde::{Deserialize, Serialize}; 7 8 /// Protocol version for compatibility checking. 9 pub const RELAY_PROTOCOL_VERSION: u8 = 1; 10 11 /// Maximum message payload size (1 MB). 12 pub const MAX_PAYLOAD_SIZE: usize = 1024 * 1024; 13 14 /// Default message TTL in hours. 15 pub const DEFAULT_MESSAGE_TTL_HOURS: u32 = 48; 16 17 /// Relay protocol messages. 18 /// 19 /// These messages are exchanged between clients and relay servers over WebSocket. 20 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] 21 pub enum RelayMessage { 22 /// Register a public key with the relay to create a mailbox. 23 /// 24 /// The relay creates a mailbox associated with this public key. 25 /// Other users can send messages to this mailbox. 26 Register { 27 /// X25519 public key (32 bytes) for this user. 28 public_key: [u8; 32], 29 }, 30 31 /// Acknowledgment of successful registration. 32 RegisterAck { 33 /// Unique mailbox identifier assigned by the relay. 34 mailbox_id: [u8; 16], 35 }, 36 37 /// Send a message to a recipient via the relay. 38 /// 39 /// The relay stores the message until the recipient fetches it. 40 Send { 41 /// Recipient's X25519 public key (32 bytes). 42 recipient_key: [u8; 32], 43 /// Encrypted message payload (already encrypted by sender). 44 payload: Vec<u8>, 45 }, 46 47 /// Acknowledgment of successful message submission. 48 SendAck { 49 /// Unique message identifier assigned by the relay. 50 message_id: [u8; 16], 51 }, 52 53 /// Fetch messages from the mailbox. 54 /// 55 /// Returns all messages received since the given timestamp. 56 Fetch { 57 /// Unix timestamp (seconds) - fetch messages newer than this. 58 since: u64, 59 }, 60 61 /// Response containing stored messages. 62 Messages { 63 /// List of messages stored in the mailbox. 64 messages: Vec<StoredMessage>, 65 }, 66 67 /// Acknowledge receipt of messages (allows relay to delete them). 68 Ack { 69 /// Message IDs that were successfully received. 70 message_ids: Vec<[u8; 16]>, 71 }, 72 73 /// Keepalive ping. 74 Ping, 75 76 /// Keepalive pong response. 77 Pong, 78 79 /// Error response from the relay. 80 Error { 81 /// Error code. 82 code: RelayErrorCode, 83 /// Human-readable error message. 84 message: String, 85 }, 86 87 // ========================================================================= 88 // GOSSIP PROTOCOL MESSAGES (Store-and-Forward Mesh) 89 // ========================================================================= 90 91 /// Gossip digest exchange - bloom filter of recipient key hashes. 92 /// 93 /// When two peers connect, they exchange digests to determine 94 /// which forwarded messages to sync. 95 GossipDigest { 96 /// Bloom filter bytes containing recipient key hashes. 97 bloom: Vec<u8>, 98 /// Number of messages we currently hold for forwarding. 99 message_count: u32, 100 /// Protocol version for gossip compatibility. 101 gossip_version: u8, 102 }, 103 104 /// Request forwarded messages for specific recipients. 105 /// 106 /// Sent after receiving a GossipDigest, requesting messages 107 /// for recipients we're interested in. 108 GossipRequest { 109 /// Hashes of recipient keys we want messages for. 110 recipient_hashes: Vec<[u8; 32]>, 111 /// Maximum number of messages to return. 112 limit: u32, 113 }, 114 115 /// Response containing forwarded messages. 116 GossipResponse { 117 /// Forwarded messages matching the request. 118 messages: Vec<GossipForwardedMessage>, 119 }, 120 121 /// Anonymous delivery confirmation. 122 /// 123 /// When a message is successfully delivered to its recipient, 124 /// this confirmation propagates back through the mesh to reward 125 /// nodes that helped forward it. 126 DeliveryConfirmation { 127 /// Truncated hash of the delivered message (16 bytes for privacy). 128 message_hash: [u8; 16], 129 /// Remaining hops (decrements each hop, stops at 0). 130 hops_remaining: u8, 131 }, 132 } 133 134 /// A message stored on the relay. 135 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] 136 pub struct StoredMessage { 137 /// Unique message identifier. 138 pub id: [u8; 16], 139 /// Sender's X25519 public key. 140 pub sender_key: [u8; 32], 141 /// Encrypted payload (cannot be read by relay). 142 pub payload: Vec<u8>, 143 /// Unix timestamp when the message was received by the relay. 144 pub timestamp: u64, 145 } 146 147 /// Error codes from the relay. 148 #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] 149 #[repr(u8)] 150 pub enum RelayErrorCode { 151 /// Unknown or unspecified error. 152 Unknown = 0, 153 /// Invalid message format. 154 InvalidFormat = 1, 155 /// Message payload too large. 156 PayloadTooLarge = 2, 157 /// Recipient not registered. 158 RecipientNotFound = 3, 159 /// Rate limit exceeded. 160 RateLimited = 4, 161 /// Server is overloaded. 162 ServerBusy = 5, 163 /// Authentication required or failed. 164 AuthRequired = 6, 165 /// Invalid protocol version. 166 InvalidVersion = 7, 167 } 168 169 impl RelayMessage { 170 /// Serialize the message to bytes using bincode. 171 pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> { 172 bincode::serialize(self) 173 } 174 175 /// Deserialize a message from bytes using bincode. 176 pub fn from_bytes(data: &[u8]) -> Result<Self, bincode::Error> { 177 bincode::deserialize(data) 178 } 179 } 180 181 impl StoredMessage { 182 /// Create a new stored message. 183 pub fn new(id: [u8; 16], sender_key: [u8; 32], payload: Vec<u8>, timestamp: u64) -> Self { 184 Self { 185 id, 186 sender_key, 187 payload, 188 timestamp, 189 } 190 } 191 } 192 193 /// A forwarded message in the gossip protocol. 194 /// 195 /// This is the wire format for messages exchanged between peers 196 /// in the store-and-forward mesh. Contains minimal metadata to 197 /// preserve privacy. 198 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] 199 pub struct GossipForwardedMessage { 200 /// Unique message identifier (for deduplication). 201 pub message_id: [u8; 16], 202 /// BLAKE2b hash of recipient's exchange key (not the key itself). 203 pub recipient_key_hash: [u8; 32], 204 /// Encrypted payload (opaque to forwarders). 205 pub payload: Vec<u8>, 206 /// Unix timestamp when originally received for forwarding. 207 pub received_at: u64, 208 /// Unix timestamp when this message expires. 209 pub expires_at: u64, 210 /// Number of relay hops this message has taken. 211 #[serde(default)] 212 pub hop_count: u8, 213 } 214 215 impl GossipForwardedMessage { 216 /// Create a new gossip forwarded message. 217 pub fn new( 218 message_id: [u8; 16], 219 recipient_key_hash: [u8; 32], 220 payload: Vec<u8>, 221 received_at: u64, 222 expires_at: u64, 223 ) -> Self { 224 Self { 225 message_id, 226 recipient_key_hash, 227 payload, 228 received_at, 229 expires_at, 230 hop_count: 0, 231 } 232 } 233 234 /// Check if this message has expired. 235 pub fn is_expired(&self) -> bool { 236 use std::time::{SystemTime, UNIX_EPOCH}; 237 let now = SystemTime::now() 238 .duration_since(UNIX_EPOCH) 239 .unwrap() 240 .as_secs(); 241 now >= self.expires_at 242 } 243 244 /// Get the payload size in bytes. 245 pub fn payload_size(&self) -> usize { 246 self.payload.len() 247 } 248 } 249 250 /// Gossip protocol version. 251 pub const GOSSIP_PROTOCOL_VERSION: u8 = 1; 252 253 /// Default bloom filter size in bytes (1 KB = ~10K items at 1% FPR). 254 pub const DEFAULT_BLOOM_FILTER_SIZE: usize = 1024; 255 256 /// Maximum messages per gossip response. 257 pub const MAX_GOSSIP_MESSAGES: u32 = 100; 258 259 /// Maximum hops for delivery confirmations. 260 pub const MAX_DELIVERY_CONFIRMATION_HOPS: u8 = 10; 261 262 #[cfg(test)] 263 mod tests { 264 use super::*; 265 266 #[test] 267 fn test_relay_message_register_roundtrip() { 268 let msg = RelayMessage::Register { 269 public_key: [0xAB; 32], 270 }; 271 272 let bytes = msg.to_bytes().unwrap(); 273 let decoded = RelayMessage::from_bytes(&bytes).unwrap(); 274 275 assert_eq!(msg, decoded); 276 } 277 278 #[test] 279 fn test_relay_message_send_roundtrip() { 280 let msg = RelayMessage::Send { 281 recipient_key: [0xCD; 32], 282 payload: vec![1, 2, 3, 4, 5], 283 }; 284 285 let bytes = msg.to_bytes().unwrap(); 286 let decoded = RelayMessage::from_bytes(&bytes).unwrap(); 287 288 assert_eq!(msg, decoded); 289 } 290 291 #[test] 292 fn test_relay_message_messages_roundtrip() { 293 let stored = StoredMessage::new( 294 [0x01; 16], 295 [0x02; 32], 296 vec![0xDE, 0xAD, 0xBE, 0xEF], 297 1234567890, 298 ); 299 300 let msg = RelayMessage::Messages { 301 messages: vec![stored.clone()], 302 }; 303 304 let bytes = msg.to_bytes().unwrap(); 305 let decoded = RelayMessage::from_bytes(&bytes).unwrap(); 306 307 if let RelayMessage::Messages { messages } = decoded { 308 assert_eq!(messages.len(), 1); 309 assert_eq!(messages[0], stored); 310 } else { 311 panic!("Expected Messages variant"); 312 } 313 } 314 315 #[test] 316 fn test_relay_message_error_roundtrip() { 317 let msg = RelayMessage::Error { 318 code: RelayErrorCode::PayloadTooLarge, 319 message: "Message exceeds 1MB limit".to_string(), 320 }; 321 322 let bytes = msg.to_bytes().unwrap(); 323 let decoded = RelayMessage::from_bytes(&bytes).unwrap(); 324 325 assert_eq!(msg, decoded); 326 } 327 328 #[test] 329 fn test_stored_message_new() { 330 let msg = StoredMessage::new( 331 [0xFF; 16], 332 [0xAA; 32], 333 vec![1, 2, 3], 334 9999999999, 335 ); 336 337 assert_eq!(msg.id, [0xFF; 16]); 338 assert_eq!(msg.sender_key, [0xAA; 32]); 339 assert_eq!(msg.payload, vec![1, 2, 3]); 340 assert_eq!(msg.timestamp, 9999999999); 341 } 342 343 #[test] 344 fn test_gossip_digest_roundtrip() { 345 let msg = RelayMessage::GossipDigest { 346 bloom: vec![0xAB; 128], 347 message_count: 42, 348 gossip_version: GOSSIP_PROTOCOL_VERSION, 349 }; 350 351 let bytes = msg.to_bytes().unwrap(); 352 let decoded = RelayMessage::from_bytes(&bytes).unwrap(); 353 354 assert_eq!(msg, decoded); 355 } 356 357 #[test] 358 fn test_gossip_request_roundtrip() { 359 let msg = RelayMessage::GossipRequest { 360 recipient_hashes: vec![[0x01; 32], [0x02; 32]], 361 limit: 50, 362 }; 363 364 let bytes = msg.to_bytes().unwrap(); 365 let decoded = RelayMessage::from_bytes(&bytes).unwrap(); 366 367 assert_eq!(msg, decoded); 368 } 369 370 #[test] 371 fn test_gossip_response_roundtrip() { 372 let forwarded = GossipForwardedMessage::new( 373 [0x01; 16], 374 [0x02; 32], 375 vec![0xDE, 0xAD, 0xBE, 0xEF], 376 1234567890, 377 1234567890 + 48 * 3600, 378 ); 379 380 let msg = RelayMessage::GossipResponse { 381 messages: vec![forwarded.clone()], 382 }; 383 384 let bytes = msg.to_bytes().unwrap(); 385 let decoded = RelayMessage::from_bytes(&bytes).unwrap(); 386 387 if let RelayMessage::GossipResponse { messages } = decoded { 388 assert_eq!(messages.len(), 1); 389 assert_eq!(messages[0], forwarded); 390 } else { 391 panic!("Expected GossipResponse variant"); 392 } 393 } 394 395 #[test] 396 fn test_delivery_confirmation_roundtrip() { 397 let msg = RelayMessage::DeliveryConfirmation { 398 message_hash: [0xAB; 16], 399 hops_remaining: 5, 400 }; 401 402 let bytes = msg.to_bytes().unwrap(); 403 let decoded = RelayMessage::from_bytes(&bytes).unwrap(); 404 405 assert_eq!(msg, decoded); 406 } 407 408 #[test] 409 fn test_gossip_forwarded_message() { 410 let msg = GossipForwardedMessage::new( 411 [0xFF; 16], 412 [0xAA; 32], 413 vec![1, 2, 3], 414 1000, 415 2000, 416 ); 417 418 assert_eq!(msg.message_id, [0xFF; 16]); 419 assert_eq!(msg.recipient_key_hash, [0xAA; 32]); 420 assert_eq!(msg.payload, vec![1, 2, 3]); 421 assert_eq!(msg.received_at, 1000); 422 assert_eq!(msg.expires_at, 2000); 423 assert_eq!(msg.payload_size(), 3); 424 } 425 }