Como construir uma API Node.js para Ethereum Blockchain

Publicados: 2022-03-10
Resumo rápido ↬ Neste artigo, John Agbanusi explica como você pode construir uma API Node.js do zero construindo e implantando um Blockchain Ethereum para descentralização. Ele também mostra um processo passo a passo de integração da API e do blockchain em uma única API chamada “API de aplicativo descentralizado”.

A tecnologia Blockchain está em ascensão nos últimos dez anos e deu vida a um bom número de produtos e plataformas, como Chainalysis (tecnologia financeira), Burstiq (tecnologia de saúde), Filament (IoT), Opus (transmissão de música) e Ocular (cibersegurança).

A partir desses exemplos, podemos ver que o blockchain abrange muitos produtos e casos de uso - tornando-o muito essencial e útil. Em fintech (tecnologia financeira), é usado como ledgers descentralizados para segurança e transparência em locais como Chain, Chainalysis e também é útil em tecnologia de saúde para a segurança de dados de saúde confidenciais em Burstiq e Robomed - para não esquecer tecnologia de mídia como Opus e Audius que também usam blockchain para transparência de royalties e, assim, obtêm royalties totais.

Ocular usa segurança que vem com blockchain para gerenciamento de identidade para sistemas biométricos, enquanto Filament usa ledgers blockchain para comunicação criptografada em tempo real. Isso mostra como o blockchain se tornou essencial para nós, tornando nossas vidas melhores. Mas o que exatamente é uma blockchain?

Um blockchain é um banco de dados que é compartilhado em uma rede de computadores. Uma vez que um registro foi adicionado à cadeia, é muito difícil alterá-lo. Para garantir que todas as cópias do banco de dados sejam iguais, a rede faz verificações constantes.

Então, por que precisamos de blockchain? Blockchain é uma maneira segura de registrar atividades e manter os dados atualizados, mantendo um registro de seu histórico em comparação com os registros ou bancos de dados tradicionais, onde hacks, erros e tempos de inatividade são muito possíveis. Os dados não podem ser corrompidos por ninguém ou excluídos acidentalmente, e você se beneficia tanto de uma trilha histórica de dados quanto de um registro instantaneamente atualizado que não pode ser apagado ou ficar inacessível devido ao tempo de inatividade de um servidor.

Como todo o blockchain é duplicado em muitos computadores, qualquer usuário pode visualizar todo o blockchain. As transações ou registros são processados ​​não por um administrador central, mas por uma rede de usuários que trabalham para verificar os dados e chegar a um consenso.

Os aplicativos que usam blockchain são chamados de dApps (Aplicativos Descentralizados). Olhando em volta hoje, encontraremos principalmente aplicativos descentralizados em fintech, mas o blockchain vai além das finanças descentralizadas. Temos plataformas de saúde, plataformas de streaming/compartilhamento de música, plataformas de comércio eletrônico, plataformas de segurança cibernética e IOTs avançando para aplicativos descentralizados (dApps), conforme citado acima.

Então, quando faria sentido considerar o uso de blockchain para nossos aplicativos, em vez de um banco de dados ou registro padrão?

Mais depois do salto! Continue lendo abaixo ↓

