Cum să construiți un API Node.js pentru Ethereum Blockchain

Publicat: 2022-03-10
Rezumat rapid ↬ În acest articol, John Agbanusi explică cum puteți construi un API Node.js de la zero prin construirea și implementarea unui Ethereum Blockchain pentru descentralizare. El vă arată, de asemenea, un proces pas cu pas de integrare atât a API-ului, cât și a blockchain-ului într-un singur API numit „API de aplicație descentralizată”.

Tehnologia Blockchain a fost în creștere în ultimii zece ani și a adus la viață un număr bun de produse și platforme, cum ar fi Chainalysis (tehnologie financiară), Burstiq (tehnologie de sănătate), Filament (IoT), Opus (streaming de muzică). și Ocular (securitate cibernetică).

Din aceste exemple, putem vedea că blockchain-ul cuprinde multe produse și cazuri de utilizare, ceea ce îl face foarte esențial și util. În fintech (tehnologia financiară), este folosit ca registre descentralizate pentru securitate și transparență în locuri precum Chain, Chainalysis și este, de asemenea, util în tehnologia sănătății pentru securitatea datelor sensibile de sănătate în Burstiq și Robomed - să nu uităm de tehnologia media, cum ar fi Opus și Audius care utilizează, de asemenea, blockchain pentru transparența redevențelor și astfel obține redevențe complete.

Ocular folosește securitatea care vine cu blockchain pentru gestionarea identității pentru sistemele biometrice, în timp ce Filament folosește registre blockchain pentru comunicare criptată în timp real. Acest lucru arată cât de esențial a devenit blockchain-ul pentru noi, făcându-ne viața mai bună. Dar ce este exact un blockchain?

Un blockchain este o bază de date care este partajată printr-o rețea de computere. Odată ce o înregistrare a fost adăugată în lanț, este destul de dificil de schimbat. Pentru a se asigura că toate copiile bazei de date sunt aceleași, rețeaua face verificări constante.

Deci, de ce avem nevoie de blockchain? Blockchain este o modalitate sigură de a înregistra activitățile și de a păstra datele actuale, menținând în același timp o evidență a istoriei sale, în comparație cu înregistrările sau bazele de date tradiționale în care hack-urile, erorile și perioadele de nefuncționare sunt foarte posibile. Datele nu pot fi corupte de nimeni sau șterse accidental și beneficiați atât de un istoric al datelor, cât și de o înregistrare actualizată instantaneu, care nu poate fi ștearsă sau deveni inaccesibilă din cauza timpului de nefuncționare a unui server.

Deoarece întregul blockchain este duplicat pe mai multe computere, orice utilizator poate vizualiza întregul blockchain. Tranzacțiile sau înregistrările sunt procesate nu de un administrator central, ci de o rețea de utilizatori care lucrează pentru a verifica datele și a obține un consens.

Aplicațiile care folosesc blockchain se numesc dApps (aplicații descentralizate). Privind în jur astăzi, vom găsi în cea mai mare parte aplicații descentralizate în fintech, dar blockchain depășește finanțele descentralizate. Avem platforme de sănătate, platforme de streaming/partajare de muzică, platforme de comerț electronic, platforme de securitate cibernetică și IOT-uri care se îndreaptă către aplicații descentralizate (dApps), așa cum s-a menționat mai sus.

Deci, când ar avea sens să luăm în considerare utilizarea blockchain pentru aplicațiile noastre, mai degrabă decât o bază de date sau o înregistrare standard?

Mai multe după săritură! Continuați să citiți mai jos ↓

