Bitcoin Smart Contract 101
Affronteremo la teoria e la pratica che ci permette di utilizzare il linguaggio Bitcoin Script per creare uno Smart Contract che consegna i suoi bitcoin solo a chi conosce una password.
Last updated
Affronteremo la teoria e la pratica che ci permette di utilizzare il linguaggio Bitcoin Script per creare uno Smart Contract che consegna i suoi bitcoin solo a chi conosce una password.
Last updated
Il codice Bitcoin Script che scriveremo e', anche se banale, uno Smart Contract!
NB: e' una demo esclusivamente a fini ludici per introdurre il funzionamento del linguaggio Bitcoin Script.
NBB: si sconsiglia vivamente di utilizzare in mainnet questo tipo di Smart Contract!
Entrare in Hansel, eseguire
Prima di procedere siamo costretti ad introdurre tre aspetti fondamentali
i passaggi necessari per sviluppare lo Smart Contract
cosa sono le UTXO
cosa significa PS2H
Paragonare il processo da svolgere per creare ed eseguire uno Smart Contract (SC) Bitcoin al processo di una web app e' arduo, proviamoci!
(SC build/compile) Trasformare il redeem script
Nella sua rappresentazione esadecimale
In Script Hash tramite l'applicazione di SHA256+RIPEMD-160
(SC deploy) Pubblicazione di una transazione che invia bitcoin all'indirizzo (4.3) vincolandoci dei bitcoin.
L'esecuzione dello Smart Contract avviene quando si vuole accedere ai fondi - eg. inviandoli ad un nuovo indirizzo Bitcoin - a lui assegnati: si crea una transazione contenente il sorgente del redeem script assieme ai valori di input e la si invia ai nodi della rete.
Se il sorgente del redeem script presentato per l'esecuzione corrisponde allo Script Hash dal quale e' stato derivato il Bitcoin address, e la sua esecuzione utilizzando i valori di input ha esito positivo, abbiamo a disposizione i bitcoin assegnati allo Smart Contract e possiamo consegnarli ad un altro indirizzo Bitcoin.
Nei passaggi seguenti alterneremo teoria e coding pratico, gli snippet che trovate colorati eseguiteli dentro Hansel o Gretel.
Le nozioni che seguono sono una sintesi spinta per fornire il minimo indispensabile alla realizzazione del primo Smart Contract Bitcoin.
Non vengono volutamente approfonditi argomenti come: ScriptPubKey, serializzazione/deserializzazione dello ScriptSig e costruzione di input/output associati ad una transazione con conseguenti modalita' di firma tramite le opzioni SIGHASH.
Nella sezione Studiare Bitcoin a fondo trovate il materiale per approfondire.
Un concetto, anomalo rispetto ai sistemi di pagamento ai quali siamo abituati, riguarda la gestione del saldo in Bitcoin.
Parlando di pagamenti classici, quando ne riceviamo uno, che sia un bonifico o contante, l'ammontare ricevuto va a sommarsi ad un conto unico al quale abbiamo accesso. Quando dobbiamo inviare denaro preleviamo dal conto unico la quantita' che ci interessa e la consegnamo al beneficiario.
In Bitcoin l'ammontare di un pagamento ricevuto non va ad unirsi ad un conto unico, rimane indipendente, per questo si utilizza il termine UTXO. Quando vogliamo creare un pagamento in Bitcoin, invece che prelevare dal conto, si seleziona una gruppo di UTXO i quali saldi sono sufficienti per colmare il pagamento verso il beneficiario desiderato.
Potenzialmente, ed e' anche prassi diffusa e caldamente consigliata, tutte le volte che ricevete un pagamento potete generare un indirizzo Bitcoin dedicato. Essendo le UTXO visibili pubblicamente sulla blockchain di Bitcoin, la differrenzazione dei beneficiari - anche facenti capo ad una sola entita' - permette di migliorare la privacy finanziaria.
Ogni UTXO e' vincolata ad un solo Bitcoin address.
In Bitcoin, nel momento che si crea una UTXO, si deve indicare il beneficiario associandovi un identificativo chiamato Bitcoin address.
Esistono differenti tipologie di Bitcoin address, P2SH e' una di queste.
Il beneficiario di una UTXO P2SH e' un listato di codice Bitcoin Script.
Ebbene si, in Bitcoin si puo' effettuare un pagamento utilizzando come destinatario uno snippet di codice.
Solo chi e' a conoscenza di tale snippet ed, eventualmente, aggiungendo parametri di input (nel nostro use case il parametro di input sara' una password), e' in grado di ottenere come risultato il valore 1 dalla sua esecuzione potra' riscattare tale pagamento.
PS2H e' l'acronimo di Pay to Script Hash
Tramite i passaggi che vedremo piu' avanti il codice sorgente dello script viene trasformato in Script Hash e successivamente in Bitcoin address.
Grazie a questo approccio solo chi e' a conoscenza del codice sorgente dello script, e degli eventuali parametri di input, potra' riscattare i bitcoin che hanno come beneficiario tale script.
Finche' la UTXO PS2H non viene reclamata l'unico dato visibile e' lo Script Hash. NB: non bisogna dimenticare che quando si pubblica l'eventuale transazione di reclamo della UTXO, che rende visibile il sorgente dello script e dei suoi parametri di input, questa risiedera' nella cosiddetta mempool di ogni nodo della rete Bitcoin.
In quel frangente di tempo chiunque abbia un nodo Bitcoin puo' analizzare il vostro script e tentare di attaccarlo!
Per questo motivo, in mainnet, dove i bitcoin hanno valore, si utilizzano P2SH che richiedono come parametri di input firme digitali, cosi' da vincolare inequivocalbilmente la UTXO al/ai beneficiari/o, e non una semplice password!
Dopo aver sottratto con un sotterfugio i bitcoin dalla strega, Hansel decide di bloccare la parte destinata a Gretel utilizzando una password.
Per far questo Hansel crea un address P2SH prevedendo
come parametri di input un unico valore, lo SHA256 della loro parola segreta che risulta essere "fegatini"
come business logic (redeem script) l'esecuzione di un ulteriore SHA256 sul parametro di input e il confronto del risultato con il valore SHA256(SHA256('fegatini')) da lui precalcolato
Solo chi fornisce la giusta password può sbloccare interamente il tesoro!
Riuscirà la nostra Gretel a ricordarsi dei fegatini, sbloccare i bitcoin e vivere felice e contenta con Hansel e suo padre taglialegna a bordo di una LAMBO? 🚘
Abbiamo detto che Hansel vuole realizzare uno script che verifica se e' stato fornito un parametro di input che corrisponde ad un determinato digest.
A grandi linee potremmo rappresentare la funzione che verifica la password cosi'
A differenza di molti linguaggi di programmazione, in Bitcoin script, i valori precedono le istruzioni che ne fanno uso; vedi RPN - Reverse Polish.
Il corpo della nostra funzione diventerebbe qualcosa del genere
Il primo doppio SHA256 e' costante quindi possiamo calcolarlo
Tra il primo SHA256 ed il secondo facciamo uso di xxd perche' in Bitcoin tutti i valori nello stack sono considerati array di byte.
Il corpo della nostra funzione diventa quindi
per essere piu' concisi potremmo
Essendo Bitcoin Script stack-based la pseudo codifica conviene descriverla seguendo la stessa prassi
Infine non ci resta che utilizzare i nomi convenzionali degli operatori a disposizione in Bitcoin Script detti opcodes.
Come detto in precedenza nel redeem script non dobbiamo descrivere i parametri di input, quindi sara' composto da
Visto cosi' sembra incompresibile!
Vediamo come si comportano le opcodes OP_SHA265 e OP_EQUAL riguardo allo stack.
OP_SHA256
, il quale ricade nella sezione crypto, ha il compito di effettuare un'operazione di pop (estrazione dello stack), applicare la funzione crittografica SHA256 ed effettuare l'operazione di push (inserimento nello stack) del risultato.
OP_SHA256 avendo necessita' di effettuare l'operazione di POP sottoindende che servira' un parametro di input da inserire nello stack prima della sua esecuzione!
Successivamente abbiamoOP_EQUAL
facente parte delle opcodes di Bitwise logic.
OP_EQUAL
esegue due volte l'operazione di pop. Successivamente esegue l'operazione di push con valore 1 se i valori estratti tramite pop sono uguali oppure 0 se sono diversi.
OP_EQUAL ha necessita' di svolgere due operazioni di pop.
Nel nostro caso il primo elemento dello stack estratto e' il valore inserito tramite push da OP_SHA256. Il secondo elemento dello stack estratto e' il doppio SHA256 di 'fegatini' posizionato sopra OP_EQUAL!
Salviamolo in una variabile per comodita'
Come descritto poco sopra, la transazione è bloccata da una password che Gretel deve forinire.
Hansel, nella sua business logic, ha hardcodato il doppio SHA256 quindi la password da fornire e' lo SHA256 di 'fegatini'
con il quale otteniamo 273c25751f15926d6064916b8765461d77a6d525ee3fd25a37b88755c6d4db20
Per una rapida verifica possiamo dare in pasto a btcc redeem script e parametri di input.
Successivamente possiamo verificare tutta l'esecuzione con l'ausilio di btcdeb.
A sinistra vi comparira' una rappresentazione dello script di Hansel, compreso dei parametri di input; sulla destra viene visualizzato lo stack che all'avvio di ogni Smart Contract Bitcoin e' vuoto.
Digitate
step
e premete invio per eseguire lo script un passaggio alla volta!
Per uscire da btcdeb usate la shortcut
CTRL+D
Se l'ultimo elemento rimanente nello stack e' 01 significa che l'esecuzione e' terminata con successo!
Possiamo usare btcc, questa volta pero' utilizzeremo solo il redeem script senza il parametro di input
otteniamo a820cef813adf3b25b5f92ab0ced01fcbaa5bc6a6cdb64693566ca775f44192799d787
Per chi vuole scoprire cosa btcc svolge sotto il cofano consigliamo la lettura del file main.sh
disponibile nella cartella /opt/wald/ps2h_password.
Risultato 0482e32347749e785e203e1c663b190c1db89071
.
Ehy ma prima del RIPEMD-160 viene svolto uno SHA256, che succede?! The exact reason why SHA-256 was used in combination with RIPEMD-160 isn't known. [...]
Per completare il Bitcoin address bisogna prependere un prefisso e codificare il tutto in Base58Check.
Il prefisso serve ad indicare se operiamo in mainnet o testnet/regtest e che tipo di Bitcoin address stiamo utilizzando.
Il playground opera in regtest, il prefisso che ci interessa e' Testnet script hash che corrisponde al valore esadecimale c4
.
Possiamo svolgere quindi l'encoding utilizzando base58
e finalmente ottenere il nostro indirizzo P2SH!
2Msf5VFsuGZBthnwXvUGxajtJKgiGd5GDQi
Adesso abbiamo bisogno di inviare bitcoin al nostro indirizzo P2SH.
Aprendo http://localhost:8094/regtest/address/2Msf5VFsuGZBthnwXvUGxajtJKgiGd5GDQi possiamo verificare di aver ricevuto UTXO al nostro address.
Aprendo dal blockchain regtest explorer una qualsiasi delle transazioni associate possiamo verificare che lo Script Hash e' quello desiderato ovvero 0482e32347749e785e203e1c663b190c1db89071
Siamo arrivati all'ultimo passaggio! L'esecuzione dello Smart Contract!
Adesso dobbiamo creare una transazione che, fornendo password e redeem script, permette di utilizzare la UTXO associata al P2SH ed invia i bitcoin ad un nuovo indirizzo.
Partiamo con il passaggio semplice, creiamo un indirizzo
Adesso dobbiamo selezionare la transazione, fra tutte quelle ricevute dal nostro indirizzo P2SH, che ha raggiunto la coinbase maturity.
Questo passaggio riguardante la coinbase maturity e' necessario solo perche' stiamo usando la transazione coinbase come UTXO, ottenuta durante il mining dei blocchi con il comando generatetoaddress
.
Adesso viene il bello! Dobbiamo costruire una transazione che fa uso di
La UTXO di TXID_WITH_MATURITY
REDEEM_SCRIPT_COMPILED
BENEFICIARIO
Quando si lavora con Bitcoin Script custom come questo, purtroppo (o per fortuna :D), non esistono tool che permettono la costruzione interattiva della transazione.
A scopo dimostrativo abbiamo predisposto un file bash che svolge i passaggi necessari
L'output del file bash e' la transazione, gia' pronta nel suo formato esadecimale.
NB: prima di inviare una transazione che esegue uno smart contract custom e' sempre bene verificarne l'esito!
Ci viene in soccorso btcdeb!
La transazione da inviare potere recuperarla con
Questo passaggio si puo' fare utilizzando la chiamata RPC sendrawtransaction
oppure tramite http://localhost:8094/regtest/tx/push.
NB: importante ripetere che, una volta inviata la transazione, tutti i nodi della rete sono a conoscenza del codice sorgente del redeem script e dei valori di input.
Ogni nodo eseguira' il nostro Smart Contract e se l'esecuzione completera' con successo inoltrera' la transazione ai nodi vicini.
Attualmente la transazione si trova in mempool, possiamo visualizzare la mempool tramite i link qui sulla sinistra.
Per confermare la transazione e farla uscire dalla mempool bastera' minare un blocco.
Se proseguite con l'esempio successivo Attacare uno Smart Contract non effettuate il mining!!
interpretare l'stdout ed i sorgenti dello script per capire cosa succede