Aplicações comuns do Blockchain

  • Gerenciando e protegendo relacionamentos digitais
    Sempre que você quiser manter um registro transparente e de longo prazo de ativos (por exemplo, para registrar direitos de propriedade ou apartamento), o blockchain pode ser a solução ideal. Os 'contratos inteligentes' da Ethereum, em particular, são ótimos para facilitar os relacionamentos digitais. Com um contrato inteligente, os pagamentos automatizados podem ser liberados quando as partes em uma transação concordam que suas condições foram atendidas.
  • Eliminando intermediários/porteiros
    Por exemplo, a maioria dos provedores atualmente precisa interagir com os hóspedes por meio de uma plataforma agregadora centralizada, como Airbnb ou Uber (que, por sua vez, reduz cada transação). Blockchain pode mudar tudo isso.
    Por exemplo, a TUI está tão convencida do poder do blockchain que é uma forma pioneira de conectar hoteleiros e clientes diretamente. Dessa forma, eles podem realizar transações via blockchain de maneira fácil, segura e consistente, em vez de uma plataforma central de reservas.
  • Registre transações seguras entre parceiros para garantir a confiança
    Um banco de dados tradicional pode ser bom para registrar transações simples entre duas partes, mas quando as coisas ficam mais complicadas, o blockchain pode ajudar a reduzir gargalos e simplificar os relacionamentos. Além disso, a segurança adicional de um sistema descentralizado torna o blockchain ideal para transações em geral.
    Um exemplo é a Universidade de Melbourne que começou a armazenar seus registros em blockchain. O caso de uso mais promissor para blockchain no ensino superior é transformar a “manutenção de registros” de diplomas, certificados e diplomas. Isso economiza muito custo de servidores dedicados para armazenamento ou registros.
  • Mantendo registros de ações anteriores para aplicativos em que os dados estão em fluxo constante
    Blockchain é uma maneira melhor e mais segura de registrar a atividade e manter os dados atualizados, mantendo um registro de seu histórico. Os dados não podem ser corrompidos por ninguém ou excluídos acidentalmente, e você se beneficia tanto de uma trilha histórica de dados quanto de um registro instantaneamente atualizado. Um exemplo de um bom caso de uso é o blockchain no e-commerce, tanto o blockchain quanto o e-commerce envolvem transações.
    O Blockchain torna essas transações mais seguras e rápidas, enquanto as atividades de comércio eletrônico dependem delas. A tecnologia Blockchain permite que os usuários compartilhem e armazenem com segurança ativos digitais de forma automática e manual. Essa tecnologia tem a capacidade de lidar com atividades do usuário, como processamento de pagamentos, pesquisas de produtos, compras de produtos e atendimento ao cliente. Também reduz os gastos com gestão de estoque e processamento de pagamentos.
  • A descentralização possibilita o uso em qualquer lugar
    Ao contrário de antes, onde temos que nos restringir a uma determinada região devido a vários motivos, como políticas de câmbio, limitações de gateways de pagamento dificultam o acesso a recursos financeiros de muitos países que não estão em sua região ou continente. Com a ascensão e o poder da descentralização do blockchain ou sistema peer-to-peer, fica mais fácil trabalhar com outros países.
    Por exemplo, uma loja de comércio eletrônico na Europa pode ter consumidores na África e não exigir um intermediário para processar suas solicitações de pagamento. Além disso, essas tecnologias estão abrindo portas para os varejistas online fazerem uso dos mercados consumidores em países distantes com bitcoin, ou seja, uma criptomoeda.
  • Blockhain é tecnologicamente neutro
    Blockchain funciona com toda e qualquer pilha de tecnologia usada por um desenvolvedor. Você não precisa aprender Node como desenvolvedor Python para usar blockchain ou aprender Golang. Isso torna o blockchain muito fácil de usar.
    Na verdade, podemos usá-lo diretamente com nossos aplicativos front-end em Vue/React com o blockchain atuando como nosso único banco de dados para tarefas simples e descomplicadas e casos de uso como upload de dados ou obtenção de hashes para exibir registros para nossos usuários ou construir jogos front-end como cassino jogos e jogos de apostas (nos quais é necessária uma grande confiança). Além disso, com o poder do web3, podemos armazenar dados diretamente na cadeia.

Agora, vimos várias vantagens de usar blockchain, mas quando não devemos nos preocupar em usar um blockchain?

Desvantagens do Blockchain

  • Velocidade reduzida para transações digitais
    Blockchains exigem grandes quantidades de poder de computação, o que tende a reduzir a velocidade das transações digitais, embora existam soluções alternativas, é aconselhável usar bancos de dados centralizados quando precisar de transações de alta velocidade em milissegundos.
  • Imutabilidade de dados
    A imutabilidade dos dados sempre foi uma das maiores desvantagens do blockchain. É claro que vários sistemas se beneficiam, incluindo cadeia de suprimentos, sistemas financeiros e assim por diante. No entanto, ele sofre com o fato de que, uma vez que os dados são gravados, eles não podem ser removidos. Cada pessoa na terra tem o direito à privacidade. No entanto, se a mesma pessoa utilizar uma plataforma digital que roda na tecnologia blockchain, ela não poderá remover seu rastro do sistema quando não quiser. Em palavras simples, não há como ele remover seu rastro – deixando os direitos de privacidade em pedaços.
  • Requer Conhecimento Especializado
    Implementar e gerenciar um projeto blockchain é difícil. Requer conhecimento profundo para passar por todo o processo. É por isso que é difícil encontrar especialistas ou especialistas em blockchain, porque leva muito tempo e esforço para treinar um especialista em blockchain. Portanto, este artigo é um bom lugar para começar e um bom guia se você já começou.
  • Interoperabilidade
    Várias redes blockchain trabalhando duro para resolver o problema do livro-razão distribuído tornam difícil relacioná-las ou integrá-las umas às outras. Isso dificulta a comunicação entre diferentes cadeias.
  • Integração de aplicativos legados
    Muitos negócios e aplicativos ainda usam sistemas e arquiteturas legados; A adoção da tecnologia blockchain requer uma revisão completa desses sistemas, o que devo dizer que não é viável para muitos deles.

Blockchain ainda está evoluindo e amadurecendo o tempo todo, então não se surpreenda se esses contras mencionados hoje se transformarem em profissionais mais tarde. Bitcoin, que é uma criptomoeda, é um exemplo popular de blockchain, uma blockchain popular que está em ascensão, além da criptomoeda bitcoin, é a blockchain Ethereum. O Bitcoin se concentra em criptomoedas, enquanto o Ethereum se concentra mais em contratos inteligentes, que têm sido a principal força motriz das novas plataformas de tecnologia.

Leitura recomendada : Bitcoin vs. Ethereum: Qual é a diferença?

Vamos começar a construir nossa API

Com uma sólida compreensão do blockchain, agora vamos ver como construir um blockchain Ethereum e integrá-lo a uma API padrão no Node.js. O objetivo final é obter uma boa compreensão de como as plataformas dApps e Blockchain estão sendo construídas.

A maioria dos dApps tem arquitetura e estrutura semelhantes. Basicamente, temos um usuário que interage com o front-end do dApp - web ou móvel - que interage com as APIs de back-end. O back-end, então, a pedido, interage com o(s) contrato(s) inteligente(s) ou blockchain por meio de nós públicos; eles executam aplicativos Node.js ou o back-end usa blockchain executando diretamente o software Node.js. Ainda há muitas coisas entre esses processos, desde a escolha de criar um aplicativo totalmente descentralizado ou semi-descentralizado até a escolha do que deve ser descentralizado e como armazenar chaves privadas com segurança.

Leitura recomendada : Arquitetura de aplicativos descentralizados: back-end, segurança e padrões de design

Coisas que devemos saber primeiro

Para este tutorial, tentaremos construir o backend de um aplicativo de loja de música descentralizado que usa o poder do blockchain Ethereum para armazenar músicas e compartilhá-las para downloads ou streaming.

A estrutura básica do aplicativo que estamos tentando construir tem três partes:

  1. Autenticação , que é feita por e-mail; é claro que precisamos adicionar uma senha criptografada ao aplicativo.
  2. Armazenamento de dados , com os dados de música são armazenados primeiro em ipfs e o endereço de armazenamento é armazenado no blockchain para recuperação.
  3. Recuperação , com qualquer usuário autenticado podendo acessar os dados armazenados em nossa plataforma e utilizá-los.

Estaremos construindo isso com Node.js, mas você também pode construir com Python ou qualquer outra linguagem de programação. Também veremos como armazenar dados de mídia no IPFS, obter o endereço e escrever funções para armazenar esse endereço — e recuperar esse endereço de um blockchain com a linguagem de programação Solidity.

