crop hacker typing on laptop with information on screen

“smart contract” è un umbrella term, ovvero un termine con il quale si raccolgono insieme concetti a volte disparati e non necessariamente collegati. L’introduzione del concetto di smart contract risale agli scritti di N. Szabo e successivi. E risalgono al 1994 ovvero prima dell’invenzione di Bitcoin

Possiamo immaginare uno smart contract come l’implementazione di una certa logica attraverso un codice di programmazione che viene messo in esecuzione non un singolo computer ma un’intera rete che registra le proprie transazioni su una blockchain. 

La rete non viene utilizzata per parallelizzare il carico di lavoro e renderlo più efficiente ma ha al contrario come scopo la certificazione crittografica, tramite protocollo di consenso, degli input, degli output e del calcolo stesso in modo da garantire tutti gli osservatori che il calcolo non è stato manomesso o alterato durante il suo corso. 

Sebbene gli smart contract siano in qualche modo presenti in Bitcoin e in realtà teorizzati perfino prima della sua nascita, il progetto che ha reso popolare l’idea di smart contract è Ethereum.

E’ bene considerare che gli smart contract sono un caso di misnaming. Essi infatti non sono legalmente dei contratti e non sono neanche smart se per smart intendiamo qualcosa in grado di prendere decisioni sulla base di informazioni ridotte o insufficienti.

Smart contracts are neither smart nor contracts.

Lo stesso Vitalik Buterin, ha ammesso che la scelta di chiamare Smart Contract i programmi in esecuzione su Ethereum è stato probabilmente un errore e che forse il termine tecnicamente più adatto sarebbe dovuto essere “persistent script”.

Gli smart contract sono programmi dunque ma l’aspetto economico e l’aspetto tecnologico sono legati in modo molto stretto. Un errore nella logica o nell’implementazione di uno smart contract può causare un danno diretto con possibile distruzione della quantità di criptovaluta coinvolta. 

Smart contract in Bitcoin

Sebbene chiarito che gli smart contract non sono contratti, continueremo a chiamarli così per ragioni di semplicità

Essi possono di fatto funzionare come delle transazioni articolate e condizionali.

Bitcoin è dotato di una forma elementare di programmabilità delle transazioni. Infatti anche un semplice pagamento come:  

Si paghi 1 coin a Bob — firmato Alice  

Nasconde un procedimento sottostante piuttosto complesso. Per essere più precisi un pagamento semplice in Bitcoin andrebbe visto in questi termini: 

Prendi come l’output non speso della transazione A, usalo come input e crea un nuovo output di 1 coin a favore di Bob — firmato Alice 

Di fatto input ed output sono dei veri e propri programmi, o smart contract se si preferisce, scritti in un linguaggio di programmazione chiamato Script che ha una sintassi simile a quella del linguaggio Forth. Il programma che risiede sull’output di una transazione viene chiamato script di blocco. Questo significa che Alice ha bloccato un coin attraverso uno script e chiunque potrebbe in teoria fornire un script di sblocco. 

Durante la validazione di una transazione i miner caricano lo script di blocco e quello di sblocco, li mettono in esecuzione uno di seguito all’altro e se il risultato complessivo dell’operazione è “true” (vero booleano) allora il coin viene speso, ovvero messo a disposizione di un successivo script di blocco, altrimenti no. 

Storicamente lo script di blocco è stato chiamato con il codename scriptPubKey mentre quello di sblocco è stato chiamato scriptSig

Un pagamento verso un indirizzo ad esempio è codificato normalmente con uno script di tipo pay-to-pubkey-hash. Ad esempio un pagamento da Alice a Bob potrebbe essere formulato in una transazione creata da Alice che crea un nuovo output con uno script di blocco come segue:

OP_DUP OP_HASH160 <Bob Key Hash> OP_EQUAL OP_CHECKSIG

Bob potrà spendere il suo coin quando vorrà a patto di inserire in una nuova transazione il seguente script di sblocco.

<Bob Signature> <Bob Public Key>

Sebbene relativamente semplici, la leggibilità degli script Bitcoin non è il massimo. Per questo motivo nelle sezioni successive si farà largo uso di pseudo codice e descrizioni in lingua italiana per garantire una migliore leggibilità. 

Grazie a questa programmabilità delle transazioni anche in Bitcoin è possibile realizzare delle logiche più complesse del semplice “Alice paga 1 coin a Bob”. Tra queste logiche ad esempio è possibile il pagamento multifirma. Un pagamento multifirma di fatto non è altro che una somma spendibile a favore di un determinato beneficiario ma solo a patto che tale transazione sia firmata da due o più chiavi private. La logica del pagamento multifirma potrebbe essere descritta con qualcosa tipo: 

Alice blocca 1 coin che può essere sbloccato se almeno due tra Alice, Bob e Trent sono d’accordo. 

Lo script di blocco nella formulazione in Script potrebbe essere scritto come:

2 <Alice> <Bob> <Trent> 3 OP_CHECKMULTISIG

Dove con <Alice> intendiamo la chiave pubblica di Alice e così via. Un possibile script di sblocco potrebbe essere:

OP_0 <Firma di Alice> <Firma di Trent>

Questa tipologia di script consente di implementare gli escrow, ovvero degli arbitri che decidono sulla regolarità di una transazione. Supponiamo che Alice acquisti una bicicletta online da Bob e paghi con i bitcoin. Nel caso di acquisto senza escrow potrebbero verificarsi tre diverse situazioni: 

  1. Alice paga Bob ma questi non spedisce la bicicletta o la bicicletta è rotta. 
  2. Bob spedisce la bicicletta ma Alice non vuole pagare. 
  3. Alice paga Bob che spedisce una bicicletta in buone condizioni. 

Solo l’ultimo caso è quello “felice”, negli altri casi qualcosa va storto e ricordiamo che un pagamento in criptovaluta è irreversibile e normalmente non conosciamo l’identità di chi sta dall’altra parte, cosa che rende vano o molto improbabile ogni tentativo di recupero. 

Nel caso particolare della vendita della bicicletta possiamo costruire un contratto con un bitcoin bloccato da Alice che può essere sbloccato solo da 2 su 3 firme fra quelle di Alice, Bob e Trent. A questo punto possiamo analizzare nel dettaglio i vari casi precedenti. 

Alice spedisce il coin 2 su 3 a Bob ma Bob non spedisce la bicicletta. Bob non può da solo sbloccare il coin a proprio favore. Alice si lamenta con Trent, Trent non firmerà lo sblocco del coin a meno che Bob non fornisca prova di aver veramente spedito la bicicletta. 

Alice spedisce il coin 2 su 3 a Bob ma la bici e rotta. Alice si lamenta con Trent e documenta attraverso immagini che la bicicletta è rotta. Bob non è in grado di provare che fosse integra quindi Trent non sblocca il coin. 

Bob spedisce la bicicletta ma Alice non vuole sbloccare il coin agendo dunque in modo disonesto. Bob si rivolge a Trent e le loro due firme possono ora sbloccare il coin a favore di Bob. 

Nell’ultimo caso Alice riceve la bicicletta ed è soddisfatta. Alice e Bob firmano per sbloccare il coin a favore di Bob. Trent non deve nemmeno intervenire. 

L’applicazione del pagamento multifirma alla vendita tramite escrow è solo uno dei casi possibili, di seguito riportiamo altri casi d’uso:

  • 1-of-2: Fondo familiare corrente – una sola firma tra quelle dei coniugi è sufficiente a sbloccare un pagamento..
  • 2-of-2: Conto familiare di risparmio — i coniugi devono entrambi firmare per sbloccare un pagamento.
  • 2-of-3: Conto familiare per i figli – il figlio può spendere i fondi solo se almeno uno dei genitori approva. Da parte loro nessuno dei genitori può individualmente prelevare i fondi a meno che non si accordino entrambi.
  • 2-of-2: Wallet di autenticazione a due fattori –  Una chiave privata risiede sul PC ed un’altra sul cellulare, è necessario disporre di entrambi i dispositivi per prelevare i fondi. Questa soluzione si adotta per prevenire l’attacco da parte di hacker che potrebbero riuscire a ottenere il controllo di uno dei vostri dispositivi.

Gli script introducono molte possibilità nelle transazioni Bitcoin tuttavia sono un po’ difficili da usare. Ad esempio nel caso di uno script multisig con molte parti in causa (ad esempio un quorum 5 s 7) ognuna di queste dovrebbe capire come realizzare uno script custom e in generale le transazioni risultanti sarebbero molto più grandi, richiedendo maggiori fee e occupando molto spazio nella memoria dei miner.

