Capire il Bitcoin Script
Che cos'è il Bitcoin Script?
Ogni volta che invii Bitcoin, non stai davvero spostando una moneta da un posto a un altro. Stai creando un output e collegandovi un piccolo programma. Quel programma definisce le condizioni esatte alle quali quelle monete potranno essere spese in seguito. Il linguaggio usato per scrivere questi programmi si chiama Bitcoin Script.
Satoshi Nakamoto lo ha descritto nel modo migliore, definendo uno script una sorta di predicato: una piccola equazione che si risolve in vero o falso. Se chi spende riesce a rendere vera l'equazione, le monete si muovono. In caso contrario, restano bloccate. È tutto qui il compito di Script. È un meccanismo di blocco, non un computer generico.
Il Bitcoin Script è volutamente piccolo e limitato. È non Turing completo, il che significa che non ha cicli, non ha ricorsione e non ha modo di eseguire un programma senza fine. Non è una funzione mancante. È una decisione di sicurezza fondamentale. Un linguaggio che non può girare all'infinito può sempre essere controllato in modo rapido ed economico da ogni nodo della rete, ed è anche per questo che persone comuni possono verificare Bitcoin su hardware modesto.
Tutto ciò è in deliberato contrasto con le reti che usano un linguaggio per contratti Turing completo. Quella maggiore espressività ha prodotto ripetutamente comportamenti inattesi, fondi congelati ed exploit che hanno prosciugato valore per centinaia di milioni di Euro o Dollaro. Su quelle piattaforme i token possono anche essere congelati o sequestrati dall'emittente o da un'organizzazione che li controlla. Bitcoin ha scelto un linguaggio più piccolo e prevedibile proprio perché nessuno tenga in mano quel tipo di interruttore sulle tue monete.
Se non hai ancora letto come una transazione viene controllata dalla rete, Come vengono verificate le transazioni Bitcoin è un buon complemento a questo articolo.
Lo stack: come ragiona Script
Il Bitcoin Script è un linguaggio basato sullo stack. Uno stack è una semplice pila di dati in cui puoi solo aggiungere un elemento in cima (un "push") o togliere un elemento dalla cima (un "pop"). L'ultima cosa che metti è la prima che togli. I computer gestiscono questo tipo di memoria in modo molto efficiente, anche se per noi umani risulta un po' strano.
A causa dello stack, Script non si scrive nel modo in cui scriviamo di solito i calcoli. Invece di scrivere 9 più 12, scrivi prima i due numeri e poi l'operazione: 9 12 OP_ADD. I due numeri vengono messi sullo stack, poi OP_ADD li toglie entrambi, li somma e rimette il risultato.
I comandi come OP_ADD si chiamano opcode, abbreviazione di operation code. Sono i verbi del linguaggio. Ci sono circa 80 opcode attivi, che coprono calcoli semplici, confronti, hashing crittografico e verifica delle firme. Ognuno toglie alcuni elementi dallo stack, fa il suo lavoro e può rimettere un risultato.
Ecco un esempio completo, anche se un po' sciocco, di uno script funzionante:
9 12 OP_ADD 21 OP_EQUAL
Leggendo da sinistra a destra: metti 9, metti 12, poi OP_ADD li sostituisce con 21. Ora metti un altro 21. Infine OP_EQUAL toglie i due valori, vede che coincidono e mette un 1. Resta un solo 1 sullo stack, quindi lo script è valido. In parole povere, questo output è bloccato dietro l'enigma "dammi due numeri che sommati fanno 21", e chiunque sappia fare aritmetica di base potrebbe sbloccarlo.
Script di blocco e script di sblocco
In Bitcoin quel singolo script è in realtà diviso in due metà che si trovano in posti diversi.
Lo script di blocco, tecnicamente lo scriptPubKey, è collegato a un output. Stabilisce le condizioni per la spesa. Nell'esempio sopra, la parte di blocco è OP_ADD 21 OP_EQUAL, la regola da soddisfare.
Lo script di sblocco, tecnicamente lo scriptSig, viene fornito più tardi da chi vuole spendere quell'output. Fornisce i valori di input che soddisfano la regola. Nell'esempio, la parte di sblocco è 9 12, la risposta all'enigma.
Quindi inviare Bitcoin a qualcuno significa davvero creare un output il cui script di blocco solo quella persona può soddisfare. La versione classica blocca le monete su una chiave, così solo chi possiede la chiave privata corrispondente può produrre uno script di sblocco valido.
Un dettaglio inganna quasi tutti. Anche se lo script di sblocco viene fornito da chi spende dopo che l'output esiste già, quando un nodo esegue la validazione completa mette prima lo script di sblocco, poi lo script di blocco. Lo script di sblocco posa i dati sullo stack, e lo script di blocco poi li verifica.
How Locking and Unlocking Fit Together
Unlocking Script
scriptSig
Provided by the spender.
Locking Script
scriptPubKey
Attached to the output.
During validation the unlocking script runs first, then the locking script.
If the combined script leaves a single TRUE on the stack, the output is unlocked.
Come viene convalidato uno script
Un nodo convalida una transazione combinando gli script di sblocco e di blocco ed eseguendoli da sinistra a destra. L'esito è binario.
Uno script è valido se, dopo aver terminato, resta esattamente un elemento diverso da zero sullo stack. In pratica significa un singolo OP_1, o qualunque valore che conti come "vero".
Uno script è non valido se alla fine lo stack è vuoto, se l'unica cosa rimasta è uno zero, se resta più di un elemento, oppure se lo script si interrompe in anticipo. Non esiste un "forse". O le condizioni sono soddisfatte, o le monete non si muovono.
Questa chiarezza del tipo passa o non passa è ciò che rende Bitcoin verificabile. Ogni nodo esegue gli stessi script e arriva alla stessa risposta, senza ambiguità e senza dipendere da informazioni esterne.
Pay-to-Public-Key: il blocco originale
Il primo tipo di script standard è Pay-to-Public-Key (P2PK). Lo script di blocco contiene semplicemente una chiave pubblica seguita dall'opcode OP_CHECKSIG. Per spendere, il proprietario fornisce una firma nello script di sblocco.
Blocco: <ChiavePubblica> OP_CHECKSIG
Sblocco: <Firma>
OP_CHECKSIG toglie la firma e la chiave pubblica, verifica che la firma sia stata prodotta dalla chiave privata corrispondente e mette un 1 se il controllo va a buon fine. P2PK compare in molti dei primissimi blocchi, inclusi gli output coinbase del blocco genesi.
Oggi quasi nessuno usa P2PK. Il problema è pratico: per pagare qualcuno servirebbe la sua chiave pubblica completa, che è lunga, illeggibile e facile da copiare in modo errato. Satoshi ha risolto questo con il pattern successivo.
Pay-to-Public-Key-Hash: il cavallo di battaglia
Pay-to-Public-Key-Hash (P2PKH) memorizza nello script di blocco solo un hash della chiave pubblica, non la chiave stessa. Pensa all'hash come a una breve impronta della chiave. La chiave pubblica vera e propria viene rivelata solo più tardi, nello script di sblocco, quando le monete vengono spese.
L'hash si ottiene eseguendo SHA-256 e poi RIPEMD-160, una combinazione che Bitcoin racchiude in un unico opcode chiamato OP_HASH160. Il risultato è di soli 160 bit, molto più corto della chiave pubblica. Da quell'hash, con una codifica e un checksum, un wallet costruisce il familiare indirizzo Bitcoin. Un dettaglio su cui vale la pena soffermarsi: gli indirizzi non esistono davvero dentro la blockchain. Sono una comodità creata dai wallet sopra questi hash. Ciò che il protocollo vede è l'hash.
Il compromesso è che la chiave pubblica non è più visibile nell'output, eppure OP_CHECKSIG ne ha bisogno per verificare la firma. Quindi chi spende deve fornire sia la chiave pubblica sia la firma, e lo script controlla prima che la chiave fornita produca davvero l'hash memorizzato nel blocco.
Blocco: OP_DUP OP_HASH160 <HashChiavePubblica> OP_EQUALVERIFY OP_CHECKSIG
Sblocco: <Firma> <ChiavePubblica>
Per approfondire il passaggio di hashing in sé, vedi Che cos'è un hash?.
Esecuzione passo passo di uno script P2PKH
Ecco lo script P2PKH completo eseguito passo dopo passo. Lo script di sblocco va per primo.
- La firma e la chiave pubblica vengono messe sullo stack.
OP_DUPduplica l'elemento in cima, la chiave pubblica, perché allo script serve una copia da sottoporre a hash e una copia per verificare la firma più avanti.OP_HASH160prende la copia in cima della chiave pubblica e la sottopone a hash, lasciando sullo stack un nuovo hash della chiave pubblica.- L'hash atteso dallo script di blocco viene messo sullo stack, e
OP_EQUALVERIFYconfronta i due hash. Se differiscono, lo script fallisce subito. Se coincidono, entrambi vengono rimossi e l'esecuzione continua. - Ciò che resta è ora di fatto uno script P2PK: una firma e una chiave pubblica.
OP_CHECKSIGverifica la firma rispetto alla chiave e mette un 1.
Resta un solo 1 sullo stack, quindi l'output è sbloccato. La meccanica è identica sia che le monete siano state bloccate con il P2PKH legacy sia con il suo equivalente moderno SegWit, P2WPKH. Cambia solo dove vengono memorizzati i dati della firma.
P2PKH Script Execution
Step through how a node unlocks a Pay-to-Public-Key-Hash output.
Stack (top to bottom)
Operation
Initial State
The script is ready. The unlocking script runs first, then the locking script.
Script multifirma
Uno dei trucchi più utili di Script è richiedere più di una firma per spendere. Uno script multisig standard, di tipo P2MS, codifica una regola "m di n": tra n chiavi pubbliche elencate, qualsiasi m firme valide sbloccano i fondi.
Un multisig 2 di 3, per esempio, richiede 2 firme da un insieme di 3 chiavi. È la base della custodia condivisa, dove nessuna singola chiave può muovere il denaro da sola. È alla base delle configurazioni multifirma trattate nei nostri articoli sulla sicurezza.
Blocco: OP_2 <ChiavePub1> <ChiavePub2> <ChiavePub3> OP_3 OP_CHECKMULTISIG
Sblocco: <Firma1> <Firma2>
OP_CHECKMULTISIG verifica sia che sia presente il numero richiesto di firme valide sia che ciascuna appartenga a una delle chiavi elencate. Scritto direttamente così, però, il multisig ha la stessa scomodità di P2PK. Chi invia deve conoscere ogni chiave pubblica e lo script esatto, l'output diventa grande e costoso, e la tua configurazione di sicurezza è esposta on-chain a vista di tutti. Il pattern successivo risolve tutto questo in un colpo solo.
Pay-to-Script-Hash: nascondere la complessità
Pay-to-Script-Hash (P2SH) ti permette di bloccare le monete sull'hash di uno script invece che sullo script stesso. Il tuo script personalizzato completo, chiamato redeem script, resta nascosto fino a quando spendi.
Quando si invia a un output P2SH, lo script di blocco contiene solo l'hash. Chi invia non ha bisogno di conoscere la tua struttura multisig o le tue condizioni personalizzate. Paga semplicemente verso un indirizzo breve che inizia con la cifra 3. Il tuo redeem script, insieme ai dati che lo soddisfano, viene fornito solo più tardi nello script di sblocco.
Blocco: OP_HASH160 <HashScript> OP_EQUAL
Sblocco: <Firma1> <Firma2> <RedeemScript>
La validazione avviene in due fasi. Prima lo script esterno conferma che il redeem script fornito produce l'hash presente nel blocco. Poi il redeem script viene spacchettato ed eseguito come script a sé stante, rispetto alle firme fornite. Solo se entrambe le fasi vanno a buon fine le monete si muovono. P2SH ti offre tre vantaggi insieme: output più piccoli ed economici, un indirizzo standardizzato e pulito, e le tue condizioni mantenute private fino al momento in cui spendi.
I tipi di script standard
Anche se puoi costruire quasi qualsiasi script di blocco tu voglia, la maggior parte dei nodi inoltra solo un piccolo insieme di pattern standard. La tabella qui sotto presenta i tipi di script che hanno plasmato Bitcoin, dall'era legacy fino a SegWit e Taproot, con i loro prefissi di indirizzo e il punto in cui ciascuno memorizza i dati di sblocco.
La distinzione tra legacy e SegWit è importante. Gli script legacy (P2PK, P2PKH, P2MS, P2SH) mettono i dati di sblocco nello scriptSig. Gli script SegWit (P2WPKH, P2WSH, P2TR) li mettono invece in un campo separato chiamato witness, ed è proprio ciò che ha risolto la malleabilità delle transazioni e abbassato le commissioni.
Standard Script Types
| Type | Pattern | Address | Unlocking | Era |
|---|---|---|---|---|
P2PK Pay to Public Key | <PubKey> OP_CHECKSIG | none | scriptSig | Legacy |
P2PKH Pay to Public Key Hash | OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG | 1... | scriptSig | Legacy |
P2MS Bare Multisig | OP_m <PubKey> ... <PubKey> OP_n OP_CHECKMULTISIG | none | scriptSig | Legacy |
P2SH Pay to Script Hash | OP_HASH160 <ScriptHash> OP_EQUAL | 3... | scriptSig | Legacy |
P2WPKH Pay to Witness Public Key Hash | OP_0 <PubKeyHash> | bc1q... | witness | SegWit |
P2WSH Pay to Witness Script Hash | OP_0 <ScriptHash> | bc1q... | witness | SegWit |
P2TR Pay to Taproot | OP_1 <TaprootOutputKey> | bc1p... | witness | Taproot |
P2PK
Pay to Public Key
Pattern
<PubKey> OP_CHECKSIG
Address
noneUnlocking
scriptSigP2PKH
Pay to Public Key Hash
Pattern
OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
Address
1...Unlocking
scriptSigP2MS
Bare Multisig
Pattern
OP_m <PubKey> ... <PubKey> OP_n OP_CHECKMULTISIG
Address
noneUnlocking
scriptSigP2SH
Pay to Script Hash
Pattern
OP_HASH160 <ScriptHash> OP_EQUAL
Address
3...Unlocking
scriptSigP2WPKH
Pay to Witness Public Key Hash
Pattern
OP_0 <PubKeyHash>
Address
bc1q...Unlocking
witnessP2WSH
Pay to Witness Script Hash
Pattern
OP_0 <ScriptHash>
Address
bc1q...Unlocking
witnessP2TR
Pay to Taproot
Pattern
OP_1 <TaprootOutputKey>
Address
bc1p...Unlocking
witnessP2PK and bare P2MS have no standard address format. Multisig is almost always wrapped in P2SH or P2WSH today.
SegWit e Taproot: oltre lo Script classico
L'aggiornamento SegWit del 2017 ha spostato i dati delle firme fuori dal corpo principale della transazione e dentro il campo witness. Questo ha risolto un problema di lunga data chiamato malleabilità delle transazioni, in cui la firma visibile poteva essere modificata per cambiare l'identificatore di una transazione senza cambiarne il significato, e ha anche reso più economici i dati della firma. P2WPKH e P2WSH sono semplicemente le versioni SegWit di P2PKH e P2SH, e producono indirizzi che iniziano con bc1q.
È interessante notare che questi nuovi tipi di script usano a malapena l'intero linguaggio Script. Ognuno segue un pattern interno fisso che i nodi eseguono in modo prestabilito, ed è esattamente la semplificazione "basta controllare una chiave e una firma" che lo Script classico rendeva possibile ma non imponeva. Il linguaggio completo continua a vivere dentro P2WSH, dove puoi incapsulare qualsiasi script personalizzato ti serva.
Pay-to-Taproot (P2TR), attivato a novembre 2021, è lo standard più recente e produce indirizzi che iniziano con bc1p. Abbina le firme Schnorr a una struttura chiamata MAST (Merkelized Abstract Syntax Trees), che permette di codificare molte condizioni di spesa alternative in un albero. La cosa cruciale è che viene rivelato solo il ramo che usi davvero quando spendi. Un wallet con elaborate condizioni di backup e recupero può quindi spendere in un modo identico a un comune pagamento a chiave singola, il che migliora sia la privacy sia l'efficienza.
Oltre le firme: timelock e rompicapi
Script può esprimere molto più di "dimostra di possedere questa chiave". Due opcode aggiungono la dimensione del tempo. OP_CHECKLOCKTIMEVERIFY (introdotto con BIP 65) rende un output non spendibile fino a una data o a un'altezza di blocco scelta. OP_CHECKSEQUENCEVERIFY (BIP 112) impone un ritardo relativo, misurato da quando le monete sono state ricevute. Insieme rendono condizioni come "questi fondi non possono muoversi per sei mesi" applicabili dal protocollo stesso.
Questi timelock sono i mattoni di costruzioni più avanzate. Un Hash Time Locked Contract (HTLC), per esempio, combina un enigma di hash con un timelock, così che un pagamento o si completa quando un segreto viene rivelato, oppure viene rimborsato dopo una scadenza. È quel pattern a rendere possibile la Lightning Network.
Script consente anche enigmi puri. Un enigma di hash blocca le monete dietro "fornisci dati che producono questo hash". Esiste perfino un enigma di collisione di hash che paga chiunque riesca a fornire due input diversi che producono lo stesso hash, di fatto una taglia pubblica su una funzione di hash compromessa. E lo speciale opcode OP_RETURN ti permette di allegare un piccolo dato a un output marcandolo come definitivamente non spendibile, un modo pulito di ancorare informazioni nella catena senza gonfiare l'insieme delle monete spendibili.
I limiti che mantengono Bitcoin sicuro
Script ha limiti rigidi di dimensione, e esistono per lo stesso motivo per cui il linguaggio evita i cicli: mantenere la verifica veloce e impedire a chiunque di trasformarlo in un'arma contro la rete.
I limiti rigidi di validità sono chiari. Uno script completo non può superare i 10.000 byte. Può eseguire al massimo 201 opcode, senza contare le operazioni che si limitano a mettere dati sullo stack. Nessun singolo elemento messo sullo stack può superare i 520 byte, e lo stack stesso non può contenere più di 1.000 elementi alla volta. Viola uno qualsiasi di questi limiti e lo script è non valido, punto.
C'è anche uno strato più morbido. Uno script può essere perfettamente valido ma non standard, il che significa che i nodi rifiuteranno di inoltrarlo attraverso la rete anche se un miner potrebbe comunque includerlo in un blocco. È una misura di sicurezza: solo i pattern standard ben testati circolano liberamente, così un attaccante non può inondare la rete di script strani lenti da verificare. In pratica, una transazione non standard deve essere consegnata direttamente a un miner o minata da te.
Perché tutto questo conta
Si è tentati di vedere i limiti di Script come Bitcoin in ritardo rispetto a catene più "programmabili". È vero il contrario. Ogni restrizione qui è uno scambio deliberato di espressività in cambio di prevedibilità, verificabilità e sicurezza.
Poiché nessuno script può girare all'infinito, ogni nodo può confermare l'intera catena senza fidarsi di nessuno. Poiché il linguaggio è piccolo, la superficie di attacco è piccola. Poiché non esiste uno stato globale condiviso in cui i contratti possano intervenire, non c'è un singolo punto in cui qualcuno possa intervenire e congelare le tue monete. La stessa flessibilità che permette ad altre reti di promettere di più è anche ciò che le ha esposte a exploit catastrofici e che permette a parti che le controllano di sequestrare o congelare i saldi a piacimento.
Bitcoin ha fatto una scommessa diversa. Un linguaggio modesto e prevedibile, controllato nello stesso modo da tutti, a protezione di un insieme di regole fisso e neutrale. È questo che permette a una canoa di muoversi su acque aperte senza chiedere permesso, ed è esattamente questo il punto. Per il quadro più ampio di come queste regole vengono applicate in tutta la rete, vedi Il meccanismo di consenso di Bitcoin: un'analisi approfondita, e per la struttura che gli script in definitiva proteggono, Che cos'è una blockchain?.
Fatti Chiave
Il Bitcoin Script è non Turing completo per scelta. Non ha cicli né ricorsione, quindi ogni script termina in un numero di passi limitato e prevedibile.
Ogni output porta con sé uno script di blocco (scriptPubKey). Per spenderlo, la transazione deve fornire uno script di sblocco corrispondente (scriptSig, oppure il campo witness per SegWit).
Uno script è valido quando l'esecuzione lascia un singolo valore diverso da zero sullo stack. Qualsiasi altra cosa fallisce.
Un singolo script Bitcoin è limitato a 10.000 byte e ad al massimo 201 opcode eseguiti, senza alcun elemento di dati più grande di 520 byte.
P2WPKH (SegWit nativo, 2017) e P2TR (Taproot, 2021) sono i tipi di script standard moderni e vengono sbloccati tramite il campo witness anziché lo scriptSig.
Domande frequenti
No, ed è una scelta voluta. Il Bitcoin Script è non Turing completo, il che significa che non può eseguire cicli o programmi arbitrari. Questa decisione di design elimina intere categorie di bug e rende ogni transazione rapida ed economica da verificare, anche su hardware modesto.
Lo scriptPubKey è lo script di blocco collegato a un output. Stabilisce le condizioni che devono essere soddisfatte per spendere quelle monete. Lo scriptSig è lo script di sblocco fornito da chi spende. Fornisce i dati, di solito una firma e una chiave pubblica, che soddisfano quelle condizioni.
Sì. Il Bitcoin Script supporta enigmi, timelock e condizioni multifirma ben oltre un semplice controllo di firma. Gli script personalizzati sono validi e possono essere minati, ma la maggior parte dei nodi inoltra solo un piccolo insieme di pattern standard, quindi uno script non standard è più difficile da far confermare a meno che non sia incapsulato in P2SH o P2WSH.
Taproot ha introdotto Pay-to-Taproot (P2TR), che abbina le firme Schnorr a un albero di possibili condizioni di spesa chiamato MAST. Solo il ramo effettivamente usato viene rivelato on-chain, quindi un contratto complesso appare identico a un pagamento semplice, migliorando sia la privacy sia l'efficienza dello spazio nei blocchi.
Fonti
- 1.Greg Walker: Bitcoin Script (learnmeabitcoin.com)
- 2.Antonopoulos: Mastering Bitcoin, Chapter 7, Authorization and Authentication
- 3.Bitcoin Wiki: Script and Opcode List
- 4.BIP 16: Pay to Script Hash
- 5.BIP 141: Segregated Witness
- 6.BIP 341 and BIP 342: Taproot and Tapscript
Non è una consulenza finanziaria. CanoeBit pubblica esclusivamente contenuti educativi. Nulla di quanto scritto costituisce una raccomandazione di acquisto, vendita o detenzione di asset.
Continua il percorso Come funziona Bitcoin
Passo 9 di 11