Aplicații comune ale Blockchain

  • Gestionarea și securizarea relațiilor digitale
    Oricând doriți să păstrați o evidență pe termen lung și transparentă a activelor (de exemplu, pentru a înregistra drepturile de proprietate sau de apartament), blockchain-ul ar putea fi soluția ideală. „Contractele inteligente” Ethereum, în special, sunt excelente pentru a facilita relațiile digitale. Cu un contract inteligent, plățile automate pot fi eliberate atunci când părțile dintr-o tranzacție convin că condițiile lor au fost îndeplinite.
  • Eliminarea Mijlocarilor/Gatekeeperilor
    De exemplu, majoritatea furnizorilor trebuie să interacționeze în prezent cu oaspeții printr-o platformă de agregare centralizată, cum ar fi Airbnb sau Uber (care, la rândul său, ia o reducere la fiecare tranzacție). Blockchain ar putea schimba toate astea.
    De exemplu, TUI este atât de convins de puterea blockchain-ului încât este modalități de pionierat de a conecta hotelieri și clienții direct. În acest fel, ei pot tranzacționa prin blockchain într-un mod ușor, sigur și consecvent, mai degrabă decât printr-o platformă centrală de rezervare.
  • Înregistrați tranzacțiile securizate între parteneri pentru a asigura încrederea
    O bază de date tradițională poate fi bună pentru înregistrarea tranzacțiilor simple între două părți, dar atunci când lucrurile devin mai complicate, blockchain poate ajuta la reducerea blocajelor și la simplificarea relațiilor. În plus, securitatea adăugată a unui sistem descentralizat face ca blockchainul să fie ideal pentru tranzacții în general.
    Un exemplu este Universitatea din Melbourne, care a început să-și stocheze înregistrările în blockchain. Cel mai promițător caz de utilizare pentru blockchain în învățământul superior este transformarea „înregistrării” diplomelor, certificatelor și diplomelor. Acest lucru economisește o mulțime de costuri de la serverele dedicate pentru stocare sau înregistrări.
  • Păstrarea înregistrărilor acțiunilor anterioare pentru aplicațiile în care datele sunt în flux constant
    Blockchain este o modalitate mai bună și mai sigură de a înregistra activitatea și de a păstra datele actuale, păstrând în același timp o evidență a istoriei acesteia. Datele nu pot fi corupte de nimeni sau șterse accidental și beneficiați atât de un istoric al datelor, cât și de o înregistrare actualizată instantaneu. Un exemplu de caz de utilizare bun este blockchain-ul în comerțul electronic, atât blockchain-ul, cât și comerțul electronic implică tranzacții.
    Blockchain face aceste tranzacții mai sigure și mai rapide, în timp ce activitățile de comerț electronic se bazează pe ele. Tehnologia Blockchain permite utilizatorilor să partajeze și să stocheze în siguranță activele digitale atât automat, cât și manual. Această tehnologie are capacitatea de a gestiona activitățile utilizatorilor, cum ar fi procesarea plăților, căutările de produse, achizițiile de produse și îngrijirea clienților. De asemenea, reduce cheltuielile cheltuite cu gestionarea stocurilor și procesarea plăților.
  • Descentralizarea face posibilă utilizarea oriunde
    Spre deosebire de până acum, când trebuie să ne limităm la o anumită regiune din diverse motive, cum ar fi politicile de schimb valutar, limitările gateway-urilor de plată îngreunează accesul la resursele financiare ale multor țări care nu se află în regiunea sau continentul dvs. Odată cu creșterea și puterea descentralizării blockchain sau a sistemului peer-to-peer, acest lucru devine mai ușor de lucrat cu alte țări.
    De exemplu, un magazin de comerț electronic din Europa poate avea consumatori în Africa și nu poate solicita unui intermediar să le proceseze cererile de plată. Mai mult, aceste tehnologii deschid uși retailerilor online pentru a utiliza piețele de consum din țări îndepărtate cu bitcoin, adică o criptomonedă.
  • Blockhain este neutru din punct de vedere tehnologic
    Blockchain funcționează cu toate și orice stiva de tehnologie utilizată de un dezvoltator. Nu trebuie să înveți Node ca dezvoltator Python pentru a folosi blockchain sau pentru a învăța Golang. Acest lucru face blockchain-ul foarte ușor de utilizat.
    De fapt, îl putem folosi direct cu aplicațiile noastre front-end în Vue/React, blockchain-ul acționând ca singura noastră bază de date pentru sarcini simple, necomplicate și cazuri de utilizare, cum ar fi încărcarea datelor sau obținerea hash-urilor pentru afișarea înregistrărilor pentru utilizatorii noștri sau construirea de jocuri frontend precum cazinoul. jocuri și jocuri de pariuri (în care este nevoie de o mare încredere). De asemenea, cu puterea web3, putem stoca datele direct în lanț.

Acum, am văzut un număr destul de mare de avantaje ale utilizării blockchain-ului, dar când nu ar trebui să ne deranjez deloc să folosim un blockchain?

Dezavantajele Blockchain-ului

  • Viteză redusă pentru tranzacțiile digitale
    Blockchain-urile necesită cantități uriașe de putere de calcul, ceea ce tinde să reducă viteza tranzacțiilor digitale, deși există soluții, este recomandabil să folosiți baze de date centralizate atunci când aveți nevoie de tranzacții de mare viteză în milisecunde.
  • Imuabilitatea datelor
    Imuabilitatea datelor a fost întotdeauna unul dintre cele mai mari dezavantaje ale blockchain-ului. Este clar că mai multe sisteme beneficiază de el, inclusiv lanțul de aprovizionare, sistemele financiare și așa mai departe. Cu toate acestea, suferă de faptul că odată ce datele sunt scrise, acestea nu pot fi eliminate. Fiecare persoană de pe pământ are dreptul la intimitate. Cu toate acestea, dacă aceeași persoană utilizează o platformă digitală care rulează pe tehnologia blockchain, atunci nu va putea să-și îndepărteze urma din sistem atunci când nu o dorește acolo. Cu cuvinte simple, nu există nicio modalitate de a-și elimina urma - lăsând în bucăți drepturile de confidențialitate.
  • Necesită cunoștințe de expertiză
    Implementarea și gestionarea unui proiect blockchain este dificilă. Este nevoie de cunoștințe aprofundate pentru a parcurge întregul proces. Acesta este motivul pentru care este greu să întâlniți specialiști sau experți în blockchain, deoarece este nevoie de mult timp și efort pentru a pregăti un expert în blockchain. Prin urmare, acest articol este un loc bun de început și un ghid bun dacă ați început deja.
  • Interoperabilitate
    Rețelele blockchain multiple care lucrează din greu pentru a rezolva problema registrului contabil distribuit în mod unic face dificilă relaționarea sau integrarea lor între ele. Acest lucru îngreunează comunicarea între diferite lanțuri.
  • Integrarea aplicațiilor moștenite
    Multe companii și aplicații folosesc încă sisteme și arhitectură vechi; adoptarea tehnologiei blockchain necesită o revizuire completă a acestor sisteme, ceea ce trebuie să spun că nu este fezabilă pentru multe dintre ele.

Blockchain încă evoluează și se maturizează tot timpul, așa că nu fi surprins dacă aceste dezavantaje menționate astăzi devin transformate mai târziu într-un profesionist. Bitcoin, care este o criptomonedă, este un exemplu popular de blockchain, un blockchain popular care a fost în creștere în afară de criptomoneda bitcoin este blockchainul Ethereum. Bitcoin se concentrează pe criptomonede, în timp ce Ethereum se concentrează mai mult pe contractele inteligente, care au fost forța motrice majoră pentru noile platforme tehnologice.

Lectură recomandată : Bitcoin vs. Ethereum: Care este diferența?

Să începem să construim API-ul nostru

Cu o înțelegere solidă a blockchain-ului, acum să ne uităm la cum să construim un blockchain Ethereum și să-l integrăm într-un API standard în Node.js. Scopul final este de a obține o bună înțelegere a modului în care sunt construite platformele dApps și Blockchain.

