[Articolo Aggiornato al 22.03.2018]
Bene, eccoci arrivati alla parte che forse molti attendevano di più, quella dove vedremo in concreto come sviluppare qualcosa che realmente funzioni su di una blockchain!
Freno però un pò il vostro entusiasmo, così come avviene ad esempio per gli hello world delle app mobile questo esempio che vedremo sarà solo un primo passo per capire meglio come unire alcuni pezzi del puzzle. Le Dapp, quelle vere, vengono realizzate seguendo un’altra procedura, ma arriveremo anche a quello e vedremo come realizzarle più avanti. Per ora è utile procedere per passi e non rischiare di non capirci poi un tubo.
Quello che qui andremo a sviluppare è una applicazione estremamente semplice. Una delle tante applicazioni della blockchain è quella di poter mettere su, in un tempo relativamente breve, un sistema di votazione. Perchè il voto ? Beh perchè questo tipo di applicazione richiede alcune caratteristiche tipiche ed uniche che ritroviamo nella blockchain.
Il voto:
- deve essere anonimo
- deve svolgersi in sicurezza
- non deve essere ripetuto due volte
- non deve contenere azioni fraudolente
- non deve essere costoso
- non deve avere un controllo centrale e/o manuale
- deve essere garantito contro gli attacchi informatici di qualsiasi tipo
Bene, adesso trovatemi una tecnologia che soddisfi tutti i punti sopra esposti. Non la troverete se non nella blockchain che è stata ideata in maniera matematica con l’obiettivo di soddisfare tutti questi punti.
Tornando al nostro Hello World, l’applicazione di voto non dovrà fare altro che inizializzare un insieme di costanti, permettere a chiunque di votare i candidati e visualizzare i voti totali per ogni candidato. L’obiettivo in questa parte non è tanto soffermarci sul come codare una applicazione del genere ma imparare il processo di compilazione, deploy ed interazione.
Ho deliberatamente lasciato da parte l’uso dei framework dapp per costruire l’applicazione in quanto l’astrazione del framework nasconde alcuni dettagli che è utile all’inizio capire e comprendere.
Quindi lo scopo di questa 3 parte sarà:
- Effettuare il setup dell’ambiente di sviluppo
- Imparare il processo attraverso cui si scrive un “contratto”, come lo si compila e come lo si deploya
- Interagire con il contratto su di una blockchain attraverso una console nodejs
- Interagire con il contratto attraverso una semplice pagina web per visualizzare il numero di voti per ogni candidato.
Il setup dell’intera applicazione è stato costruito su una virtual machine vergine di ubuntu 16.04 xenial ma gli stessi precetti valgono su macos.
Questo è uno schema della app che andremo a costruire:

