101 intro alla programmazione di Smart Contract (Contratti intelligenti) su Ethereum per i novizi
101 intro alla programmazione di Smart Contract (Contratti intelligenti) su Ethereum per i novizi
Originariamente pubblicato su consensys.github.io/developers (dove alcune formattazioni di codici potrebbero essere più facili da leggere).
Alcune persone dicono che Ethereum ha una logica complicata ed è difficile da utilizzare, ma questo testo vi darà un idea su come costruire smart contract e applicanzioni con esso. Strumenti, portafogli, applicazioni e l’ecosistema sono ancora in fase di sviluppo con i quali tutto diventerà più semplice!
- Parte I è una panoramica dei termini chiave e spiega i Client Ethereum e i linguaggi utilizzati dagli smart contract.
- Parte II spiega il flusso di lavoro generale ed alcuni Strumenti e DApp Framework
- Parte III è la Parte di Programmazione, una rapida procedura di scrittura di test e costruzione di DApp per uno smart contract usando Truffle.
Parte I. Introduzione
Se sei nuovo a tutto questo materiale riguardante una criptovaluta, incluso Bitcoin e come funziona, consulta i primi capitoli del libro su Bitcoin di Andreas Antonopoulos per fare i primi passi. Poi consultate il Whitepape Ethereum.
Non è necessario comprendere tutte le scienze informatiche sulle criptovalute per iniziare a costruire qualcosa, anche perchè molti di essi riguardano i miglioramenti di Ethereum rispetto all’architettura di Bitcoin.
Tutorial Iniziali
Il luogo ufficiale dove iniziare è ethereum.org che ha un tutorial iniziale e tutorial su follow-up token e crowdsale. Ci sono anche i documenti ufficiali di Solidity. Un altro buon posto per iniziare con gli smart contract (dove io ho iniziato) è dappsForBeginners, anche se potrebbe essere obsoleto.
L’obiettivo di questo documento è quello di completare tali esercitazioni e introdurre alcuni strumenti di sviluppo utili che consentono di iniziare con Ethereum, con gli smart contract e sviluppare DApp (applicazioni decentralizzate) facilmente. E provare a spiegare il flusso generale di quello che sta succedendo. Questo è dalla mia (ancora poco esperta) prospettiva e con molto aiuto dei fantastici sviluppatori di ConsenSys.
Concetti basilari
Sarebbe opportuno conoscere alcuni di questi termini:
Chiave pubblica crittografata. Alice ha una chiave pubblica e una chiave privata. Può usare la sua chiave privata per creare una firma digitale, e Bob può usare la chiave pubblica di Alice per verificare che la firma proviene veramente dalla chiave privata di Alice, ovvero, veramente da Alice. Quando crei un portafoglio Ethereum o Bitcoin il lungo indirizzo ‘0xdf…5f’ è una chiave pubblica e la chiave privata viene memorizzata da qualche parte. Un servizio di portafoglio Bitcoin come Coinbase conserva la chiave privata complementaria del tuo portafoglio per te, o puoi memorizzarla da solo. Se perdi la tua chiave privata per un portafoglio con dei fondi reali avrai perso tutti i tuoi fondi per sempre, quinsi è consigliabile eseguire un back up delle tue chiavi. La soluzione più dura è quella di farsi male per imparare! Io l’ho fatto.
Networking Peer-to-Peer. Come BitTorrent, tutti i nodi Ethereum sono peer in una rete distribuita, non esiste un server centralizzato. [In futuro, ci saranno servizi ibridi semi-centralizzati per Ethereum come comodità per gli utenti e gli sviluppatori, più dettagli su questo in seguito.]
Blockchain. Come un registro globale o un semplice database di tutte le transazioni, l’intera storia di tutte le transazioni sulla rete.
Ethereum Virtual Machine. Con essa puoi scrivere molti programmi potenti a differenza di Bitcoin. Si riferisce alla blockchain, a cosa esegue gli smart contract, tutto.
Nodo. Usare questo significa che puoi eseguire un nodo e tramite esso leggere e scrivere la blockchain Ethereum, ossia usare la Ethereum Virtual Machine. Un nodo completo deve scaricare l’intera blockchain. I nodi leggeri sono possibili ma in via di sviluppo (sono stati già sviluppati).
Miner (Minatore). Un nodo nella rete che mina, ovvero, lavora per elaborare i blocchi sulla blockchain. Puoi vedere una lista parziale dei miner su Ethereum qui: stats.ethdev.com.
Proof of Work (Prova di lavoro). I Miner competono per eseguire alcuni prolemi matematici. Il primo a risolvere il problema (il blocco successivo sulla Blockchain) vince una ricompensa: alcuni ether. Ogni nodo quindi viene aggiornato al nuovo blocco. Ogni miner vuole vincere il blocco successivo quindi sono incentivati ad aggiornarsi e avere la vera blockchain aggiornata che ognuno ha, quindi la rete raggiunge sempre un consenso. [Nota: Ethereum sta pianificando il passaggio ad un sistema Proof of Stake eventualmente senza miner, ma che è al di là della portata dei novizi.]
Ether. O ETH abbreviato. E’ una vera e propria valuta digitale che puoi comprare ed usare! Ecco un grafico da uno dei diversi exchange per esso. Al momento della scrittura, 1 ETH vale circa 65 centesimi in dollari. (Ora 1 ETH = 340 dollari).
Gas. L’immagazzinamento e l’esecuzione di cose su Ethereum costa piccole quantità di ether. Mantenendo le cose efficienti.
DApp. Applicazione Decentralizzata, cosi vengono chiamate dalla comunità Ethereum tutte quelle applicazioni che usano smart contract. L’obiettivo di una DApp è (beh, dovrebbe essere) quella di avere una buona IU (Interfaccia Utente) per i tuoi smart contract con in più eventuali sottigliezze come IPFS (un modo pulito per memorizzare e servire materiale in una rete decentralizzata, non fatta da Ethereum ma da uno spirito affine). Mentre le DApp possono essere eseguite da un server centrale se quest'ultimo può comunicare con un nodo Ethereum, possono anche essere eseguiti localmente in cima a qualsiasi altro nodo peer Ethereum. [Prenditi un minuto: a differenza delle normali applicazioni per il web, le DApps non possono essere eseguite da un server. Possono usare la blockchain per inviare transazioni e recuperare dati (dati importanti!) piuttosto che un database centrale. A differenza di un tipico sistema di login per gli utenti, gli utenti possono essere rappresentati da un indirizzo di un portafoglio e mantenere tutti i dati utente conservati localmente. Molte cose possono essere progettate in modo differente dal web normale.]
Se cerchi un altro angolo per novizi con altri concetti come quelli citati qui sopra puoi leggere qui: Basta abbastanza Bitcoin per Ethereum.
Client Ethereum, Linguaggi degli Smart Contract
Non è necessario eseguire un nodo Ethereum per scrivere e distribuire smart contract. Vedi le IDE e le API basate sul Browser in basso. Ma se stai imparando a eseguire un nodo Ethereum, è bene conoscere come un componente base e non è difficile da impostare.
Client per eseguire un nodo Ethereum
Ethereum ha diverse implementazioni del client (cioè diversi modi per eseguire un nodo ed interagire con la rete Ethereum) incluso C++, Go, Python, Java, Haskell, ecc. Perchè? Tratti differenti per gente diversa (come Haskell è presumibilmente matematicamente verificabile), ed esso migliora la sicurezza dell’ecosistema di Ethereum. C’è anche un client basato su interfaccia grafica in via di sviluppo, AlethZero.
Al momento della scrittura, ho usato geth, il linguaggio Go language (go-ethereum) e in altri giorni uno strumento chiamato testrpc che usa il client Python, pyethereum. [Aggiornamento: Un nuovo strumento popolare che usiamo in sostituzione a testrpc ora è ethersim che usa ethereumJS. EthereumJS è un client JavaScript che non supporta il mining della blockchain reale, quindi non è un client completo come gli altri precedentemente presentati, ma il mining può essere simulato per scopi di testing e sviluppo.] Gli esempi successivi coinvolgeranno tali strumenti.
[Supplemento: Ho anche provato quello su C++ e ancora uso il suo componente ethminer per il mining con geth come nodo, quindi diverse parti possono lavorare insieme. Supplemento sul Mining: il Mining può essere divertente, un pò come avere una pianta in casa che tende a crescere, un altro modo per imparare l’ecosistema…anche se ora il prezzo di ETH non vale i costi elettrici, questo potrebbe cambiare. Specialmente se ognuno inizia a costruire fantastiche DApps e Ethereum diventa molto popolare.]
Console Interattiva. Una volta che disponi di un nodo che utilizza uno dei client, puoi sincronizzarlo con la blockchain, creare un portafoglio e inviare e ricevere ether reali. Un modo per fare questo è con geth attraverso la Console JavaScript. Un altro modo è attraverso JSON RPC (chiamata procedura remota) usando un comando come cURL per ottenere materiale tramite URL. Tuttavia l’obiettivo di questo articolo è quello di attraversare lo scenario di sviluppo di una DApp quindi procediamo. Tuttavia questi strumenti sono buoni da ricordare per il debugging, la configurazione dei nodi e per usare un portafoglio su riga di comando.
Eseguire un nodo su una rete di test. Se installi un client come geth e lo esegui sulla rete, ci vorrà un pò di tempo per scaricare l’intera blockchain e sincronizzarlo con la rete. (Puoi controllare se è sincronizzato vedendo se hai l’ultimo blocco che è inserito in cima di stats.ethdev.com e comparando questo numero al numero di blocco inviato dai registri del tuo client.)
Tuttavia per eseguire smart contract sulla rete dovresti avere un certo numero di ether. Diversamente ci sono dei modi per eseguire i client in una locale rete di test (testnet) gratuitamente. Non devi scaricare tutta la blockchain e puoi crare un’istanza privata dalla rete Ethereum con la sua blockchain, quindi sono più veloci da utilizzare per lo sviluppo.
testrpc. Puoi eseguire una rete di test usando geth, o un altro modo veloce per ottenere una testnet è quello di usare testrpc. Testrpc creerà un gruppo di account pre-finanziati che verranno inseriti all’inizio. E’ anche super veloce, quindi più facile per sviluppare e testare. Puoi iniziare con testrpc, poi quando i tuoi contratti sono in buono stato, spostati a geth su una testnet, che può essere iniziata specificando networkid (id di rete) come: geth — networkid “12345”. Ecco qui il testrpc repo ma recensirò tutto il necessario per installare in una parte del tutorial più avanti. [Aggiornamento: Lo sviluppatore di testrpc si sta concentrando su ethersim in sostituzione di testrpc e aggiornerò questo tutorial eventualmente per usare anche ethersim. Puoi iniziare ad usarlo ora, se vuoi. Ethersim è basato su ethereumJS e simula il mining per scopi di sviluppo ed è molto veloce.]
Parliamo di linguaggi di programmazione ora, quindi immergerci effettivamente in materiale di codifica.
Linguaggi di Programmazione degli Smart Contract
Basta usare Solidity. Per scrivere smart contract ci sono pochi diversi linguaggi: Solidity, che è come JavaScript e ha .sol come estensione dei file, Serpent, come Python con estensione .se, ed un terzo, LLL, basato su Lisp. Serpent è stato il più popolare per un pò di tempo ma ora Solidity è il più popolare e più robusto, quindi basta usare Solidity. Preferisci Python? Usa Solidity.
Compilatore solc. Dopo aver scritto un contratto con Solidity, usa solc per compilarlo. Proviene dalle librerie di C++ (implementazioni diverse che si completano di nuovo) che può essere installato qui. [Se non vuoi installare solc puoi anche usare un compilatore utilizzabile su Browser come il compillatore in tempo reale di Solidity o Cosmo, ma la parte di programmazione successiva richiede l'installazione di solc.]
[Nota: Le librerie di Ethereum stanno attraversando un attivo sviluppo e qualche volta le cose vanno fuori sincronizzazione con le nuove versioni. Assicurati di avere l’ultima versione di sviluppo, o una versione stabile. Chiedila in uno degli Ethereum Gitter su Github o su forums.ethereum.org che versione stanno usando le persone se le cose che usavi per lavorare non funzionano più.]
web3.js API. Una volta che un contratto su Solidity viene compilato con solc e inviato sulla rete, puoi richiamarlo utilizzando le Ethereum web3.js JavaScript API e costruire le applicazioni web che interagiscono con i contratti. (Non è necessario installarlo ancora, leggi prima DApp Frameworks qui sotto.)
Questi sono gli strumenti di base Ethereum per codificare smart contract e interagire con essi per costruire DApps
Parte II. Framework per DApp, Strumenti e flusso di lavoro
Framework per costruire DApp
Puoi eseguire tutte queste operazioni solo con gli strumenti sopra citati, ma alcuni sviluppatori hanno creato utili framework per rendere lo sviluppo di DApp più semplice.
Truffle e Embark. Il primo che ho iniziato ad usare è Truffle. (Prima di Truffle ho guardato la scorsa estate un gruppo di studenti astuti che avevano a che fare con materiale di codifica per un hackathon (sebbene con ottimi risultati) e mi ritrassi dalla paura. Poi è arrivato Truffle che ha fatto un sacco di roba grigliata succosa per te, quindi puoi iniziare la scrittura-compilazione-distribuzione-costruzione e i test per DApp subito.) Un altro framework molto simile per costruire e testare DApp è Embark. Tra questi due, ho usato solo Truffle, ma ci sono molti sviluppatori di DApp di successo con entrambi i framework. [Aggiornamento: Alcuni altri buoni framework per costruire DApp sono Dapple e Populus. Dapple ha ottenuto un aiuto da uno sviluppatore per essere migliorato.]
Meteor. Un altra grande quantità di sviluppatori di DApp includono web3.js + Meteor che è un framework generale di applicazioni web (La repo ethereum-meteor-wallet ha buoni esempi iniziali, e SilentCiero sta costruendo molte integrazioni di Meteor con template di web3.js e DApp). Ho scaricato ed eseguito un fantastica DApps che fa le cose in questo modo. Ci saranno alcune discussioni interessanti su questi strumenti e le migliori pratiche per costruire DApps alla conferenza su Ethereum ÐΞVCON1 9–13 Novembre (che sarà anche in streaming o su YouTube).
API. BlockApps.net sta creando una RESTful API per le DApp basate su un nodo Haskell che eseguono come un servizio centralizzato per risparmiarti il problema di eseguire un nodo Ethereum locale. Questo si discosta dal modello completamente decentralizzato delle DApp ma è utile quando eseguire un nodo Ethereum localmente non è realistico. Per esempio se vuoi servire la tua DApp ad utenti che non vogliono eseguire nè un nodo locale nè raggiungere un pubblico più ampio con solo un browser web o un dispositivo mobile. BlockApps ha uno strumento a linea di comando chiamato bloc che può essere utilizzato dopo aver creato un account sviluppatore con loro.
Se gli utenti devono usare un nodo Ethereum locale per utilizzare le DApp non è uno svantaggio? Come BlockApps ci sono una serie di strumenti in sviluppo che non richiederanno questo. Metamask ti consente di eseguire materiale su Ethereum in un browser senza un nodo,l’AlethZero o l’AlethOne di Ethereum sono programmi con interfacce grafiche semplici da utilizzare che sono stati sviluppati e un LightWallet ConsenSys che è in costruzione, sono modi per interagire con DApp più facilmente. Nodi Light (SPV) e sharding sono anche in funzionamento o pianificati. E’ un ecosistema P2P ma può implicare architetture ibride.
IDE per gli Smart Contract
IDE.C’è un Mix IDE per scrivere contratti messi fuori da Ethereum. Non l’ho provato ma lo proverò presto.
IDE basati su Browser. Il compilatore in tempo reale di Solidity e Cosmo sono entrambi un modo veloce per iniziare a compilare i tuoi smart contract direttamente su browser. Puoi anche puntare al tuo nodo locale in queste istanze aprendo una porta (non devi fidarti del sito e non devi mettere i tuoi risparmi di vita in ether sul tuo nodo locale per questo! Vedi la Cosmo UI per istruzioni su come fare questo con geth). Ma una volta che il tuo contratto funziona bene è bene usare un framework per aggiungere un UI e l’imballaggio di tutto come una DApp, che è quello che Truffle fa e sarà spiegato nella parte di programmazione più avanti.
Un altro potente IDE su browser è in funzione su Ether.Camp. Il loro IDE viene fornito con una sandbox (rete per i test) con una GUI automaticamente generata per i test (anzichè scrivere test manualmente come vi verrà mostrato in seguito) nonchè un explorer di transazioni della sandbox a test.ether.camp. Quando sei pronto a distribuire il tuo smart contract semi-reale, usare la loro testnet può essere un buon modo per confermare che il tuo smart contract funziona come pianificato attraverso un test vicino alla realtà. Lo stesso explorer per la rete Ethereum in tempo reale puoi trovarlo su frontier.ether.camp ed esso mostra i dettagli su ogni transazione fatta. La IDE Ether.Camp è un invito solo per le cavie ora, ma sarà lanciato presto.
Semplici Contratti e DApp. Cerca su Github per repo di DApp e file con estensione .sol per vedere che materiale fantastico la gente fa e come. Una grande lista di DApp con le rispettive rep puoi trovarla qui: dapps.ethercasts.com, sebbene alcune di questa lista sono obsolete. Anche Ether.fund/contracts ha alcuni esempi di contratti scritti da persone su Solidity e Serpent, ma non essere sicuro che questi siano stati testati e verificati correttamente. Ci sarà un’intera giornata di presentazioni di DApp, martedi 12 novembre al ÐΞVCON1.
Flusso di lavoro per distribuire smart contract
Il flusso di lavoro viene cosi diviso:
- Avvia un nodo Ethereum (ad esempio con geth o testrpc o ethersim)
- Compila il tuo smart contract scritto su Solidity usando solc => riprendi il binario
- Distribuisci il tuo contratto compilato sulla rete. (Questo passo costa ether e firma il conratto usando il tuo indirizzo del portafoglio del tuo nodo, o puoi specificare un altro indirizzo.) => restituisci l’indirizzo blockchain del contratto e ABI (una rappresentazione basata su JSON delle variabili del contratto compilato, degli eventi e metodi che puoi chiamare)
- Chiamare cose nel contratto usando le JavaScript API di web3.js per interagire con esso (Questo passaggio potrebbe costare ether a seconda del tipo di invocazione.)
Questo flusso di lavoro è raffigurato in dettaglio nel diagramma seguente:
E’ possibile creare DApp che forniscono un interfaccia utente per gli utenti e distribuire il contratto per poi usarla (Passaggi 1 o 4). Oppure la tua DApp potrebbe supporre che il contratto è stato già ditribuito (comune) e avviare il flusso dell’interfaccia utente da essi (6° passaggio).
Parte III. La parte di programmazione, Finalmente
Testing su Truffle
Truffle è ottimo per lo sviluppo di test guidati degli smart contract che è altamente raccomandato per mantenere la sanità quanto stai iniziando a imparare come funzionano le cose. E’ anche utile come maniera per imparare a scrivere promise in JavaScript, ovvero, chiamate in differita ed asincrone. Una Promise è come dire “fai questo, poi quando torni indietro, fai quello, e quando torni indietro, fai quell’altra cosa… e non farci aspettare mentre tutto quello che sta succedendo, ok?” Truffle usa promise su framework JS chiamate Pudding in cima a web3.js (quindi installa web3.js per te).
Tempi di transazione. Le Promise sono estremamente utili per le DApp perchè le transazioni necessinato di essere estratte nella blockchain (impiega 12–15 secondi su Ethereum). Anche se non sembra che prendano cosi tanto tempo su una rete di test possono richiedere più tempo sulla rete in tempo reale, o per scoprire che non è accaduto (ad esempio la tua transazione potrebbe non avere abbastanza gas, o può essere stata estratta in un blocco che è stato orfano).
Quindi facciamo una semplice copia di uno smart contract e scriviamo un test per esso.
Usare Truffle
Assicurati di avere 1. solc e 2. testrpc. installato (Per testrpc hai bisogno di Python e pip. Se sei nuovo con Python, per installarlo hai anche bisogno di usare una virtualenv, un modo per tenere le librerie di python separate su un singolo computer.)
Installa 3. Truffle (Puoi farlo usando NodeJS’s npm: npm install -g truffle, il -g può richiedere sudo). Per verificare se sia installato, digita truffle list in una finestra di console per far elencare tutti i comandi di truffle. Poi crea una nuova cartella di progetto (Sto chiamando la mia cartella ‘conference’), cambiala in essa, e fai truffle init. Questo creerà questa struttura di cartelle:
Ora avvia un nodo client su una nuova finestra console eseguendo testrpc (o avviando un nodo geth):
Torna alla prima finestra della console di truffle, ora digita truffle deploy. Questo distribuirà il contratto d’esmpio truffle init creato come template. Eventuali messaggi di errore che si possono visualizzare verranno mostrati sia sulla finestra della console di testrpc che quella di truffle.
Mentre stai sviluppando puoi fare truffle compile per assicurarti che il tuo contratto si compili (usando solc puoi anche eseguire run solc YourContract.sol), truffle distribuisce per compilare e lo distribuisce alla rete, e truffle fa test per eseguire i tuoi test sullo smart contract.
Primo Contratto, Primo Test
Ecco un contratto su Solidity per una conferenza dove i registrati possono comprare ticket, e l’organizzatore può impostare una quota massima di partecipanti e può anche fornire rimborsi. Tutto il codice presentato in questo tutorial è in questa repo.
contract Conference { address public organizer; mapping (address => uint) public registrantsPaid; uint public numRegistrants; uint public quota;
// so you can log these events event Deposit(address _from, uint _amount); event Refund(address _to, uint _amount);
function Conference() { // Constructor organizer = msg.sender; quota = 500; numRegistrants = 0; } function buyTicket() public returns (bool success) { if (numRegistrants >= quota) { return false; } // see footnote registrantsPaid[msg.sender] = msg.value; numRegistrants++; Deposit(msg.sender, msg.value); return true; } function changeQuota(uint newquota) public { if (msg.sender != organizer) { return; } quota = newquota; } function refundTicket(address recipient, uint amount) public { if (msg.sender != organizer) { return; } if (registrantsPaid[recipient] == amount) { address myAddress = this; if (myAddress.balance >= amount) { recipient.send(amount); registrantsPaid[recipient] = 0; numRegistrants--; Refund(recipient, amount); } } } function destroy() { // so funds not locked in contract forever if (msg.sender == organizer) { suicide(organizer); // send funds to organizer } } }
[Nota: Attualmente se il contingente è raggiunto, buyTicket() mantiene i soldi nel contratto ma l’acquirente non riceverà un ticket. Quindi buyTicket() dovrebbe usare ‘throw’ per far ritornare la transazione dell’acquirente. Aggiornerò il codice e aggiungerò un test per questo presto. (Grazie a F.V. dai commenti per la cattura di questo.)]
Andiamo a distribuirlo.
[Nota: Al momento della scrittura ho solc 0.1.3+ (installato tramite brew), Truffle v.0.2.3, testrpc v.0.1.18 (usando un venv) suMac OS X 10.10.5.]
Distribuire il contratto
Aggiungi un nuovo smart contract. Dopo che hai fatto truffle init, o nella cartella esistente del progetto, copia-incolla il contratto “Conference” in contracts/Conference.sol. Poi nel file config/app.json, modifica l’array “deploy” includendo “Conference”.
Avvia testrpc. avvia testrpc in una finestra della console separata usando testrpc se non è già in esecuzione.
Compila o Distribuisci. Esegui il compilatore di truffle per vedere se il contratto viene compilato, o fai truffle deploy per compilarlo e distribuirlo allo stesso tempo. Questo aggiungerà l’indirizzo del contratto distribuito e ABI (che è la versione basata su JSON del contratto compilato) per configurare la cartella, mentre truffle testa ed esso creerà più avanti.
Errori? Ha compilato? Di nuovo, i messaggi di errore possono essere visualizzati sia nella finestra della console di testrpc che in quella di truffle.
Ridistribuisci dopo aver riavviato un nodo! Se fermi il tuo nodo testrpc, ricordati di ridistribuire ogni contratto usando truffle deploy prima di usarli di nuovo. Ogni volta che testrpc si riavvia è vuoto.
Analizzare il contratto
Cominciamo con le variabili in cima allo smart contract:
address public organizer; mapping (address => uint) public registrantsPaid; uint public numRegistrants; uint public quota;
address. La prima variabile è l’indirizzo del portafoglio dell’organizzatore. Questo è impostato quando il costruttore è chiamato nella funzione Conference(). Molti contratti lo chiamano anche ‘owner’.
uint. Un numero intero senza segno. Lo spazio è importante sulla blockchain quindi mantieni le cose più piccole possibili.
public. Significa che può essere chiamato all’esterno del contratto. Un modificatore private significa che può essere chiamato solo all’interno del contratto (o da contratti derivati). Se stai provando a chiamare una variabile da una chiamata web3.js in test assicurati che è pubblica.
Mapping o Array. Prima che Solidity aggiungesse il supporto per gli array, mapping come mapping (address => uint) erano usati. Questo potrebbe essere scritto come indirizzo registrantsPaid[] ma i mapping hanno un’impronta più piccola. Questo mapping sarà usato per memorizzare quanto ogni registrato (rappresentato dal loro indirizzo del portafoglio) ha pagato quindi possono ricevere rimborsi più avanti.
Maggiori informazioni sugli indirizzi. Il tuo nodo client (ad esempio, testrpc o geth in questi esempi) possono avere uno o più account. In testrpc, all’avvio un’array di 10 “Indirizzi iponibili” viene visualizzato:
Il primo, accounts[0], è usato come predefinito per chiamare transazioni se non è specificato nessun altro.
Organizer address vs. Contract address (indirizzo organizzatore vs. indirizzo del contratto). Il tuo contratto distribuito avrà il proprio indirizzo del contratto (diverso dall’indirizzo dell’organizzatore) sulla blockchain. Questo indirizzo è accessibile in un contratto su Solidity utilizzando this, come usato all’interno della funzione refundTicket nel contratto: address myAddress = this;
Suicide, una buona cosa in Solidity. I fondi inviati al contratto sono conservati nel contratto stesso. Nella funzione di distruzione i fondi sono finalmente rilasciati all’organizzatore impostato nel costruttore. suicide(organizer); fa questo. Senza di esso, i fondi possono finire per essere bloccati per sempre (qualcuno su reddit ha perso degli ether in questo modo), quindi assicurati di includere questo metodo se il tuo contratto raccoglie fondi!
Se desideri simulare un altro utente o controparte (ad esmpio simulare un acquisto se se tu sei il venditore), puoi usare un altro indirizzo dall’array con gli indirizzi. Per comprare un ticket come un utente diverso, diciamo accounts[1], usalo nel campo from:
conference.buyTicket({ from: accounts[1], value: some_ticket_price_integer });
Alcune chiamate di funzioni possono essere transazioni.Chiamate di funzioni che cambiano lo stato del contratto (modificano valori, aggiungono records, ecc.) sono transazioni e hanno mittente e valore implicito. Così dentro le parentesi graffe { from: __, value: __ } può essere specificato in una chiamata di funzione web3.js di inviare fondi a una funzione di transazione da un address wallet. Alla fine su Solidity, puoi recuperare questi valori usando msg.sender e msg.value, che sono implicite nelle funzioni di transazione di Solidity:
function buyTicket() public { ... registrantsPaid[msg.sender] = msg.value; ... }
Eventi. Sono totalmente opzionali. Deposit e Send nel contratto sono eventi che possono essere registrati nei logs della Ethereum Virtual Machine. In realtà non fanno nulla, ma sono un buon metodo per tenere traccia che una transazione è avvenuta.
Okay, scriviamo un test per questo smart contract per assicurarci che funzioni.
Scrivere un Test
Nella cartella del tuo progetto test/ directory rinomina il file test example.js in conference.js. Modifica tutte le istanze di “Example” in “Conference”.
contract('Conference', function(accounts) { it("should assert true", function(done) { var conference = Conference.at(Conference.deployed_address); assert.isTrue(true); done(); // stops tests at this point }); });
Durante l'esecuzione del test di truffle dalla directory principale del progetto dovresti vedere il passaggio del test. Nel test sopra, truffle ottiene l'address del contratto sulla blockchain da Conference.deployed_address.
Scriviamo un test per inizializzare una nuova Conference è verifichiamo che le variabili iniziali siano impostate correttamente. Sostituisci il test in conference.js con questo:
contract('Conference', function(accounts) { it("Initial conference settings should match", function(done) { var conference = Conference.at(Conference.deployed_address); // same as previous example up to here Conference.new({ from: accounts[0] }).then( function(conference) { conference.quota.call().then( function(quota) { assert.equal(quota, 500, "Quota doesn't match!"); }).then(function() { return conference.numRegistrants.call(); }).then( function(num) { assert.equal(num, 0, "Registrants should be zero!"); return conference.organizer.call(); }).then( function(organizer) { assert.equal(organizer, accounts[0], "Owner doesn't match!"); done(); // to stop these tests earlier, move this up }).catch(done); }).catch(done); }); });
Costruttore. Conference.new({ from: accounts[0] }) crea un'istanza di Conference chiamando il costruttore del contratto. Dato che accounts[0] è usato di default se non specificato, si può tralasciare quando si chiama il costruttore:
Conference.new({ from: accounts[0] }); // same as Conference.new();
_Promise. _Le promise lo appiattiscono evitando le nidificazioni, consente alle chiamate di restituire in modo asincrono è semplificano la sintassi della scrittura “on success do this” vs. “on failure do that”. Web3.js fornisce callbacks per richieste asincrone (docs) così non devi aspettare che le transazioni completino la roba nel front-end. (Truffle usa un framwork web3.js di promise chiamato Pudding, basato sul framwork Bluebird, il quale ha anche caratteristiche avanzate di promise.)
conference.numRegistrants.call().then( function(num) { assert.equal(num, 0, "Registrants should be zero!"); conference.organizer.call().then( function(organizer) { assert.equal(organizer, accounts[0], "Doesn't match!"); }).then( function(...)) }).then( function(...)) // Because this would soon get hairy...
chiamata. Usa questo per controllare i valori della variabili come conference.quota.call() o con un argomento come call(0) per chiamare una mappatura e ottenere l'indice 0. La documentazione di Solidity dice questa è una “chiamata di messaggio” che è 1. non minata e quindi 2. non deve provenire da un conto/portafoglio (dunque non è firmata con chiavi private di un proprietario di conto). Le transazioni d'altro lato, minate, devono venire da un conto(cioè, firmate), e sono registrate sulla blockchain. Modificare qualsiasi valore in un contratto è una transazione. Controllare il valore di una variabile non lo è. Quindi non dimenticare di aggiungere call() quando chiami variabili! Cose pazzesche possono avvenire. [Inoltre, se stai provando a chiamare una variabile e hai problemi assicurati che sia pubblica.] Le call() possono essere anche usate per chiamare funzioni che non sono transazioni. Se sono designate per essere transazioni e provi a call() (chiamarle), non saranno eseguite come transazioni sulla blockchain.
asserzione. L'asserzione per i test standard JS (se tu digiti 'asserts' al plurale accidentalmente truffle avrà errori e non saprà cosa sta succedendo), consulta i Chai docs per altri tipi di asserzioni ma assert.equal solitamente è ciò che ti serve.
Esegui truffle e testa di nuovo per assicurarti che funziona.
Scrittura di un Test che chiama una funzione del contratto
Ora testiamo che la funzione che cambia la quota funziona. All'interno del contratto(‘Conference’, function(accounts) {…}; corpo del test/conference.js attacca questo test aggiuntivo:
it("Should update quota", function(done) { var c = Conference.at(Conference.deployed_address); Conference.new({from: accounts[0] }).then( function(conference) { conference.quota.call().then( function(quota) { assert.equal(quota, 500, "Quota doesn't match!"); }).then( function() { return **conference.changeQuota(300);** }).then( function(result) { console.log(result); // printed will be a long hex, the transaction hash return conference.quota.call(); }).then( function(quota) { assert.equal(quota, 300, "New quota is not correct!"); done(); }).catch(done); }).catch(done); });
La cosa nuova è la linea che chiama la funzione changeQuota. I console.log sono anche utili per il debugging per stampare il risultato nella console di output di truffle. Aggiungi un console.log per vedere se l'esecuzione avviene fino ad un certo punto. Assicurati anche che la funzione changeQuota nel contratto su Solidity sia public, o non potrai richiamarla:
function changeQuota(uint newquota) **public** { }
Scrivere un Test per una Transazione
Chiamiamo una funzione che richiede fondi da un indirizzo di un portafoglio.
Wei. Ether ha molte denominazioni (qui c'è un utile convertitore) e quello che viene utilizzato solitamente nei contratti è Wei, il più piccolo. Web3.js fornisce metodi convenienti per convertire da Wei/ a ether, come in web3.toWei(.05, ‘ether’). JavaScript ha problemi con numeri molto grandi quindi web3.js usa una Libreria di Grandi Numeri e raccomandano di mantenere le cose in Wei nel tuo codice fin quando gli utenti non vedono questo (docs).
Account Balances (saldi degli account). Web3.js fornisce molti metodi convenienti qui, e un altro usato nel test qui sotto è web3.eth.getBalance(some_address). Ricorda che i fondi inviati al contratto rimarranno al suo interno fin quando il metodo suicide verrà chiamato.
All'interno del contratto(Conference, function(accounts) {…}; attacca questo test aggiuntivo al corpo del test. Nel metodo sottolineato qui sotto, il test ha un secondo utente (accounts[1]) che compra un biglietto per ticketPrice. Poi esso controlla che il saldo del contratto è aumentato dal ticketPrice inviato e il secondo utente è stato aggiunto alla lista dei registrati.
In questo test buyTicket è una Transazione:
it("Should let you buy a ticket", function(done) { var c = Conference.at(Conference.deployed_address); Conference.new({ from: accounts[0] }).then( function(conference) { var ticketPrice = web3.toWei(.05, 'ether'); var initialBalance = web3.eth.getBalance(conference.address).toNumber(); **conference.buyTicket({ from: accounts[1], value: ticketPrice })** .then( function() { var newBalance = web3.eth.getBalance(conference.address).toNumber(); var difference = newBalance - initialBalance; assert.equal(difference, ticketPrice, "Difference should be what was sent"); return conference.numRegistrants.call(); }).then( function(num) { assert.equal(num, 1, "there should be 1 registrant"); return conference.registrantsPaid.call(accounts[1]); }).then(function(amount) { assert.equal(amount.toNumber(), ticketPrice, "Sender's paid but is not listed"); done(); }).catch(done); }).catch(done); });
Le transazioni sono firmate. Diversamente dalle precedenti chiamate di funzione, questa è una transazione che ha inviato fondi, quindi il secondo utente (accounts[1]) sta firmando la transazione chiamando buyTicket() con le sue chiavi. (In geth l'utente deve inserire la password per approvare questa transazione o sbloccare il suo account prima di inviare i fondi.)
toNumber(). A volte i risultati di Solidity devono essere convertiti da esadecimale. Se dovesse essere un numero grande usa web3.toBigNumber(numberOrHexString) perchè Javascript può confondersi con i grandi numeri.
Scrittura di un Test per un contratto che invia una transazione
Infine, per sicurezza, assicurati che il metodo refundTicket funzioni e può essere attivato solo dall'organizzatore. Qui un test per esso:
it("Should issue a refund by owner only", function(done) { var c = Conference.at(Conference.deployed_address); Conference.new({ from: accounts[0] }).then( function(conference) { var ticketPrice = web3.toWei(.05, 'ether'); var initialBalance = web3.eth.getBalance(conference.address).toNumber(); conference.buyTicket({ from: accounts[1], value: ticketPrice }).then( function() { var newBalance = web3.eth.getBalance(conference.address).toNumber(); var difference = newBalance - initialBalance; assert.equal(difference, ticketPrice, "Difference should be what was sent"); // Now try to issue refund as second user - **should fail** return **conference.refundTicket(accounts[1], ticketPrice, {from: accounts[1]});** }).then(function() { var balance = web3.eth.getBalance(conference.address).toNumber(); assert.equal(web3.toBigNumber(balance), ticketPrice, "Balance should be unchanged"); // Now try to issue refund as organizer/owner - **should work** return **conference.refundTicket(accounts[1], ticketPrice, {from: accounts[0]});** }).then( function() { var postRefundBalance = web3.eth.getBalance(conference.address).toNumber(); assert.equal(postRefundBalance, initialBalance, "Balance should be initial balance"); done(); }).catch(done); }).catch(done); });
La corrispondente funzione Solidity per il test sopra è qui:
function **refundTicket**(address recipient, uint amount) public returns (bool success) { if (msg.sender != organizer) { return false; } if (registrantsPaid[recipient] == amount) { address myAddress = this; if (myAddress.balance >= amount) { recipient.send(amount); Refund(recipient, amount); registrantsPaid[recipient] = 0; numRegistrants--; return true; } } return false; }
Invio di ether da un contratto.L'indirizzo myAddress = this; la linea mostra come ottenere l'indirizzo dell'istanza della conference, in modo da poter controllare il saldo nella linea successiva (o usando this.balance). Inoltre il metodo recipient.send(amount) è dove il contratto invia i fondi al destinatario.
Le transazioni non possono restituire risultati a web3.js. Prendi nota! La funzione refundTicket restituisce un valore booleano ma questo non può essere controllato nel tuo test. Questo metodo è una transazione (cioè, qualcosa che modifica valori o invia ether), e il risultato di una transazione in web3.js è un hash di transazione (se stampi il risultato, sarà un lungo oggetto esadecimale strano). Allora perché aggiungere un valore di ritorno alla chiamata di refundTicket? Il valore di ritorno può essere letto in Solidity, come da un altro contratto che richiama refundTicket(). Quindi i contratti in Solidity possono leggere valori di ritorno da una transazione, ma le chiamate di transazioni web3.js non possono. D'altra parte, altri contratti non possono usare Eventi (trattati sotto) in questo modo puoi monitorare le transazioni in web3.js, o controllare se una transazione ha modificato variabili d'istanza in un test successivo di promise usando call().
Maggiori informazioni su sendTransaction(). Quando chiami una transazione come buyTicket() o refundTicket() usando web3.js (che utilizza web3.eth.sendTransaction), la transazione non viene eseguita immediatamente. La transazione è inviata alla rete dai miner, e il codice non viene eseguito finchè uno di questi miner non estrae un blocco e la transazione viene registrata nella blockchain. Quindi per verificare una transazione devi aspettare che venga registrata sulla blockchain e poi sul tuo nodo locale. Con testrpc questo potrebbe sembrare instantaneo perchè è molto veloce ma su di una rete reale sarà più lento.
Eventi. Invece di utilizzare valori di ritorno puoi ascoltare gli eventi in web3.js. Lo smart contract dell'esempio ha questi eventi:
event Deposit(address _from, uint _amount); event Refund(address _to, uint _amount);
E sono innescati in buyTicket() e refundTicket(). Sta diventando evidente a tutti che c'è un problema di scambio ... quindi ogni giorno che non dispongono di mercati liquidi alt alt ... c'è meno possibilità che essi attraversano. Questi potete vederli registrati in output a testrpc quando chiamati. Per ascoltarli, puoi anche aggiungere ascoltatori web3.js. Al momento della scrittura non sono stato in grado di registrare eventi all'interno dei test di truffle ma li ho registraati in un'applicazione:
Conference.new({ from: accounts[0] }).then( function(conference) { var event = conference.allEvents().watch({}, ''); // or use conference.Deposit() or .Refund() event.watch(function (error, result) { if (error) { console.log("Error: " + error); } else { console.log("Event: " + result.event); } });
Filters (Filtri). Invece di correggere tutti gli eventi qui sopra che potrebbero portare ad un sacco di interrogativi, potrebbero essere utilizzati i filters. Ti consentono di iniziare e fermare una visione di loro quando la tua transazione viene processata. Più informazioni sui filters possono essere trovate in Solidity docs.
Nel complesso, l'utilizzo di eventi e filters è economico in termini di gas poi controllare le variabili potrebbe essere utile quando hai bisogno di verificare transazioni sulla rete reale.
Gas. Fino a questo punto non abbiamo avuto bisogno di discutere del gas, di solito non richiede particolari impostazioni con testrpc. Spostandosi su geth e poi alla rete reale verrà fatto. Nelle tue chiamate di transazioni puoi inviare gas implicitamente all'interno degli oggetti {from: __, value: __, gas: __}. Web3.js ha una chiamata per il controllo del prezzo del gas web3.eth.gasPrice (Il risultato di questo non è quanto gas devi inviare con la tua transazione ma il prezzo di 1 unità di gas o step. Per quanto gas devi inviare per ora è probabilmente migliore ad inviare qualcosa vicina al gasLimit in modo da essere sicuri dell'esecuzine. Ad ora la quota massima di gas permessa in un blocco è 3141592. Io utilizzo 3000000). Il compilatore di Solidity dispone anche di una flag dove puoi chiamare da linea di comando per ricevere un riepilogo delle spese di gas per il tuo contratto: solc — gas YourContract.sol. Ecco l'output per Conference.sol:
Creazione di una UI DApp per il tuo contratto
Questa sezione successiva presuppone che tu potresti essere nuovo ad alcune pratiche di sviluppo web.
Tutti i test su truffle scritti qui sopra stanno utilizzando metodi JavaScript riutilizzabili in una front-end UI. Aggiungi la tua UI alla cartella di truffle app/. Durante l'esecuzione del truffle build verrà compilato assieme al materiale di compilazione presente sulla cartella build/. Quando sviluppi usa truffle watch per compilare costantemente qualsiasi cambiamento in app/* a build/*. Poi ricaricare ciò che si trova nella cartella build/ nel tuo browser. (truffle serve può anche eseguire un webserver per te da build/.)
Nella cartella app/ ci sarà qualche boilerplate iniziato per te:
index.html già carica app.js:
Quindi possiamo aggiungere del codice ad app.js.
app.js ha un console.log con “Hello from Truffle!” che verrà mostrato anche nella console di sviluppo del browser. Avvia truffle watch nella cartella principale del progetto e poi apri build/index.html in una finestra del browser, e apri la console di sviluppo del browser (In molti browse come Chrome, click-col tasto destro del mouse » Ispeziona Elemento e passa alla Console tab qui sotto.)
In app.js, aggiungi una funzione window.onload che sarà chiamata al caricamento della pagina. Questo frammento di seguito conferma che web3.js è caricato e mostra tutti gli account disponibili. [Nota: il tuo nodo testrpc deve essere ancora in esecuzione.]
window.onload = function() { var accounts = web3.eth.accounts; console.log(accounts); }
Scopri se stampa un array di account nella console del browser.
Ora puoi semplicemente copiare alcune funzioni da tests/conference.js (rimuovi le asserzioni poichè quelle sono per il test), e l'output che viene restituito dalla console per confermare il suo funzionamento. Ecco un esempio:
window.onload = function() { var accounts = web3.eth.accounts; var c = Conference.at(Conference.deployed_address); Conference.new({ from: accounts[0] }).then( function(conference) { var ticketPrice = web3.toWei(.05, 'ether'); var initialBalance = web3.eth.getBalance(conference.address).toNumber(); console.log("The conference's initial balance is: " + initialBalance); conference.buyTicket({ from: accounts[1], value: ticketPrice }).then( function() { var newBalance = web3.eth.getBalance(conference.address).toNumber(); console.log("After someone bought a ticket it's: " + newBalance); return conference.refundTicket(accounts[1], ticketPrice, {from: accounts[0]});}).then( function() { var balance = web3.eth.getBalance(conference.address).toNumber(); console.log("After a refund it's: " + balance); }); }); };
Il codice qui sopra dovrebbe mandare in output:
Ora utilizzando qualsiasi altro strumento web desideri, jQuery, ReactJS, Meteor, Ember, AngularJS, etc., puoi cominciare a costruire la tua DApp UI in app/ per interagire con uno smart contract Ethereum! Di seguito viene mostrato una semplicissima UI basata su jQuery-based UI come esempio.
Qui c'è il index.html. E qui si trova app.js.
Ora che sto interagendo con uno smart contract in una UI mi sto rendendo conto che sarebbe meglio aggiungere controlli per essere sicuri che lo stesso utente non si possa registrare due volte. Inoltre poichè questo è in esecuzione su testrpc, che è veramente veloce, sarebbe meglio passare a geth e assicurarsi che le transazioni siano ancora reattive agli utenti. Altrimenti la UI dovrebbe avere alcuni messaggi di caricamento e pulsanti disabilitati durante le transazioni sono state processate se stanno per prendere un pò di tempo.
Provando geth. Se stai utilizzando geth, questo comando ha funzionato con me (geth v1.2.3):
geth --rpc --rpcaddr="0.0.0.0" --rpccorsdomain="*" --mine --unlock='0 1' --verbosity=5 --maxpeers=0 --minerthreads='4' --networkid '12345' --genesis test-genesis.json
Questo sblocca due account, 0 e 1. Note: 1. Potrebbe essere necessario inserire le password ad entrambi gli account dopo che la console di geth si avvia. 2. Inoltre, sarà necessario un file test-genesis.json con entrambi gli account ben finanziati sotto ‘alloc’ nel test-genesis.json.) 3. Infine, per geth potresti avere la necessità di aggiungere gas quando chiami il costruttore:
Conference.new({from: accounts[0], gas: 3141592})
Poi rifare tutto le cose truffle deploy, truffle build.
Codici per questo tutorial. Di nuovo, tutti i codici presentati in questo tutorial sono in questa repo.
Auto-generazione di UI da contratti. SilentCicero ha anche costruito uno strumento chiamato DApp Builder per auto-generare da un contratto Solidity HTML, jQuery e chiamate web3.js che puoi modificare. Anche questo sta iniziando ad essere un tema comune di altri smart contract e stanno uscendo anche altri strumenti di sviluppo.
Ok il tutorial è terminato! Quest'ultima parte è stata una carrelata di una serie di strumenti, principalmente di Truffle e testrpc. Anche all'interno di ConsenSys, diversi sviluppatori usano diversi strumenti e framework. Potresti trovare questi strumenti là fuori che sono più adatti per te, e alcune cose potrebbero cambiare in pochi mesi (Souptacular mantiene un gitbook aggiornato di risorse e note). Ma questo flusso di lavoro mi ha aiutato ad iniziare ad imparare a costruire Dapp.
(⊙ω⊙) wonk wonk
Grazie a Joseph Chow per un sacco di bozze e suggerimenti, come anche a Christian Lundkvist, Daniel Novy, Jim Berry, Peter Borah and Tim Coulter per correzioni, e Tim Coulter, Nchinda Nchinda and Mike Goldin per aver aiutato con dei diagrammi di passi di DApp Front-end.
Tutorial tradotto da Luigi21 e alex9919
Se il tuotorial è stato utile e di tuo gradimento saremo lieti di ricevere un piccolo supporto per il nostro sforzo:
Indirizzo Ethereum: 0x47045D1eE924AD6Ff890Ce8B52D6c1F08Ad42235
Indirizzo Litecoin: LZhpQiYdA2d3J6wT2JTJovQEZKWvLSPsZP
Indirizzo Bitcoin: 1cnPe4DP5eKEEzkD6MLLPe4J3uc8ki84b
good info friend resteemed .
nice
Gw8PKHviWnlLdAdSXIJ0
Ottimo articolo, mi ha aiutato a capirne un po' di più sui contratti intelligenti di Ethereum!
Complimenti, ottimo lavoro!
Good info for ETH