Técnico › Red

Una transacción byte a byte

Desglose completo del hex de una transacción RingCT: cada campo, cada función, con datos reales y aleatorios

En resumen

Una transacción de Monero son tres bloques de bytes consecutivos: el prefijo (entradas, salidas, extra), la base RingCT (comisión, importes cifrados, compromisos) y la parte podable (Bulletproofs+, CLSAG, pseudo-salidas). Abajo hay una herramienta que analiza byte a byte cualquier transacción v2, calcula el txid, comprueba el balance y, si le das una clave de visualización, encuentra «tus» salidas y descifra sus importes. Con una transacción aleatoria muestra todos los secretos, para que veas cómo se aplica cada función a números concretos.

Cómo leer los bytes

La serialización de Monero es sencilla: sin cabeceras ni nombres de campo, solo valores uno detrás de otro. Hay dos tipos de piezas:

  • varint: un entero de longitud variable: 7 bits por byte, y el bit más alto significa «sigue otro byte». 0x02 = 2, 0x80 0x01 = 128, 0x80 0xb5 0x18 = 400000.
  • clave / punto / escalar: exactamente 32 bytes, sin longitud. Las listas de puntos cuya longitud se conoce por el contexto (por ejemplo, los s de CLSAG, por el tamaño del anillo) tampoco llevan longitud.

Así que el analizador tiene que conocer el formato de antemano: el tipo RingCT, que se lee a mitad de camino, decide cómo leer todo lo que viene después.

prefijo version · unlock_time entradas: anillo (16 offsets) + key image salidas: clave de un uso P + view tag extra: R, payment ID… base RingCT type = 6 comisión (en claro) importes (8 B, cifr.) outPk = C podable Bulletproofs+ CLSAG: s×16, c1, D pseudoOuts C̃ un nodo con pruning puede descartarla Keccak Keccak Keccak txid = Keccak(h₁ ‖ h₂ ‖ h₃)

Qué funciones se aplican y dónde

CampoLo calculaFórmulaMás
extra: Rel remitenteR = r·Gstealth addresses
salida: Pel remitenteP = Hs(8·r·A ‖ i)·G + Bstealth addresses
view tagel remitenteKeccak("view_tag" ‖ 8rA ‖ i)[0]view tags
ecdhInfoel remitentev ⊕ Keccak("amount" ‖ Hs(8rA ‖ i))[0..8]Pedersen
outPkel remitenteC = y·G + v·H, y = Hs("commitment_mask" ‖ Hs(8rA ‖ i))Pedersen
anilloel monedero del remitente16 índices globales, distribución gamma sobre la antigüedadseñuelos
key imageel dueño de la entradaI = x·Hp(P)key image
pseudoOutsel remitenteC̃ = y'·G + v·H, máscaras elegidas para que Σ C̃ = Σ C + fee·HRingCT
CLSAGel dueño de la entradaun anillo de desafíos sobre P_i y C_i − C̃firmas de anillo
Bulletproofs+el remitenteprueba de que 0 ≤ v < 2⁶⁴ para todas las salidaspruebas de rango

Qué hace un nodo con estos bytes

  1. Los analiza; cualquier byte de más o de menos supone el rechazo.
  2. Comprueba que las key images son nuevas y están en el subgrupo correcto.
  3. Reconstruye los 16 índices globales a partir de los offsets y carga de su base de datos las claves y los compromisos de esas salidas.
  4. Comprueba el balance Σ C̃ = Σ outPk + fee·H; la herramienta de abajo también lo hace.
  5. Verifica Bulletproofs+ y la CLSAG de cada entrada.

Qué hace el monedero del destinatario

  1. Toma R de extra y calcula el secreto compartido 8·a·R (una operación por transacción).
  2. Para cada salida: un hash → compara el view tag; si no coincide, se para.
  3. Si coincide, compara Hs(8aR ‖ i)·G + B con la clave de la salida.
  4. Si la salida es nuestra, descifra el importe, reconstruye la máscara y comprueba y·G + v·H frente a outPk.

La herramienta muestra todos estos pasos con números concretos.

Comprobado en mainnet

El analizador se ha comprobado contra la decodificación JSON de monerod de transacciones reales de tipo 5 y tipo 6 y de una transacción coinbase: todos los campos y el txid coinciden. El botón «tx real de mainnet» carga una de ellas; compara el txid con cualquier explorador de bloques.

¿Cómo será una transacción FCMP++?

El formato de las transacciones FCMP++ aún puede cambiar antes del hard fork, así que aquí no hay un analizador de bytes para él. Pero a partir de la implementación actual la estructura general está clara, y se parece sorprendentemente a la de hoy:

prefijo version · unlock_time entradas: solo key image L (ya sin offsets del anillo) salidas: clave O (Carrot) + view tag de 3 bytes extra: clave efímera, … base nuevo tipo RingCT comisión importes + C bloque de referencia (qué raíz del árbol) podable Bulletproofs+ por entrada: Õ, Ĩ, R, C̃ por entrada: SA+L una prueba de pertenencia para todas las entradas en lugar de «16 índices + CLSAG» por entrada: «una tupla rerandomizada + SA+L» y una prueba de pertenencia común estructura según la implementación actual; los detalles pueden cambiar antes del hard fork
En RingCTEn FCMP++Funciones
16 offsets por entradauna referencia al bloque con cuyo árbol se construye la pruebaraíz del Curve Tree sobre todas las salidas
key image I = x·Hp(P)la misma L = x·Hp(O)los dobles gastos se detectan igual que antes
pseudo-salida C̃C̃ = C + r_c·G, parte de la tupla rerandomizadarerandomización
CLSAG por entradaSA+L por entrada (unos pocos puntos y escalares)Schnorr generalizado
—una prueba de pertenencia para toda la transacciónGeneralized Bulletproofs + divisores
view tag de 1 byte, ECDH sobre Ed25519view tag de 3 bytes, X25519, ancla JanusCarrot

Mira la rerandomización con números concretos en la herramienta. Cuando el formato sea definitivo, aquí se añadirá también un desglose byte a byte de las transacciones FCMP++.