Aqui estão algumas ferramentas que devemos ter à nossa disposição para construir ou trabalhar com Ethereum e Node.js.

  • Node.js
    O primeiro requisito é um aplicativo Node. Estamos tentando construir um aplicativo Node.js, então precisamos de um compilador. Certifique-se de ter o Node.js instalado — e faça o download do binário de suporte de longo prazo mais recente ( LTS ).
  • Suíte Trufado
    Truffle é um ambiente de desenvolvimento e teste de contrato, bem como um pipeline de ativos para o blockchain Ethereum. Ele fornece um ambiente para compilar, canalizar e executar scripts. Uma vez que você está falando sobre o desenvolvimento de blockchain, Truffle é uma parada popular para ir. Confira sobre o Truffle Suite no Truffle Suite: Sweet Tools for Smart Contracts.
  • Ganache CLI
    Outra ferramenta que funciona bem com o Truffle é o Ganache-CLI. É construído e mantido pela equipe Truffle Suite. Depois de construir e compilar, você precisa de um emulador para desenvolver e executar aplicativos blockchain e, em seguida, implantar contratos inteligentes a serem usados. O Ganache facilita a implantação de um contrato em um emulador sem usar dinheiro real para custos de transação, contas recicláveis ​​e muito mais. Leia mais sobre Ganache CLI em Ganache CLI e Ganache.
  • Remixar
    O Remix é como uma alternativa ao Ganache, mas também vem com uma GUI para ajudar a navegar na implantação e teste de contratos inteligentes Ethereum. Você pode aprender mais sobre isso em Remix — Ethereum IDE & community. Tudo o que você precisa fazer é visitar https://remix.ethereum.org e usar a GUI para escrever e implantar contratos inteligentes.
  • Web3
    Web3 é uma coleção de bibliotecas que permite interagir com um nó Ethereum. Estes podem ser nós locais ou remotos do contrato através de HTTP, IPC ou Web Sockets. Introdução ao Web3.js · Ethereum Blockchain Developer Crash Course é um bom lugar para aprender um pouco sobre Web3.
  • IPFS
    Um protocolo principal que está sendo usado na criação de dApps. O InterPlanetary File System (IPFS) é um protocolo e rede ponto a ponto para armazenar e compartilhar dados em um sistema de arquivos distribuído. O IPFS Powers the Distributed Web explica mais sobre o IPFS e como ele é normalmente usado.

Criando uma API de back-end do zero

Então, primeiro temos que criar um backend para ser usado, e estamos usando o Node.js. Quando queremos criar uma nova API Node.js, a primeira coisa que faremos é inicializar um pacote npm. Como você provavelmente sabe, npm significa Node Package Manager e vem pré-empacotado com o binário Node.js. Então criamos uma nova pasta e a chamamos de “blockchain-music” . Abrimos o terminal nesse diretório de pasta e, em seguida, executamos o seguinte comando:

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

Isso inicia o projeto com um arquivo package.json e responde sim a todos os prompts. Em seguida, também criamos um arquivo server.js e um arquivo routes.js para escrever as funções de routes na API.

Depois de tudo isso, você terá que instalar os pacotes que precisamos para tornar nossa construção fácil e direta. Este processo é contínuo, ou seja, você pode instalar um pacote a qualquer momento durante o desenvolvimento do seu projeto.

Vamos instalar os mais importantes que precisamos agora:

  • Express.js
  • @trufa/contrato
  • Trufa.js
  • web3.js
  • dotenv
  • short-id
  • MongoDB
  • nómon

Você também precisará instalar o Truffle.js globalmente , para poder usá-lo em qualquer lugar em seu ambiente local. Se você quiser instalar todos eles de uma vez, execute o seguinte código no seu Terminal:

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

O sinalizador --save serve para salvar o nome do pacote no arquivo package.json . O sinalizador -g é para armazenar esse pacote específico globalmente, para que possamos usá-lo em qualquer projeto em que trabalharemos.

Em seguida, criamos um arquivo .env onde podemos armazenar nosso URI secreto do banco de dados MongoDB para uso. Fazemos isso executando touch.env no Terminal. Se você ainda não tem uma conta de banco de dados com o MongoDB, comece com a página do MongoDB primeiro.

O pacote dotenv exporta nossa variável armazenada para o ambiente de processo Node.js. Certifique-se de não enviar o arquivo .env ao enviar para repositórios públicos para evitar o vazamento de suas senhas e dados privados.

Em seguida, temos que adicionar scripts para as fases de construção e desenvolvimento do nosso projeto em nosso arquivo package.json . Atualmente nosso package.json está assim:

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

Em seguida, adicionaremos um script de início ao arquivo package.json para usar o servidor nodemon para que, sempre que fizermos alterações, ele reinicie o próprio servidor, e um script de compilação que use o servidor node diretamente, poderia ficar assim:

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

Em seguida, precisamos inicializar o Truffle para uso em nosso contrato inteligente usando o pacote Truffle que instalamos globalmente anteriormente. Na mesma pasta de nossos projetos, executamos o seguinte comando abaixo em nosso terminal:

 $ truffle init

Então podemos começar a escrever nosso código em nosso arquivo server.js . Novamente, estamos tentando construir um aplicativo simples de loja de música descentralizada, onde os clientes podem fazer upload de músicas para que todos os outros usuários acessem e ouçam.

Nosso server.js deve ser limpo para facilitar o acoplamento e desacoplamento de componentes, então as rotas e outras funcionalidades serão colocadas em outros arquivos como o routes.js . Nosso exemplo server.js poderia ser:

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

Basicamente, acima importamos as bibliotecas que precisamos com require , depois adicionamos um middleware que permite o uso de JSON em nossa API usando app.use , depois conectamos ao nosso banco de dados MongoDB e obtemos o acesso ao banco de dados, e então especificamos qual cluster de banco de dados estamos tentando acessar (para este tutorial é “Cluster0” ). Depois disso, chamamos a função e a importamos do arquivo de rotas . Por fim, ouvimos qualquer tentativa de conexão na porta 8082 .

Este arquivo server.js é apenas um barebone para iniciar o aplicativo. Observe que importamos routes.js . Este arquivo conterá os endpoints de rota para nossa API. Também importamos os pacotes que precisávamos usar no arquivo server.js e os inicializamos.

Vamos criar cinco endpoints para consumo do usuário:

  1. Ponto de extremidade de registro para registrar usuários apenas por e-mail. Idealmente, faríamos isso com um e-mail e senha, mas como queremos apenas identificar cada usuário, não vamos nos aventurar em segurança de senha e hashing por causa da brevidade deste tutorial.
     POST /register Requirements: email
  2. Ponto de extremidade de login para usuários por e-mail.
     POST /login Requirements: email
  3. Endpoint de upload para usuários — a API que obtém os dados do arquivo de música. O frontend converterá os arquivos MP3/WAV em um buffer de áudio e enviará esse buffer para a API.
     POST /upload Requirements: name, title of music, music file buffer or URL stored
  4. Endpoint de acesso que fornecerá os dados do buffer de música para qualquer usuário registrado que o solicite e registra quem o acessou.
     GET /access/{email}/{id} Requirements: email, id
  5. Também queremos fornecer acesso a toda a biblioteca de música e devolver os resultados a um usuário registrado.
     GET /access/{email} Requirements: email

Em seguida, escrevemos nossas funções de rota em nosso arquivo routes.js . Utilizamos os recursos de armazenamento e recuperação do banco de dados e, em seguida, nos certificamos de exportar a função de rota no final do arquivo para possibilitar a importação em outro arquivo ou pasta.

 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

Dentro desta função de route , temos muitas outras funções chamadas dentro dos parâmetros app e db . Essas são as funções de endpoint da API que permitem que os usuários especifiquem um endpoint na URL. Por fim, escolhemos uma dessas funções para ser executada e fornecemos resultados como resposta às solicitações recebidas.

Temos quatro funções principais de endpoint:

  1. get : para ler operações de registro
  2. post : para criar operações de registro
  3. put : para atualizar operações de registro
  4. delete : para deletar operações de registro

Nesta função de routes , usamos as operações get e post . Usamos post para operações de registro, login e upload, e get para acessar as operações de dados. Para um pouco mais de explicação sobre isso, você pode conferir o artigo de Jamie Corkhill sobre “How To Get Started With Node: An Introduction to APIs, HTTP And ES6+ JavaScript”.

No código acima, também podemos ver algumas operações do banco de dados como na rota de registro . Armazenamos o email de um novo usuário com db.createa e verificamos o email na função de login com db.findOne . Agora, antes que possamos fazer tudo isso, precisamos nomear uma coleção ou tabela com o método db.collection . É exatamente isso que abordaremos a seguir.

Nota : Para saber mais sobre as operações do banco de dados no MongoDB, consulte a documentação dos métodos do shell do mongo.

Construindo um contrato inteligente de blockchain simples com solidez

Agora vamos escrever um contrato Blockchain no Solidity (essa é a linguagem em que os contratos inteligentes são escritos) para simplesmente armazenar nossos dados e recuperá-los quando precisarmos. Os dados que queremos armazenar são os dados do arquivo de música, o que significa que precisamos fazer o upload da música para o IPFS e armazenar o endereço do buffer em um blockchain.

Primeiro, criamos um novo arquivo na pasta do contrato e o nomeamos Inbox.sol . Para escrever um contrato inteligente, é útil ter uma boa compreensão do Solidity, mas não é difícil, pois é semelhante ao JavaScript.

Observação : se você estiver interessado em saber mais sobre o Solidity, adicionei alguns recursos na parte inferior do artigo para você começar.

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

Em nosso contrato, temos duas funções principais: as funções sendIPFS e getHash . Antes de falarmos sobre as funções, podemos ver que primeiro tivemos que definir um contrato chamado Inbox . Dentro desta classe, temos estruturas utilizadas no objeto ipfsInbox (primeiros eventos, depois modificadores).

Após definir as estruturas e eventos, temos que inicializar o contrato chamando a função constructor . Em seguida, definimos três funções. (A função checkInbox foi usada no teste para testar os resultados.)

O sendIPFS é onde o usuário insere o identificador e o endereço de hash após o qual é armazenado no blockchain. A função getHash recupera o endereço de hash quando recebe o identificador. Novamente, a lógica por trás disso é que, em última análise, queremos armazenar a música no IPFS. Para testar como funciona, você pode acessar um Remix IDE, copiar, colar e testar seu contrato, bem como depurar quaisquer erros e executar novamente (espero que não seja necessário!).

Depois de testar se nosso código funciona corretamente no remix, vamos compilá-lo localmente com a suíte Truffle. Mas primeiro, precisamos fazer algumas alterações em nossos arquivos e configurar nosso emulador usando ganache-cli :

Primeiro, vamos instalar o ganache-cli . No mesmo diretório, execute o seguinte comando em seu terminal:

 $ npm install ganache-cli -g

Então vamos abrir outro Terminal e executar outro comando na mesma pasta:

 $ ganache-cli

Isso inicia o emulador para que nosso contrato de blockchain se conecte e funcione. Minimize o Terminal e continue com o outro Terminal que você está usando.

Agora vá para o arquivo trufa.js se você estiver usando um Linux/Mac OS ou trufa-config.js no Windows e modifique este arquivo para ficar assim:

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

Basicamente, o que fizemos foi adicionar o caminho da pasta de compilação onde o contrato inteligente é convertido em arquivos JSON. Em seguida, também especificamos a rede que o Truffle deve usar para migração.

