/ core / src / relay / protocol.rs
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  }