Dati on-chain e SQL · Lezione 2 di 9
Transazioni Bitcoin grezze e tipi di script
14 min di lettura
Gli explorer mostrano una transazione Bitcoin sotto forma di elenchi ordinati di indirizzi e importi, ma questa vista è una decodifica di byte grezzi e parte di ciò che mostra non è affatto presente nella transazione. La lettura della struttura grezza indica a un investigatore che cosa è registrato on-chain e che cosa è stato derivato da uno strumento, perché la commissione non può essere calcolata dalla sola transazione e quali campi contengono le impronte del wallet su cui si basano le euristiche successive. La lettura dei formati degli indirizzi indica quale tipo di script è in uso, restringendo il campo del software del wallet e del relativo periodo, e che cosa diventerà pubblico quando le monete saranno spese.
La serializzazione, campo per campo
Una transazione viene memorizzata e trasmessa come una stringa di byte. Da SegWit (BIP 141, attivato nell'agosto 2017) esistono due forme: la forma legacy e la forma estesa del BIP 144, utilizzata quando almeno un input contiene dati witness.
| Campo | Dimensione | Note |
|---|---|---|
| version | 4 byte | Intero little-endian, solitamente 1 o 2 |
| marker, flag | 2 byte | 00 01, presenti solo nella forma SegWit |
| numero di input | variabile | Intero CompactSize |
| input | variabile | Ciascuno: txid precedente (32), indice dell'output (4), lunghezza di scriptSig e scriptSig, sequence (4) |
| numero di output | variabile | Intero CompactSize |
| output | variabile | Ciascuno: valore in satoshi (8), lunghezza di scriptPubKey e scriptPubKey |
| witness | variabile | Solo nella forma SegWit: per ciascun input, un conteggio degli elementi e gli elementi stessi |
| locktime | 4 byte | Altezza del blocco o istante temporale minimo a partire dal quale la transazione è valida |
Gli interi sono little-endian: il byte meno significativo viene per primo. Un intero CompactSize occupa un byte per valori inferiori a 253, altrimenti un byte di prefisso (fd, fe o ff) seguito da 2, 4 o 8 byte. Di seguito una transazione SegWit con un input e due output suddivisa nei suoi campi, con i valori lunghi troncati:
02000000 versione 2
00 01 marker e flag SegWit
01 numero di input 1
ab09…1d7c txid precedente, ordine interno dei byte (32 byte)
01000000 indice dell'output precedente 1
00 lunghezza di scriptSig 0 (input SegWit nativo)
fdffffff sequence 0xfffffffd
02 numero di output 2
40420f0000000000 valore 1.000.000 sat
16 0014 3c1e…9d02 script, 22 byte: P2WPKH
0c6a070000000000 valore 485.900 sat
16 0014 77b4…e051 script, 22 byte: P2WPKH
02 47 3044…01 21 02a1…5f witness: 2 elementi, firma (71 byte), chiave pubblica (33 byte)
00000000 locktime 0
Si legge il primo valore invertendo i suoi byte: 40 42 0f 00 … diventa 0x0f4240, che corrisponde a 1.000.000 satoshi (0,01 BTC). Si noti ciò che manca: nessun indirizzo del mittente, nessun importo dell'input e nessuna commissione.
Input e outpoint
Un input non contiene monete; punta a un output precedente, chiamato outpoint e scritto txid:vout. Il txid di 32 byte all'interno dell'input è memorizzato nell'ordine interno dei byte, inverso rispetto a quello visualizzato dagli explorer: ab09…1d7c nei byte è 7c1d…09ab sullo schermo. Dimenticare questa inversione è una causa classica del messaggio "transazione non trovata".
L'importo e lo script che viene speso si trovano nell'output precedente, quindi per sapere chi ha pagato e quanto occorre ricercare ciascun outpoint. Lo scriptSig dell'input (script legacy) o il witness (script SegWit) fornisce i dati che soddisfano le condizioni dell'output precedente, solitamente firme e chiavi pubbliche. Un input coinbase, che crea nuove monete, ha un txid precedente composto interamente da zeri e indice 0xffffffff.
Sequence, version, locktime e impronte del wallet
I campi attorno agli input e agli output sembrano elementi di routine, ma veicolano informazioni:
- Sequence. I valori inferiori a
0xfffffffesegnalano la sostituibilità secondo il BIP 125, con0xfffffffdcome scelta comune. Da Bitcoin Core 28.0 (2024), i nodi accettano per impostazione predefinita le sostituzioni indipendentemente dal fatto che la transazione originale le abbia segnalate, quindi oggi il valore conta soprattutto come impronta. I blocchi temporali relativi (BIP 68) utilizzano lo stesso campo. - Version. Quasi sempre 1 o 2; la versione 2 è necessaria per i blocchi temporali relativi e i wallet differiscono nella scelta della versione utilizzata.
- Locktime. Viene interpretato come altezza del blocco se inferiore a 500.000.000 e come timestamp Unix altrimenti; zero significa nessuna restrizione. Molti wallet lo impostano all'altezza corrente per scoraggiare i miner dal riscrivere blocchi recenti per acquisire commissioni (anti-fee-sniping), quindi un locktime di 880.213 in una transazione confermata a 880.214 fornisce indicazioni sul software che l'ha costruita.
Questi valori, insieme ai tipi di script utilizzati, all'ordine degli output e alla codifica delle firme, formano un'impronta del wallet. Una singola impronta non dimostra nulla; una combinazione coerente tra più transazioni è uno dei segnali che la lezione 4.2 utilizza per distinguere un pagamento dal resto.
Script di output e formati degli indirizzi
Ogni output contiene un valore in satoshi e uno scriptPubKey, la condizione per spenderlo. Gli script standard seguono modelli fissi, quindi il tipo può essere riconosciuto dai primi byte. Un indirizzo è una codifica di uno script standard, non un campo della transazione: i decodificatori lo derivano e il formato ne rivela il tipo.
| Tipo | Modello di scriptPubKey | Byte | Definito in | Codifica | Indirizzo | type di Bitcoin Core |
|---|---|---|---|---|---|---|
| P2PKH | 76 a9 14 {20} 88 ac | 25 | originale | Base58Check, versione 0x00 | 1…, fino a 34 caratteri | pubkeyhash |
| P2SH | a9 14 {20} 87 | 23 | BIP 16 | Base58Check, versione 0x05 | 3…, 34 caratteri | scripthash |
| P2WPKH | 00 14 {20} | 22 | BIP 141 | bech32 | bc1q…, 42 caratteri | witness_v0_keyhash |
| P2WSH | 00 20 {32} | 34 | BIP 141 | bech32 | bc1q…, 62 caratteri | witness_v0_scripthash |
| P2TR | 51 20 {32} | 34 | BIP 341 | bech32m | bc1p…, 62 caratteri | witness_v1_taproot |
| OP_RETURN | 6a … | variabile | — | nessuna | nessuno | nulldata |
In P2PKH, 76 a9 14 … 88 ac corrisponde a OP_DUP OP_HASH160 {push di 20 byte} OP_EQUALVERIFY OP_CHECKSIG. Gli output SegWit iniziano con un opcode di versione (00 per la versione 0, 51 per la versione 1, Taproot) seguito da un push del programma. Un output OP_RETURN è dimostrabilmente non spendibile e contiene dati, non valore. Le testnet utilizzano altri prefissi (m o n per P2PKH, 2 per P2SH, tb1 per SegWit), quindi un indirizzo testnet fuori posto in un dataset è facile da individuare. Alcuni vecchi script, come i primi output pay-to-public-key e gli script multisig senza incapsulamento, non hanno alcun indirizzo standard; i data warehouse possono lasciare vuota la colonna dell'indirizzo e un filtro su address IS NOT NULL li esclude senza alcuna segnalazione.
Base58Check
Gli indirizzi legacy prendono un byte di versione, aggiungono il payload di 20 byte (un HASH160 di una chiave pubblica o di uno script), aggiungono un checksum costituito dai primi 4 byte di SHA-256(SHA-256(versione ‖ payload)) e rappresentano i 25 byte con un alfabeto di 58 caratteri privo di 0, O, I e l. Ogni byte zero iniziale diventa un 1 iniziale, motivo per cui tutti gli indirizzi P2PKH iniziano con questo carattere. La codifica distingue tra maiuscole e minuscole e il checksum respinge quasi ogni errore di digitazione. Il byte di versione è la chiave di classificazione: 0x00 significa P2PKH, 0x05 P2SH, qualsiasi altro valore indica un'altra rete o moneta.
Bech32 e bech32m
Gli indirizzi SegWit nativi utilizzano bech32 (BIP 173): una parte leggibile dall'uomo (bc per la mainnet, tb per la testnet), il separatore 1 e una parte dati in un alfabeto di 32 caratteri che esclude 1, b, i e o. Il primo carattere dei dati è la versione witness (q = 0, p = 1), seguita dal programma e da un checksum di 6 caratteri. Un programma di 20 byte produce 42 caratteri (P2WPKH), uno di 32 byte ne produce 62 (P2WSH). Bech32 è interamente minuscolo (il maiuscolo è consentito per i codici QR, l'uso misto di maiuscole e minuscole deve essere respinto), ha una lunghezza massima di 90 caratteri e il suo checksum rileva qualsiasi errore che interessi fino a quattro caratteri.
Presenta un difetto: quando una stringa termina con p, l'inserimento o l'eliminazione di caratteri q immediatamente prima può lasciare valido il checksum. I vincoli rigorosi sulla lunghezza del programma della versione 0 rendono questo problema perlopiù innocuo, ma le versioni successive consentono altre lunghezze, quindi il BIP 350 ha introdotto bech32m, identico tranne che per la costante del checksum (0x2bc830a3 invece di 1). La versione 0 mantiene bech32; le versioni da 1 a 16, compreso Taproot, utilizzano bech32m e i decodificatori devono respingere il checksum errato per ciascuna. Nelle pipeline, non si deve mai classificare in base a "inizia con bc1": occorre leggere il quarto carattere e la lunghezza. I parser scritti prima del 2021 possono respingere indirizzi bc1p validi e una pipeline che scarta le righe non interpretabili sottostima l'attività Taproot senza alcun avviso.
Dati witness, peso e dimensione
SegWit sposta le firme in un witness separato. Il suo contenuto dipende dal tipo di output che viene speso:
- P2WPKH: una firma DER con un byte sighash (solitamente 71 o 72 byte) e una chiave pubblica compressa di 33 byte.
- P2WSH: gli elementi che soddisfano le condizioni seguiti dallo script witness stesso, quindi la politica di spesa diventa visibile solo quando l'output viene speso.
- P2TR tramite percorso della chiave: una firma Schnorr di 64 byte (BIP 340), oppure 65 byte con un tipo sighash non predefinito. Una spesa tramite percorso dello script rivela uno script (BIP 342) e un blocco di controllo.
- SegWit incapsulato in P2SH: lo scriptSig contiene soltanto un breve redeem script come
0014…e le firme si trovano nel witness.
Ai byte del witness viene applicato uno sconto. Il BIP 141 definisce il peso come dimensione di base × 3 + dimensione totale, dove la dimensione di base esclude marker, flag e witness; la dimensione virtuale (vsize) è il peso ÷ 4, arrotondato per eccesso, e i tassi di commissione sono espressi per vbyte. Nell'esempio, la forma di base è di 113 byte e la sezione witness, contando marker e flag, ne aggiunge 109, per un totale di 222 byte. Il peso è 113 × 3 + 222 = 561 e 561 ÷ 4 si arrotonda per eccesso a 141 vbyte.
La vista decodificata e la commissione
Il comando getrawtransaction di Bitcoin Core con livello di dettaglio 2 restituisce la transazione decodificata, più ciascun output speso (prevout) e la commissione, quando il nodo dispone dei dati di annullamento del blocco (la lezione 2.1 tratta le chiamate al nodo):
{
"txid": "4f3a…c21e", "hash": "9b07…e4d2",
"version": 2, "size": 222, "vsize": 141, "weight": 561, "locktime": 0,
"vin": [{
"txid": "7c1d…09ab", "vout": 1,
"scriptSig": {"asm": "", "hex": ""},
"txinwitness": ["3044…01", "02a1…5f"],
"prevout": {"value": 0.01500000, "height": 868120,
"scriptPubKey": {"type": "witness_v0_keyhash", "address": "bc1q…7k3f"}},
"sequence": 4294967293
}],
"vout": [
{"value": 0.01000000, "n": 0,
"scriptPubKey": {"type": "witness_v0_keyhash", "address": "bc1q…m2d8"}},
{"value": 0.00485900, "n": 1,
"scriptPubKey": {"type": "witness_v0_keyhash", "address": "bc1q…x9qa"}}
],
"fee": 0.00014100
}
La commissione non è mai scritta nella transazione; è la differenza tra input e output. Qui l'unico outpoint vale 1.500.000 sat, gli output totalizzano 1.000.000 + 485.900 = 1.485.900 sat, quindi la commissione è di 14.100 sat e 14.100 ÷ 141 = 100 sat/vB. Il semplice decoderawtransaction restituisce la stessa struttura senza prevout e fee, perché non può vedere gli output precedenti. Gli importi arrivano in BTC come numeri JSON; occorre convertirli in satoshi interi prima di qualsiasi operazione aritmetica (lezione 2.6). Una transazione coinbase non ha commissione: i suoi output riscuotono il sussidio più tutte le altre commissioni. La lezione 2.9 mostra lo stesso calcolo in SQL sulle tabelle degli input e degli output.
txid e wtxid a confronto
Il txid è il doppio SHA-256 della transazione serializzata senza marker, flag e witness; il wtxid calcola l'hash dell'intera forma SegWit e Bitcoin Core lo mostra come hash. Senza dati witness i due sono identici. L'esclusione del witness ha risolto la malleabilità da parte di terzi: alterare la codifica di una firma cambiava il txid di una transazione per il resto identica. Ora le modifiche al witness alterano soltanto il wtxid, quindi le catene di transazioni non confermate possono fare riferimento le une alle altre in sicurezza. I blocchi vincolano crittograficamente i wtxid tramite un commitment witness nella coinbase. Nei rapporti, le transazioni devono essere identificate tramite il txid nell'ordine dei byte di visualizzazione.
Che cosa rivelano gli script sui wallet e sulla custodia
I formati degli indirizzi riflettono la storia dei wallet:
1…: vecchi wallet, servizi esclusivamente legacy, alcuni wallet di exchange in uso da lungo tempo; oggi una nuova attività P2PKH indica spesso software datato o personalizzato.3…: ambiguo fino alla spesa: multisig oppure un wallet a chiave singola che utilizza SegWit annidato, un'impostazione predefinita comune nel periodo 2017–2019. Al momento della spesa, il redeem script risolve l'ambiguità:0014più 20 byte indica P2WPKH annidato,0020più 32 byte indica P2WSH annidato, uno script che termina conae(OP_CHECKMULTISIG) indica multisig classico.bc1q…: il tipo di indirizzo di ricezione predefinito della maggior parte dei wallet più diffusi negli ultimi anni; P2WSH di 62 caratteri indica solitamente multisig o un altro script personalizzato.bc1p…: wallet che hanno adottato Taproot dopo la sua attivazione al blocco 709.632 nel novembre 2021, oltre a strumenti per iscrizioni e token.
I tipi di script si mescolano anche all'interno delle transazioni: se tutti gli input sono P2WPKH e gli output sono uno P2WPKH e uno P2SH, è più probabile che l'output corrispondente agli input sia il resto (un'euristica, non un fatto; si veda la lezione 4.2).
Multisig classico
Il multisig classico nasconde la propria politica finché i fondi restano non spesi (solo un hash è on-chain) e la pubblica alla prima spesa. Un input P2WSH 2-su-3:
witness[0] (vuoto) -- elemento fittizio richiesto da OP_CHECKMULTISIG
witness[1] 3044…01 -- prima firma (DER, byte sighash 01)
witness[2] 3045…01 -- seconda firma
witness[3] 5221…21…21…53ae -- OP_2 <key1> <key2> <key3> OP_3 OP_CHECKMULTISIG
52 è OP_2 (sono richieste due firme), ciascun 21 inserisce una chiave di 33 byte, 53 è OP_3 e ae è OP_CHECKMULTISIG. Una sola spesa rivela la soglia, tutte e tre le chiavi pubbliche e quali due hanno firmato. I custodi adottano spesso politiche distintive (2-su-3, 3-su-5) e la presenza della stessa chiave pubblica in molti script multisig collega quegli indirizzi a un unico detentore della chiave in modo molto più affidabile rispetto alle euristiche comportamentali. Molti wallet ordinano le chiavi lessicograficamente (BIP 67), il che costituisce a sua volta una piccola impronta.
Taproot
Taproot combina firme Schnorr con chiavi di 32 byte "solo x" (BIP 340), una chiave di output Q = P + t·G dove P è una chiave interna e il tweak t vincola crittograficamente P e la radice Merkle di un albero di script opzionale (BIP 341), e regole degli script che sostituiscono OP_CHECKMULTISIG con OP_CHECKSIGADD (BIP 342).
| Spesa | Witness | Che cosa apprende un analista |
|---|---|---|
| Multisig P2WSH 2-su-3 | Due firme più lo script completo | Soglia, tutte le chiavi, quali chiavi hanno firmato |
| P2TR tramite percorso della chiave | Una firma di 64 byte (65 con sighash esplicito) | Nulla sulla politica |
| P2TR tramite percorso dello script | Script foglia, i suoi input, blocco di controllo di 33 + 32m byte | L'unica condizione utilizzata, la sua profondità m, la chiave interna |
Nel percorso della chiave, un wallet a chiave singola, un gruppo n-su-n che utilizza l'aggregazione delle chiavi (MuSig2) e uno schema a soglia appaiono tutti uguali. Il percorso dello script rivela soltanto la foglia utilizzata, anche se la lunghezza del percorso Merkle lascia trapelare qualche informazione sulle dimensioni dell'albero. Taproot elimina l'impronta multisig ogni volta che viene utilizzato il percorso della chiave; non nasconde gli importi, il grafo o la tempistica e il formato bc1p stesso continua a distinguere i suoi utenti. Le spese tramite percorso dello script contengono anche iscrizioni: dal 2023, witness di grandi dimensioni con un involucro OP_FALSE OP_IF … OP_ENDIF incorporano immagini e dati di token, uno schema da escludere mediante filtro dall'analisi dei pagamenti.
Focus sull'esame: è necessario saper decodificare o classificare, non soltanto ricordare: ordine e dimensioni dei campi, valori little-endian e CompactSize; outpoint e ordine invertito dei byte del txid; modelli di scriptPubKey, BIP, prefissi e lunghezze (1,3,bc1qcon 42 e 62 caratteri,bc1pcon 62); byte di versione Base58Check e confronto tra bech32 e bech32m; il fatto che indirizzi, importi degli input e commissioni siano derivati, non memorizzati; calcoli del peso e della vsize; commissione ricavata dagli output precedenti; confronto tra txid e wtxid e malleabilità; come si risolve l'ambiguità di un P2SH speso, che cosa rivela una spesa multisig e che cosa nascondono le spese Taproot tramite percorso della chiave e percorso dello script.