Jak zbudować Node.js API dla Ethereum Blockchain?
Opublikowany: 2022-03-10Technologia Blockchain rośnie w ciągu ostatnich dziesięciu lat i wprowadziła do życia wiele produktów i platform, takich jak Chainalysis (technologia finansowa), Burstiq (technologia zdrowotna), Filament (IoT), Opus (strumieniowanie muzyki) i Okularowe (cyberbezpieczeństwo).
Na podstawie tych przykładów widzimy, że blockchain obejmuje wiele produktów i przypadków użycia — dzięki czemu jest bardzo istotny i użyteczny. W fintech (technologia finansowa) jest używany jako zdecentralizowane rejestry dla bezpieczeństwa i przejrzystości w miejscach takich jak Chain, Chainalysis, a także jest przydatny w technologii medycznej dla bezpieczeństwa poufnych danych zdrowotnych w Burstiq i Robomed – nie zapominając o technologii medialnej, takiej jak Opus i Audius, który również używa blockchain do przejrzystości tantiem, a tym samym uzyskuje pełne tantiemy.
Ocular wykorzystuje zabezpieczenia, które są dostarczane z blockchainem do zarządzania tożsamością w systemach biometrycznych, podczas gdy Filament wykorzystuje księgi blockchain do szyfrowanej komunikacji w czasie rzeczywistym. To pokazuje, jak ważny stał się dla nas blockchain, czyniąc nasze życie lepszym. Ale czym właściwie jest blockchain?
Blockchain to baza danych udostępniana w sieci komputerów. Gdy rekord zostanie dodany do łańcucha, dość trudno jest go zmienić. Aby upewnić się, że wszystkie kopie bazy danych są takie same, sieć przeprowadza ciągłe kontrole.
Dlaczego więc potrzebujemy blockchaina? Blockchain to bezpieczny sposób na rejestrowanie działań i utrzymywanie aktualności danych przy jednoczesnym zachowaniu historii ich historii w porównaniu z tradycyjnymi zapisami lub bazami danych, gdzie włamania, błędy i przestoje są bardzo możliwe. Dane nie mogą zostać uszkodzone przez nikogo ani przypadkowo usunięte, a Ty korzystasz zarówno z historycznego śladu danych, jak i natychmiastowo aktualnego rekordu, którego nie można usunąć lub stać się niedostępnym z powodu przestoju serwera.
Ponieważ cały łańcuch bloków jest zduplikowany na wielu komputerach, każdy użytkownik może wyświetlić cały łańcuch bloków. Transakcje lub zapisy są przetwarzane nie przez jednego centralnego administratora, ale przez sieć użytkowników, którzy pracują nad weryfikacją danych i osiągnięciem konsensusu.
Aplikacje wykorzystujące blockchain nazywane są dApps (zdecentralizowane aplikacje). Rozglądając się dzisiaj, najczęściej znajdziemy zdecentralizowane aplikacje w fintechu, ale blockchain wykracza poza zdecentralizowane finanse. Mamy platformy zdrowotne, platformy do strumieniowego przesyłania/udostępniania muzyki, platformy handlu elektronicznego, platformy cyberbezpieczeństwa i IOT zmierzające w kierunku zdecentralizowanych aplikacji (dApps), jak wspomniano powyżej.
Kiedy więc warto rozważyć użycie łańcucha bloków w naszych aplikacjach zamiast standardowej bazy danych lub rekordu?
Typowe zastosowania Blockchain
- Zarządzanie i zabezpieczanie relacji cyfrowych
Za każdym razem, gdy chcesz prowadzić długoterminową, przejrzystą ewidencję aktywów (na przykład, aby rejestrować prawa własności lub mieszkania), blockchain może być idealnym rozwiązaniem. W szczególności „inteligentne kontrakty” Ethereum doskonale nadają się do ułatwiania relacji cyfrowych. Dzięki inteligentnej umowie automatyczne płatności mogą być zwalniane, gdy strony transakcji zgadzają się, że ich warunki zostały spełnione. - Eliminacja pośredników/stróżów
Na przykład większość dostawców obecnie musi komunikować się z gośćmi za pośrednictwem scentralizowanej platformy agregatora, takiej jak Airbnb lub Uber (co z kolei ma wpływ na każdą transakcję). Blockchain może to wszystko zmienić.
Na przykład firma TUI jest tak przekonana o sile technologii blockchain, że jest pionierem w zakresie bezpośredniego łączenia hotelarzy i klientów. W ten sposób mogą dokonywać transakcji za pośrednictwem blockchain w łatwy, bezpieczny i spójny sposób, a nie za pośrednictwem centralnej platformy rezerwacji. - Rejestruj bezpieczne transakcje między partnerami, aby zapewnić zaufanie
Tradycyjna baza danych może być dobra do rejestrowania prostych transakcji między dwiema stronami, ale gdy sprawy stają się bardziej skomplikowane, blockchain może pomóc w zmniejszeniu wąskich gardeł i uproszczeniu relacji. Co więcej, dodatkowe bezpieczeństwo zdecentralizowanego systemu sprawia, że blockchain jest idealnym rozwiązaniem dla transakcji w ogóle.
Przykładem jest University Of Melbourne, który zaczął przechowywać swoje rekordy w blockchain. Najbardziej obiecującym przypadkiem użycia technologii blockchain w szkolnictwie wyższym jest przekształcenie „ewidencji” stopni naukowych, certyfikatów i dyplomów. Oszczędza to wiele kosztów związanych z serwerami dedykowanymi do przechowywania lub rejestrowania. - Prowadzenie rejestrów przeszłych działań dla aplikacji, w których dane są w stałym przepływie
Blockchain to lepszy i bezpieczniejszy sposób rejestrowania aktywności i utrzymywania aktualności danych przy jednoczesnym zachowaniu historii ich historii. Dane nie mogą zostać przez nikogo uszkodzone ani przypadkowo usunięte, a Ty korzystasz zarówno z historycznego śladu danych, jak i natychmiastowo aktualnego rekordu. Przykładem dobrego przypadku użycia jest blockchain w e-commerce, zarówno blockchain, jak i e-commerce obejmują transakcje.
Blockchain sprawia, że te transakcje są bezpieczniejsze i szybsze, podczas gdy na nich opierają się działania e-commerce. Technologia Blockchain umożliwia użytkownikom udostępnianie i bezpieczne przechowywanie zasobów cyfrowych zarówno automatycznie, jak i ręcznie. Technologia ta może obsługiwać czynności użytkownika, takie jak przetwarzanie płatności, wyszukiwanie produktów, zakupy produktów i obsługa klienta. Zmniejsza również wydatki na zarządzanie zapasami i przetwarzanie płatności. - Decentralizacja sprawia, że można go używać w dowolnym miejscu
W przeciwieństwie do sytuacji, w której musimy ograniczyć się do określonego regionu z różnych powodów, takich jak polityka wymiany walut, ograniczenia bramek płatności utrudniają dostęp do zasobów finansowych wielu krajów spoza Twojego regionu lub kontynentu. Wraz z rozwojem i potęgą decentralizacji blockchain lub systemu peer-to-peer, współpraca z innymi krajami staje się łatwiejsza.
Na przykład sklep e-commerce w Europie może mieć konsumentów w Afryce i nie wymagać od pośrednika przetwarzania ich żądań płatności. Co więcej, technologie te otwierają sklepom internetowym drzwi do korzystania z rynków konsumenckich w odległych krajach z bitcoinem, czyli kryptowalutą. - Blockhain jest neutralny pod względem technologii
Blockchain działa ze wszystkimi stosami technologicznymi używanymi przez programistę. Nie musisz uczyć się Node'a jako programista Pythona, aby używać blockchain lub uczyć się Golanga. To sprawia, że blockchain jest bardzo łatwy w użyciu.
W rzeczywistości możemy go używać bezpośrednio z naszymi aplikacjami front-endowymi w Vue/React, a łańcuch bloków działa jako nasza jedyna baza danych do prostych, nieskomplikowanych zadań i przypadków użycia, takich jak przesyłanie danych lub uzyskiwanie skrótów do wyświetlania rekordów dla naszych użytkowników lub budowanie gier frontendowych, takich jak kasyno gry i gry bukmacherskie (w których potrzebne jest duże zaufanie). Ponadto, dzięki potędze web3, możemy bezpośrednio przechowywać dane w łańcuchu.
Widzieliśmy już sporo zalet korzystania z blockchaina, ale kiedy w ogóle nie powinniśmy zawracać sobie głowy używaniem blockchaina?
Wady Blockchain
- Zmniejszona prędkość transakcji cyfrowych
Łańcuchy bloków wymagają ogromnych ilości mocy obliczeniowej, co zwykle zmniejsza szybkość transakcji cyfrowych, chociaż istnieją rozwiązania, które zalecają stosowanie scentralizowanych baz danych, gdy potrzebne są szybkie transakcje w milisekundach. - Niezmienność danych
Niezmienność danych zawsze była jedną z największych wad łańcucha bloków. Oczywiste jest, że wiele systemów korzysta z tego, w tym łańcuch dostaw, systemy finansowe i tak dalej. Cierpi jednak na to, że raz zapisanych danych nie można ich usunąć. Każdy człowiek na ziemi ma prawo do prywatności. Jeśli jednak ta sama osoba korzysta z platformy cyfrowej działającej w oparciu o technologię blockchain, to nie będzie w stanie usunąć jej śladu z systemu, gdy tego nie chce. W prostych słowach nie ma sposobu, aby mógł usunąć swój ślad — pozostawiając prawo do prywatności na kawałki. - Wymaga wiedzy eksperckiej
Wdrażanie i zarządzanie projektem blockchain jest trudne. Przejście przez cały proces wymaga gruntownej wiedzy. Właśnie dlatego trudno jest znaleźć specjalistów lub ekspertów od blockchain, ponieważ wyszkolenie eksperta od blockchain zajmuje dużo czasu i wysiłku. Dlatego ten artykuł jest dobrym miejscem do rozpoczęcia i dobrym przewodnikiem, jeśli już zacząłeś. - Interoperacyjność
Wiele sieci blockchain ciężko pracujących nad rozwiązaniem problemu rozproszonej księgi w unikalny sposób utrudnia ich powiązanie lub integrację. To utrudnia komunikację między różnymi sieciami. - Integracja ze starszymi aplikacjami
Wiele firm i aplikacji nadal korzysta ze starszych systemów i architektury; przyjęcie technologii blockchain wymaga całkowitej przebudowy tych systemów, co muszę powiedzieć, że dla wielu z nich jest niewykonalne.
Blockchain wciąż ewoluuje i dojrzewa, więc nie zdziw się, jeśli te wady, o których mowa dzisiaj, zostaną później przekształcone w profesjonalistę. Bitcoin, który jest kryptowalutą, jest jednym z popularnych przykładów blockchain, popularnego blockchainu, który oprócz kryptowaluty bitcoin rośnie w siłę, to blockchain Ethereum. Bitcoin koncentruje się na kryptowalutach, podczas gdy Ethereum skupia się bardziej na inteligentnych kontraktach, które były główną siłą napędową nowych platform technologicznych.
Zalecana literatura : Bitcoin vs. Ethereum: jaka jest różnica?
Zacznijmy budować nasze API
Mając solidne zrozumienie blockchain, przyjrzyjmy się teraz, jak zbudować blockchain Ethereum i zintegrować go ze standardowym API w Node.js. Ostatecznym celem jest dobre zrozumienie, w jaki sposób budowane są platformy dApps i Blockchain.
Większość dApps ma podobną architekturę i strukturę. Zasadniczo mamy użytkownika, który wchodzi w interakcję z frontendem dApp — internetowym lub mobilnym — który następnie wchodzi w interakcję z interfejsami API zaplecza. Backend następnie, na żądanie, wchodzi w interakcję z inteligentnymi umowami lub blockchainem za pośrednictwem publicznych węzłów; albo uruchamiają one aplikacje Node.js, albo backend wykorzystuje blockchain, bezpośrednio uruchamiając oprogramowanie Node.js. Wciąż jest tak wiele rzeczy pomiędzy tymi procesami, od wyboru stworzenia w pełni zdecentralizowanej lub częściowo zdecentralizowanej aplikacji po wybór tego, co powinno być zdecentralizowane i jak bezpiecznie przechowywać klucze prywatne.
Zalecana literatura : Zdecentralizowana architektura aplikacji: zaplecza, wzorce bezpieczeństwa i projektowania
Rzeczy, które powinniśmy wiedzieć najpierw
W tym samouczku spróbujemy zbudować zaplecze zdecentralizowanej aplikacji sklepu muzycznego, która wykorzystuje moc blockchain Ethereum do przechowywania muzyki i udostępniania jej do pobierania lub przesyłania strumieniowego.
Podstawowa struktura aplikacji, którą próbujemy zbudować, składa się z trzech części:
- Uwierzytelnianie , które odbywa się przez e-mail; oczywiście musimy dodać do aplikacji zaszyfrowane hasło.
- Przechowywanie danych , przy czym dane muzyczne są najpierw przechowywane w ipfs, a adres przechowywania jest przechowywany w łańcuchu bloków w celu ich pobrania.
- Pobieranie , przy czym każdy uwierzytelniony użytkownik może uzyskać dostęp do danych przechowywanych na naszej platformie i z nich korzystać.
Będziemy to budować za pomocą Node.js, ale możesz także budować za pomocą Pythona lub dowolnego innego języka programowania. Zobaczymy również, jak przechowywać dane multimedialne w IPFS, uzyskać adres i funkcje zapisu, w których ten adres będzie przechowywany — oraz jak pobrać ten adres z łańcucha bloków za pomocą języka programowania Solidity.
Oto kilka narzędzi, które powinniśmy mieć do dyspozycji do budowania lub pracy z Ethereum i Node.js.
- Node.js
Pierwszym wymaganiem jest aplikacja typu Node. Próbujemy zbudować aplikację Node.js, więc potrzebujemy kompilatora. Upewnij się, że masz zainstalowany Node.js — i pobierz najnowszą wersję binarną długoterminowego wsparcia ( LTS ). - Apartament z truflami
Truffle to środowisko do opracowywania i testowania kontraktów, a także potok zasobów dla blockchain Ethereum. Zapewnia środowisko do kompilowania, potokowania i uruchamiania skryptów. Kiedy mówisz o rozwijaniu blockchain, Truffle jest popularnym przystankiem. Sprawdź Truffle Suite w Truffle Suite: Sweet Tools for Smart Contracts. - Ganache CLI
Innym narzędziem, które dobrze współpracuje z Truffle, jest Ganache-CLI. Jest zbudowany i utrzymywany przez zespół Truffle Suite. Po skompilowaniu i skompilowaniu potrzebujesz emulatora do tworzenia i uruchamiania aplikacji blockchain, a następnie wdrażania inteligentnych kontraktów do użycia. Ganache ułatwia wdrożenie umowy w emulatorze bez wykorzystywania rzeczywistych pieniędzy na koszty transakcji, konta nadające się do recyklingu i wiele więcej. Przeczytaj więcej o Ganache CLI w Ganache CLI i Ganache. - Remiks
Remix jest jak alternatywa dla Ganache, ale jest również wyposażony w graficzny interfejs użytkownika, który pomaga nawigować we wdrażaniu i testowaniu inteligentnych kontraktów Ethereum. Możesz dowiedzieć się więcej na ten temat w Remix — Ethereum IDE i społeczność. Wszystko, co musisz zrobić, to odwiedzić https://remix.ethereum.org i użyć GUI do pisania i wdrażania inteligentnych kontraktów. - Sieć3
Web3 to zbiór bibliotek, które umożliwiają interakcję z węzłem Ethereum. Mogą to być lokalne lub zdalne węzły kontraktu poprzez HTTP, IPC lub Web Sockets. Wprowadzenie do Web3.js · Ethereum Blockchain Developer Crash Course to dobre miejsce, aby dowiedzieć się trochę o Web3. - IPFS
Podstawowy protokół używany w budowaniu dApps. Międzyplanetarny system plików (IPFS) to protokół i sieć peer-to-peer do przechowywania i udostępniania danych w rozproszonym systemie plików. IPFS Powers the Distributed Web wyjaśnia więcej na temat IPFS i sposobu, w jaki jest zwykle używany.
Tworzenie Backend API od podstaw
Więc najpierw musimy stworzyć backend do użycia i używamy Node.js. Gdy chcemy utworzyć nowe API Node.js, pierwszą rzeczą, którą zrobimy, jest zainicjowanie pakietu npm. Jak zapewne wiesz, npm to skrót od Node Package Manager , który jest dostarczany z plikiem binarnym Node.js. Tworzymy więc nowy folder i nazywamy go „blockchain-music” . Otwieramy terminal w tym katalogu folderów, a następnie uruchamiamy następujące polecenie:
$ npm init -y && touch server.js routes.js
To uruchamia projekt z plikiem package.json i odpowiada tak na wszystkie monity. Następnie tworzymy również plik server.js i plik route.js do zapisywania funkcji routes
w API.
Po tym wszystkim będziesz musiał zainstalować pakiety, których potrzebujemy, aby nasza kompilacja była łatwa i nieskomplikowana. Jest to proces ciągły, tzn. pakiet możesz zainstalować w dowolnym momencie rozwoju swojego projektu.
Zainstalujmy teraz najważniejsze, których potrzebujemy:
- Express.js
- @trufla/umowa
- trufla.js
- web3.js
- dotenv
-
short-id
- MongoDB
- nodemon
Będziesz także musiał zainstalować Truffle.js globalnie , więc możesz go używać wszędzie w swoim lokalnym środowisku. Jeśli chcesz zainstalować je wszystkie naraz, uruchom następujący kod w swoim terminalu:
$ npm install nodemon truffle-contract dotenv mongodb shortid express web3 --save && npm install truffle -g
Flaga --save
służy do zapisania nazwy pakietu w pliku package.json . Flaga -g
służy do przechowywania tego konkretnego pakietu globalnie, abyśmy mogli go użyć w dowolnym projekcie, nad którym będziemy pracować.
Następnie tworzymy plik .env , w którym możemy przechowywać nasz tajny identyfikator URI bazy danych MongoDB do użytku. Robimy to, uruchamiając touch.env w Terminalu. Jeśli nie masz jeszcze konta bazy danych w MongoDB, zacznij od strony MongoDB.
Pakiet dotenv eksportuje naszą przechowywaną zmienną do środowiska procesowego Node.js. Upewnij się, że nie wypychasz pliku .env podczas wypychania do publicznych repozytoriów, aby uniknąć wycieku haseł i prywatnych danych.
Następnie musimy dodać skrypty dla faz budowania i rozwoju naszego projektu w naszym pliku package.json . Obecnie nasz plik package.json wygląda tak:
{ "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" } }
Następnie dodamy skrypt startowy do pliku package.json , aby użyć serwera nodemon, aby za każdym razem, gdy dokonamy zmiany, sam zrestartuje serwer, a skrypt kompilacji, który korzysta bezpośrednio z serwera node, może wyglądać tak:
{ "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" } }
Następnie musimy zainicjować Truffle do użycia w naszym inteligentnym kontrakcie, używając pakietu Truffle, który zainstalowaliśmy globalnie wcześniej. W tym samym folderze naszych projektów uruchamiamy w naszym terminalu następujące polecenie:
$ truffle init
Następnie możemy zacząć pisać nasz kod w naszym pliku server.js . Ponownie próbujemy zbudować prostą zdecentralizowaną aplikację sklepu muzycznego, w której klienci będą mogli przesyłać muzykę, aby każdy inny użytkownik miał do niej dostęp i mógł jej słuchać.
Nasz server.js powinien być czysty, aby ułatwić łączenie i rozprzęganie komponentów, więc trasy i inne funkcje zostaną umieszczone w innych plikach, takich jak route.js . Nasz przykładowy plik server.js może wyglądać tak:
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'); }) })
Zasadniczo powyżej importujemy potrzebne nam biblioteki z require
, następnie dodajemy oprogramowanie pośredniczące, które pozwala na użycie JSON w naszym API za pomocą app.use
, następnie łączymy się z naszą bazą danych MongoDB i uzyskujemy dostęp do bazy danych, a następnie określamy, który klaster bazy danych próbujemy uzyskać dostęp (w tym samouczku jest to „Cluster0” ). Następnie wywołujemy funkcję i importujemy ją z pliku tras . Na koniec nasłuchujemy wszelkich prób połączeń na porcie 8082
.
Ten plik server.js jest tylko podstawowym narzędziem do uruchomienia aplikacji. Zauważ, że zaimportowaliśmy routes.js . Ten plik będzie zawierał punkty końcowe trasy dla naszego API. Zaimportowaliśmy również pakiety, których potrzebowaliśmy do użycia w pliku server.js i zainicjowaliśmy je.
Stworzymy pięć punktów końcowych do wykorzystania przez użytkowników:
- Punkt końcowy rejestracji do rejestracji użytkowników tylko przez e-mail. Idealnie zrobilibyśmy to za pomocą adresu e-mail i hasła, ale ponieważ chcemy tylko zidentyfikować każdego użytkownika, nie zamierzamy zapuszczać się na zabezpieczenia haseł i haszowanie ze względu na zwięzłość tego samouczka.
POST /register Requirements: email
- Punkt końcowy logowania dla użytkowników przez e-mail.
POST /login Requirements: email
- Prześlij punkt końcowy dla użytkowników — interfejs API, który pobiera dane z pliku muzycznego. Frontend przekonwertuje pliki MP3/WAV na bufor audio i wyśle ten bufor do API.
POST /upload Requirements: name, title of music, music file buffer or URL stored
- Punkt końcowy dostępu, który dostarczy dane z bufora muzycznego każdemu zarejestrowanemu użytkownikowi, który o to poprosi, i zarejestruje, kto uzyskał do niego dostęp.
GET /access/{email}/{id} Requirements: email, id
- Chcemy również zapewnić dostęp do całej biblioteki muzycznej i zwrócić wyniki zarejestrowanemu użytkownikowi.
GET /access/{email} Requirements: email
Następnie zapisujemy nasze funkcje tras w naszym pliku Routes.js . Korzystamy z funkcji przechowywania i pobierania bazy danych, a następnie upewniamy się, że eksportujemy funkcję trasy na końcu pliku, aby umożliwić import do innego pliku lub folderu.
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
Wewnątrz tej funkcji route
mamy wiele innych funkcji wywoływanych zarówno w parametrach app
, jak i db
. Są to funkcje punktu końcowego interfejsu API, które umożliwiają użytkownikom określenie punktu końcowego w adresie URL. Ostatecznie wybieramy jedną z tych funkcji do wykonania i dostarczamy wyniki w odpowiedzi na przychodzące żądania.
Mamy cztery główne funkcje punktów końcowych:
-
get
: do odczytu operacji na rekordach -
post
: do tworzenia operacji na rekordach -
put
: do aktualizacji operacji na rekordach -
delete
: do usuwania operacji na rekordach
W tej funkcji routes
użyliśmy operacji get
i post
. Używamy post
do rejestracji, logowania i operacji przesyłania, a także get
dostępu do operacji na danych. Więcej informacji na ten temat można znaleźć w artykule Jamiego Corkhilla „Jak zacząć pracę z węzłem: wprowadzenie do API, HTTP i ES6+ JavaScript”.
W powyższym kodzie możemy również zobaczyć niektóre operacje na bazie danych, takie jak w ścieżce rejestru . Zapisaliśmy wiadomość e-mail nowego użytkownika za pomocą db.createa
i sprawdziliśmy e-mail w funkcji logowania za pomocą db.findOne
. Teraz, zanim będziemy mogli to wszystko zrobić, musimy nazwać kolekcję lub tabelę za pomocą metody db.collection
. Dokładnie to omówimy w następnej kolejności.
Uwaga : Aby dowiedzieć się więcej o operacjach na bazie danych w MongoDB, zapoznaj się z dokumentacją Mongo Shell Methods.
Budowanie prostego inteligentnego kontraktu Blockchain z solidnością
Teraz napiszemy kontrakt Blockchain w Solidity (w tym języku są napisane inteligentne kontrakty), aby po prostu przechowywać nasze dane i pobierać je, gdy ich potrzebujemy. Dane, które chcemy przechowywać, to dane pliku muzycznego, co oznacza, że musimy przesłać muzykę do IPFS, a następnie zapisać adres bufora w łańcuchu bloków.
Najpierw tworzymy nowy plik w folderze kontraktu i nazywamy go Inbox.sol . Aby napisać inteligentny kontrakt, przydatne jest dobre zrozumienie Solidity, ale nie jest to trudne, ponieważ jest podobne do JavaScript.
Uwaga : jeśli chcesz dowiedzieć się więcej o Solidity, dodałem kilka zasobów na dole artykułu, aby zacząć.
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; } }
W naszej umowie mamy dwie główne funkcje: funkcje sendIPFS
i getHash
. Zanim zaczniemy mówić o funkcjach, widzimy, że najpierw musieliśmy zdefiniować kontrakt o nazwie Inbox
. Wewnątrz tej klasy mamy struktury użyte w obiekcie ipfsInbox
(najpierw zdarzenia, potem modyfikatory).
Po zdefiniowaniu struktur i zdarzeń musimy zainicjować kontrakt wywołując funkcję constructor
. Następnie zdefiniowaliśmy trzy funkcje. (Funkcja checkInbox
została użyta w teście do testowania wyników.)
sendIPFS
to miejsce, w którym użytkownik wprowadza identyfikator i adres skrótu, po czym jest przechowywany w łańcuchu bloków. Funkcja getHash
pobiera adres skrótu, gdy otrzymuje identyfikator. Ponownie, logika za tym kryje się w tym, że ostatecznie chcemy przechowywać muzykę w IPFS. Aby przetestować, jak to działa, możesz wskoczyć do Remix IDE, skopiować, wkleić i przetestować swój kontrakt, a także debugować wszelkie błędy i uruchomić ponownie (mam nadzieję, że nie będzie to potrzebne!).
Po przetestowaniu, czy nasz kod działa poprawnie w remiksie, przejdźmy do kompilacji go lokalnie za pomocą pakietu Truffle. Ale najpierw musimy dokonać pewnych zmian w naszych plikach i skonfigurować nasz emulator za pomocą ganache-cli
:
Najpierw zainstalujmy ganache-cli
. W tym samym katalogu uruchom następujące polecenie w swoim terminalu:
$ npm install ganache-cli -g
Następnie otwórzmy inny Terminal i uruchommy inne polecenie w tym samym folderze:
$ ganache-cli
To uruchamia emulator naszej umowy blockchain, aby połączyć się i działać. Zminimalizuj terminal i kontynuuj z innym używanym terminalem.
Teraz przejdź do pliku truffle.js , jeśli używasz systemu Linux/Mac OS lub truffle-config.js w systemie Windows, i zmodyfikuj ten plik, aby wyglądał tak:
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 } } };
Zasadniczo dodaliśmy ścieżkę folderu kompilacji, w którym inteligentny kontrakt jest konwertowany na pliki JSON. Następnie określiliśmy również sieć, której Truffle powinien używać do migracji.
Następnie, również w folderze migrations , utwórz nowy plik o nazwie 2_migrate_inbox.js i dodaj następujący kod wewnątrz plików:
var IPFSInbox = artifacts.require("./Inbox.sol"); module.exports = function(deployer) { deployer.deploy(IPFSInbox); };
Zrobiliśmy to, aby pobrać plik umowy i automatycznie wdrożyć go do JSON, korzystając z funkcji wdrażania podczas migracji deployer
.
Po powyższych zmianach uruchamiamy:
$ truffle compile
Powinniśmy zobaczyć na końcu komunikaty, które pokazują udaną kompilację, takie jak:
> Compiled successfully using: - solc: 0.5.16+commit.9c3226ce.Emscripten.clang
Następnie migrujemy nasz kontrakt, uruchamiając:
$ truffle migrate
Po pomyślnej migracji naszych kontraktów powinniśmy mieć na końcu coś takiego:
Summary ======= > Total deployments: 1 > Final cost: 0.00973432 ETH
I prawie skończyliśmy! Zbudowaliśmy nasze API za pomocą Node.js, a także skonfigurowaliśmy i zbudowaliśmy nasz inteligentny kontrakt.
Powinniśmy również napisać testy dla naszej umowy, aby przetestować zachowanie naszej umowy i upewnić się, że jest to pożądane zachowanie. Testy są zwykle zapisywane i umieszczane w folderze test
. Przykładowy test zapisany w pliku o nazwie InboxTest.js utworzonym w folderze test to:
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") }) })
Dlatego uruchamiamy nasz test, uruchamiając:
$ truffle test
Testuje naszą umowę z plikami w folderze test i pokazuje liczbę pomyślnych i nieudanych testów. W tym samouczku powinniśmy otrzymać:
$ 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)
Integracja Smart Contract z Backend API za pomocą Web3
W większości przypadków, gdy widzisz samouczki, widzisz zdecentralizowane aplikacje zbudowane w celu integracji frontendu bezpośrednio z blockchainem. Ale zdarzają się sytuacje, w których potrzebna jest również integracja z backendem, na przykład podczas korzystania z zewnętrznych interfejsów API i usług backendowych lub podczas korzystania z blockchain do budowy CMS.
Korzystanie z Web3 jest bardzo ważne w tym celu, ponieważ pomaga nam uzyskać dostęp do zdalnych lub lokalnych węzłów Ethereum i używać ich w naszych aplikacjach. Zanim przejdziemy dalej, omówimy lokalne i zdalne węzły Ethereum. Węzły lokalne to węzły wdrożone w naszym systemie z emulatorami, takimi jak ganache-cli
ale węzeł zdalny to taki, który jest wdrożony na kranach/platformach online, takich jak ropsten lub rinkeby . Aby zagłębić się głębiej, możesz skorzystać z samouczka, jak wdrożyć na ropsten 5-minutowy przewodnik po wdrażaniu inteligentnych kontraktów za pomocą Truffle i Ropsten lub możesz użyć dostawcy portfela truffle i wdrożyć za pomocą łatwiejszego sposobu wdrażania inteligentnych kontraktów.
W tym samouczku używamy ganache-cli
, ale gdybyśmy wdrażali na ropsten, powinniśmy skopiować lub zapisać nasz adres umowy gdzieś, tak jak w naszym pliku .env, a następnie przejść do aktualizacji pliku server.js , importować web3, importować zmigrowanej umowy i skonfiguruj instancję 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)); }) })
W pliku server.js sprawdzamy, czy instancja web3 jest już zainicjalizowana. Jeśli nie, inicjujemy go na porcie sieciowym, który zdefiniowaliśmy wcześniej ( 8545
). Następnie budujemy kontrakt na podstawie zmigrowanego pliku JSON i pakietu truffle-contract
oraz ustawiamy dostawcę kontraktu na dostawcę instancji Web3, który do tej pory musiał zostać zainicjowany.
Następnie otrzymujemy konta przez web3.eth.getAccounts
. Na etapie programowania wywołujemy wdrożoną funkcję w naszej klasie kontraktu, która prosi ganache-cli
— który wciąż działa — o podanie adresu kontraktu do użycia. Ale jeśli już wdrożyliśmy nasz kontrakt w zdalnym węźle, wywołujemy funkcję wprowadzającą adres jako argument. Przykładowa funkcja jest skomentowana pod zdefiniowaną zmienną lms
w powyższym kodzie. Następnie wywołujemy funkcję routes
wprowadzając instancję aplikacji, instancję bazy danych, instancję kontraktu ( lms
) i dane kont jako argumenty. Na koniec nasłuchujemy żądań na porcie 8082
.
Do tej pory powinniśmy również zainstalować pakiet MongoDB, ponieważ używamy go w naszym API jako naszej bazy danych. Gdy już to mamy, przechodzimy na stronę tras, gdzie używamy metod określonych w umowie, aby wykonać zadania, takie jak zapisywanie i pobieranie danych muzycznych.
W końcu nasz route.js powinien wyglądać tak:
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.
Co następne?
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