//! Mensajes del protocolo de sincronización (versión recursiva sobre //! la estructura del MST). //! //! El protocolo es simétrico — ambos peers ejecutan el mismo rol y //! emiten los mismos mensajes — y consta de seis tipos: //! //! 1. `Hello { root_subtree_hash }` anuncia el hash Merkle del MST raíz //! del emisor. Si ambos hashes coinciden, los dos repos son idénticos //! y la sincronización termina sin un solo byte adicional. //! //! 2. `ProbeReq { subtree_hash }` solicita la **estructura** (level + //! keys + child_hashes) de un subárbol previamente anunciado por el //! otro peer. Es lo que permite descender el árbol del peer paso a //! paso, podando ramas idénticas por igualdad de hash. //! //! 3. `ProbeRes { subtree_hash, probe }` responde con el `NodeProbe`, //! o `None` si el subárbol era el vacío. Cada subárbol que el peer //! no reconoce dispara un `ProbeReq` recursivo; cuando el peer ya //! tiene un subárbol con el mismo hash, la rama se poda. //! //! 4. `Fetch { hash }` y `Deliver { hash, stored }` mueven los nodos //! propiamente dichos. El receptor del `Deliver` **verifica //! criptográficamente** que `hash_stored(stored) == hash` antes de //! insertar — un peer malicioso no puede colar un `StoredNode` //! distinto bajo un hash anunciado. //! //! 5. `Done` cierra el lado del emisor: ya recibió el `Hello` del otro, //! no tiene probes ni fetches pendientes. Cuando ambos `Done`s han //! cruzado, la sesión termina con ambos repos convergentes. use minga_core::{Attestation, ContentHash, Did, NodeProbe, Signature, StoredNode}; #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub enum Message { /// Reto de session-handshake: 32 bytes aleatorios. Cada peer envía /// uno al inicio. El otro lado lo incrustará en el payload del /// `Hello` que firme con su llave privada — así un `Hello` /// capturado en una sesión no puede replayearse en otra (que /// tendrá un nonce distinto). Challenge { nonce: [u8; 32], }, /// Saludo autenticado anti-replay: el emisor presenta su DID, el /// hash del subárbol raíz de su MST, y una firma sobre el payload /// `(peer_did || root_subtree_hash || nonce_recibido_del_peer)`. /// El receptor reconstruye el payload con su PROPIO nonce (el que /// envió en su Challenge) y verifica con la llave pública del /// peer. Sin Challenge previo no hay Hello válido posible. Hello { peer_did: Did, root_subtree_hash: ContentHash, signature: Signature, }, ProbeReq { subtree_hash: ContentHash, }, ProbeRes { subtree_hash: ContentHash, probe: Option, }, Fetch { hash: ContentHash, }, Deliver { hash: ContentHash, stored: StoredNode, }, /// Empuje de atestaciones: el emisor entrega al peer las pruebas /// criptográficas de autoría que conoce. Cada `Attestation` es /// auto-verificable (firma + autor + contenido), así que el /// receptor puede validar y mezclar sin confiar en la palabra del /// remitente. Se envían tras el `Hello` autenticado para que el /// peer verifique la identidad del remitente antes de procesarlas. AttestPush { attestations: Vec, }, Done, } impl Message { /// Codifica el mensaje a bytes vía postcard. Diseñado para /// transferir sobre cualquier transporte que mueva `Vec`. /// Postcard es compacto, sin overhead de schema runtime. pub fn encode(&self) -> Vec { postcard::to_allocvec(self).expect("postcard encoding cannot fail for our types") } /// Decodifica bytes a un `Message`. `Err` si los bytes son /// malformados o no representan un `Message` válido. pub fn decode(bytes: &[u8]) -> Result { postcard::from_bytes(bytes) } }