Entendendo a Blockchain na Prática! - Parte 2

Cripto

Entendendo a Blockchain na Prática! - Parte 2

Luiz Duarte
Escrito por Luiz Duarte em 21/02/2022
Junte-se a mais de 34 mil devs

Entre para minha lista e receba conteúdos exclusivos e com prioridade

Recentemente eu escrevi um tutorial para ajudar minha audiência a entender mais sobre blockchain. Ao invés de ser um artigo meramente teórico, uni teoria e prática com JavaScript para de uma vez por todas dar sentido a tudo aquilo que já ouvimos falar de descentralização, cadeia de blocos, prova de trabalho, etc.

Ou melhor, comecei a dar sentido, certo?

Porque vários assuntos ligados a este universo nem sequer foram tocados, o que devemos corrigir nesta segunda parte que está ainda mais recheada de blockchain na prática com JavaScript!

Se preferir, pode assistir à playlist abaixo ao invés de acompanhar o texto (a parte 2 começa no vídeo 6). Tem o mesmo conteúdo.

Curso Node.js e MongoDB

Como os nós validam a Blockchain?

Entendemos como um bloco se parece e como a blockchain funciona de maneira geral. Mas em certo momento eu chamei a nossa implementação de “ingênua”, certo?

E por que ela seria uma implementação ingênua?

Porque do jeito que está, hoje, ela confia no uso que fizerem dela. Ela confia que as informações não serão adulteradas, sendo que abaixo tem uma adulteração muito simples de ser feita.

Neste exemplo, eu estou manualmente alterando o conteúdo de uma transação, para fazer com que o registro da blockchain informe que a transferiu 100 vezes mais recursos para uma carteira.

Talvez você pense que uma solução seria esconder o acesso aos dados internos dos blocos ou qualquer coisa do tipo, mas lembre-se que blockchains são descentralizadas e todos os nós possuem os fontes do projeto em suas mãos, podendo adulterar a qualquer momento. Neste cenário, não temos como impedir alterações maliciosas.

Mas temos como verificar se elas foram feitas!

Lembra que cada bloco possui uma assinatura digital chamada hash? O hash é gerado junto do bloco e se qualquer dado interno no bloco for alterado, o hash não vai mais dar match.

Ah Luiz, mas neste caso não é só mandar gerar o hash de novo? Sim, mas falaremos mais disso mais à frente.

Por enquanto, vamos entender que precisamos de um mecanismo de validação da Blockchain. Algo que nos diga se ela foi adulterada ou não. Para isso, vamos criar uma nova função na nossa classe Blockchain, chamada isValid.

Nesta implementação, nós estamos validando a blockchain de trás pra frente. Isso porque se um atacante quisesse alterar uma transação muito provavelmente ele escolheria uma mais recente, já que o esforço computacional de recalcular hashes seria menor neste caso e/ou seria mais fácil de iludir a rede buscando um consenso para a sua versão fraudulenta do livro-razão.

Assim, validando a blockchain de trás para frente aumentamos a velocidade com a qual a validação é feita em cenários de fraude, enquanto que em cenários comuns, o tempo será sempre igual ao total de elementos na blockchain.

A validação em si tem três pernas:

  1. validamos se o hash do bloco atende às regras de geração de hash;
  2. validamos se o previousHash do bloco é igual ao hash do bloco anterior;
  3. validamos se o índice do bloco é igual ao índice do bloco anterior +1;

Isso garante a integridade das assinaturas, das referências e da ordem dos blocos!

Se quiser verificar o funcionamento, pode ajustar seu index.js como segue.

Repare como no segundo cenário, em que adulteramos um dos blocos a blockchain deixa de ser considerada válida, apenas baseado nos hashes que agora não batem mais.

No entanto, esta ainda é uma implementação ingênua…

O que impede de blocos falsos serem adicionados na Blockchain?

Eu mencionei em duas ocasiões que nossa implementação de Blockchain é ingênua.

Imagine uma blockchain como a do Bitcoin. Porque e a moeda possui valor, já parou pra pensar nisso?

Pois assim como o ouro ele não é obtido gratuitamente. Tem um preço a se pagar, através do trabalho duro. No caso do Bitcoin, trabalho computacional duro ou mais especificamente prova de trabalho (proof of work), a popular “mineração”.