Majoritatea aplicațiilor dApp au arhitectură și structură similare. Practic, avem un utilizator care interacționează cu interfața dApp - fie web, fie mobil - care apoi interacționează cu API-urile backend. Backend-ul, apoi, la cerere, interacționează cu contractul(ele) inteligent(e) sau blockchain prin noduri publice; acestea fie rulează aplicații Node.js, fie backend-ul folosește blockchain rulând direct software-ul Node.js. Există încă atât de multe lucruri între aceste procese, de la alegerea de a construi o aplicație complet descentralizată sau o aplicație semi-descentralizată până la alegerea a ceea ce ar trebui să fie descentralizat și cum să stocați în siguranță cheile private.

Lectură recomandată : Arhitectură de aplicații descentralizate: back-end, securitate și modele de design

Lucruri pe care ar trebui să le știm mai întâi

Pentru acest tutorial, vom încerca să construim backend-ul unei aplicații de magazin de muzică descentralizat care folosește puterea blockchain-ului Ethereum pentru stocarea muzicii și partajarea acesteia pentru descărcări sau streaming.

Structura de bază a aplicației pe care încercăm să o construim are trei părți:

  1. Autentificare , care se face prin e-mail; desigur, trebuie să adăugăm o parolă criptată la aplicație.
  2. Stocarea datelor , cu datele muzicale sunt mai întâi stocate în ipfs, iar adresa de stocare este stocată în blockchain pentru recuperare.
  3. Preluare , cu orice utilizator autentificat putând accesa datele stocate pe platforma noastră și să le folosească.

Vom construi acest lucru cu Node.js, dar puteți construi și cu Python sau orice alt limbaj de programare. Vom vedea, de asemenea, cum să stocăm datele media în IPFS, să obținem adresa și să scriem funcții în care să stocăm această adresă - și să preluăm această adresă dintr-un blockchain cu limbajul de programare Solidity.

Iată câteva instrumente pe care ar trebui să le avem la dispoziție pentru a construi sau a lucra cu Ethereum și Node.js.

  • Node.js
    Prima cerință este o aplicație Node. Încercăm să construim o aplicație Node.js, așa că avem nevoie de un compilator. Vă rugăm să vă asigurați că aveți instalat Node.js - și vă rugăm să descărcați cel mai recent binar de suport pe termen lung ( LTS ).
  • Suită Truffle
    Truffle este un mediu de dezvoltare și testare a contractelor, precum și o conductă de active pentru blockchain-ul Ethereum. Oferă un mediu pentru compilarea, pipelinizarea și rularea scripturilor. Odată ce vorbiți despre dezvoltarea blockchain-ului, Truffle este o oprire populară la care să mergeți. Consultați despre Truffle Suite pe Truffle Suite: instrumente dulci pentru contracte inteligente.
  • Ganache CLI
    Un alt instrument care funcționează bine în mână cu Truffle este Ganache-CLI. Este construit și întreținut de echipa Truffle Suite. După construirea și compilarea, aveți nevoie de un emulator pentru a dezvolta și rula aplicații blockchain, apoi implementați contracte inteligente pentru a fi utilizate. Ganache vă ajută să implementați un contract într-un emulator fără a utiliza bani reali pentru costul tranzacției, conturi reciclabile și multe altele. Citiți mai multe despre Ganache CLI la Ganache CLI și Ganache.
  • Remix
    Remix este ca o alternativă la Ganache, dar vine și cu o interfață grafică pentru a ajuta la navigarea prin implementarea și testarea contractelor inteligente Ethereum. Puteți afla mai multe despre el pe Remix — Ethereum IDE și comunitate. Tot ce trebuie să faceți este să vizitați https://remix.ethereum.org și să utilizați GUI pentru a scrie și a implementa contracte inteligente.
  • Web3
    Web3 este o colecție de biblioteci care vă permite să interacționați cu un nod Ethereum. Acestea pot fi noduri locale sau la distanță ale contractului prin HTTP, IPC sau Web Sockets. Introducere în Web3.js · Ethereum Blockchain Developer Crash Course este un loc bun pentru a învăța puțin despre Web3.
  • IPFS
    Un protocol de bază care este utilizat în construirea dApps. Sistemul de fișiere InterPlanetary (IPFS) este un protocol și o rețea peer-to-peer pentru stocarea și partajarea datelor într-un sistem de fișiere distribuit. IPFS Powers the Distributed Web explică mai multe despre IPFS și despre cum este folosit de obicei.