Per rendere più pratico l’utilizzo degli script in Bitcoin, nel 2012 è stata introdotta una nuova possibilità detta pay-to-script-hash (P2SH). In pratica invece di memorizzare nell’output di una transazione un intero script ne viene memorizzata solo un hash di 20 byte.

OP_HASH160 <20-byte hash dello script di riscatto> OP_EQUAL

A questo punto è onere di chi vuole sbloccare l’output presentare sia il testo dello script di blocco che il corrispondente script di sblocco, che in parole povere si traduce nel farsi anche carico di tutte le spese supplementari dovute per la creazione di una transazione contenente il testo dello script.

Un’altra innovazione della funzionalità P2SH è la possibilità di codificare un hash di script come un indirizzo. Gli indirizzi P2SH sono riconoscibili perché iniziano con un “3”. Ad esempio uno script multisig codificato come un indirizzo Bitcoin diventa un vero e proprio address al quale possono essere spedite delle somme come ad un qualunque altro indirizzo. 

Un altro caso d’uso elementare di uno smart contract è quello del pagamento post-datato. Sebbene molto comune nel mondo analogico per mezzo degli assegni cartacei, il pagamento a tempo è meno banale di quanto si pensi nel caso del denaro elettronico. Infatti se interagendo con una banca possiamo impostare pagamenti a tempo questo implica che un sistema esterno (il computer della banca) gestisca il pagamento per noi. Di fatto stiamo dando mandato a qualcuno di ricordarsi di fare un pagamento ad una certa data. Nel caso dello smart contract invece il denaro resta sotto il nostro controllo ma possiamo programmarlo per trasferirsi dopo un certo tempo a qualcun altro. In tali casi si parla di timelock, ovvero una transazione che può essere confermata in blockchain solo dopo una determinata data.

Ethereum e gli smart contract Turing-complete


Tecnicamente si definisce Turing completo un sistema di regole, e quindi anche un linguaggio di programmazione, che abbia la capacità di eseguire qualunque logica eseguibile da una macchina di Turing che a sua volta è un modello di calcolo definito dal celebre scienziato Alan Turing. La macchina di Turing è una macchina ideale che con teoriche operazioni di scrittura su una casella, lettura da una casella e movimento del nastro contenente le caselle è in grado di risolvere tutti i problemi calcolabili.

Abbiamo visto come la programmabilità di Bitcoin consenta di andare oltre il mero trasferimento diretto da un partecipante ad un altro. Gli scripts possono anche essere usati per pagamenti multifirma, per sbloccare premi per la soluzione di problemi di calcolo e si può anche costruire uno script che affermi qualcosa come “questo Bitcoin UTXO è tuo se sei in grado di provare tramite Merkle tree che tu mi invii una transazione di Dogecoin di questo valore”, consentendo essenzialmente la funzione di cambio fra criptovalute diverse.

Tuttavia gli script eseguibili dai nodi Bitcoin sono limitati “by design” in diversi ambiti:

  • non sono Turing completi in quanto non possono eseguire costrutti iterativi;
  • sono funzioni pure, ovvero stateless, e non hanno nessuna visibilità né dello stato della blockchain (ad esempio numero del blocco corrente) né dello stato del coin che sono programmati a bloccare o sbloccare. La blockchain Bitcoin è basata sul concetto di UTXO (Unspent Transaction Output) e ogni UTXO durante una transazione è speso integralmente o non lo è affatto.


Questo significa che non sarà mai possibile implementare una logica del tipo:

Esegui N volte le seguenti istruzioni: op_code1 op_code2 … 

dove N è una variabile scelta dall’utente al momento di creazione della transazione. Una logica del genere potrebbe essere architettata in modo malevolo (ad esempio con N scelto artificiosamente molto grande) e tutti i nodi della rete una volta raccolta tale transazione per la validazione e la conferma si troverebbero ad eseguire praticamente all’infinito il codice senza mai terminare l’esecuzione. Per ragioni di sicurezza Bitcoin quindi non consente la logica iterativa in nessuna forma per non compromettere la sicurezza della rete.

Anche se l’esecuzione di una transazione Bitcoin è “stateless” questo non significa che la blockchain non abbia uno stato, anzi possiamo identificare lo stato S come l’insieme di tutti gli UTXO e formalizzare una transazione TX come:

