Tecnico › Chiavi e indirizzi

Subaddress

Un numero qualsiasi di indirizzi non collegabili da un'unica coppia di chiavi

In breve

Il subaddress con indice (i, j) è la coppia di punti D = B + m·G e C = a·D, dove m = Hs("SubAddr\0" ‖ a ‖ i ‖ j). Senza la view key a non si può capire che dei subaddress appartengono allo stesso wallet.

Perché

Se hai dato lo stesso indirizzo a due persone diverse, queste possono confrontarsi e accorgersi di aver pagato lo stesso destinatario. I subaddress risolvono il problema: un indirizzo per ogni pagatore, mentre il wallet resta uno solo e la scansione non rallenta.

I subaddress formano una gerarchia a due livelli: l'account i (indice principale) e l'indice all'interno dell'account j (indice secondario). Il subaddress (0, 0) è l'indirizzo principale.

Perché l'indirizzo (1,0) non inizia con 4?

Un wallet ha esattamente un indirizzo principale, (0,0), che è semplicemente B e A con prefisso 18. Qualsiasi altro indice, compreso il "primo" indirizzo del secondo account (1,0), è un subaddress: le sue chiavi sono derivate tramite m = Hs("SubAddr\0" ‖ a ‖ 1 ‖ 0) ed è codificato con prefisso 42, da cui 8…. Un account è solo un modo per raggruppare i subaddress; non ha un proprio indirizzo "principale".

Come si calcola

m = Hs( "SubAddr\0" ‖ a ‖ i ‖ j )
D = B + m·G     C = a·D i e j sono numeri little-endian a 32 bit; "SubAddr\0" sono 8 byte con un byte zero finale

Un subaddress è codificato come un normale indirizzo ma con prefisso 42 (inizia con 8 sulla mainnet): Base58(42 ‖ D ‖ C ‖ checksum).

Nota che la "view key" pubblica del subaddress, C, non è a·G ma a·D. Questo trucco serve per l'invio.

Inviare a un subaddress

Quando si invia a un indirizzo normale la chiave della transazione è R = r·G. Quando si invia a un subaddress, il mittente usa R = r·D. Il destinatario calcola poi il segreto condiviso come al solito:

a·R = a·r·D = r·(a·D) = r·C

Il mittente conosce r e C, il destinatario conosce a e R, ed entrambi ottengono lo stesso punto.

Come il wallet riconosce i propri subaddress

Il wallet precalcola D per qualche centinaio di indici e tiene una tabella D → (i, j). Per ogni output calcola un candidato

D' = P − Hs(8·a·R ‖ n)·G

e cerca D' nella tabella. Se lo trova, l'output è nostro, e sappiamo anche a quale subaddress è arrivato. Ecco perché i subaddress non rallentano la scansione: una sottrazione e una ricerca in una tabella hash.

Sottigliezza: subaddress e output multipli

Se una transazione ha un output verso un subaddress, il wallet mittente usa una chiave di transazione separata per ogni output (chiavi aggiuntive in tx_extra). Altrimenti il destinatario potrebbe verificare se il resto del mittente appartiene a un certo indirizzo.