Fragmentos y recibos

Un mensaje nunca viaja entero. Se parte en fragmentos que recorren nodos distintos, y cada fragmento entregado produce un recibo anónimo: la prueba de que el trabajo se hizo, sin decir para quién.

Reed–Solomon 3 de 5

Cada sobre relleno se trocea en franjas de k = 3 de n = 5 con Reed–Solomon sobre GF(256): de cada cinco trozos, bastan tres cualesquiera. Pueden perderse dos nodos y el mensaje llega igual.

Esquema Ocupa Probabilidad de entrega si cada ruta acierta el 95 %
4 de 4, sin redundancia 1,00× 81,5 %
4 + 1 paridad (v1) 1,25× 97,7 %
3 de 5 (v2) 1,67× 99,9 %

El tamaño del trozo depende del cubo: un tercio del cubo redondeado a 1 KB, con un máximo de 24 KB. Los sobres grandes usan varias franjas.

Shard = { id, msg, mailbox, stripe, idx, k, n, bucket, data, rh, exp }
id    = sha512(msg ‖ stripe ‖ idx)[0..32]       msg es aleatorio en cada envío

El trozo no lleva el tamaño real del mensaje (va dentro del sobre, cifrado) ni nada sobre quién lo envía.

Buzones que rotan

Los fragmentos esperan en un buzón. Cada dispositivo tiene su semilla de buzón y su buzón cambia cada día:

buzón(día) = hex(HMAC-SHA512(mailboxSeed, "day:" + floor(ts / 86 400 000)))[0..40]

Quien envía usa el buzón de hoy del destinatario; el destinatario abre el de hoy y el de ayer. Un nodo no puede unir el buzón del lunes con el del martes.

Solo el dueño abre su buzón

Abrir un buzón no se firma con la identidad. Quien envía incluye en cada trozo una marca mbAuth derivada del secreto de buzón del destinatario. Para abrir, el dueño presenta una prueba firmada sobre velum/v2/open|<buzón>|<ts> que el nodo comprueba contra esa marca. Así solo el dueño recoge sus trozos y el nodo no aprende quién es.

Prueba de Entrega: el recibo anónimo

Dentro del sobre (que solo abre el destinatario) va una semilla aleatoria rseed. Para cada trozo:

secreto_i = HMAC-SHA512(rseed, shardId)[0..32]
rh_i      = SHA-512(secreto_i)          ← viaja en el trozo, a la vista

Al recomponer el mensaje, el destinatario revela cada secreto_i. Cualquiera puede comprobar que SHA-512(secreto) == rh, pero nadie más podía conocer el secreto: un nodo no puede fabricar recibos.

{
  "shard": "7a0c…",          // shard id
  "mailbox": "c2e81f…",      // today's rotating mailbox (40 hex)
  "rh": "…128 hex…",         // sha512(secret), already inside the shard
  "secret": "…64 hex…",      // HMAC-SHA512(rseed, shardId)[0..32], only the recipient can compute it
  "carrier": "vel…",         // node that accepted the shard
  "courier": "vel…",         // node that delivered it
  "ts": 1793577600000
}

El recibo no lleva firma ni dirección de quien recibe. Cada recibo da créditos:

  • 1 crédito al carrier (el nodo que aceptó el trozo).
  • 1 crédito al courier (el nodo que lo entregó).
  • Si son el mismo nodo, 2 créditos.

De recibos a reclamaciones

Con miles de millones de mensajes al día, los recibos no pueden entrar uno a uno en los bloques. Cada nodo junta los suyos en un árbol de Merkle (SHA-512) y publica solo la raíz y el total (work.claim). La época siguiente, la baliza elige 16 recibos al azar y el nodo debe enseñarlos con su secreto y su ruta (work.prove). Si falla uno, pierde la recompensa de la época. Detalles en Monta un nodo.

Antitrampas

  • Un mismo trozo cuenta una sola vez.
  • Tope de créditos por época y par (nodo, buzón): 400.
  • Recibos fuera de ±15 minutos respecto al bloque no cuentan.
  • Nadie puede reclamar más de 1,5 veces lo esperable para su peso en el sorteo.
  • Inventar tráfico no rinde: para generar recibos hace falta abrir los sobres, y eso solo puede hacerlo un destinatario real.
  • Los nodos que llevan cada trozo salen de un sorteo público de 3 candidatos; el cliente solo elige el más cercano de esos tres.

Borrado

En cuanto un nodo ve un recibo válido para un trozo, lo borra. El recibo se propaga por la red y todos los nodos que tuvieran copia la borran también. Si nadie recoge un trozo, se destruye a los 7 días.

Resumen del viaje