APPLY(S,TX) -> S’

Dove S’ è lo stato della blockchain successivo alla transazione TX.

A differenza di Bitcoin, Ethereum non esclude la possibilità di scrivere codice contenente cicli di iterazione, anzi fa proprio della Turing completezza un fattore distintivo rispetto a Bitcoin. Inoltre definisce un modello di computazione stateful, ovvero i suoi script possono avere visibilità sia dei registri in memoria interni, che chiamiamo variabili di stato, che di variabili esterne agli stessi script ma legate allo stato generale della blockchain come ad esempio il saldo di un determinato account, il numero di blocco precedente, il suo timestamp e così via.

Quando un miner riceve una transazione indirizzata ad un contratto, se tale richiesta è legittima, il contratto eseguirà il cambiamento del suo stato. Lo stato di uno smart contract non è registrato nella memoria di un solo computer ma in quella di tutti i computer partecipanti. La rete nella sua interezza si comporta quindi come un computer globale e ogni programma anche composto da poche istruzioni viene eseguito da migliaia di computer in modo identico con lo scopo di poter raggiungere un consenso che tradotto in linguaggio umano apparirebbe come: acconsento, siamo d’accordo sullo stato successivo del programma. 

Questa esecuzione appare terribilmente inefficiente  ma risulta essere praticamente inarrestabile, infatti una volta che il contratto è in esecuzione questo non può essere interrotto o sospeso da nessuno a meno che tali stati di interruzione e sospensione non siano stati previsti dai suoi programmatori. Questa caratteristica rende gli smart contract resistenti alla censura. Tale resistenza in realtà dipende dalla capacità della blockchain sottostante di assorbire un attacco, anzi più in generale possiamo dire che uno smart contract potrà essere resistente al massimo quanto lo è la blockchain in cui viene eseguito ma in realtà lo sarà di meno in quanto le istruzioni e la logica dello smart contract potrebbero contenere errori di programmazione e risultare quindi meno sicuri della blockchain sottostante. In una rete di fatto controllata da poche entità, qualunque programma potrebbe essere fermato o manipolato facilmente se gli attori che governano il sistema si mettono d’accordo. La varietà e la quantità dei partecipanti ad una rete e alla scrittura della sua blockchain sono fattori essenziali per garantirne la sicurezza. Per tale motivo l’esecuzione di smart contract su una blockchain privata, di consorzio o, come spesso accade, denominata ledger distribuito (DL) in contrapposizione alle blockchain di tipo pubblico, non può essere considerata né immutabile né resistente alla censura.

Da un punto di vista operativo ogni nodo Ethereum mette in esecuzione una Ethereum Virtual Machine (EVM), ovvero un modulo software in grado di emulare il comportamento di uno pseudo-processore dotato di istruzioni elementari che possono compiere operazioni aritmetiche, crittografiche e di controllo del flusso di esecuzione come IF e cicli FOR. Senza entrare nei dettagli tecnici della EVM, diciamo semplicemente che è come una CPU ma virtuale, ed è dedicata all’esecuzione di un set di istruzioni pensate per l’esecuzione di codice in blockchain e non un set di istruzioni “general purpose” come quelle ad esempio eseguite da un PC. 

Il linguaggio macchina finale eseguito dalla EVM sarebbe costituito solo da numeri (bytecode) e come tale poco leggibile e non sarebbe utile riportarne qui un esempio. I programmatori utilizzano un linguaggio detto di alto livello che possa essere successivamente tradotto in istruzioni macchina elementari da un apposito software chiamato “compilatore”. Tra questi linguaggi di alto livello, quello che va per la maggiore su Ethereum è il linguaggio Solidity ed un semplice contratto potrebbe essere scritto come segue:

