⎇ 53dbdf0f1deea10d6fc567ea2b43e1e75d6d60cd
crates/modules/semantic_dht/minga-p2p/src/message.rs
Historia
Quién tocó qué
crudo
chore: monorepo inicial con arje + minga + yahweh absorbidos
1//! Mensajes del protocolo de sincronización (versión recursiva sobre
2//! la estructura del MST).
3//!
4//! El protocolo es simétrico — ambos peers ejecutan el mismo rol y
5//! emiten los mismos mensajes — y consta de seis tipos:
6//!
7//! 1. `Hello { root_subtree_hash }` anuncia el hash Merkle del MST raíz
8//! del emisor. Si ambos hashes coinciden, los dos repos son idénticos
9//! y la sincronización termina sin un solo byte adicional.
10//!
11//! 2. `ProbeReq { subtree_hash }` solicita la **estructura** (level +
12//! keys + child_hashes) de un subárbol previamente anunciado por el
13//! otro peer. Es lo que permite descender el árbol del peer paso a
14//! paso, podando ramas idénticas por igualdad de hash.
15//!
16//! 3. `ProbeRes { subtree_hash, probe }` responde con el `NodeProbe`,
17//! o `None` si el subárbol era el vacío. Cada subárbol que el peer
18//! no reconoce dispara un `ProbeReq` recursivo; cuando el peer ya
19//! tiene un subárbol con el mismo hash, la rama se poda.
20//!
21//! 4. `Fetch { hash }` y `Deliver { hash, stored }` mueven los nodos
22//! propiamente dichos. El receptor del `Deliver` **verifica
23//! criptográficamente** que `hash_stored(stored) == hash` antes de
24//! insertar — un peer malicioso no puede colar un `StoredNode`
25//! distinto bajo un hash anunciado.
26//!
27//! 5. `Done` cierra el lado del emisor: ya recibió el `Hello` del otro,
28//! no tiene probes ni fetches pendientes. Cuando ambos `Done`s han
29//! cruzado, la sesión termina con ambos repos convergentes.
30
31use minga_core::{Attestation, ContentHash, Did, NodeProbe, Signature, StoredNode};
32
33#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
34pub enum Message {
35 /// Reto de session-handshake: 32 bytes aleatorios. Cada peer envía
36 /// uno al inicio. El otro lado lo incrustará en el payload del
37 /// `Hello` que firme con su llave privada — así un `Hello`
38 /// capturado en una sesión no puede replayearse en otra (que
39 /// tendrá un nonce distinto).
40 Challenge {
41 nonce: [u8; 32],
42 },
43
44 /// Saludo autenticado anti-replay: el emisor presenta su DID, el
45 /// hash del subárbol raíz de su MST, y una firma sobre el payload
46 /// `(peer_did || root_subtree_hash || nonce_recibido_del_peer)`.
47 /// El receptor reconstruye el payload con su PROPIO nonce (el que
48 /// envió en su Challenge) y verifica con la llave pública del
49 /// peer. Sin Challenge previo no hay Hello válido posible.
50 Hello {
51 peer_did: Did,
52 root_subtree_hash: ContentHash,
53 signature: Signature,
54 },
55 ProbeReq {
56 subtree_hash: ContentHash,
57 },
58 ProbeRes {
59 subtree_hash: ContentHash,
60 probe: Option<NodeProbe>,
61 },
62 Fetch {
63 hash: ContentHash,
64 },
65 Deliver {
66 hash: ContentHash,
67 stored: StoredNode,
68 },
69 /// Empuje de atestaciones: el emisor entrega al peer las pruebas
70 /// criptográficas de autoría que conoce. Cada `Attestation` es
71 /// auto-verificable (firma + autor + contenido), así que el
72 /// receptor puede validar y mezclar sin confiar en la palabra del
73 /// remitente. Se envían tras el `Hello` autenticado para que el
74 /// peer verifique la identidad del remitente antes de procesarlas.
75 AttestPush {
76 attestations: Vec<Attestation>,
77 },
78 Done,
79}
80
81impl Message {
82 /// Codifica el mensaje a bytes vía postcard. Diseñado para
83 /// transferir sobre cualquier transporte que mueva `Vec<u8>`.
84 /// Postcard es compacto, sin overhead de schema runtime.
85 pub fn encode(&self) -> Vec<u8> {
86 postcard::to_allocvec(self).expect("postcard encoding cannot fail for our types")
87 }
88
89 /// Decodifica bytes a un `Message`. `Err` si los bytes son
90 /// malformados o no representan un `Message` válido.
91 pub fn decode(bytes: &[u8]) -> Result<Self, postcard::Error> {
92 postcard::from_bytes(bytes)
93 }
94}