Tecnico › Rete

Una transazione byte per byte

Analisi completa dell'hex di una transazione RingCT: ogni campo, ogni funzione, su dati reali e casuali

In breve

Una transazione Monero è composta da tre blocchi di byte consecutivi: il prefisso (input, output, extra), la base RingCT (commissione, importi cifrati, commitment) e la parte potabile (prunable: Bulletproofs+, CLSAG, pseudo-output). Qui sotto c'è uno strumento che analizza byte per byte qualsiasi transazione v2, calcola il txid, verifica il bilancio e, data una view key, trova i "tuoi" output e ne decifra gli importi. Per una transazione casuale mostra tutti i segreti, così puoi vedere come ogni funzione viene applicata a numeri concreti.

Come leggere i byte

La serializzazione di Monero è semplice: niente intestazioni né nomi dei campi, solo valori uno dopo l'altro. Due tipi di mattoncini:

  • varint: un intero a lunghezza variabile, 7 bit per byte, il bit più alto significa "segue un altro byte". 0x02 = 2, 0x80 0x01 = 128, 0x80 0xb5 0x18 = 400000.
  • chiave / punto / scalare: esattamente 32 byte, senza lunghezza. Anche gli array di punti la cui lunghezza è nota dal contesto (ad es. gli s di CLSAG, in base alla dimensione del ring) arrivano senza lunghezza.

Quindi il parser deve conoscere il formato in anticipo: il tipo RingCT, letto a metà, decide come leggere tutto ciò che segue.

prefisso version · unlock_time input: ring (16 offset) + key image output: chiave monouso P + view tag extra: R, payment ID… base RingCT type = 6 commissione (in chiaro) importi (8 B, cifrati) outPk = C prunable Bulletproofs+ CLSAG: s×16, c1, D pseudoOuts C̃ può essere scartata da un nodo pruned Keccak Keccak Keccak txid = Keccak(h₁ ‖ h₂ ‖ h₃)

Quali funzioni si applicano dove

CampoCalcolato daFormulaApprofondimento
extra: RmittenteR = r·Gstealth address
output: PmittenteP = Hs(8·r·A ‖ i)·G + Bstealth address
view tagmittenteKeccak("view_tag" ‖ 8rA ‖ i)[0]view tag
ecdhInfomittentev ⊕ Keccak("amount" ‖ Hs(8rA ‖ i))[0..8]Pedersen
outPkmittenteC = y·G + v·H, y = Hs("commitment_mask" ‖ Hs(8rA ‖ i))Pedersen
ringwallet del mittente16 indici globali, distribuzione gamma sull'etàesche
key imageproprietario dell'inputI = x·Hp(P)key image
pseudoOutsmittenteC̃ = y'·G + v·H, maschere scelte in modo che Σ C̃ = Σ C + fee·HRingCT
CLSAGproprietario dell'inputun anello di sfide su P_i e C_i − C̃ring signature
Bulletproofs+mittenteprova che 0 ≤ v < 2⁶⁴ per tutti gli outputrange proof

Cosa fa un nodo con questi byte

  1. Li analizza; qualsiasi byte in più o in meno comporta il rifiuto.
  2. Verifica che le key image siano nuove e nel sottogruppo corretto.
  3. Ricostruisce i 16 indici globali dagli offset e carica dal proprio database le chiavi e i commitment di quegli output.
  4. Verifica il bilancio Σ C̃ = Σ outPk + fee·H: lo fa anche lo strumento qui sotto.
  5. Verifica Bulletproofs+ e la CLSAG di ogni input.

Cosa fa il wallet del destinatario

  1. Prende R da extra e calcola il segreto condiviso 8·a·R (un'operazione per transazione).
  2. Per ogni output: un hash → confronta il view tag; se non coincide, stop.
  3. Se coincide, confronta Hs(8aR ‖ i)·G + B con la chiave dell'output.
  4. L'output è nostro: decifra l'importo, ricostruisce la maschera e verifica y·G + v·H rispetto a outPk.

Lo strumento mostra tutti questi passaggi con numeri concreti.

Verificato sulla mainnet

Il parser è stato verificato rispetto alla decodifica JSON di monerod di transazioni reali di tipo 5 e tipo 6 e di una transazione coinbase: ogni campo e il txid coincidono. Il pulsante "tx reale dalla mainnet" ne carica una: confronta il txid con un qualsiasi block explorer.

Come sarà una transazione FCMP++?

Il formato delle transazioni FCMP++ potrebbe ancora cambiare prima dell'hard fork, quindi qui non c'è un parser di byte per esso. Ma dall'implementazione attuale la struttura generale è chiara, ed è sorprendentemente simile a quella di oggi:

prefisso version · unlock_time input: solo key image L (niente più offset del ring) output: chiave O (Carrot) + view tag da 3 byte extra: chiave effimera, … base nuovo tipo RingCT commissione importi + C blocco di riferimento (quale radice dell'albero) prunable Bulletproofs+ per input: Õ, Ĩ, R, C̃ per input: SA+L una prova di appart. per tutti gli input invece di "16 indici + CLSAG" per input: "una tupla rerandomizzata + SA+L" e una prova di appartenenza comune struttura secondo l'implementazione attuale; i dettagli del formato potrebbero cambiare prima dell'hard fork
In RingCTIn FCMP++Funzioni
16 offset per inputun riferimento al blocco rispetto al cui albero è costruita la provaradice del Curve Tree su tutti gli output
key image I = x·Hp(P)la stessa L = x·Hp(O)doppie spese individuate come prima
pseudo-output C̃C̃ = C + r_c·G: parte della tupla rerandomizzatarerandomizzazione
CLSAG per inputSA+L per input (pochi punti e scalari)Schnorr generalizzato
—una sola prova di appartenenza per l'intera transazioneGeneralized Bulletproofs + divisori
view tag da 1 byte, ECDH su Ed25519view tag da 3 byte, X25519, ancora JanusCarrot

Guarda la rerandomizzazione su numeri concreti nello strumento. Quando il formato sarà definitivo, qui verrà aggiunta anche un'analisi byte per byte delle transazioni FCMP++.