Come creare un'API Node.js per la blockchain di Ethereum
Pubblicato: 2022-03-10La tecnologia blockchain è in aumento negli ultimi dieci anni e ha dato vita a un buon numero di prodotti e piattaforme come Chainalysis (tecnologia finanziaria), Burstiq (tecnologia sanitaria), Filament (IoT), Opus (streaming musicale) e Ocular (sicurezza informatica).
Da questi esempi, possiamo vedere che la blockchain attraversa molti prodotti e casi d'uso, rendendola molto essenziale e utile. Nella fintech (tecnologia finanziaria), viene utilizzata come registro decentralizzato per la sicurezza e la trasparenza in luoghi come Chain, Chainalysis ed è utile anche nella tecnologia sanitaria per la sicurezza dei dati sanitari sensibili in Burstiq e Robomed, per non dimenticare la tecnologia multimediale come Opus e Audius che utilizzano anche blockchain per la trasparenza delle royalties e quindi ottengono le royalties complete.
Ocular utilizza la sicurezza fornita con blockchain per la gestione dell'identità per i sistemi biometrici, mentre Filament utilizza registri blockchain per comunicazioni crittografate in tempo reale. Questo dimostra quanto la blockchain sia diventata essenziale per noi migliorando le nostre vite. Ma cos'è esattamente una blockchain?
Una blockchain è un database condiviso su una rete di computer. Una volta che un record è stato aggiunto alla catena, è abbastanza difficile cambiarlo. Per garantire che tutte le copie del database siano uguali, la rete effettua controlli costanti.
Allora perché abbiamo bisogno della blockchain? Blockchain è un modo sicuro per registrare le attività e mantenere aggiornati i dati mantenendo un registro della sua cronologia rispetto ai record o ai database tradizionali in cui sono molto possibili hack, errori e tempi di inattività. I dati non possono essere danneggiati da nessuno o eliminati accidentalmente e puoi beneficiare sia di una traccia storica dei dati che di un record immediatamente aggiornato che non può essere cancellato o diventare inaccessibile a causa dei tempi di inattività di un server.
Poiché l'intera blockchain è duplicata su molti computer, qualsiasi utente può visualizzare l'intera blockchain. Le transazioni o le registrazioni sono elaborate non da un unico amministratore centrale, ma da una rete di utenti che lavorano per verificare i dati e ottenere un consenso.
Le applicazioni che utilizzano blockchain sono chiamate dApp (Applicazioni Decentralizzate). Guardandoci intorno oggi, troveremo principalmente app decentralizzate nel fintech, ma la blockchain va oltre la finanza decentralizzata. Abbiamo piattaforme sanitarie, piattaforme di streaming/condivisione di musica, piattaforme di e-commerce, piattaforme di sicurezza informatica e IOT che si muovono verso applicazioni decentralizzate (dApp) come sopra citato.
Quindi, quando avrebbe senso considerare l'utilizzo della blockchain per le nostre applicazioni, piuttosto che un database o un record standard?
Applicazioni comuni della blockchain
- Gestione e protezione delle relazioni digitali
Ogni volta che vuoi mantenere un registro trasparente e a lungo termine delle risorse (ad esempio, per registrare i diritti di proprietà o appartamento), la blockchain potrebbe essere la soluzione ideale. Gli "smart contract" di Ethereum, in particolare, sono ottimi per facilitare le relazioni digitali. Con uno smart contract, i pagamenti automatizzati possono essere rilasciati quando le parti in una transazione concordano che le loro condizioni sono state soddisfatte. - Eliminazione di intermediari/gatekeeper
Ad esempio, la maggior parte dei provider attualmente deve interagire con gli ospiti tramite una piattaforma di aggregazione centralizzata, come Airbnb o Uber (che, a sua volta, riduce ogni transazione). Blockchain potrebbe cambiare tutto questo.
Ad esempio, TUI è così convinta della potenza della blockchain che sta sperimentando modi per connettere direttamente albergatori e clienti. In questo modo, possono effettuare transazioni tramite blockchain in modo facile, sicuro e coerente, anziché tramite una piattaforma di prenotazione centrale. - Registra transazioni sicure tra partner per garantire la fiducia
Un database tradizionale può essere utile per registrare semplici transazioni tra due parti, ma quando le cose si complicano, la blockchain può aiutare a ridurre i colli di bottiglia e semplificare le relazioni. Inoltre, la maggiore sicurezza di un sistema decentralizzato rende la blockchain ideale per le transazioni in generale.
Un esempio è l'Università di Melbourne che ha iniziato a memorizzare i suoi record in blockchain. Il caso d'uso più promettente per blockchain nell'istruzione superiore è trasformare la "tenuta dei registri" di lauree, certificati e diplomi. Ciò consente di risparmiare un sacco di costi da server dedicati per l'archiviazione o i record. - Conservazione dei registri delle azioni passate per le applicazioni in cui i dati sono in costante flusso
Blockchain è un modo migliore e più sicuro per registrare l'attività e mantenere i dati aggiornati mantenendo un registro della sua cronologia. I dati non possono essere danneggiati da nessuno o eliminati accidentalmente e puoi beneficiare sia di una traccia storica dei dati, sia di un record immediatamente aggiornato. Un esempio di un buon caso d'uso è la blockchain nell'e-commerce, sia blockchain che e-commerce coinvolgono transazioni.
Blockchain rende queste transazioni più sicure e veloci mentre le attività di e-commerce fanno affidamento su di esse. La tecnologia Blockchain consente agli utenti di condividere e archiviare in modo sicuro le risorse digitali sia automaticamente che manualmente. Questa tecnologia è in grado di gestire le attività degli utenti come l'elaborazione dei pagamenti, le ricerche di prodotti, gli acquisti di prodotti e l'assistenza clienti. Riduce anche le spese per la gestione dell'inventario e l'elaborazione dei pagamenti. - La decentralizzazione rende possibile l'utilizzo ovunque
A differenza di prima, dove dobbiamo limitarci a una particolare regione a causa di vari motivi come le politiche di cambio valuta, le limitazioni dei gateway di pagamento rendono difficile l'accesso alle risorse finanziarie di molti paesi non nella tua regione o continente. Con l'ascesa e il potere del decentramento della blockchain o del sistema peer-to-peer, diventa più facile lavorare con altri paesi.
Ad esempio, un negozio di e-commerce in Europa può avere consumatori in Africa e non richiedere un intermediario per elaborare le loro richieste di pagamento. Inoltre, queste tecnologie stanno aprendo le porte ai rivenditori online per utilizzare i mercati di consumo in paesi lontani con bitcoin, ovvero una criptovaluta. - Blockhain è neutrale dal punto di vista tecnologico
Blockchain funziona con qualsiasi stack tecnologico utilizzato da uno sviluppatore. Non devi imparare Node come sviluppatore Python per usare blockchain o imparare Golang. Questo rende la blockchain molto facile da usare.
Possiamo effettivamente usarlo direttamente con le nostre app front-end in Vue/React con la blockchain che funge da unico database per semplici attività e casi d'uso come caricare dati o ottenere hash per visualizzare record per i nostri utenti o creare giochi front-end come il casinò giochi e giochi di scommesse (in cui è necessaria un'elevata fiducia). Inoltre, con la potenza di web3, possiamo archiviare i dati direttamente nella catena.
Ora, abbiamo visto un certo numero di vantaggi nell'usare blockchain, ma quando non dovremmo preoccuparci affatto di usare una blockchain?
Svantaggi della Blockchain
- Velocità ridotta per le transazioni digitali
Le blockchain richiedono enormi quantità di potenza di calcolo, che tende a ridurre la velocità delle transazioni digitali, sebbene ci siano soluzioni alternative per cui è consigliabile utilizzare database centralizzati quando è necessario eseguire transazioni ad alta velocità in millisecondi. - Immutabilità dei dati
L'immutabilità dei dati è sempre stata uno dei maggiori svantaggi della blockchain. È chiaro che più sistemi ne traggono vantaggio, inclusa la catena di approvvigionamento, i sistemi finanziari e così via. Tuttavia, soffre del fatto che una volta che i dati sono stati scritti, non possono essere rimossi. Ogni persona sulla terra ha diritto alla privacy. Tuttavia, se la stessa persona utilizza una piattaforma digitale che gira su tecnologia blockchain, allora non sarà in grado di rimuovere la sua traccia dal sistema quando non lo vuole lì. In parole semplici, non c'è modo che possa rimuovere la sua traccia, lasciando a pezzi i diritti alla privacy. - Richiede Competenza Conoscenza
Implementare e gestire un progetto blockchain è difficile. Richiede una conoscenza approfondita per passare attraverso l'intero processo. Questo è il motivo per cui è difficile imbattersi in specialisti o esperti di blockchain perché ci vuole molto tempo e sforzi per formare un esperto di blockchain. Quindi questo articolo è un buon punto di partenza e una buona guida se hai già iniziato. - Interoperabilità
Molteplici reti blockchain che lavorano duramente per risolvere il problema del libro mastro distribuito rendono difficile collegarle o integrarle tra loro. Ciò rende difficile la comunicazione tra diverse catene. - Integrazione di applicazioni legacy
Molte aziende e applicazioni utilizzano ancora sistemi e architetture legacy; l'adozione della tecnologia blockchain richiede una revisione completa di questi sistemi che devo dire non è fattibile per molti di loro.
Blockchain è ancora in evoluzione e maturando continuamente, quindi non sorprenderti se questi svantaggi menzionati oggi vengono trasformati in professionisti in seguito. Bitcoin, che è una criptovaluta, è un esempio popolare di blockchain, una blockchain popolare che è in aumento oltre alla criptovaluta bitcoin è la blockchain di Ethereum. Bitcoin si concentra sulle criptovalute mentre Ethereum si concentra maggiormente sugli smart contract che sono stati la principale forza trainante per le nuove piattaforme tecnologiche.
Letture consigliate : Bitcoin vs. Ethereum: qual è la differenza?
Iniziamo a costruire la nostra API
Con una solida conoscenza della blockchain, ora diamo un'occhiata a come costruire una blockchain di Ethereum e integrarla in un'API standard in Node.js. L'obiettivo finale è ottenere una buona comprensione di come vengono costruite le dApp e le piattaforme Blockchain.
La maggior parte delle dApp ha un'architettura e una struttura simili. Fondamentalmente, abbiamo un utente che interagisce con il frontend di dApp, Web o mobile, che quindi interagisce con le API di backend. Il backend, poi, su richiesta interagisce con lo/i contratto/i intelligente/i o blockchain tramite nodi pubblici; questi o eseguono applicazioni Node.js o il backend utilizza blockchain eseguendo direttamente il software Node.js. Ci sono ancora così tante cose tra questi processi, dalla scelta di creare un'applicazione completamente decentralizzata o un'applicazione semi-decentralizzata alla scelta di cosa dovrebbe essere decentralizzato e come archiviare in modo sicuro le chiavi private.
Letture consigliate : Architettura delle applicazioni decentralizzate: back-end, sicurezza e design pattern
Cose che dovremmo sapere prima
Per questo tutorial, proveremo a costruire il back-end di un'app di negozio di musica decentralizzata che utilizza la potenza della blockchain di Ethereum per archiviare musica e condividerla per il download o lo streaming.
La struttura di base dell'applicazione che stiamo cercando di creare è composta da tre parti:
- Autenticazione , che viene eseguita tramite e-mail; ovviamente dobbiamo aggiungere una password crittografata all'app.
- L' archiviazione dei dati , con i dati musicali, viene prima archiviata in ipfs e l'indirizzo di archiviazione viene archiviato nella blockchain per il recupero.
- Recupero , con qualsiasi utente autenticato che può accedere ai dati memorizzati sulla nostra piattaforma e utilizzarli.
Lo costruiremo con Node.js, ma puoi anche creare con Python o qualsiasi altro linguaggio di programmazione. Vedremo anche come archiviare i dati multimediali in IPFS, ottenere l'indirizzo e scrivere le funzioni in cui archiviare questo indirizzo e recuperare questo indirizzo da una blockchain con il linguaggio di programmazione Solidity.
Ecco alcuni strumenti che dovremmo avere a nostra disposizione per costruire o lavorare con Ethereum e Node.js.
- Node.js
Il primo requisito è un'applicazione Node. Stiamo cercando di creare un'app Node.js, quindi abbiamo bisogno di un compilatore. Assicurati di aver installato Node.js e scarica l'ultimo binario di supporto a lungo termine ( LTS ). - Suite al tartufo
Truffle è un ambiente di sviluppo e test di contratti, nonché una pipeline di risorse per la blockchain di Ethereum. Fornisce un ambiente per la compilazione, il pipelining e l'esecuzione di script. Quando si parla di sviluppo di blockchain, il tartufo è una tappa popolare in cui andare. Scopri Truffle Suite su Truffle Suite: Sweet Tools for Smart Contracts. - Ganache CLI
Un altro strumento che funziona bene insieme al tartufo è Ganache-CLI. È costruito e mantenuto dal team di Truffle Suite. Dopo la creazione e la compilazione, è necessario un emulatore per sviluppare ed eseguire app blockchain e quindi distribuire contratti intelligenti da utilizzare. Ganache semplifica la distribuzione di un contratto in un emulatore senza utilizzare denaro effettivo per costi di transazione, account riciclabili e molto altro. Ulteriori informazioni su Ganache CLI su Ganache CLI e Ganache. - Remixa
Remix è come un'alternativa a Ganache, ma include anche una GUI per aiutare a navigare nella distribuzione e nel test degli smart contract di Ethereum. Puoi saperne di più su Remix — Ethereum IDE & community. Tutto quello che devi fare è visitare https://remix.ethereum.org e utilizzare la GUI per scrivere e distribuire smart contract. - Web3
Web3 è una raccolta di librerie che ti permette di interagire con un nodo Ethereum. Questi potrebbero essere nodi locali o remoti del contratto tramite HTTP, IPC o Web Socket. Introduzione a Web3.js · Ethereum Blockchain Developer Crash Course è un buon posto per imparare qualcosa su Web3. - IPFS
Un protocollo principale utilizzato nella creazione di dApp. L' InterPlanetary File System (IPFS) è un protocollo e una rete peer-to-peer per l'archiviazione e la condivisione di dati in un file system distribuito. IPFS alimenta il Web distribuito spiega di più su IPFS e su come viene solitamente utilizzato.
Creazione di un'API di back-end da zero
Quindi prima dobbiamo creare un backend da utilizzare e stiamo usando Node.js. Quando vogliamo creare una nuova API Node.js, la prima cosa che faremo è inizializzare un pacchetto npm. Come probabilmente saprai, npm sta per Node Package Manager e viene fornito preconfezionato con il binario Node.js. Quindi creiamo una nuova cartella e la chiamiamo "blockchain-music" . Apriamo il terminale in quella directory della cartella, quindi eseguiamo il seguente comando:
$ npm init -y && touch server.js routes.js
Questo avvia il progetto con un file package.json e risponde sì a tutte le richieste. Quindi creiamo anche un file server.js e un file route.js per scrivere le funzioni di routes
nell'API.
Dopo tutto questo, dovrai installare i pacchetti di cui abbiamo bisogno per rendere la nostra build facile e diretta. Questo processo è continuo, cioè puoi installare un pacchetto in qualsiasi momento durante lo sviluppo del tuo progetto.
Installiamo quelli più importanti di cui abbiamo bisogno in questo momento:
- Express.js
- @tartufo/contratto
- Tartufo.js
- web3.js
- dotenv
-
short-id
- MongoDB
- nodomon
Dovrai anche installare Truffle.js a livello globale , in modo da poterlo utilizzare ovunque nel tuo ambiente locale. Se vuoi installarli tutti in una volta, esegui il seguente codice nel tuo Terminale:
$ npm install nodemon truffle-contract dotenv mongodb shortid express web3 --save && npm install truffle -g
Il flag --save
serve a salvare il nome del pacchetto nel file package.json . Il flag -g
serve per memorizzare questo particolare pacchetto a livello globale, in modo che possiamo usarlo in qualsiasi progetto su cui lavoreremo.
Quindi creiamo un file .env in cui possiamo memorizzare il nostro URI segreto del database MongoDB per l'uso. Lo facciamo eseguendo touch.env nel Terminale. Se non hai ancora un account di database con MongoDB, inizia prima con la pagina MongoDB.
Il pacchetto dotenv esporta la nostra variabile memorizzata nell'ambiente di processo Node.js. Assicurati di non eseguire il push del file .env quando esegui il push in repository pubblici per evitare la divulgazione di password e dati privati.
Successivamente, dobbiamo aggiungere gli script per le fasi di compilazione e sviluppo del nostro progetto nel nostro file package.json . Attualmente il nostro package.json si presenta così:
{ "name": "test", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.17.1", "socket.io": "^2.3.0", "truffle-contract": "^4.0.31", "web3": "^1.3.0" } }
Aggiungeremo quindi uno script di avvio al file package.json per utilizzare il server nodemon in modo che ogni volta che apportiamo modifiche riavvii il server stesso e uno script di compilazione che utilizzi direttamente il server del nodo, potrebbe assomigliare a questo:
{ "name": "test", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "nodemon server.js", "build": "node server.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.17.1", "socket.io": "^2.3.0", "truffle-contract": "^4.0.31", "web3": "^1.3.0" } }
Successivamente, dobbiamo inizializzare Truffle per l'uso nel nostro contratto intelligente utilizzando il pacchetto Truffle che abbiamo installato a livello globale in precedenza. Nella stessa cartella dei nostri progetti, eseguiamo il seguente comando nel nostro terminale:
$ truffle init
Quindi possiamo iniziare a scrivere il nostro codice nel nostro file server.js . Ancora una volta, stiamo cercando di creare una semplice app per store di musica decentralizzata, in cui i clienti possono caricare musica affinché ogni altro utente possa accedervi e ascoltarla.
Il nostro server.js dovrebbe essere pulito per un facile accoppiamento e disaccoppiamento dei componenti, quindi i percorsi e altre funzionalità verranno inseriti in altri file come il route.js . Il nostro esempio server.js potrebbe essere:
require('dotenv').config(); const express= require('express') const app =express() const routes = require('./routes') const Web3 = require('web3'); const mongodb = require('mongodb').MongoClient const contract = require('truffle-contract'); app.use(express.json()) mongodb.connect(process.env.DB,{ useUnifiedTopology: true },(err,client)=>{ const db =client.db('Cluster0') //home routes(app,db) app.listen(process.env.PORT || 8082, () => { console.log('listening on port 8082'); }) })
Fondamentalmente, sopra importiamo le librerie di cui abbiamo bisogno con require
, quindi aggiungiamo un middleware che consenta l'uso di JSON nella nostra API utilizzando app.use
, quindi ci connettiamo al nostro database MongoDB e otteniamo l'accesso al database, quindi specifichiamo quale cluster di database stiamo cercando di accedere (per questo tutorial è "Cluster0" ). Dopodiché, chiamiamo la funzione e la importiamo dal file delle rotte . Infine, ascoltiamo eventuali tentativi di connessione sulla porta 8082
.
Questo file server.js è solo un barebone per avviare l'applicazione. Nota che abbiamo importato rotte.js . Questo file conterrà gli endpoint del percorso per la nostra API. Abbiamo anche importato i pacchetti che dovevamo utilizzare nel file server.js e li abbiamo inizializzati.
Creeremo cinque endpoint per il consumo degli utenti:
- Endpoint di registrazione per la registrazione degli utenti solo via e-mail. Idealmente, lo faremmo con un'e-mail e una password, ma poiché vogliamo solo identificare ogni utente, non ci avventureremo nella sicurezza delle password e nell'hashing per motivi di brevità di questo tutorial.
POST /register Requirements: email
- Endpoint di accesso per gli utenti tramite e-mail.
POST /login Requirements: email
- Carica l'endpoint per gli utenti: l'API che ottiene i dati del file musicale. Il frontend convertirà i file MP3/WAV in un buffer audio e invierà quel buffer all'API.
POST /upload Requirements: name, title of music, music file buffer or URL stored
- Endpoint di accesso che fornirà i dati del buffer musicale a qualsiasi utente registrato che lo richieda e registrerà chi vi ha effettuato l'accesso.
GET /access/{email}/{id} Requirements: email, id
- Vogliamo anche fornire l'accesso all'intera libreria musicale e restituire i risultati a un utente registrato.
GET /access/{email} Requirements: email
Quindi scriviamo le nostre funzioni di percorso nel nostro file rotte.js . Utilizziamo le funzionalità di archiviazione e recupero del database, quindi ci assicuriamo di esportare la funzione di percorso alla fine del file per rendere possibile l'importazione in un altro file o cartella.
const shortid = require('short-id') function routes(app, db){ app.post('/register', (req,res)=>{ let email = req.body.email let idd = shortid.generate() if(email){ db.findOne({email}, (err, doc)=>{ if(doc){ res.status(400).json({"status":"Failed", "reason":"Already registered"}) }else{ db.insertOne({email}) res.json({"status":"success","id":idd}) } }) }else{ res.status(400).json({"status":"Failed", "reason":"wrong input"}) } }) app.post('/login', (req,res)=>{ let email = req.body.email if(email){ db.findOne({email}, (err, doc)=>{ if(doc){ res.json({"status":"success","id":doc.id}) }else{ res.status(400).json({"status":"Failed", "reason":"Not recognised"}) } }) }else{ res.status(400).json({"status":"Failed", "reason":"wrong input"}) } }) app.post('/upload', (req,res)=>{ let buffer = req.body.buffer let name = req.body.name let title = req.body.title if(buffer && title){ }else{ res.status(400).json({"status":"Failed", "reason":"wrong input"}) } }) app.get('/access/:email/:id', (req,res)=>{ if(req.params.id && req.params.email){ }else{ res.status(400).json({"status":"Failed", "reason":"wrong input"}) } }) } module.exports = routes
All'interno di questa funzione route
, abbiamo molte altre funzioni chiamate all'interno dei parametri app
e db
. Queste sono le funzioni dell'endpoint API che consentono agli utenti di specificare un endpoint nell'URL. Infine scegliamo una di queste funzioni da eseguire e forniamo risultati come risposta alle richieste in arrivo.
Abbiamo quattro principali funzioni di endpoint:
-
get
: per leggere le operazioni di registrazione -
post
: per la creazione di operazioni di registrazione -
put
: per l'aggiornamento delle operazioni di registrazione -
delete
: per eliminare le operazioni di registrazione
In questa funzione di routes
, abbiamo utilizzato le operazioni get
e post
. Usiamo post
per le operazioni di registrazione, accesso e caricamento e per get
alle operazioni sui dati. Per un po' più di spiegazione in merito, puoi consultare l'articolo di Jamie Corkhill su "Come iniziare con Node: un'introduzione alle API, HTTP ed ES6+ JavaScript".
Nel codice sopra, possiamo anche vedere alcune operazioni di database come nel percorso del registro . Abbiamo archiviato l'e-mail di un nuovo utente con db.createa
e controllato l'e-mail nella funzione di accesso con db.findOne
. Ora, prima di poter fare tutto, dobbiamo nominare una raccolta o una tabella con il metodo db.collection
. Questo è esattamente ciò di cui parleremo dopo.
Nota : per ulteriori informazioni sulle operazioni del database in MongoDB, consultare la documentazione sui metodi della shell mongo.
Costruire un semplice contratto intelligente Blockchain con solidità
Ora scriveremo un contratto Blockchain in Solidity (questa è la lingua in cui sono scritti i contratti intelligenti) per archiviare semplicemente i nostri dati e recuperarli quando ne abbiamo bisogno. I dati che vogliamo archiviare sono i dati del file musicale, il che significa che dobbiamo caricare la musica su IPFS, quindi memorizzare l'indirizzo del buffer in una blockchain.
Innanzitutto, creiamo un nuovo file nella cartella del contratto e lo chiamiamo Inbox.sol . Per scrivere uno smart contract è utile avere una buona conoscenza di Solidity, ma non è difficile in quanto è simile a JavaScript.
Nota : se sei interessato a saperne di più su Solidity, ho aggiunto alcune risorse in fondo all'articolo per iniziare.
pragma solidity ^0.5.0; contract Inbox{ //Structure mapping (string=>string) public ipfsInbox; //Events event ipfsSent(string _ipfsHash, string _address); event inboxResponse(string response); //Modifiers modifier notFull (string memory _string) { bytes memory stringTest = bytes(_string); require(stringTest.length==0); _; } // An empty constructor that creates an instance of the conteact constructor() public{} //takes in receiver's address and IPFS hash. Places the IPFSadress in the receiver's inbox function sendIPFS(string memory _address, string memory _ipfsHash) notFull(ipfsInbox[_address]) public{ ipfsInbox[_address] = _ipfsHash; emit ipfsSent(_ipfsHash, _address); } //retrieves hash function getHash(string memory _address) public view returns(string memory) { string memory ipfs_hash=ipfsInbox[_address]; //emit inboxResponse(ipfs_hash); return ipfs_hash; } }
Nel nostro contratto abbiamo due funzioni principali: le funzioni sendIPFS
e getHash
. Prima di parlare delle funzioni, possiamo vedere che abbiamo dovuto definire prima un contratto chiamato Inbox
. All'interno di questa classe, abbiamo le strutture utilizzate nell'oggetto ipfsInbox
(prima gli eventi, poi i modificatori).
Dopo aver definito le strutture e gli eventi, dobbiamo inizializzare il contratto chiamando la funzione di constructor
. Quindi abbiamo definito tre funzioni. (La funzione checkInbox
è stata utilizzata nel test per i risultati del test.)
sendIPFS
è il punto in cui l'utente inserisce l'identificatore e l'indirizzo hash dopo di che viene archiviato sulla blockchain. La funzione getHash
recupera l'indirizzo hash quando gli viene fornito l'identificatore. Ancora una volta, la logica alla base di ciò è che alla fine vogliamo archiviare la musica in IPFS. Per testare come funziona, puoi accedere a un IDE Remix, copiare, incollare e testare il tuo contratto, nonché eseguire il debug di eventuali errori ed eseguire di nuovo (si spera che non sia necessario!).
Dopo aver verificato che il nostro codice funzioni correttamente nel remix, passiamo alla compilazione in locale con la suite Truffle. Ma prima, dobbiamo apportare alcune modifiche ai nostri file e configurare il nostro emulatore usando ganache-cli
:
Innanzitutto, installiamo ganache-cli
. Nella stessa directory, esegui il seguente comando nel tuo terminale:
$ npm install ganache-cli -g
Quindi apriamo un altro Terminale ed eseguiamo un altro comando nella stessa cartella:
$ ganache-cli
Questo avvia l'emulatore per il nostro contratto blockchain per connettersi e funzionare. Riduci a icona il Terminale e continua con l'altro Terminale che stavi utilizzando.
Ora vai al file tartufo.js se stai usando un sistema operativo Linux/Mac o tartufo-config.js in Windows e modifica questo file in modo che assomigli a questo:
const path = require("path"); module.exports = { // to customize your Truffle configuration! contracts_build_directory: path.join(__dirname, "/build"), networks: { development: { host: "127.0.0.1", port: 8545, network_id: "*" //Match any network id } } };
Fondamentalmente quello che abbiamo fatto è aggiungere il percorso della cartella build in cui lo smart contract viene convertito in file JSON. Poi abbiamo anche specificato la rete che Truffle dovrebbe utilizzare per la migrazione.
Quindi, sempre nella cartella delle migrazioni , crea un nuovo file chiamato 2_migrate_inbox.js e aggiungi il seguente codice all'interno dei file:
var IPFSInbox = artifacts.require("./Inbox.sol"); module.exports = function(deployer) { deployer.deploy(IPFSInbox); };
Lo abbiamo fatto per ottenere il file del contratto e distribuirlo automaticamente su un JSON, utilizzando la funzione di deployer
durante la migrazione di Truffle.
Dopo le modifiche di cui sopra eseguiamo:
$ truffle compile
Dovremmo vedere alcuni messaggi alla fine che mostrano la compilazione riuscita, come ad esempio:
> Compiled successfully using: - solc: 0.5.16+commit.9c3226ce.Emscripten.clang
Successivamente, migriamo il nostro contratto eseguendo:
$ truffle migrate
Dopo aver migrato con successo i nostri contratti, alla fine dovremmo avere qualcosa del genere:
Summary ======= > Total deployments: 1 > Final cost: 0.00973432 ETH
E abbiamo quasi finito! Abbiamo creato la nostra API con Node.js e abbiamo anche impostato e creato il nostro contratto intelligente.
Dovremmo anche scrivere test per il nostro contratto per testare il comportamento del nostro contratto e assicurarci che sia il comportamento desiderato. I test vengono generalmente scritti e inseriti nella cartella del test
. Un esempio di test scritto in un file denominato InboxTest.js creato nella cartella test è:
const IPFSInbox = artifacts.require("./Inbox.sol") contract("IPFSInbox", accounts =>{ it("emit event when you send a ipfs address", async()=>{ //ait for the contract const ipfsInbox = await IPFSInbox.deployed() //set a variable to false and get event listener eventEmitted = false //var event = () await ipfsInbox.ipfsSent((err,res)=>{ eventEmitted=true }) //call the contract function which sends the ipfs address await ipfsInbox.sendIPFS(accounts[1], "sampleAddress", {from: accounts[0]}) assert.equal(eventEmitted, true, "sending an IPFS request does not emit an event") }) })
Quindi eseguiamo il nostro test eseguendo quanto segue:
$ truffle test
Verifica il nostro contratto con i file nella cartella di test e mostra il numero di test superati e falliti. Per questo tutorial, dovremmo ottenere:
$ truffle test Using network 'development'. Compiling your contracts... =========================== > Compiling .\contracts\Inbox.sol > Artifacts written to C:\Users\Ademola\AppData\Local\Temp\test--2508-n0vZ513BXz4N > Compiled successfully using: — solc: 0.5.16+commit.9c3226ce.Emscripten.clang Contract: IPFSInbox √ emit event when you send an ipfs address (373ms) 1 passing (612ms)
Integrazione dello Smart Contract con l'API di backend tramite Web3
La maggior parte delle volte quando vedi tutorial, vedi app decentralizzate create per integrare il frontend direttamente nella blockchain. Ma ci sono momenti in cui è necessaria anche l'integrazione con il back-end, ad esempio quando si utilizzano API e servizi di back-end di terze parti o quando si utilizza blockchain per creare un CMS.
L'uso di Web3 è molto importante per questa causa, poiché ci aiuta ad accedere ai nodi Ethereum remoti o locali e ad utilizzarli nelle nostre applicazioni. Prima di andare avanti, discuteremo dei nodi Ethereum locali e remoti. I nodi locali sono i nodi distribuiti sul nostro sistema con emulatori come ganache-cli
ma un nodo remoto è uno distribuito su faucet/piattaforme online come ropsten o rinkeby . Per approfondire, puoi seguire un tutorial su come distribuire su ropsten una guida di 5 minuti per distribuire smart contract con Truffle e Ropsten oppure puoi utilizzare il provider di wallet di tartufi e distribuire tramite un modo più semplice per distribuire i tuoi smart contract.
Stiamo usando ganache-cli
in questo tutorial, ma se stavamo distribuendo su ropsten, avremmo dovuto copiare o archiviare il nostro indirizzo di contratto da qualche parte come nel nostro file .env, quindi passare ad aggiornare il file server.js , importare web3, importare il contratto migrato e configurare un'istanza Web3.
require('dotenv').config(); const express= require('express') const app =express() const routes = require('./routes') const Web3 = require('web3'); const mongodb = require('mongodb').MongoClient const contract = require('truffle-contract'); const artifacts = require('./build/Inbox.json'); app.use(express.json()) if (typeof web3 !== 'undefined') { var web3 = new Web3(web3.currentProvider) } else { var web3 = new Web3(new Web3.providers.HttpProvider('https://localhost:8545')) } const LMS = contract(artifacts) LMS.setProvider(web3.currentProvider) mongodb.connect(process.env.DB,{ useUnifiedTopology: true }, async(err,client)=>{ const db =client.db('Cluster0') const accounts = await web3.eth.getAccounts(); const lms = await LMS.deployed(); //const lms = LMS.at(contract_address) for remote nodes deployed on ropsten or rinkeby routes(app,db, lms, accounts) app.listen(process.env.PORT || 8082, () => { console.log('listening on port '+ (process.env.PORT || 8082)); }) })
Nel file server.js , controlliamo se l'istanza web3 è già inizializzata. In caso contrario, lo inizializziamo sulla porta di rete che abbiamo definito in precedenza ( 8545
). Quindi costruiamo un contratto basato sul file JSON migrato e sul pacchetto truffle-contract
e impostiamo il provider del contratto sul provider dell'istanza Web3 che deve essere stato inizializzato a questo punto.
Quindi otteniamo account da web3.eth.getAccounts
. Per la fase di sviluppo, chiamiamo la funzione deployed nella nostra classe di contratto che chiede ganache-cli
, che è ancora in esecuzione, di fornirci un indirizzo di contratto da utilizzare. Ma se abbiamo già distribuito il nostro contratto su un nodo remoto, chiamiamo una funzione inserendo l'indirizzo come argomento. La funzione di esempio è commentata sotto la variabile lms
definita nel nostro codice sopra. Quindi chiamiamo la funzione routes
inserendo l'istanza dell'app, l'istanza del database, l'istanza del contratto ( lms
) e i dati degli account come argomenti. Infine, ascoltiamo le richieste sulla porta 8082
.
Inoltre, a questo punto, dovremmo aver installato il pacchetto MongoDB, perché lo stiamo usando nella nostra API come nostro database. Una volta ottenuto questo, passiamo alla pagina dei percorsi in cui utilizziamo i metodi definiti nel contratto per eseguire attività come il salvataggio e il recupero dei dati musicali.
Alla fine, i nostri percorsi.js dovrebbero assomigliare a questo:
const shortid = require('short-id') const IPFS =require('ipfs-api'); const ipfs = IPFS({ host: 'ipfs.infura.io', port: 5001,protocol: 'https' }); function routes(app, dbe, lms, accounts){ let db= dbe.collection('music-users') let music = dbe.collection('music-store') app.post('/register', (req,res)=>{ let email = req.body.email let idd = shortid.generate() if(email){ db.findOne({email}, (err, doc)=>{ if(doc){ res.status(400).json({"status":"Failed", "reason":"Already registered"}) }else{ db.insertOne({email}) res.json({"status":"success","id":idd}) } }) }else{ res.status(400).json({"status":"Failed", "reason":"wrong input"}) } }) app.post('/login', (req,res)=>{ let email = req.body.email if(email){ db.findOne({email}, (err, doc)=>{ if(doc){ res.json({"status":"success","id":doc.id}) }else{ res.status(400).json({"status":"Failed", "reason":"Not recognised"}) } }) }else{ res.status(400).json({"status":"Failed", "reason":"wrong input"}) } }) app.post('/upload', async (req,res)=>{ let buffer = req.body.buffer let name = req.body.name let title = req.body.title let id = shortid.generate() + shortid.generate() if(buffer && title){ let ipfsHash = await ipfs.add(buffer) let hash = ipfsHash[0].hash lms.sendIPFS(id, hash, {from: accounts[0]}) .then((_hash, _address)=>{ music.insertOne({id,hash, title,name}) res.json({"status":"success", id}) }) .catch(err=>{ res.status(500).json({"status":"Failed", "reason":"Upload error occured"}) }) }else{ res.status(400).json({"status":"Failed", "reason":"wrong input"}) } }) app.get('/access/:email', (req,res)=>{ if(req.params.email){ db.findOne({email: req.body.email}, (err,doc)=>{ if(doc){ let data = music.find().toArray() res.json({"status":"success", data}) } }) }else{ res.status(400).json({"status":"Failed", "reason":"wrong input"}) } }) app.get('/access/:email/:id', (req,res)=>{ let id = req.params.id if(req.params.id && req.params.email){ db.findOne({email:req.body.email},(err,doc)=>{ if(doc){ lms.getHash(id, {from: accounts[0]}) .then(async(hash)=>{ let data = await ipfs.files.get(hash) res.json({"status":"success", data: data.content}) }) }else{ res.status(400).json({"status":"Failed", "reason":"wrong input"}) } }) }else{ res.status(400).json({"status":"Failed", "reason":"wrong input"}) } }) } module.exports = routes
At the beginning of the routes file, we imported the short-id
package and ipfs-http-client
and then initialized IPFS with the HTTP client using the backend URL ipfs.infura.io
and port 5001
. This allowed us to use the IPFS methods to upload and retrieve data from IPFS (check out more here).
In the upload route, we save the audio buffer to IPFS which is better compared to just storing it on the blockchain for anyone registered or unregistered to use. Then we saved the address of the buffer in the blockchain by generating an ID and using it as an identifier in the sendIFPS
function. Finally, then we save all the other data associated with the music file to our database. We should not forget to update our argument in the routes function since we changed it in the server.js file.
In the access route using id
, we then retrieve our data by getting the id
from the request, using the id
to access the IPFS hash address, and then access the audio buffer using the address. But this requires authentication of a user by email which is done before anything else.
Phew, we're done ! Right now we have an API that can receive requests from users, access a database, and communicate to a node that has the software running on them. We shouldn't forget that we have to export our function with module.exports
though!
As we have noticed, our app is a decentralized app . However, it's not fully decentralized as we only stored our address data on the blockchain and every other piece of data was stored securely in a centralized database which is the basis for semi-dApps . So the consumption of data can be done directly via request or using a frontend application in JavaScript to send fetch requests.
Our music store backend app can now safely store music data and provide access to anyone who needs to access it, provided it is a registered user. Using blockchain for music sharing makes it cheaper to store music data while focusing on connecting artists directly with users, and perhaps it could help them generate revenue that way. This wouldn't require a middleman that uses royalty; instead, all of the revenue would go to the artist as users request their music to either download or stream. A good example of a music streaming application that uses blockchain just like this is Opus OPUS: Decentralized music sharing platform. However, there are also a few others like Musicoin, Audius, and Resonate.
Cosa succede dopo?
The final thing after coding is to start our server by running npm run start
or npm run build
and test our backend endpoints on either the browser or with Postman. After running and testing our API we could add more features to our backend and blockchain smart contract. If you'd like to get more guidance on that, please check the further reading section for more articles.
It's worth mentioning that it is critical to write unit and integration tests for our API to ensure correct and desirable behaviors. Once we have all of that done, we can deploy our application on the cloud for public use. This can be done on its own with or without adding a frontend (microservices) on Heroku, GCP, or AWS for public use. Happy coding!
Note : You can always check my repo for reference. Also, please note that the .env file containing the MongoDB database URI is included for security reasons.
Further Reading And Related Resources
- “How to Build Ethereum Dapp with React.js: Complete Step-By-Step Guide,” Gregory McCubbin
- “Ethereum + IPFS + React DApp Tutorial Pt. 1,” Alexander Ma
- “Ethereum Development with Go,” Miguel Mota
- “Create your first Ethereum dAPP with Web3 and Vue.JS (Part 1),” Nico Vergauwen
- “Deploy a Smart Contract on Ethereum with Python, Truffle and web3py,” Gabriel Saldanha
- “Why Use Blockchain Technology?,” Bernard Marr
- “How To Build Your Own Blockchain Using Node.js,” DevTeam.Space
- “How To Build A Blockchain App With Ethereum, Web3.js & Solidity Smart Contracts,” Gregory McCubbin
- “How To Build A Simple Cryptocurrency Blockchain In Node.js,” Alfrick Opidi
- “How Blockchain Technology Is Going To Revolutionize Ecommerce,” Sergii Shanin
- “4 Ways Blockchain Will Transform Higher Education — Smarter With Gartner,” Susan Moore
- “How To Learn Solidity: The Ultimate Ethereum Coding Tutorial,” Ryan Molecke
- “Developing Ethereum Smart Contracts For Beginners,” Coursetro
- “Learn about Ethereum,” Ethereum official site