Crearea unui API de backend de la zero

Deci, mai întâi trebuie să creăm un backend care să fie folosit și folosim Node.js. Când dorim să creăm un nou API Node.js, primul lucru pe care îl vom face este să inițializam un pachet npm. După cum probabil știți, npm înseamnă Node Package Manager și vine pre-ambalat cu binarul Node.js. Așa că creăm un folder nou și îl numim „blockchain-music” . Deschidem terminalul din directorul respectiv, apoi rulăm următoarea comandă:

 $ npm init -y && touch server.js routes.js

Acest lucru pornește proiectul cu un fișier package.json și răspunde da la toate solicitările. Apoi creăm și un fișier server.js și un fișier routes.js pentru scrierea funcțiilor routes în API.

După toate acestea, va trebui să instalați pachetele de care avem nevoie pentru a face construirea noastră ușoară și simplă. Acest proces este unul continuu, adică poți instala un pachet oricând în timpul dezvoltării proiectului tău.

Să le instalăm pe cele mai importante de care avem nevoie acum:

  • Express.js
  • @trufă/contract
  • Truffle.js
  • web3.js
  • dotenv
  • short-id
  • MongoDB
  • nodemon

De asemenea, va trebui să instalați Truffle.js la nivel global , astfel încât să îl puteți utiliza oriunde în mediul dvs. local. Dacă doriți să le instalați pe toate simultan, rulați următorul cod în terminalul dvs.:

 $ npm install nodemon truffle-contract dotenv mongodb shortid express web3 --save && npm install truffle -g

Indicatorul --save este pentru a salva numele pachetului în fișierul package.json . Indicatorul -g este de a stoca acest pachet particular la nivel global, astfel încât să-l putem folosi în orice proiect la care vom lucra.

Apoi creăm un fișier .env unde putem stoca URI-ul secret al bazei de date MongoDB pentru utilizare. Facem acest lucru rulând touch.env în Terminal. Dacă nu aveți încă un cont de bază de date cu MongoDB, începeți mai întâi cu pagina MongoDB.

Pachetul dotenv exportă variabila noastră stocată în mediul de proces Node.js. Asigurați-vă că nu împingeți fișierul .env atunci când trimiteți în depozite publice pentru a evita scurgerea parolelor și a datelor private.

Apoi, trebuie să adăugăm scripturi pentru fazele de construire și dezvoltare ale proiectului nostru în fișierul package.json . În prezent, pachetul nostru.json arată astfel:

 { "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" } }

Apoi vom adăuga un script de pornire la fișierul package.json pentru a folosi serverul nodemon, astfel încât de fiecare dată când facem o schimbare, acesta repornește serverul însuși și un script de compilare care folosește serverul nod direct, ar putea arăta astfel:

 { "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" } }

În continuare, trebuie să inițializam Truffle pentru a fi utilizat în contractul nostru inteligent utilizând pachetul Truffle pe care l-am instalat la nivel global mai devreme. În același folder al proiectelor noastre, rulăm următoarea comandă de mai jos în terminalul nostru:

 $ truffle init

Apoi putem începe să scriem codul nostru în fișierul server.js . Din nou, încercăm să construim o aplicație simplă de magazin de muzică descentralizată, în care clienții pot încărca muzică pentru ca fiecare alt utilizator să o acceseze și să o asculte.

Server.js ar trebui să fie curat pentru cuplarea și decuplarea ușoară a componentelor, astfel încât rutele și alte funcționalități vor fi puse în alte fișiere, cum ar fi routes.js . Exemplul nostru server.js ar putea fi:

 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'); }) })

Practic, mai sus importăm bibliotecile de care avem nevoie cu require , apoi adăugăm un middleware care permite utilizarea JSON în API-ul nostru folosind app.use , apoi ne conectăm la baza noastră de date MongoDB și obținem acces la baza de date, apoi specificăm ce cluster de baze de date încercăm să accesăm (pentru acest tutorial este „Cluster0” ). După aceasta, apelăm funcția și o importăm din fișierul rute . În cele din urmă, ascultăm orice încercare de conexiune pe portul 8082 .

Acest fișier server.js este doar un element simplu pentru a porni aplicația. Observați că am importat routes.js . Acest fișier va păstra punctele finale ale rutei pentru API-ul nostru. De asemenea, am importat pachetele pe care trebuia să le folosim în fișierul server.js și le-am inițializat.

Vom crea cinci puncte finale pentru consumul utilizatorilor:

  1. Punct final de înregistrare pentru înregistrarea utilizatorilor doar prin e-mail. În mod ideal, am face acest lucru cu un e-mail și o parolă, dar întrucât vrem doar să identificăm fiecare utilizator, nu ne vom aventura în securitatea parolei și hashing de dragul conciziei acestui tutorial.
     POST /register Requirements: email
  2. Conectați-vă la punctul final pentru utilizatori prin e-mail.
     POST /login Requirements: email
  3. Încărcați punctul final pentru utilizatori — API-ul care primește datele fișierului muzical. Interfața va converti fișierele MP3/WAV într-un buffer audio și va trimite acel buffer către API.
     POST /upload Requirements: name, title of music, music file buffer or URL stored
  4. Punct final de acces care va furniza datele bufferului muzical oricărui utilizator înregistrat care le solicită și înregistrează cine a accesat-o.
     GET /access/{email}/{id} Requirements: email, id
  5. De asemenea, dorim să oferim acces la întreaga bibliotecă muzicală și să returnăm rezultatele unui utilizator înregistrat.
     GET /access/{email} Requirements: email

Apoi scriem funcțiile de traseu în fișierul nostru routes.js . Utilizăm funcțiile de stocare și regăsire a bazei de date și apoi ne asigurăm că exportăm funcția de rută la sfârșitul fișierului pentru a face posibilă importarea într-un alt fișier sau folder.

 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

În interiorul acestei funcții route , avem multe alte funcții numite atât în app , cât și în parametrii db . Acestea sunt funcțiile punctului final API care permit utilizatorilor să specifice un punct final în adresa URL. În cele din urmă, alegem una dintre aceste funcții pentru a fi executată și oferim rezultate ca răspuns la solicitările primite.

Avem patru funcții finale principale:

  1. get : pentru citirea operațiunilor de înregistrare
  2. post : pentru crearea operațiunilor de înregistrare
  3. put : pentru actualizarea operațiunilor de înregistrare
  4. delete : pentru ștergerea operațiunilor de înregistrare

În această funcție routes , am folosit operațiunile get și post . Folosim post pentru operațiuni de înregistrare, autentificare și încărcare și get pentru accesarea operațiunilor de date. Pentru mai multe explicații despre asta, puteți consulta articolul lui Jamie Corkhill despre „Cum să începeți cu Node: o introducere în API-uri, HTTP și ES6+ JavaScript”.

În codul de mai sus, putem vedea și unele operațiuni ale bazei de date, cum ar fi în ruta de înregistrare . Am stocat emailul unui utilizator nou cu db.createa și am verificat e-mailul în funcția de autentificare cu db.findOne . Acum, înainte de a putea face totul, trebuie să denumim o colecție sau un tabel cu metoda db.collection . Exact asta vom trata în continuare.

Notă : Pentru a afla mai multe despre operațiunile bazei de date din MongoDB, verificați documentația privind metodele mongo Shell.

Construirea unui contract inteligent blockchain simplu cu soliditate

Acum vom scrie un contract Blockchain în Solidity (aceasta este limba în care sunt scrise contractele inteligente) pentru a ne stoca pur și simplu datele și a le prelua atunci când avem nevoie. Datele pe care vrem să le stocăm sunt datele fișierului muzical, ceea ce înseamnă că trebuie să încărcăm muzica pe IPFS, apoi să stocăm adresa tamponului într-un blockchain.

Mai întâi, creăm un fișier nou în folderul contract și îl denumim Inbox.sol . Pentru a scrie un contract inteligent, este util să înțelegeți bine Solidity, dar nu este dificil, deoarece este similar cu JavaScript.

Notă : dacă sunteți interesat să aflați mai multe despre Solidity, am adăugat câteva resurse în partea de jos a articolului pentru a începe.

 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; } }

În contractul nostru, avem două funcții principale: funcțiile sendIPFS și getHash . Înainte de a vorbi despre funcții, putem vedea că a trebuit să definim mai întâi un contract numit Inbox . În cadrul acestei clase, avem structuri utilizate în obiectul ipfsInbox (întâi evenimente, apoi modificatori).

După definirea structurilor și evenimentelor, trebuie să inițializam contractul apelând funcția constructor . Apoi am definit trei funcții. (Funcția checkInbox a fost utilizată în test pentru rezultatele testării.)

sendIPFS este locul în care utilizatorul introduce identificatorul și adresa hash, după care este stocat pe blockchain. Funcția getHash preia adresa hash atunci când i se dă identificatorul. Din nou, logica din spatele acestui lucru este că în cele din urmă dorim să stocăm muzica în IPFS. Pentru a testa modul în care funcționează, puteți să accesați un IDE Remix, să copiați, să lipiți și să testați contractul, precum și să depanați orice erori și să rulați din nou (sperăm că nu va fi necesar!).

După ce am testat că codul nostru funcționează corect în remix, să trecem la compilarea locală cu suita Truffle. Dar mai întâi, trebuie să facem câteva modificări la fișierele noastre și să ne setăm emulatorul folosind ganache-cli :

Mai întâi, să instalăm ganache-cli . În același director, rulați următoarea comandă în terminalul dvs.:

 $ npm install ganache-cli -g

Apoi, să deschidem un alt terminal și să rulăm o altă comandă în același folder:

 $ ganache-cli

Aceasta pornește emulatorul pentru ca contractul nostru blockchain să se conecteze și să funcționeze. Minimizați terminalul și continuați cu celălalt terminal pe care l-ați folosit.

Acum accesați fișierul truffle.js dacă utilizați un sistem de operare Linux/Mac sau truffle-config.js în Windows și modificați acest fișier pentru a arăta astfel:

 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 } } };

Practic, ceea ce am făcut a fost să adăugăm calea folderului de compilare în care contractul inteligent este convertit în fișiere JSON. Apoi am specificat și rețeaua pe care ar trebui să o folosească Truffle pentru migrare.

Apoi, tot în folderul migrații , creați un fișier nou numit 2_migrate_inbox.js și adăugați următorul cod în interiorul fișierelor:

 var IPFSInbox = artifacts.require("./Inbox.sol"); module.exports = function(deployer) { deployer.deploy(IPFSInbox); };

Am făcut acest lucru pentru a obține fișierul de contract și a-l implementa automat într-un JSON, folosind funcția de deployer în timpul migrării Truffle.

După modificările de mai sus rulăm:

 $ truffle compile

Ar trebui să vedem câteva mesaje la sfârșit care arată o compilare reușită, cum ar fi:

 > Compiled successfully using: - solc: 0.5.16+commit.9c3226ce.Emscripten.clang

În continuare, migrăm contractul rulând:

 $ truffle migrate

Odată ce ne-am migrat cu succes contractele, ar trebui să avem ceva de genul acesta la sfârșit:

 Summary ======= > Total deployments: 1 > Final cost: 0.00973432 ETH

Și aproape am terminat! Ne-am construit API-ul cu Node.js și, de asemenea, am configurat și construit contractul nostru inteligent.

De asemenea, ar trebui să scriem teste pentru contractul nostru pentru a testa comportamentul contractului nostru și pentru a ne asigura că este comportamentul dorit. Testele sunt de obicei scrise și plasate în folderul de test . Un exemplu de test scris într-un fișier numit InboxTest.js creat în folderul de testare este:

 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") }) })

Deci, rulăm testul rulând următoarele:

 $ truffle test

Testează contractul nostru cu fișierele din folderul de testare și arată numărul de teste trecute și nereușite. Pentru acest tutorial, ar trebui să obținem:

 $ 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)

Integrarea contractului inteligent în API-ul de backend folosind Web3

De cele mai multe ori, când vezi tutoriale, vezi aplicații descentralizate construite pentru a integra interfața direct în blockchain. Dar există momente când este necesară și integrarea în backend, de exemplu atunci când utilizați API-uri și servicii backend terțe sau când utilizați blockchain pentru a construi un CMS.

Utilizarea Web3 este foarte importantă pentru această cauză, deoarece ne ajută să accesăm nodurile Ethereum la distanță sau locale și să le folosim în aplicațiile noastre. Înainte de a continua, vom discuta despre nodurile Ethereum locale și la distanță. Nodurile locale sunt nodurile implementate pe sistemul nostru cu emulatori precum ganache-cli dar un nod la distanță este unul care este implementat pe robinete/platforme online precum ropsten sau rinkeby . Pentru a aprofunda, puteți urma un tutorial despre cum să implementați pe ropsten un ghid de 5 minute pentru implementarea contractelor inteligente cu Truffle și Ropsten sau puteți utiliza furnizorul de portofel truffle și implementați printr-o modalitate mai ușoară de implementare a contractelor dvs. inteligente.

Folosim ganache-cli în acest tutorial, dar dacă am fi implementat pe ropsten, ar fi trebuit să copiam sau să ne stocăm adresa contractului undeva, ca în fișierul nostru .env, apoi să trecem la actualizarea fișierului server.js , import web3, import contractul migrat și configurați o instanță 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)); }) })

În fișierul server.js , verificăm dacă instanța web3 este deja inițializată. Dacă nu, îl inițializam pe portul de rețea pe care l-am definit mai devreme ( 8545 ). Apoi construim un contract bazat pe fișierul JSON migrat și pachetul truffle-contract și setăm furnizorul de contract la furnizorul de instanță Web3 care trebuie să fi fost inițializat până acum.

Apoi obținem conturi prin web3.eth.getAccounts . Pentru etapa de dezvoltare, numim funcția implementată din clasa noastră de contract care cere ganache-cli - care încă rulează - să ne dea o adresă de contract pe care să o folosim. Dar dacă am implementat deja contractul nostru pe un nod la distanță, apelăm la o funcție care introduce adresa ca argument. Funcția eșantion este comentată sub variabila lms definită în codul nostru de mai sus. Apoi apelăm funcția routes introducând instanța aplicației, instanța bazei de date, instanța contractului ( lms ) și datele contului ca argumente. În cele din urmă, ascultăm cererile de pe portul 8082 .

De asemenea, până acum, ar fi trebuit să instalăm pachetul MongoDB, deoarece îl folosim în API-ul nostru ca bază de date. Odată ce avem asta, trecem la pagina rute unde folosim metodele definite în contract pentru a îndeplini sarcini precum salvarea și preluarea datelor muzicale.

În cele din urmă, routes.js-ul nostru ar trebui să arate astfel:

 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.

Ce urmează?

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