Em seguida, também na pasta migrations , crie um novo arquivo chamado 2_migrate_inbox.js e adicione o seguinte código dentro dos arquivos:

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

Fizemos isso para obter o arquivo de contrato e implantá-lo automaticamente em um JSON, usando a função de deployer durante a migração do Truffle.

Após as alterações acima, executamos:

 $ truffle compile

Devemos ver algumas mensagens no final que mostram uma compilação bem-sucedida, como:

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

Em seguida, migramos nosso contrato executando:

 $ truffle migrate

Depois de migrarmos nossos contratos com sucesso, devemos ter algo assim no final:

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

E estamos quase terminando! Construímos nossa API com Node.js e também configuramos e construímos nosso contrato inteligente.

Também devemos escrever testes para nosso contrato para testar o comportamento de nosso contrato e garantir que seja o comportamento desejado. Os testes geralmente são escritos e colocados na pasta de test . Um exemplo de teste escrito em um arquivo chamado InboxTest.js criado na pasta de teste é:

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

Então, executamos nosso teste executando o seguinte:

 $ truffle test

Ele testa nosso contrato com os arquivos na pasta de teste e mostra o número de testes aprovados e reprovados. Para este tutorial, devemos obter:

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

Integrando o contrato inteligente à API de back-end usando o Web3

Na maioria das vezes, quando você vê tutoriais, vê aplicativos descentralizados criados para integrar o frontend diretamente ao blockchain. Mas há momentos em que a integração com o back-end também é necessária, por exemplo, ao usar APIs e serviços de back-end de terceiros ou ao usar blockchain para criar um CMS.

O uso do Web3 é muito importante para esta causa, pois nos ajuda a acessar nós Ethereum remotos ou locais e usá-los em nossas aplicações. Antes de prosseguirmos, discutiremos os nós locais e remotos do Ethereum. Os nós locais são os nós implantados em nosso sistema com emuladores como ganache-cli mas um nó remoto é aquele que é implantado em faucets/plataformas online como ropsten ou rinkeby . Para se aprofundar, você pode seguir um tutorial sobre como implantar no ropsten um guia de 5 minutos para implantar contratos inteligentes com Truffle e Ropsten ou você pode usar o provedor de carteira de trufas e implantar por meio de uma maneira mais fácil de implantar seus contratos inteligentes.

Estamos usando ganache-cli neste tutorial, mas se estivéssemos implantando em ropsten, deveríamos ter copiado ou armazenado nosso endereço de contrato em algum lugar como em nosso arquivo .env, e então seguir para atualizar o arquivo server.js , importar web3, importar o contrato migrado e configurar uma instância 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)); }) })

No arquivo server.js , verificamos se a instância web3 já foi inicializada. Caso contrário, inicializamos na porta de rede que definimos anteriormente ( 8545 ). Em seguida, criamos um contrato com base no arquivo JSON migrado e no pacote truffle-contract e definimos o provedor de contrato para o provedor de instância Web3 que já deve ter sido inicializado.

Em seguida, obtemos contas por web3.eth.getAccounts . Para o estágio de desenvolvimento, chamamos a função implantada em nossa classe de contrato que solicita ganache-cli — que ainda está em execução — para nos fornecer um endereço de contrato a ser usado. Mas se já implantamos nosso contrato em um nó remoto, chamamos uma função inserindo o endereço como argumento. A função de exemplo é comentada abaixo da variável lms definida em nosso código acima. Em seguida, chamamos a função de routes inserindo a instância do aplicativo, a instância do banco de dados, a instância do contrato ( lms ) e os dados das contas como argumentos. Por fim, ouvimos solicitações na porta 8082 .

Além disso, já deveríamos ter instalado o pacote MongoDB, pois estamos usando-o em nossa API como nosso banco de dados. Uma vez que temos isso, passamos para a página de rotas onde usamos os métodos definidos no contrato para realizar tarefas como salvar e recuperar os dados da música.

No final, nosso routes.js deve ficar assim:

 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.

Qual o proximo?

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