contract Hello{ 

    uint public x=0; 

Nell’esempio precedente il contratto di nome Hello dichiara di contenere un registro chiamato x, di tipo “intero” senza segno e inizializzato al valore zero. L’esempio riportato è minimalista e privo di qualsiasi utilità se non quella di mostrare pochi elementi della sintassi. Gli smart contract, sebbene in genere non lunghissimi, possono essere costituiti da centinaia o migliaia di linee di codice. Naturalmente lo spazio occupato dallo smart contract durante la sua esecuzione, spazio che include anche il suo codice eseguibile, è un costo per la rete e come tale viene tassato. Questo pone agli sviluppatori un ulteriore requisito, quello di scrivere contratti brevi e compatti oltreché sicuri.

Smart contract e gas

Appurato che una transazione Ethereum è il risultato dell’esecuzione di tante istruzioni elementari che modificano lo stato di uno smart contract e quindi in generale lo stato della blockchain. Tali istruzioni vengono eseguite dai miner che competono per formare il blocco successivo e ottenere l’incentivo. Le istruzioni all’interno di una transazione devono essere eseguite fino in fondo senza errori altrimenti l’intera transazione fallisce e tutte le variabili di stato del contratto sono ripristinate ai valori precedenti la transazione, incluso ogni eventuale somma spedita o ricevuta dallo smart contract. Ogni istruzione viene eseguita dai miner e contabilizzata con un’unità di costo macchina chiamata gas. Ogni unità di gas a sua volta potrà essere “comprata” ad un certo prezzo espresso in ether. 

Perché non è stato direttamente assegnato un prezzo in ether alle istruzioni della macchina? La ragione è che si è voluto separare il costo computazionale da quello economico. In un certo senso il gas è una misura empirica di quanto costa alla rete eseguire un certo pezzo di codice. Un po’ come calcolare il consumo di un’auto per percorrere un determinato tratto di strada, tale costo si misura in litri e non in euro. Poi se troviamo un distributore economico spendiamo meno magari, ma il consumo resta quello.

Quando creiamo una transazione possiamo decidere due parametri: uno è il gas limit e l’altro è il gas price. Il gas limit è la quantità massima di gas di cui vogliamo dotare la nostra transazione. Questo valore deve essere opportunamente stimato perché con un gas limit troppo basso rischiamo di finire il gas nel mezzo della transazione e quindi questa non sarà completata. Sarebbe come decidere di far decollare un razzo per Marte senza mettere abbastanza carburante per raggiungere la destinazione. Se il carburante finisce difficilmente troveremo un distributore a metà strada e l’impresa fallirà. Come abbiamo già descritto in precedenza per sua natura una transazione o è completa oppure viene ripristinato lo stato precedente. D’altro canto assegnare un gas troppo abbondante significa impegnare troppi dei nostri ether in attesa della fine della transazione anche se il gas che avanza ci verrà restituito. La restituzione del gas in eccesso impegnato per una transazione viene restituito ma a patto che la transazione vada fino in fondo con successo. Se il miner si blocca per un errore dovuto alla logica del nostro smart contract il gas viene comunque interamente trattenuto dal miner. Questa è una scelta di progetto dell’intero sistema che è volta a penalizzare l’esecuzione di transazioni che risultano non consentite come deterrente contro l’orchestrazione di attacchi di tipo denial of service.

Ad esempio se abbiamo uno smart contract programmato per erogare una somma solo ad una determinata data e cerchiamo di ottenere la somma prima della data stabilita questa condizione causerà il rifiuto della transazione da parte del miner e ci costerà la perdita di tutto il gas impegnato con il parametro gas limit.

Il gas price invece è quanto siamo disposti noi a pagare per una singola unità di gas affinché la nostra transazione con uno smart contract venga eseguita. Per restare nella metafora della benzina e dell’auto è come quando al distributore troviamo una gran fila, il costo di un litro di carburante è pari a €1.50 e noi abbiamo fretta. Potremmo offrire al benzinaio di pagare €2.50 al litro per passare avanti. In nessun distributore reale sarebbe possibile, ma nella blockchain è invece esattamente quello che accade. Nessuno ci impone di prezzare il gas più o meno degli altri, ma meno intendiamo pagare meno i miner saranno incentivati a validare e inserire la nostra transazione nel prossimo blocco con il rischio che un gas price decisamente troppo basso causi un tempo di attesa enorme se non infinito. Per questo motivo è bene orientarsi attraverso delle statistiche e cercare di pagare più della media se vogliamo una conferma rapida della nostra transazione, oppure un po’ meno della media se non non abbiamo particolarmente fretta.

Vuoi sviluppare degli smart contract per il tuo progetto? Contattami attraverso questo form.

Blog at WordPress.com.

%d blogger hanno fatto clic su Mi Piace per questo: