Passo a passo: Criando um blockchain em Java!
Implementando uma simples Blockchain em Java
Neste tutorial, aprenderemos os conceitos básicos da tecnologia blockchain. Também implementaremos uma aplicação básica em Java que utilize tais conceitos.
Mas o que é blockchain?
Bem, ele remonta à sua origem até o whitepaper publicado por Satoshi Nakamoto sobre o Bitcoin, em 2008.
Blockchain é um livro de informações descentralizado. Consiste em blocos de dados conectados através do uso de criptografia. Pertence a um conjunto de computadores conectados pela rede distribuída. Vamos entender melhor isso quando implementarmos o código.
Existem algumas características importantes que devemos entender, então vamos descrever cada uma delas:
- À prova de adulteração: Em primeiro lugar, os dados como parte de um bloco são à prova de adulteração. Cada bloco é referenciado por um digestor criptográfico, comumente conhecido como hash, tornando o bloco à prova de adulteração.
- Descentralizado: toda a blockchain é completamente descentralizada em toda a rede. Isso significa que não há nó mestre, e cada nó na rede tem a mesma cópia dos dados completos da blockchain.
- Transparente: Cada nó participante da rede valida e adiciona um novo bloco à sua cadeia através de consenso com outros nódulos. Portanto, cada nó tem visibilidade completa dos dados.
Como o Blockchain funciona?
Agora, vamos entender como o blockchain funciona.
As unidades fundamentais de uma blockchain são blocos. Um único bloco pode encapsular várias transações ou outros dados valiosos.
Mineração de um Bloco
Representamos um bloco por um valor hash. Gerar o valor hash de um bloco é chamado de "mineração" do bloco. Minerar um bloco é tipicamente computacionalmente caro de se fazer, pois serve como a "prova de trabalho".
O hash de um bloco normalmente consiste nos seguintes dados:
- Principalmente, o hash de um bloco consiste nas transações que encapsula
- O hash também consiste no horário da criação do bloco
- Também inclui um nonce, um número arbitrário usado na criptografia
- Finalmente, o hash do bloco atual também inclui o hash do bloco anterior
Vários nós na rede podem competir para minerar o bloco ao mesmo tempo. Além de gerar o hash, os nós também têm que verificar se as transações que estão sendo adicionadas no bloco são legítimas. O primeiro a minar um bloco ganha a corrida!
Adicionando um bloco em blockchain
Assim, um bloco recém-minerado é adicionado ao blockchain sobre o consenso dos nódulos.
Agora, existem vários protocolos de consenso disponíveis que podemos usar para verificação. Os nódulos na rede usam o mesmo protocolo para detectar ramificações maliciosas da cadeia. Assim, um ramo malicioso, mesmo que introduzido, em breve será rejeitado pela maioria dos nódulos.
Blockchain básico em Java
public class Block {
private String hash;
private String previousHash;
private String data;
private long timeStamp;
private int nonce;
public Block(String data, String previousHash, long timeStamp) {
this.data = data;
this.previousHash = previousHash;
this.timeStamp = timeStamp;
this.hash = calculateBlockHash();
}
// standard getters and setters
}Vamos entender o que implementamos aqui:
- previousHash = Hash do bloco anterior, uma parte importante para construir a cadeia
- data = Os dados reais, qualquer informação que tenha valor, como um contrato
- timeStamp = A data da criação deste bloco
- nonce = Nonce, é um número arbitrário usado na criptografia
- hash = O hash deste bloco, calculado com base em outros dados
O que é um Hash? e como calcular o Hash
Um hash é o resultado de algo conhecido como função hash. Uma função hash é uma função que mapeia dados de entrada de qualquer tamanho e os transforma na saída em um conjunto de dados de tamanho fixo. O hash de saída é sempre sensível a qualquer alteração nos dados de entrada, por mais pequenos que sejam.
Por exemplo:
o hash de maria é 94aec9fbed989ece189a7e172c9cf41669050495152bc4c1dbf2a38d7fd85627
o hash de mario é: 59195c6c541c8307f1da2d1e768d6f2280c984df217ad5f4c64c3542b04111a4
Veja que a mudança de uma única letra na palavra foi suficiente para provocar uma grande alteração no hash resultante.
Outro exemplo, o hash resultante de "paulo e joanna" é dfcbaaa3f541374947b72572995e1dba1984ad2183da06fd1640232828fb536d.
Veja também que não importa o tamanho da mensagem de entrada o hash resultante é sempre do mesmo tamanho.
Além disso, é impossível recuperar os dados de entrada apenas a partir de seu hash. Essas propriedades tornam a função hash bastante útil na criptografia.
Então, vamos ver como podemos gerar o hash do nosso bloco em Java:
public String calculateBlockHash() {
String dataToHash = previousHash
+ Long.toString(timeStamp)
+ Integer.toString(nonce)
+ data;
MessageDigest digest = null;
byte[] bytes = null;
try {
digest = MessageDigest.getInstance("SHA-256");
bytes = digest.digest(dataToHash.getBytes(UTF_8));
} catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
logger.log(Level.SEVERE, ex.getMessage());
}
StringBuffer buffer = new StringBuffer();
for (byte b : bytes) {
buffer.append(String.format("%02x", b));
}
return buffer.toString();
}Muitas coisas foi feita aqui, vamos entender em detalhes:
- Primeiro, concatenamos diferentes partes do bloco para gerar uma única string que vai ser o nosso dado de entrada.
- Segundo, criamos uma instância da classe MessageDigest, essa classe possuí a função de hash SHA-256 que usaremos para gear o hash do bloco.
- Terceiro, geramos o valor hash de nosso dado de entrada, que é uma matriz byte
- Por último, transformamos a matriz de bytes em uma sequência hexadecimal, e em seguida convertemos essa sequência em uma string.
Já mineramos o bloco?
O nosso código parece simples e elegante até aqui, mas temos que lembrar o fato de que ainda não implementamos a mineração o bloco. Então, o que exatamente implica minerar um bloco?, isso é algo que captura a fantasia de muitos desenvolvedores!
Bem, minerar um bloco significa resolver uma tarefa computacionalmente complexa para o computador. Calcular o hash de um bloco de dados é algo tecnicamente fácil para um computador, e é aqui que entra a mineração de dados, para que um bloco seja válido na blockchain o seu hash deve começar com uma determinada quantidade de zeros, essa quantidade de zeros exigida pela blockchain é chamada de difficulty.
Por exemplo, em uma blockchain com difficulty igual a 4 (4 zeros exigidos no hash), o bloco;
nonce=1.data="10 bitcoins",timestamp=123456,previushash=0000aaa3f541374947b72572995e1dba1984ad2183da06fd1640232828fb536d
Tem como hash resultante: 1b47e3acf9cd496936430df42e017269c3bb14f54dda91b41a90fd265960f124
Esse hash ainda não é válido para a blockchain, então o que o computador faz?
Ele vai incrementando o número nonce do bloco até obter um hash de quatro zeros.
Lembra que eu falei que uma simples alteração em um dado muda completamente o seu hash resultante? Pois, bem, o número nonce vai sendo incrementado até que se encontre um número que somado aos demais dados do bloco resulte em um hash de 4 zeros.
Continuando o exemplo:
nonce=2.data="10 bitcoins",timestamp=123456,previushash=0000aaa3f541374947b72572995e1dba1984ad2183da06fd1640232828fb536d
hash resultante: 58u7e3acf9cd496936430df42e017269c3bb14f54dda91b41a90fd265tyui985
Ainda não, vamos incrementar mais......
nonce=3445.data="10 bitcoins",timestamp=123456,previushash=0000aaa3f541374947b72572995e1dba1984ad2183da06fd1640232828fb536d
hash resultante: 0000u7ecf9cd496936430df42e017269c3bb14f54dda91b41a90fd265tyui985
Por fim, depois de milhares de incrementos encontramos o número 3445, esse é o nonce que gera o hash de 4 zeros, enfim mineramos o bloco.
Encontrar o hash começando com quatro zeros não é fácil para um computador. Ainda mais complicado seria encontrar um hash começando com dez zeros, para temos uma ideia geral.
É nesse sentido que reside a segurança da blockchain, para alterar seus dados seria necessário encontrar o hash específico de cada bloco dentro de cada dificuldade especificada, o que é uma tarefa muito árdua.
public String mineBlock(int prefix) {
String prefixString = new String(new char[prefix]).replace('\0', '0');
while (!hash.substring(0, prefix).equals(prefixString)) {
nonce++;
hash = calculateBlockHash();
}
return hash;
}Vamos ver o que estamos tentando fazer aqui são:
- Começamos definindo a quantidade de zeros que desejamos encontrar
- Então verificamos se encontramos a solução
- Se não incrementamos a nonce e calculamos o hash em um loop
- O loop continua até acertarmos um hash com número de zeros especificado
Estamos começando com o valor padrão de nonce aqui e incrementando-o por um. Mas há estratégias mais sofisticadas para começar e incrementar uma nonce em aplicações do mundo real.
Vamos executar o exemplo
Agora que temos nosso bloco definido junto com suas funções, podemos usá-lo para criar um blockchain simples. Vamos armazenar isso em uma ArrayList:
List<Block> blockchain = new ArrayList<>();
int prefix = 4;
String prefixString = new String(new char[prefix]).replace('\0', '0');Além disso, definimos um prefixo de quatro, o que significa efetivamente que queremos que nosso hash comece com quatro zeros.
Vamos ver como podemos adicionar um bloco aqui:
@Test
public void givenBlockchain_whenNewBlockAdded_thenSuccess() {
Block newBlock = new Block(
"The is a New Block.",
blockchain.get(blockchain.size() - 1).getHash(),
new Date().getTime());
newBlock.mineBlock(prefix);
assertTrue(newBlock.getHash().substring(0, prefix).equals(prefixString));
blockchain.add(newBlock);
}Verificação de blockchain
Como um nó pode validar que uma blockchain é válida? Embora isso possa ser bastante complicado, vamos pensar em uma versão simples:
@Test
public void givenBlockchain_whenValidated_thenSuccess() {
boolean flag = true;
for (int i = 0; i < blockchain.size(); i++) {
String previousHash = i==0 ? "0" : blockchain.get(i - 1).getHash();
flag = blockchain.get(i).getHash().equals(blockchain.get(i).calculateBlockHash())
&& previousHash.equals(blockchain.get(i).getPreviousHash())
&& blockchain.get(i).getHash().substring(0, prefix).equals(prefixString);
if (!flag) break;
}
assertTrue(flag);
}Então, aqui estamos fazendo três verificações específicas para cada bloco:
- O hash armazenado do bloco atual é na verdade o que ele calcula
- O hash do bloco anterior armazenado no bloco atual é o hash do bloco anterior
- O bloco atual foi minado
Comentários
Postar um comentário