Autore: Benna | Shift Crypto | Pubblicazione originale: 15 feb 2023 | Tradotto da: 31febbraio | Milano Trustless | Link: Understanding Bitcoin Miniscript - Part I
In questa prima parte della nostra serie sui Miniscript, spieghiamo come funziona Bitcoin Script e perché è difficile da usare nella pratica.
Le uniche condizioni di spesa in Bitcoin oggi diffuse sono semplici single-sig e semplici multisig, anche se Bitcoin Script, il linguaggio utilizzato per codificare le condizioni di spesa nelle transazioni Bitcoin, è molto più potente di questo. Perché?
Il motivo è che Bitcoin Script, pur sembrando in superficie un semplice linguaggio stack-based è, in realtà, molto difficile da usare nella pratica. Per ogni nuova condizione di spesa che uno sviluppatore potrebbe voler creare, è necessario spendere molto tempo per assicurarsi che sia corretta e valida in tutte le circostanze, il che può essere difficile da ragionare. Tra poco verrà analizzato un esempio convincente.
C'è di più: la mancanza di standardizzazione e di strumenti per questo tipo di script può rendere difficile l'interoperabilità tra portafogli e altri software. In pratica significa che, anche se si decide di intraprendere lo sforzo di sviluppare un nuovo script, si finirà per avere un portafoglio non standard. Altri wallet potrebbero non essere compatibili il che, per gli utenti, è ovviamente negativo.
In questa serie di articoli ci addentreremo in Bitcoin Miniscript, un linguaggio di alto livello per scrivere Bitcoin Script, con l'obiettivo di facilitare gli sviluppatori di wallet a creare condizioni di spesa complesse e a valutare la loro correttezza e solidità, o per verificare che tutti i portafogli e gli strumenti interagiscano in modo corretto.
L'esempio di BitBox02
Come sviluppatori di BitBox02, siamo sempre alla ricerca di modi per migliorare la sicurezza e l'esercizio dell'autocustodia. In passato, abbiamo implementato e distribuito il protocollo anti-klepto e migliorato la sicurezza multisig per tutti i fornitori di portafogli hardware.
Kevin e Antoine di Revault/wizardsardine ci hanno contattato per discutere la possibilità di supportare Miniscript su BitBox02. È stato anche un argomento caldo della conferenza BTC Azores '22, dove Antoine ha tenuto una grande lezione al riguardo e Salvatore ha mostrato i suoi progressi nell'integrazione di Miniscript in Ledger.
Vediamo Miniscript, insieme ai covenants e a MuSig, come importanti sviluppi per l'autocustodia e per BitBox02.
Miniscript consente condizioni di spesa più avanzate, che Wizardsardine mostra in Liana, un nuovo tipo di portafoglio che si sta sviluppando.
Aggiungendo il supporto Miniscript a BitBox02, portafogli avanzati come Liana possono essere protetti da BitBox02. Questo aprirebbe anche la porta allo sviluppo di soluzioni di autocustodia più avanzate all'interno della app BitBox.
Prima di procedere all'integrazione, abbiamo dovuto approfondire le nostre conoscenze su Miniscript. Secondo precedenti esperienze, il modo migliore per imparare un argomento di ingegneria software è la sua implementazione pratica. Costruendolo da soli, si è costretti a considerare ogni dettaglio. Un altro metodo efficace è quello di spiegarlo agli altri. Questa serie di post serve a entrambi gli scopi: include un'implementazione di Miniscript e mira a insegnare Miniscript in un formato diverso da quello disponibile oggi.
Primo (e brevissimo) approfondimento su Bitcoin Script
I Bitcoin sono generalmente bloccati da script che codificano il tipo di condizioni che devono essere soddisfatte per spendere le monete. In questa serie ci concentreremo sul P2WSH (Pay-to-Witness-Script-hash). In questo caso, lo script del witness codifica le condizioni che devono essere soddisfatte per spendere un UTXO. Di solito includono le chiavi pubbliche, witness è il dato necessario per soddisfare le condizioni di spesa. I witness includono comunemente le firme corrispondenti alle chiavi pubbliche.
Per spendere una moneta bloccata con un witness script, la transazione che la spende deve includere un testimone valido. Witness e il suo script vengono valutati secondo le regole di Bitcoin Script.
Un indirizzo Bitcoin come ad esempio bc1q2fhgukymf0caaqrhfxrdju4wm94wwrch2ukntl5fuc0faz8zm49q0h6ss8
è semplicemente una codifica del fatto che si tratta di un output P2WSH contenente l'hash del witness script. I nodi Bitcoin sanno che quando vedono una moneta inviata a un output di questo tipo, la transazione deve includere il witness script corrispondente, oltre al testimone necessario per soddisfare lo script.
Ad esempio, per codificare condizione single-sig semplice, il witness script dovrebbe essere:
<publicKey> OP_CHECKSIG
e il testimone dovrebbe essere
<signature>
Quando si verifica lo script, witness e lo script vengono eseguiti in ordine, partendo da una stack vuota:
- Stack iniziale: vuota.
- La firma viene inserita nello stack:
<signature>
- La chiave pubblica
publicKey
viene inserita nello stack:<signature> <publicKey>
OP_CHECKSIG
rimuove i due elementi superiori della stack, verifica la firma e inserisce uno0
in caso di fallimento o un1
in caso di successo.
Se sullo stack rimane esattamente un elemento non nullo e lo script non è stato interrotto, il witness è valido e la coin può essere spesa.
In Bitcoin Script, oltre a OP_CHECKSIG
, esiste una serie di OP_code che possono essere utilizzati per codificare condizioni di spesa più complesse. Ad esempio, è possibile bloccare le monete in un output multi-firma utilizzando OP_CHECKMULTISIG
, oppure bloccare le monete per un periodo di tempo utilizzando OP_CHECKSEQUENCEVERIFY
.
Esempio pratioco (e convincente) su Miniscript
Miniscript aiuta gli sviluppatori a creare script Bitcoin più sicuri ed efficienti, risolvendo diversi problemi del linguaggio Bitcoin Script. Vediamo alcune semplici condizioni di spesa che si potrebbero avere per illustrare le difficoltà di lavorare direttamente con Bitcoin Script. Più avanti vedremo come Miniscript risolve questi problemi e rende molto più semplice lo sviluppo e la distribuzione di nuove condizioni di spesa.
Per esempio, supponiamo di volere che una delle due persone possa spendere fondi. La soluzione più semplice è usare OP_CHECKMULTISIG
. Lo script witness diviene:
1 <publicKey1> <publicKey2> 2 OP_CHECKMULTISIG
L'1
indica a OP_CHECKMULTISIG
quante firme devono essere fornite e 2
indica quante chiavi pubbliche ci sono.
Witness è:
<> <signature>
(l'elemento vuoto all'inizio del witness script è in realtà inutile ed esiste a causa di un bug nell'implementazione originale di OP_CHECKMULTISIG
, che rimuove un elemento di troppo dallo stack).
Lo script di verifica risultante, <> <signature> 1 <publicKey1> <publicKey2> 2 OP_CHECKMULTISIG
, lascia un 1
sullo stack se c'è una firma valida che corrisponde a una delle due chiavi pubbliche, altrimenti mette 0
.
Ora cambiamo leggermente la semantica. Invece di pubkey1 OR pubkey2
, proviamo pubkey1 OR (pubkey2 in un anno)
: la moneta può essere spesa da una persona in qualsiasi momento, oppure da un'altra persona dopo un anno di attesa. Poiché OP_CHECKMULTISIG
può controllare solo le firme e non i blocchi temporali, lo script deve cambiare completamente. Ci possono essere molti script che implementano un insieme di condizioni di spesa. Ecco una delle tante soluzioni possibili per questo script.
Soluzione
Witness script:
<pubkey1> OP_CHECKSIG OP_IFDUP OP_NOTIF
<pubkey2> OP_CHECKSIGVERIFY <52560 (one year)> OP_CHECKSEQUENCEVERIFY
OP_ENDIF
52560 è un anno espresso in numero di blocchi Bitcoin, sulla media una volta ogni dieci minuti.
Possibili witness:
- In qualsiasi momento:
<signature for pubkey1>
- Solo se è passato un anno:
<signature for pubkey2> <>
.
Vediamo l'esecuzione dello script utilizzando il secondo witness. Come in precedenza, witness e il suo script vengono eseguiti in ordine a partire da uno stack vuoto. Supponendo che sia passato un anno e che la firma sia valida:
- Stack iniziale: vuota.
- Inserire la firma:
<signature for pubkey2>
- Inserire l'elemento vuoto:
<signature for pubkey2> <>
- Inserire pubkey1:
<signature for pubkey2> <> <pubkey1>
- OP_CHECKSIG rimuove due elementi
<>
e<pubkey1>
dallo stack e verifica la firma rispetto a questa pubkey. Poiché la firma è vuota, si tratta di una firma non valida eOP_CHECKSIG
inserisce uno0
:<signature for pubkey2> 0
. - OP_IFDUP duplica l'elemento superiore dello stack se non è zero. Poiché è zero, non succede nulla:
<signature for pubkey2> 0
. - OP_NOTIF: Rimuove l'elemento superiore della stack. Se è 0, vengono eseguite le istruzioni fino a OP_ENDIF:
<signature for pubkey2>
. - Inserire pubkey2:
<signature for pubkey2> <pubkey2>
. - OP_CHECKSIGVERIFY rimuove due elementi (firma e pubkey) e verifica la firma. Se è valida, non succede nulla, altrimenti l'esecuzione viene interrotta con un errore. La stack adesso risulta: vuota.
- Inserire 52560 (valore numerico che rappresenta un anno):
<52560>
. - OP_CHECKSEQUENCEVERIFY considera l'ultimo elemento come una durata temporale. Se la moneta da spendere è più giovane, lo script si interrompe con un errore. Se è più vecchia, non succede nulla:
<52560>
. - OP_ENDIF: termina l'operatore OP_IF.
- Nello stack è rimasto esattamente un elemento non nullo, quindi lo script ha successo e la coin può essere spesa.
Al punto 5, l'elemento di firma vuoto è chiamato "insoddisfazione". È necessario per saltare la parte che verifica la prima pubkey, che non volevamo usare. Si noti che solo l'elemento vuoto è l'insoddisfazione valida rispetto alla condizione per <key> OP_CHECKSIG
secondo BIP141 - qualsiasi altra firma non valida comporta la non eseguibilità dello script:
Le firme devono essere vettori nulli se un OP_CHECKSIG o OP_CHECKMULTISIG non è riuscito (sia per script witness pre-segregato che per P2WSH. Vedere BIP146)
Si noti inoltre che lo script ha successo solo perché il time-lock di un anno è diverso da zero. Se si utilizza lo stesso script ma si sostituisce semplicemente il numero da un anno a 0, si potrebbe supporre che la sintassi originale pubkey1 OR (pubkey2 in un anno)
cambia in pubkey1 OR (pubkey2 in qualsiasi momento)
. Poiché alla fine verrebbe lasciato uno 0 in stack, lo script fallirebbe sempre e la sintassi reale sarebbe accidentalmente cambiata in pubkey1 only
e il secondo pubkey non potrebbe mai spendere.
Come esercizio pratico, provate a eseguire lo script sopra utilizzando il primo witness, supponendo che la prima persona firmi e la seconda no, e convincetevi che funziona.
Come si vede, la creazione di tali script e di witness validi in Bitcoin Script è un processo laborioso e soggetto a errori, anche per una condizione semplice. La sequenza delle operazioni di stack è complicata e impegnativa sia da ragionare che da costruire. È necessaria una profonda conoscenza di Bitcoin Scrip e, se si desidera ampliare le condizioni di spesa, il processo di sviluppo ricomincia sostanzialmente da zero. Tra le altre cose, è necessario conoscere la regola del cleanstack (che richiede che rimanga un solo elemento non nullo alla fine dell'esecuzione) e che solo l'elemento vuoto è un'insoddisfazione valida per un controllo della firma.
In sintesi, lavorare direttamente con Script è difficile per i seguenti motivi:
- Script non si compone in modo ottimale, il che significa che piccoli cambiamenti nelle condizioni di spesa desiderate possono dare luogo a script molto diversi.
- Gli OP code di Script hanno sintassi diverse per fallimento/successo, rendendo difficile la loro composizione o il riutilizzo. Alcuni di essi inseriscono uno 0 o un 1 nello stack in caso di fallimento o successo, mentre altri interrompono l'intera esecuzione in caso di fallimento e nulla in caso di successo.
- Esistono molti script di soluzione possibili per un insieme di condizioni di spesa desiderate, il che rende difficile decidere quale scegliere.
- Creare witness validi per tutte le circostanze è difficile.
- Esistono limiti di consenso e di standardizzazione sulle dimensioni degli script, sul numero di opcode e firme e sul numero di elementi della stack dei witness che lo sviluppatore deve tenere in considerazione per evitare il rifiuto da parte della rete.
- Progettare uno script complesso che lasci esattamente un elemento non vuoto sullo stack al termine dell'esecuzione è impegnativo, come abbiamo visto nell'esempio precedente.
Nella prossima puntata di questa serie, vedremo in dettaglio cos'è Miniscript e come semplifica radicalmente Bitcoin Script, rendendo possibile l'uso di condizioni di spesa elaborate nella pratica. Restate sintonizzati!
Milano Trustless (31febbraioMI)
#MilanoTrustless è un progetto personale per #orangepillare 🟠💊 Milano e infondere ai (meravigliosi) milanesi la mentalità #trustless
follow me :
Related Posts
Athletes and Bitcoin: Securing Wealth Beyond Their Careers
Oct 27, 2024
Športniki in bitcoin - Kaj pa po koncu kariere?
Oct 27, 2024
Lightning Network to Wspólny Język Ekonomii Bitcoina
Oct 25, 2024