Setup
Su ethereum esistono varie blockchain. Una soltanto è la live cioè quella dove girano le transazioni reali e che viene chiamata “Ethereum Main Net”, mentre altre sono blockchain per i test di sviluppo dove è possibile effettuare il deploy di applicazioni che si vogliono testare prima di mandarle in produzione.
Oltre a queste ultime che “simulano” la main net reale è possibile anche testare le app in blockchain costruite ad hoc che girano sulla macchina dello sviluppatore quindi scollegate agli altri nodi della blockchain oppure utilizzare la cosiddetta in-memory blockchain (pensala come un simulatore di blockchain) chiamata ganache. Nelle successive parti di questa guida interagiremo con blockchain reali ma nella fase di sviluppo è utilissimo, se non un obbligo, sviluppare su blockchain di test o su sue simulazioni in quanto a noi non interessa avere l’intera blockchain sincronizzata (più di 20GB) ma soltanto sfruttare il metodo più veloce per poter scrivere codice e testare la nostra app su di una blockchain.
L’intero setup dell’applicazione è stato effettuato su una installazione pulita di Ubuntu desktop 16.04 xenial.
Nelle istruzioni a seguire provvederemo ad installare sulla nostra macchina ganache appunto, web3js che è la libreria javascript che ci consente di comunicare con la blockchain locale e avvieremo la test blockchain.
Nota: Questo tutorial gira con web3js nella versione 0.20.2. Aggiornerò il tutorial quando la versione 1.0 stable di web3js sarà disponibile
A questo punto procediamo con questi comandi che sono autoesplicativi e non hanno bisogno di spiegazioni:
$ sudo apt-get update
$ sudo apt-get install build-essential libssl-dev
$ sudo apt install curl
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash
$ source ~/.profile
$ nvm install 7.4.0
$ node -v
$ npm install -g npm
$ sudo apt-get install git
$ sudo apt-get install build-essential python
$ mkdir hello_world_voting
$ cd hello_world_voting/
$ npm install ganache-cli web3@0.20.2
Una volta effettuato il setup iniziale diamo il comando
$ node_modules/.bin/ganache-cli
Avremo una schermata simile a questa:
Nota: ganache crea 10 account di test con cui è possibile giocare. Ognuno di quei codici esadecimali rappresenta quindi un wallet, un indirizzo pubblico di portafoglio con le relative Private Keys. Questi accounts vengono automaticamente precaricati con 100 fake ethers (moneta digitale) che ci serviranno in seguito.
Il primo smart contract
Per sviluppare smart contracts su ethereum si utilizza un linguaggio che si chiama “Solidity”. Se hai familiarità con l’object oriented programming imparare a scrivere contratti solidity dovrebbe risultarti semplice. Noi scriveremo un contratto che chiameremo Voting con un constructor che inizializza un array di candidati.
Scriveremo 2 metodi, uno che ritorna i voti totali che un candidato ha ricevuto e un altro metodo che incrementa il conteggio del voto per ogni candidato.
Nota: Il constructor è invocato soltanto una volta e soltanto quando il contratto verrà deployato sulla blockchain. A differenza di ciò che accade nel mondo web dove ad ogni deploy del codice si sovrascrive il codice scritto precedentemente, il codice deployato su di una blockchain è immutabile quindi ogni volta che aggiorniamo il contratto e lo deployamo, il vecchio contratto rimarrà sempre nella blockchain senza essere modificato con tutti i dati memorizzati in esso e il nuovo deploy creerà una nuova instanza del contratto (in realtà è possibile estendere i contratti ma quelli sono argomenti già un pò più avanzati che in questa serie non tratteremo).
Sotto potete vedere l’esempio del contratto di voto che utilizzeremo insieme delle spiegazioni inline. Per il momento non è importante tanto capire il codice solidity quindi prendete per buono tutto quello che c’è scritto.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.4.18; | |
// We have to specify what version of compiler this code will compile with | |
contract Voting { | |
/* mapping field below is equivalent to an associative array or hash. | |
The key of the mapping is candidate name stored as type bytes32 and value is | |
an unsigned integer to store the vote count | |
*/ | |
mapping (bytes32 => uint8) public votesReceived; | |
/* Solidity doesn't let you pass in an array of strings in the constructor (yet). | |
We will use an array of bytes32 instead to store the list of candidates | |
*/ | |
bytes32[] public candidateList; | |
/* This is the constructor which will be called once when you | |
deploy the contract to the blockchain. When we deploy the contract, | |
we will pass an array of candidates who will be contesting in the election | |
*/ | |
function Voting(bytes32[] candidateNames) public { | |
candidateList = candidateNames; | |
} | |
// This function returns the total votes a candidate has received so far | |
function totalVotesFor(bytes32 candidate) view public returns (uint8) { | |
require(validCandidate(candidate)); | |
return votesReceived[candidate]; | |
} | |
// This function increments the vote count for the specified candidate. This | |
// is equivalent to casting a vote | |
function voteForCandidate(bytes32 candidate) public { | |
require(validCandidate(candidate)); | |
votesReceived[candidate] += 1; | |
} | |
function validCandidate(bytes32 candidate) view public returns (bool) { | |
for(uint i = 0; i < candidateList.length; i++) { | |
if (candidateList[i] == candidate) { | |
return true; | |
} | |
} | |
return false; | |
} | |
} |
Copiamo il codice sopra descritto in un file e chiamiamo il file Voting.sol nella cartella hello_world_voting.
Adesso compileremo il codice e effettueremo il deploy sulla blockchain ganache.
Per compilare il codice Solidity installiamo il modulo npm chiamato solc
$ npm install solc
Se ricordi dal precedente articolo, web3js è una libreria che ci permette di interagire con la blockchain attraverso RPC. Useremo quindi questa libreria all’interno della console node per compilare e deployare la nostra applicazione ed interagire con la blockchain.
Apriamo adesso un nuovo terminale rechiamoci all’interno della cartella hello_world_voting e apriamo la console di node
$ node
successivamente inizializziamo gli oggetti web3 e solc
> Web3 = require('web3')
> web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
Per essere sicuri che web3 sia correttamente inizializzato e possa comunicare con la blockchain, chiediamo a quest’ultima di fornirci una lista di tutti gli accounts. Riceverai una risposta simile a questa
> web3.eth.accounts
['0x2d093c77800f6c2c32a59ee718a793ad85f45527',
'0x23835ef9509784d4656bbfc924f3606ec921ccca',
'0x4dc958aa099f9a724f2ad97668e13dd870c72613',
'0x244e4e8e7173104611f31bfda34eec7e050c3f1c',
'0x7b1dbd4e0cbee187c3943f3fa0d777734c9305e5',
'0x5194393700b09571d78425a119e0657ad92264e1',
'0xfd39e835b48d5e703f2ef894c1036fc57da827fe',
'0xe9851b766dd4d03a70423a267234708569120e49',
'0x24f7cff1422e166d5d89b0f58a3e8e08f5169f01',
'0xa69f12716cbcec87daac160559cdca832b67cdee',]
Dico simile perchè ovviamente gli indirizzi dei wallets saranno diversi in quanto sono creati randomicamente
Bene. Hai appena effettuato la tua prima operazione su di una blockchain!
Per compilare il contratto, carica il codice dal Voting.sol in una variabile stringa e compila in questo modo
>code = fs.readFileSync('Voting.sol').toString()
>solc = require('solc')
>compiledCode = solc.compile(code)
Quando compiliamo il codice e stampiamo l’oggetto contract (basta scrivere contract nella node console per vederne il contenuto), vi sono due campi importanti al quale prestare attenzione:
- contract.code: Questo è lo bytecode del codice compilato di Voting.sol. Questo è il codice che verrà deployato nella blockchain
- contract.info.abiDefinition: Questa è l’interfaccia o il template del contratto che dice all’utente del contratto quali metodi sono disponibili nel contratto. Ogni qual volta vuoi interagire con il contratto in futuro, avrai bisogno di questa abiDefinition. Puoi leggere qualcosa in più riguardo l’ ABI qui
Adesso andiamo a deployare il contratto sulla blockchain.
Innanzitutto creiamo un contract object (VotingContract) che è usato per il deploy e per inizializzare i contratti nella blockchain
> abiDefinition = JSON.parse(compiledCode.contracts[':Voting'].interface)
> VotingContract = web3.eth.contract(abiDefinition)
> byteCode = compiledCode.contracts[':Voting'].bytecode
> deployedContract = VotingContract.new(['Orlando','Ferrandelli','Trump'],{data: byteCode, from: web3.eth.accounts[0], gas: 4700000})
> deployedContract.address
> contractInstance = VotingContract.at(deployedContract.address)
VotingContract.new effettua il deploy del contratto sulla blockchain.
Il primo argomento è un array di candidati che competono alle elezioni.
Focalizziamo l’attenzione sul secondo parametro. Abbiamo:
- data: Questo è il bytecode compilato del contratto sulla blockchain
- from: La blockchain deve mantenere traccia di chi effettua il deploy del contratto. In questo caso noi stiamo dando il primo account che abbiamo ricevuto dalla chiamata web3.eth.accounts e questo account sarà l’owner del contratto. Ricordati che web3.eth.accounts ritorna un array di 10 test account creati quando abbiamo avviato la blockchain testrpc. Nella Main Net non è possibile utilizzare qualsiasi account. ma soltanto quegli account che possiedi e sbloccarli prima di effettuare la transazione. Quando creerai un account ti verra richiesto una passphrase e la passphrase è ciò che proverà il possesso del tuo account. Ganache di default sblocca tutti e dieci gli account per convenienza.
- gas: E qui c’è la genialata. Per interagire con la blockchain è necessario spendere money! A me la prima volta che l’ho saputo ha fatto storcere il naso questa cosa, ma in realtà è proprio qui che sta una delle rivoluzioni della blockchain rispetto agli altri ambienti di sviluppo. Questi soldi, pochi spicci in realtà, andranno ai miners che effettueranno tutto il lavoro per includere il codice nella blockchain validando la transazione del deploy del contratto e validando i blocchi con i loro algoritmi. Questo fa si che nessuno abbia l’interesse all’interno di questo giro, di comportarsi come non dovrebbe…in realtà su ethereum si sta pensando di implementare un’altro tipo di validazione basato sul proof of stake ma questo è un’altro discorso di cui spero potrò parlare in qualche successivo articolo. Tornando al nostro caso, in questo parametro andremo a specificare quanti soldi vorresti pagare per includere il codice nella blockchain. Questa cifra è espressa in ‘gas’. Il bilancio del tuo ether wallet specificato prima verrà utilizzato per comprare ‘gas’. Il prezzo del gas è un prezzo settato dalla rete (che è possibile consultare con dei tools), qualsiasi eccedenza ti verrà restituita nell’account. Il gas dovrà essere pagato soltanto per ogni transazione che cambia lo stato di un contratto. In questo articolo essendo che stiamo interagendo con testrpc non ci dovremmo preoccupare di questo in quanto testrpc ha il suo default gas price già settato.
Bene, adesso abbiamo effettuato il deploy del nostro contratto e abbiamo una istanza del contratto (la variabile contractInstance) che possiamo utilizzare per interagire con esso.
Vi sono centinaia di migliaia di contratti sviluppati sulla blockchain, quindi come identificare il nostro contratto sulla blockchain? La risposta la troviamo in deployedContract.address.
Quando dovrai interagire con il tuo contratto, avremo bisogno di questo indirizzo e della definizione abi di cui abbiamo parlato poc’anzi
Come Interagire con il contratto in una console nodejs
> contractInstance.totalVotesFor.call('Orlando')
{ [String: '0'] s: 1, e: 0, c: [ 0 ] }
> contractInstance.voteForCandidate('Orlando', {from: web3.eth.accounts[0]})
'0xdedc7ae544c3dde74ab5a0b07422c5a51b5240603d31074f5b75c0ebc786bf53'
> contractInstance.voteForCandidate('Orlando', {from: web3.eth.accounts[0]})
'0x02c054d238038d68b65d55770fabfca592a5cf6590229ab91bbe7cd72da46de9'
> contractInstance.voteForCandidate('Orlando', {from: web3.eth.accounts[0]})
'0x3da069a09577514f2baaa11bc3015a16edf26aad28dffbcd126bde2e71f2b76f'
> contractInstance.totalVotesFor.call('Orlando').toLocaleString()
'3'
Prova i comandi scritti sopra nella tua node console e dovresti vedere il conteggio dei voti incrementarsi. Ogni qualvolta voti per un candidato, il sistema torna indietro un id di transazione: Esempio: ‘0xdedc7ae544c3dde74ab5a0b07422c5a51b5240603d31074f5b75c0ebc786bf53’
Questo id di transazione è la conferma che la transazione è avvenuta con successo e questa conferma rimarrà per sempre sulla blockchain come testimonianza dell’avvenuto voto. Questa transazione infatti è immutabile e l’immutabilità è uno dei vantaggi delle blockchains come Ethereum.
Connettere la blockchain e il voto ad una webpage
Adesso che la maggior parte del lavoro è fatto, ciò che ci rimane da fare è semplicemente scrivere un html con i nomi dei candidati e invocare i comandi per il voto che abbiamo visto in precedenza in un file js.
Sotto potete trovare il codice html e il file js. Inserisci entrambi i file nella root della nostra cartella e apri index.html nel tuo browser.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Hello World DApp</title> | |
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'> | |
<link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' rel='stylesheet' type='text/css'> | |
</head> | |
<body class="container"> | |
<h1>A Simple Hello World Voting Application</h1> | |
<div class="table-responsive"> | |
<table class="table table-bordered"> | |
<thead> | |
<tr> | |
<th>Candidati</th> | |
<th>Voti</th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr> | |
<td>Orlando</td> | |
<td id="candidate-1"></td> | |
</tr> | |
<tr> | |
<td>Ferrandelli</td> | |
<td id="candidate-2"></td> | |
</tr> | |
<tr> | |
<td>Trump</td> | |
<td id="candidate-3"></td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
<input type="text" id="candidate" /> | |
<a href="#" onclick="voteForCandidate()" class="btn btn-primary">Vota</a> | |
</body> | |
<script src="https://cdn.rawgit.com/ethereum/web3.js/develop/dist/web3.js"></script> | |
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script> | |
<script src="./index.js"></script> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); | |
abi = JSON.parse('[{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"totalVotesFor","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"validCandidate","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"votesReceived","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"x","type":"bytes32"}],"name":"bytes32ToString","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"candidateList","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"voteForCandidate","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"contractOwner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"inputs":[{"name":"candidateNames","type":"bytes32[]"}],"payable":false,"type":"constructor"}]') | |
VotingContract = web3.eth.contract(abi); | |
// In your nodejs console, execute contractInstance.address to get the address at which the contract is deployed and change the line below to use your deployed address | |
contractInstance = VotingContract.at('0x2d093c77800f6c2c32a59ee718a793ad85f45527'); | |
candidates = {"Orlando": "candidate-1", "Ferrandelli": "candidate-2", "Trump": "candidate-3"} | |
function voteForCandidate(candidate) { | |
candidateName = $("#candidate").val(); | |
contractInstance.voteForCandidate(candidateName, {from: web3.eth.accounts[0]}, function() { | |
let div_id = candidates[candidateName]; | |
$("#" + div_id).html(contractInstance.totalVotesFor.call(candidateName).toString()); | |
}); | |
} | |
$(document).ready(function() { | |
candidateNames = Object.keys(candidates); | |
for (var i = 0; i < candidateNames.length; i++) { | |
let name = candidateNames[i]; | |
let val = contractInstance.totalVotesFor.call(name).toString() | |
$("#" + candidates[name]).html(val); | |
} | |
}); |
Nota: sostituisci VotingContract.at(‘0x2d093c77800f6c2c32a59ee718a793ad85f45527’); con l’indirizzo del wallet proprietario del contratto.
Questo è ciò che vedrai quando aprirai il file index.html nel browser

Congratulazioni!
Nella parte successiva effettueremo il deploy di questo contratto nella public test network in maniera tale che tutto il mondo potrà vederlo e votare per un candidato!
Inoltre c’è da dire che ovviamente questo passo è stato soltanto un primo timido passo verso lo sviluppo sulla blockchain. In realtà quando si sviluppa sulla blockhain si preferiscono utilizzare dei framework senza interagire con una console nodejs. I framework sono numerosi ma ne riparleremo nel prossimo articolo!
Altra cosa interessante prima di lasciarvi e che forse i più attenti avranno notato. Avete visto che la dapp viene eseguita in locale. Se avessimo messo il codice html e javascript su di un server, anche se questo avesse avuto la sua copia della blockchain, non saremmo stati in grado di votare in quanto il server non conosce il nostro indirizzo wallet attraverso cui votare! Ecco spiegate perché vengono chiamate decentralized apps, perché (in teoria) queste web app, si comportano più come le app mobile che hanno bisogno di essere installate sul nostro dispositivo, quindi ognuno ha la sua dapp (che si interfaccia alla sua copia della blockchain..). Ovviamente è impensabile costringere un utente al download della intera blockchain e per questo esistono varie soluzioni che vanno dall’installazione di particolari browser che sono in grado come un App Store di far visualizzare le varie dapp disponibili e di far girare dapps interfacciandole alle live net o plugin che si installano su chrome. Ma di questo avremo modo di parlarne meglio in articoli successivi.
Spero che questo articolo intanto vi abbia dato una idea pratica di come iniziare con lo sviluppo delle applicazioni decentralizzate su piattaforma Ethereum, ma c’è ancora moolto da dire! 😉