A prova de trabalho é um desafio computacional que toda máquina que quiser adicionar um bloco novo à blockchain deve resolver antes de ser aceito como válido. É a prova de trabalho que evita que os blocos simplesmente sejam minerados na quantidade e do jeito que as pessoas que quiserem. É a prova de trabalho também que garante que a rede se mantenha ativa e íntegra, funcionando, pois a cada desafio solucionado, o minerador que o resolveu é recompensado com moedas da própria rede.

Esse foi o mecanismo criado por Satoshi Nakamoto para não apenas recompensar o gasto elétrico que a rede consome, mas para dois outros motivos: gerar escassez e gerar proteção a ataques.

A escassez da moeda faria com que ela tivesse valor e era uma parte essencial para a economia da mesma. No momento em que algo pode ser facilmente obtido, por exemplo adicionar um bloco em nossa blockchain atual, você concorda que ninguém pagaria por ter este bloco para si, certo? Porque alguém pagaria por algo que em poucos milisegundos processando consegue gerar?

Não apenas Satoshi colocou a prova de trabalho no algoritmo do Bitcoin como programou para que quanto mais mineradores a rede tivesse, maior será a dificuldade. E para que quanto mais tempo se passasse, menor fossem as recompensas (em um processo chamado halving). Esses dois fatores tornam o Bitcoin uma das poucas moedas deflacionárias no mundo todo!

Agora, a escassez poderia sair pela culatra: com a valorização da moeda, atrairia a atenção de atacantes. No entanto, a prova de trabalho também ajuda nisso. Ela garante que teríamos mais pessoas cooperando do que atacando a blockchain, já que o esforço computacional para ataques é muito maior do que para mineração. Afinal, um ataque para ser bem sucedido envolveria recalcular diversos hashes e propagar as alterações para vários nós rapidamente antes da blockchain ser atualizada e/ou da fraude ser percebida.

A esta altura imagino que tenha entendido de maneira abstrata o que é a prova de trabalho. Mas e como tornamos isso concreto em nosso código?

Precisamos criar um algoritmo que dificulte a criação do bloco a ponto dele não ser criado sem esforço algum, mas também que não seja impossível, inclusive com uma dificuldade ajustável. O algoritmo proposto por Satoshi Nakamoto coloca uma meta no hash a ser gerado: ele deve conter um número de zeros à esquerda. Quanto mais zeros, maior a dificuldade em se achar o número.

Assim, com base em todos os dados do bloco que nós já temos, vamos adicionar o que chamamos de nonce (number used once) ou golden number: um número aleatório que quando concatenado aos demais atributos gere o hash com o número correto de zeros à esquerda. Para chegar no hash-alvo, os mineradores terão de testar diversos nonces e somente o bloco será aceito quando o desafio for solucionado.

Vamos modificar então nossa classe Block.js para que ela receba mais dois atributos e uma função:

  • nonce: o número dourado que vai fazer com que o hash gerado esteja dentro da dificuldade;
  • difficulty: a quantidade de zeros que o hash deve ter à esquerda para ser considerado válido (aka dificuldade);
  • mine: função para fazer a mineração, já que não adianta agora gerar apenas um hash;
  • generateHash: já temos esta função, mas vamos ter de ajustá-la para incluir o nonce;

Traduzindo em código…

Repare que o ajuste no constructor inclui o nonce, a construção de uma string com o prefixo de zeros à esquerda e a chamada à função de mineração, ao invés de simplesmente mandar gerar o hash.

Depois temos a função de mineração em si, que vai ficar incrementando o nonce (outra alternativa seria gerar aleatoriamente) e usando ele para gerar o hash, que testamos com o prefixo de zeros (dificuldade).

E por último, ajustamos a função generateHash para que ela inclua o nonce na geração, o que acaba também ajustando a validação da blockchain.

Agora, você pode opcionalmente ajustar o código da Blockchain para que a dificuldade seja ajustável e passada para os blocos, ou então simplesmente testar a adição de blocos e verá que além de mais lento (proporcional à dificuldade) verá o números dos nonces na impressão.

Repare que com dificuldade em 1, com pouca mineração (aka nonces testados) já conseguimos chegar no hash que atende. Aqui na minha máquina, a partir de dificuldade 4 já começou a levar alguns segundos para minerar dois blocos. E na sua?

E com isso terminamos mais uma parte desta série que tenho certeza que está lhe ajudando a entender mais sobre blockchain, na prática, com JavaScript.

Quer aprender a programar em Solidity, a linguagem usada em Smart Contracts? Dá uma olhada neste tutorial.

Curso Bot

Olá, tudo bem?

O que você achou deste conteúdo? Conte nos comentários.

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *