Comment créer une API Node.js pour la blockchain Ethereum
Publié: 2022-03-10La technologie blockchain est en plein essor depuis une dizaine d'années, et a donné vie à bon nombre de produits et plateformes comme Chainalysis (finance tech), Burstiq (health-tech), Filament (IoT), Opus (music streaming) et Ocular (cybersécurité).
À partir de ces exemples, nous pouvons voir que la blockchain concerne de nombreux produits et cas d'utilisation, ce qui la rend très essentielle et utile. Dans la fintech (technologie financière), il est utilisé comme registres décentralisés pour la sécurité et la transparence dans des endroits comme Chain, Chainalysis, et est également utile dans la technologie de la santé pour la sécurité des données de santé sensibles dans Burstiq et Robomed - sans oublier la technologie des médias comme Opus et Audius qui utilisent également la blockchain pour la transparence des redevances et obtiennent ainsi des redevances complètes.
Ocular utilise la sécurité fournie avec la blockchain pour la gestion de l'identité des systèmes biométriques, tandis que Filament utilise les registres de la blockchain pour une communication cryptée en temps réel. Cela montre à quel point la blockchain est devenue essentielle pour nous en améliorant nos vies. Mais qu'est-ce qu'une blockchain exactement ?
Une blockchain est une base de données partagée sur un réseau d'ordinateurs. Une fois qu'un enregistrement a été ajouté à la chaîne, il est assez difficile de le modifier. Pour s'assurer que toutes les copies de la base de données sont identiques, le réseau effectue des vérifications constantes.
Alors pourquoi avons-nous besoin de la blockchain ? La blockchain est un moyen sûr d'enregistrer les activités et de garder les données à jour tout en conservant un enregistrement de son historique par rapport aux enregistrements ou bases de données traditionnels où les piratages, les erreurs et les temps d'arrêt sont très possibles. Les données ne peuvent être corrompues par personne ou supprimées accidentellement, et vous bénéficiez à la fois d'une trace historique des données et d'un enregistrement instantanément mis à jour qui ne peut pas être effacé ou devenir inaccessible en raison d'un temps d'arrêt d'un serveur.
Étant donné que l'ensemble de la blockchain est dupliqué sur de nombreux ordinateurs, tout utilisateur peut afficher l'intégralité de la blockchain. Les transactions ou les enregistrements ne sont pas traités par un administrateur central, mais par un réseau d'utilisateurs qui travaillent pour vérifier les données et parvenir à un consensus.
Les applications qui utilisent la blockchain sont appelées dApps (Decentralised Applications). En regardant autour d'aujourd'hui, nous trouverons principalement des applications décentralisées dans la fintech, mais la blockchain va au-delà de la finance décentralisée. Nous avons des plateformes de santé, des plateformes de streaming/partage de musique, des plateformes de commerce électronique, des plateformes de cybersécurité et des IOT qui évoluent vers des applications décentralisées (dApps) comme cité ci-dessus.
Alors, quand serait-il judicieux d'envisager d'utiliser la blockchain pour nos applications, plutôt qu'une base de données ou un enregistrement standard ?
Applications courantes de la blockchain
- Gérer et sécuriser les relations numériques
Chaque fois que vous souhaitez conserver un enregistrement transparent et à long terme des actifs (par exemple, pour enregistrer les droits de propriété ou d'appartement), la blockchain pourrait être la solution idéale. Les « contrats intelligents » d'Ethereum, en particulier, sont parfaits pour faciliter les relations numériques. Avec un contrat intelligent, les paiements automatisés peuvent être libérés lorsque les parties à une transaction conviennent que leurs conditions ont été remplies. - Élimination des intermédiaires/gardiens
Par exemple, la plupart des fournisseurs doivent actuellement interagir avec les clients via une plate-forme d'agrégation centralisée, comme Airbnb ou Uber (qui, à son tour, prend une part sur chaque transaction). La blockchain pourrait changer tout cela.
Par exemple, TUI est tellement convaincue de la puissance de la blockchain qu'elle est à l'avant-garde des moyens de connecter directement les hôteliers et les clients. De cette façon, ils peuvent effectuer des transactions via la blockchain de manière simple, sûre et cohérente, plutôt que via une plateforme de réservation centrale. - Enregistrez les transactions sécurisées entre les partenaires pour garantir la confiance
Une base de données traditionnelle peut être utile pour enregistrer des transactions simples entre deux parties, mais lorsque les choses se compliquent, la blockchain peut aider à réduire les goulots d'étranglement et à simplifier les relations. De plus, la sécurité supplémentaire d'un système décentralisé rend la blockchain idéale pour les transactions en général.
Un exemple est l'Université de Melbourne qui a commencé à stocker ses enregistrements dans la blockchain. Le cas d'utilisation le plus prometteur de la blockchain dans l'enseignement supérieur est de transformer la « tenue de registres » des diplômes, certificats et certificats. Cela permet d'économiser beaucoup sur les serveurs dédiés au stockage ou aux enregistrements. - Conserver des enregistrements des actions passées pour les applications où les données sont en flux constant
La blockchain est un moyen meilleur et plus sûr d'enregistrer l'activité et de conserver les données à jour tout en conservant un enregistrement de son historique. Les données ne peuvent être corrompues par personne ou supprimées accidentellement, et vous bénéficiez à la fois d'un historique des données et d'un enregistrement instantanément mis à jour. Un exemple de bon cas d'utilisation est la blockchain dans le commerce électronique, la blockchain et le commerce électronique impliquant des transactions.
La blockchain rend ces transactions plus sûres et plus rapides tandis que les activités de commerce électronique en dépendent. La technologie Blockchain permet aux utilisateurs de partager et de stocker en toute sécurité des actifs numériques à la fois automatiquement et manuellement. Cette technologie a la capacité de gérer les activités des utilisateurs telles que le traitement des paiements, les recherches de produits, les achats de produits et le service client. Cela réduit également les dépenses consacrées à la gestion des stocks et au traitement des paiements. - La décentralisation permet d'être utilisé n'importe où
Contrairement à avant où nous devions nous limiter à une région particulière pour diverses raisons telles que les politiques de change, les limitations des passerelles de paiement rendent difficile l'accès aux ressources financières de nombreux pays qui ne se trouvent pas dans votre région ou votre continent. Avec l'essor et la puissance de la décentralisation ou du système peer-to-peer de la blockchain, il devient plus facile de travailler avec d'autres pays.
Par exemple, un magasin de commerce électronique en Europe peut avoir des consommateurs en Afrique et ne pas nécessiter d'intermédiaire pour traiter leurs demandes de paiement. De plus, ces technologies ouvrent des portes aux détaillants en ligne pour utiliser les marchés de consommation dans des pays lointains avec le bitcoin, c'est-à-dire une crypto-monnaie. - Blockhain est technologiquement neutre
Blockchain fonctionne avec toutes les piles technologiques utilisées par un développeur. Vous n'avez pas besoin d'apprendre Node en tant que développeur Python pour utiliser la blockchain ou apprendre Golang. Cela rend la blockchain très facile à utiliser.
Nous pouvons en fait l'utiliser directement avec nos applications frontales dans Vue/React avec la blockchain agissant comme notre seule base de données pour des tâches simples et simples et des cas d'utilisation comme le téléchargement de données ou l'obtention de hachages pour afficher des enregistrements pour nos utilisateurs, ou la création de jeux frontaux comme le casino jeux et jeux de paris (dans lesquels une grande confiance est nécessaire). De plus, avec la puissance du web3, nous pouvons stocker directement les données dans la chaîne.
Maintenant, nous avons vu un certain nombre d'avantages liés à l'utilisation de la blockchain, mais quand ne devrions-nous pas du tout nous soucier d'utiliser une blockchain ?
Inconvénients de la blockchain
- Vitesse réduite pour la transaction numérique
Les chaînes de blocs nécessitent d'énormes quantités de puissance de calcul, ce qui tend à réduire la vitesse des transactions numériques, bien qu'il existe des solutions de contournement, il est conseillé d'utiliser des bases de données centralisées lorsque vous avez besoin de transactions à grande vitesse en quelques millisecondes. - Immuabilité des données
L'immuabilité des données a toujours été l'un des principaux inconvénients de la blockchain. Il est clair que plusieurs systèmes en bénéficient, notamment la chaîne d'approvisionnement, les systèmes financiers, etc. Cependant, il souffre du fait qu'une fois les données écrites, elles ne peuvent pas être supprimées. Chaque personne sur la terre a droit à la vie privée. Cependant, si la même personne utilise une plate-forme numérique qui fonctionne sur la technologie blockchain, elle ne pourra pas supprimer sa trace du système lorsqu'elle n'en veut pas. En termes simples, il n'y a aucun moyen qu'il puisse effacer sa trace - laissant les droits à la vie privée en morceaux. - Nécessite des connaissances spécialisées
La mise en œuvre et la gestion d'un projet blockchain est difficile. Cela nécessite des connaissances approfondies pour suivre l'ensemble du processus. C'est pourquoi il est difficile de trouver des spécialistes ou des experts de la blockchain car il faut beaucoup de temps et d'efforts pour former un expert de la blockchain. Cet article est donc un bon point de départ et un bon guide si vous avez déjà commencé. - Interopérabilité
Plusieurs réseaux de blockchain travaillant dur pour résoudre le problème du grand livre distribué rendent difficile de les relier ou de les intégrer les uns aux autres. Cela rend difficile la communication entre les différentes chaînes. - Intégration des applications héritées
De nombreuses entreprises et applications utilisent encore des systèmes et une architecture hérités ; l'adoption de la technologie blockchain nécessite une refonte complète de ces systèmes, ce qui, je dois le dire, n'est pas réalisable pour beaucoup d'entre eux.
La blockchain évolue et mûrit tout le temps, alors ne soyez pas surpris si ces inconvénients mentionnés aujourd'hui se transforment en pro plus tard. Le bitcoin, qui est une crypto-monnaie, est un exemple populaire de blockchain, une blockchain populaire qui a augmenté en dehors de la crypto-monnaie bitcoin est la blockchain Ethereum. Bitcoin se concentre sur les crypto-monnaies tandis qu'Ethereum se concentre davantage sur les contrats intelligents qui ont été le principal moteur des nouvelles plates-formes technologiques.
Lecture recommandée : Bitcoin vs. Ethereum : quelle est la différence ?
Commençons à construire notre API
Avec une solide compréhension de la blockchain, voyons maintenant comment créer une blockchain Ethereum et l'intégrer dans une API standard dans Node.js. L'objectif ultime est de bien comprendre comment les plateformes dApps et Blockchain sont construites.
La plupart des dApps ont une architecture et une structure similaires. Fondamentalement, nous avons un utilisateur qui interagit avec le frontend dApp - Web ou mobile - qui interagit ensuite avec les API backend. Le backend interagit alors sur demande avec le(s) contrat(s) intelligent(s) ou la blockchain via des nœuds publics ; ceux-ci exécutent des applications Node.js ou le backend utilise la blockchain en exécutant directement le logiciel Node.js. Il y a encore tellement de choses entre ces processus, du choix de créer une application entièrement décentralisée ou semi-décentralisée au choix de ce qui doit être décentralisé et de la manière de stocker en toute sécurité les clés privées.
Lectures recommandées : Architecture des applications décentralisées : back-end, sécurité et modèles de conception
Choses que nous devrions savoir en premier
Pour ce didacticiel, nous allons essayer de créer le backend d'une application de magasin de musique décentralisée qui utilise la puissance de la blockchain Ethereum pour stocker de la musique et la partager pour les téléchargements ou le streaming.
La structure de base de l'application que nous essayons de construire comporte trois parties :
- Authentification , qui se fait par email ; bien sûr, nous devons ajouter un mot de passe crypté à l'application.
- Stockage des données , les données musicales étant d'abord stockées dans ipfs et l'adresse de stockage est stockée dans la blockchain pour récupération.
- Récupération , tout utilisateur authentifié pouvant accéder aux données stockées sur notre plateforme et les utiliser.
Nous allons construire cela avec Node.js, mais vous pouvez également construire avec Python ou tout autre langage de programmation. Nous verrons également comment stocker des données multimédias dans IPFS, obtenir l'adresse et écrire des fonctions pour stocker cette adresse - et récupérer cette adresse à partir d'une blockchain avec le langage de programmation Solidity.
Voici quelques outils que nous devrions avoir à notre disposition pour construire ou travailler avec Ethereum et Node.js.
- Node.js
La première exigence est une application Node. Nous essayons de créer une application Node.js, nous avons donc besoin d'un compilateur. Veuillez vous assurer que Node.js est installé - et veuillez télécharger le dernier binaire de support à long terme ( LTS ). - Suite Truffe
Truffle est un environnement de développement et de test de contrats, ainsi qu'un pipeline d'actifs pour la blockchain Ethereum. Il fournit un environnement pour compiler, pipeliner et exécuter des scripts. Une fois que vous parlez de développer la blockchain, Truffle est un arrêt populaire où aller. Découvrez Truffle Suite sur Truffle Suite : Sweet Tools pour Smart Contracts. - Ganache CLI
Un autre outil qui fonctionne bien avec Truffle est Ganache-CLI. Il est construit et entretenu par l'équipe Truffle Suite. Après la construction et la compilation, vous avez besoin d'un émulateur pour développer et exécuter des applications blockchain, puis déployer des contrats intelligents à utiliser. Ganache vous permet de déployer plus facilement un contrat dans un émulateur sans utiliser d'argent réel pour les coûts de transaction, les comptes recyclables et bien plus encore. En savoir plus sur Ganache CLI sur Ganache CLI et Ganache. - Remixer
Remix est comme une alternative à Ganache, mais est également livré avec une interface graphique pour aider à naviguer dans le déploiement et le test des contrats intelligents Ethereum. Vous pouvez en savoir plus à ce sujet sur Remix — Ethereum IDE & community. Tout ce que vous avez à faire est de visiter https://remix.ethereum.org et d'utiliser l'interface graphique pour écrire et déployer des contrats intelligents. - Web3
Web3 est une collection de bibliothèques qui vous permet d'interagir avec un nœud Ethereum. Il peut s'agir de nœuds locaux ou distants du contrat via HTTP, IPC ou Web Sockets. Introduction à Web3.js · Ethereum Blockchain Developer Crash Course est un bon endroit pour en apprendre un peu plus sur Web3. - IPFS
Un protocole de base utilisé pour créer des dApps. Le système de fichiers interplanétaire (IPFS) est un protocole et un réseau peer-to-peer pour stocker et partager des données dans un système de fichiers distribué. IPFS alimente le Web distribué explique davantage sur IPFS et comment il est généralement utilisé.
Créer une API backend à partir de zéro
Nous devons donc d'abord créer un backend à utiliser, et nous utilisons Node.js. Lorsque nous voulons créer une nouvelle API Node.js, la première chose que nous allons faire est d'initialiser un package npm. Comme vous le savez probablement, npm signifie Node Package Manager , et il est préemballé avec le binaire Node.js. Nous créons donc un nouveau dossier et l'appelons "blockchain-music" . Nous ouvrons le terminal dans ce répertoire de dossier, puis exécutons la commande suivante :
$ npm init -y && touch server.js routes.js
Cela démarre le projet avec un fichier package.json et répond oui à toutes les invites. Ensuite, nous créons également un fichier server.js et un fichier routes.js pour écrire les fonctions de routes
dans l'API.
Après tout cela, vous devrez installer les packages dont nous avons besoin pour rendre notre construction simple et directe. Ce processus est continu, c'est-à-dire que vous pouvez installer un package à tout moment pendant le développement de votre projet.
Installons les plus importants dont nous avons besoin en ce moment :
- Express.js
- @truffe/contrat
- Truffe.js
- web3.js
- dotenv
-
short-id
- MongoDB
- Nodemon
Vous devrez également installer Truffle.js globalement afin de pouvoir l'utiliser partout dans votre environnement local. Si vous souhaitez les installer tous en même temps, exécutez le code suivant dans votre Terminal :
$ npm install nodemon truffle-contract dotenv mongodb shortid express web3 --save && npm install truffle -g
L'indicateur --save
permet d'enregistrer le nom du package dans le fichier package.json . L'indicateur -g
consiste à stocker ce package particulier globalement, afin que nous puissions l'utiliser dans n'importe quel projet sur lequel nous allons travailler.
Nous créons ensuite un fichier .env dans lequel nous pouvons stocker l'URI secret de notre base de données MongoDB pour l'utiliser. Pour ce faire, nous exécutons touch.env dans le terminal. Si vous n'avez pas encore de compte de base de données avec MongoDB, commencez par la page MongoDB.
Le package dotenv exporte notre variable stockée vers l'environnement de processus Node.js. Assurez-vous de ne pas pousser le fichier .env lorsque vous poussez vers des référentiels publics pour éviter de divulguer vos mots de passe et vos données privées.
Ensuite, nous devons ajouter des scripts pour les phases de construction et de développement de notre projet dans notre fichier package.json . Actuellement, notre package.json ressemble à ceci :
{ "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" } }
Nous allons ensuite ajouter un script de démarrage au fichier package.json pour utiliser le serveur nodemon afin que chaque fois que nous apportons une modification, il redémarre le serveur lui-même, et un script de construction qui utilise directement le serveur de nœud, cela pourrait ressembler à ceci :
{ "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" } }
Ensuite, nous devons initialiser Truffle pour l'utiliser dans notre contrat intelligent en utilisant le package Truffle que nous avons installé globalement plus tôt. Dans le même dossier de nos projets, nous exécutons la commande suivante ci-dessous dans notre terminal :
$ truffle init
Ensuite, nous pouvons commencer à écrire notre code dans notre fichier server.js . Encore une fois, nous essayons de créer une application de magasin de musique décentralisée simple, où les clients peuvent télécharger de la musique pour que tous les autres utilisateurs puissent y accéder et l'écouter.
Notre server.js doit être propre pour faciliter le couplage et le découplage des composants, de sorte que les routes et autres fonctionnalités seront placées dans d'autres fichiers comme routes.js . Notre exemple server.js pourrait être :
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'); }) })
Fondamentalement, ci-dessus, nous importons les bibliothèques dont nous avons besoin avec require
, puis nous ajoutons un middleware qui permet l'utilisation de JSON dans notre API en utilisant app.use
, puis nous nous connectons à notre base de données MongoDB et obtenons l'accès à la base de données, puis nous spécifions quel cluster de base de données nous essayons d'accéder (pour ce tutoriel, c'est "Cluster0" ). Après cela, nous appelons la fonction et l'importons depuis le fichier routes . Enfin, nous écoutons toute tentative de connexion sur le port 8082
.
Ce fichier server.js n'est qu'un barebone pour démarrer l'application. Notez que nous avons importé routes.js . Ce fichier contiendra les points de terminaison de route pour notre API. Nous avons également importé les packages que nous devions utiliser dans le fichier server.js et les avons initialisés.
Nous allons créer cinq points de terminaison pour la consommation des utilisateurs :
- Point de terminaison d'enregistrement pour enregistrer les utilisateurs uniquement par e-mail. Idéalement, nous le ferions avec un e-mail et un mot de passe, mais comme nous voulons simplement identifier chaque utilisateur, nous n'allons pas nous aventurer dans la sécurité et le hachage des mots de passe pour des raisons de brièveté de ce tutoriel.
POST /register Requirements: email
- Point de terminaison de connexion pour les utilisateurs par e-mail.
POST /login Requirements: email
- Point de terminaison de téléchargement pour les utilisateurs — l'API qui récupère les données du fichier musical. L'interface convertira les fichiers MP3/WAV en un tampon audio et enverra ce tampon à l'API.
POST /upload Requirements: name, title of music, music file buffer or URL stored
- Point de terminaison d'accès qui fournira les données de la mémoire tampon musicale à tout utilisateur enregistré qui en fait la demande et enregistre qui y a accédé.
GET /access/{email}/{id} Requirements: email, id
- Nous souhaitons également donner accès à l'intégralité de la bibliothèque musicale et renvoyer les résultats à un utilisateur enregistré.
GET /access/{email} Requirements: email
Ensuite, nous écrivons nos fonctions de route dans notre fichier routes.js . Nous utilisons les fonctionnalités de stockage et de récupération de la base de données, puis nous nous assurons d'exporter la fonction d'itinéraire à la fin du fichier pour permettre son importation dans un autre fichier ou dossier.
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
À l'intérieur de cette fonction de route
, nous avons de nombreuses autres fonctions appelées à la fois dans les paramètres app
et db
. Il s'agit des fonctions de point de terminaison de l'API qui permettent aux utilisateurs de spécifier un point de terminaison dans l'URL. En fin de compte, nous choisissons l'une de ces fonctions à exécuter et fournissons des résultats en réponse aux demandes entrantes.
Nous avons quatre principales fonctions de point de terminaison :
-
get
: pour lire les opérations d'enregistrement -
post
: pour créer des opérations d'enregistrement -
put
: pour mettre à jour les opérations d'enregistrement -
delete
: pour supprimer les opérations d'enregistrement
Dans cette fonction routes
, nous avons utilisé les opérations get
et post
. Nous utilisons post
pour les opérations d'enregistrement, de connexion et de téléchargement, et get
pour accéder aux opérations de données. Pour un peu plus d'explications à ce sujet, vous pouvez consulter l'article de Jamie Corkhill sur "How To Get Started With Node: An Introduction To APIs, HTTP And ES6+ JavaScript".
Dans le code ci-dessus, nous pouvons également voir certaines opérations de base de données comme dans la route de registre . Nous avons stocké l'e-mail d'un nouvel utilisateur avec db.createa
et vérifié l'e-mail dans la fonction de connexion avec db.findOne
. Maintenant, avant de pouvoir tout faire, nous devons nommer une collection ou une table avec la méthode db.collection
. C'est exactement ce que nous couvrirons ensuite.
Remarque : Pour en savoir plus sur les opérations de base de données dans MongoDB, consultez la documentation mongo Shell Methods.
Construire un contrat intelligent Blockchain simple avec solidité
Nous allons maintenant écrire un contrat Blockchain dans Solidity (c'est le langage dans lequel les contrats intelligents sont écrits) pour simplement stocker nos données et les récupérer quand nous en avons besoin. Les données que nous voulons stocker sont les données du fichier musical, ce qui signifie que nous devons télécharger la musique sur IPFS, puis stocker l'adresse du tampon dans une blockchain.
Tout d'abord, nous créons un nouveau fichier dans le dossier du contrat et le nommons Inbox.sol . Pour rédiger un contrat intelligent, il est utile d'avoir une bonne compréhension de Solidity, mais ce n'est pas difficile car il est similaire à JavaScript.
Remarque : Si vous souhaitez en savoir plus sur Solidity, j'ai ajouté quelques ressources au bas de l'article pour vous aider à démarrer.
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; } }
Dans notre contrat, nous avons deux fonctions principales : les fonctions sendIPFS
et getHash
. Avant de parler des fonctions, on peut voir qu'il fallait d'abord définir un contrat appelé Inbox
. À l'intérieur de cette classe, nous avons des structures utilisées dans l'objet ipfsInbox
(d'abord les événements, puis les modificateurs).
Après avoir défini les structures et les événements, nous devons initialiser le contrat en appelant la fonction constructor
. Ensuite, nous avons défini trois fonctions. (La fonction checkInbox
a été utilisée dans le test pour tester les résultats.)
Le sendIPFS
est l'endroit où l'utilisateur saisit l'identifiant et l'adresse de hachage, après quoi il est stocké sur la blockchain. La fonction getHash
récupère l'adresse de hachage lorsqu'elle reçoit l'identifiant. Encore une fois, la logique derrière cela est que nous voulons finalement stocker la musique dans IPFS. Pour tester son fonctionnement, vous pouvez accéder à un IDE Remix, copier, coller et tester votre contrat, ainsi que déboguer les erreurs et exécuter à nouveau (espérons que cela ne sera pas nécessaire !).
Après avoir testé que notre code fonctionne correctement dans le remix, passons à sa compilation en local avec la suite Truffle. Mais d'abord, nous devons apporter quelques modifications à nos fichiers et configurer notre émulateur à l'aide ganache-cli
:
Tout d'abord, installons ganache-cli
. Dans le même répertoire, exécutez la commande suivante dans votre terminal :
$ npm install ganache-cli -g
Ouvrons ensuite un autre terminal et exécutons une autre commande dans le même dossier :
$ ganache-cli
Cela démarre l'émulateur pour que notre contrat blockchain se connecte et fonctionne. Réduisez le terminal et continuez avec l'autre terminal que vous avez utilisé.
Allez maintenant dans le fichier truffle.js si vous utilisez un système d'exploitation Linux/Mac ou truffle-config.js sous Windows, et modifiez ce fichier pour qu'il ressemble à ceci :
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 } } };
Fondamentalement, ce que nous avons fait est d'ajouter le chemin du dossier de construction où le contrat intelligent est converti en fichiers JSON. Ensuite, nous avons également spécifié le réseau que Truffle doit utiliser pour la migration.
Ensuite, également dans le dossier migrations , créez un nouveau fichier nommé 2_migrate_inbox.js et ajoutez le code suivant dans les fichiers :
var IPFSInbox = artifacts.require("./Inbox.sol"); module.exports = function(deployer) { deployer.deploy(IPFSInbox); };
Nous l'avons fait pour obtenir le fichier de contrat et le déployer automatiquement sur un JSON, en utilisant la fonction de deployer
lors de la migration de Truffle.
Après les modifications ci-dessus, nous exécutons :
$ truffle compile
Nous devrions voir quelques messages à la fin qui montrent une compilation réussie, tels que :
> Compiled successfully using: - solc: 0.5.16+commit.9c3226ce.Emscripten.clang
Ensuite, nous migrons notre contrat en exécutant :
$ truffle migrate
Une fois que nous avons réussi à migrer nos contrats, nous devrions avoir quelque chose comme ceci à la fin :
Summary ======= > Total deployments: 1 > Final cost: 0.00973432 ETH
Et nous avons presque fini ! Nous avons construit notre API avec Node.js, et également mis en place et construit notre contrat intelligent.
Nous devrions également écrire des tests pour notre contrat afin de tester le comportement de notre contrat et nous assurer qu'il s'agit du comportement souhaité. Les tests sont généralement écrits et placés dans le dossier de test
. Un exemple de test écrit dans un fichier nommé InboxTest.js créé dans le dossier test est :
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") }) })
Nous exécutons donc notre test en exécutant ce qui suit :
$ truffle test
Il teste notre contrat avec les fichiers du dossier de test et affiche le nombre de tests réussis et échoués. Pour ce tutoriel, nous devrions obtenir :
$ 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)
Intégration du contrat intelligent à l'API backend à l'aide de Web3
La plupart du temps, lorsque vous voyez des didacticiels, vous voyez des applications décentralisées conçues pour intégrer l'interface directement à la blockchain. Mais il y a des moments où l'intégration au backend est également nécessaire, par exemple lors de l'utilisation d'API et de services backend tiers, ou lors de l'utilisation de la blockchain pour créer un CMS.
L'utilisation de Web3 est très importante pour cette cause, car elle nous aide à accéder aux nœuds Ethereum distants ou locaux et à les utiliser dans nos applications. Avant de continuer, nous discuterons des nœuds Ethereum locaux et distants. Les nœuds locaux sont les nœuds déployés sur notre système avec des émulateurs comme ganache-cli
mais un nœud distant est celui qui est déployé sur des robinets/plateformes en ligne comme ropsten ou rinkeby . Pour approfondir, vous pouvez suivre un didacticiel sur la façon de déployer sur ropsten un guide de 5 minutes sur le déploiement de contrats intelligents avec Truffle et Ropsten ou vous pouvez utiliser le fournisseur de portefeuille truffle et déployer via Un moyen plus simple de déployer vos contrats intelligents.
Nous utilisons ganache-cli
dans ce tutoriel, mais si nous déployions sur ropsten, nous aurions dû copier ou stocker notre adresse de contrat quelque part comme dans notre fichier .env, puis passer à la mise à jour du fichier server.js , importer web3, importer le contrat migré et configurez une instance 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)); }) })
Dans le fichier server.js , nous vérifions si l'instance web3 est déjà initialisée. Sinon, nous l'initialisons sur le port réseau que nous avons défini précédemment ( 8545
). Ensuite, nous construisons un contrat basé sur le fichier JSON migré et le package truffle-contract
, et définissons le fournisseur de contrat sur le fournisseur d'instance Web3 qui doit déjà avoir été initialisé.
Nous obtenons ensuite des comptes par web3.eth.getAccounts
. Pour la phase de développement, nous appelons la fonction déployée dans notre classe de contrat qui demande à ganache-cli
- qui est toujours en cours d'exécution - de nous donner une adresse de contrat à utiliser. Mais si nous avons déjà déployé notre contrat sur un nœud distant, nous appelons une fonction en saisissant l'adresse comme argument. L'exemple de fonction est commenté sous la variable lms
définie dans notre code ci-dessus. Ensuite, nous appelons la fonction routes
en saisissant l'instance de l'application, l'instance de la base de données, l'instance du contrat ( lms
) et les données des comptes comme arguments. Enfin, nous écoutons les requêtes sur le port 8082
.
De plus, nous devrions maintenant avoir installé le package MongoDB, car nous l'utilisons dans notre API en tant que base de données. Une fois que nous avons cela, nous passons à la page des itinéraires où nous utilisons les méthodes définies dans le contrat pour accomplir des tâches telles que la sauvegarde et la récupération des données musicales.
Au final, notre routes.js devrait ressembler à ceci :
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.
Et ensuite ?
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