Uma implementação de uma criptomoeda em menos de 1500 linhas de código
Motivação
Criptomoedas e contratos inteligentes criados utilizando uma blockchain não são simples de entender, características como carteiras, endereços, prova-de-trabalho de um bloco, transações e suas assinaturas, fazem mais sentido quando vistas no todo. Inspirado pelo projeto Naivechain de Lauri Hartikka, o projeto Naivecoin é uma tentativa de demonstrar uma implementação concisa e simples de uma criptomoeda.
O que é uma criptomoeda
Da wikipedia: Uma criptomoeda (ou cripto moeda) é um bem digital desenhado para ser utilizado como um meio de troca utilizando criptografia para assegurar as transações e controlar a criação adicional das unidades da moeda.
Conceitos principais do Naivecoin
Componentes
- Servidor HTTP
- Nó
- Blockchain (Cadeia de blocos)
- Operador
- Minerador
Funcionalidades
- Uma interface API HTTP para controlar tudo
- Sincronização do blockchain e transações pendentes
- Uma prova-de-trabalho simples (A dificuldade aumenta a cada 5 blocos)
- Criação de endereço usando um método determinístico EdDSA
- Os dados são persistidos em uma pasta
O projeto Naivechain utiliza um websocket para comunicação P2P, mas essa funcionalidade foi deixada de lado para simplificar o entendimento da troca de mensagens. Essa implementação está somente utilizando comunicação em REST.
Comunicação dos componentes
Nem todos os componentes dessa implementação seguem a lista completa de requisitos para uma criptomoeda segura e escalável. Dentro do código-fonte você poderá encontrar comentários marcados com INFO:
que descrevem quais partes poderiam ser melhoradas (e como) e quais técnicas foram utilizadas para solucionar desafios específicos.
HTTP Server
Prove um API para gerenciar o blockchain, carteiras, endereços, criação de transações, requisição de mineração e conectividade com os nós.
É um ponto de partida para interagir com o Naivecoin, e todos os nós proveem um API swagger para tornar essa interação mais simples.
Da interface do swagger é possível acessar uma interface simples de visualização do blockchain e das transações não confirmadas.
Blockchain
O blockchain contém duas informações, a lista de blocos (uma lista ligada) e a lista de transações (um hash map).
É responsável por:
- Verificar os blocos que chegam;
- Verificar as transações que chegam;
- Sincronizar a lista de transações pendentes;
- Sincronizar a lista de blocos;
O blockchain é uma lista ligada onde o hash do próximo bloco é calculado baseado no hash do bloco anterior mais a dado contido no próprio bloco:
Um bloco é adicionado a uma lista de blocos se:
- O bloco for o último (índice do bloco anterior + 1);
- O bloco anterior for correto (hash anterior == bloco.hashAnterior);
- O hash está correto (hash calculado == bloco.hash);
- O nível de dificuldade do desafio da prova-de-trabalho estiver correto (dificuldade do bloco no índice n < dificuldade do bloco);
- Todas as transações dentro do bloco forem válidas;
- A soma das saídas das transações é igual a soma das entradas das transações + 50 moedas representando o prêmio para o minerador do bloco;
- Tem somente uma transação do tipo taxa e um do tipo premio.
Uma transação dentro de um bloco é valido se:
- O hash da transação é válido (hash da transação calculado == transação.hash);
- A assinatura de todas as transações de entrada são corretas (o data da transação é assinado com a chave pública do endereço);
- A soma das transações de entrada é maior que a soma das transações de saída, é necessário deixar um espaço para a transação do tipo taxa;
- A transação já não está no blockchain.
Você pode ler esse post do projeto Naivechain para mais detalhes de como um blockchain funciona.
As lista de transações é na verdade uma lista de transações não confirmadas. Nada especial sobre ela. Nessa implementação, a lista de transações contém somente a lista de transações não confirmadas e assim que a transação é confirmada o blockchain remove ela da lista.
[
transaction 1,
transaction 2,
transaction 3
]
Uma transação é adicionada na lista de transações se:
- Já não está na lista de transações;
- O hash da transação é correto (hash da transação calculado == transação.hash);
- A assinatura de todas as transações de entrada são corretas (o data da transação é assinado com a chave pública do endereço);
- A soma das transações de entrada é maior que a soma das transações de saída, é necessário deixar um espaço para a transação do tipo taxa;
- A transação já não está no blockchain;
- Todas as transações de entrada não foram utilizadas no blockchain.
Estrutura de um bloco:
Um bloco representa um grupo de transações e contém informações que os liga para blocos anteriores.
Os detalhes sobre o nonce e o algorítimo de prova-de-trabalho utilizado para gerar o bloco serão descritos em algum momento a frente.
Estrutura da transação:
Uma transação contém a lista de entradas e saídas representando a transferência de moedas entre seus donos e um endereço. A lista de entrada contém uma lista de saídas não utilizadas e são assinadas pelo dono do endereço. A lista de saída contém os valores que vão para outros endereços, incluindo ou não um troco para o dono do endereço.
Operador
O operador lida com as carteiras e endereços como também a criação de transações. A maioria de suas operações são CRUD. Cada operador tem sua lista de carteiras e endereços e elas não são sincronizadas entre os nós.
Estrutura da carteira:
Uma carteira consiste de um número aleatório, o hash de uma senha e um segredo gerado dessa senha. Ela também contém uma lista de pares de chaves cada par representando um endereço.
Estrutura de um endereço:
O endereço é criado de uma forma determinística, implicando que para uma determinada senha, o próximo endereço é criado baseado no endereço anterior (ou o segredo gerado pela senha, se for o primeiro endereço).
Naivecoin utiliza-se do algorítimo EdDSA para gerar um chave secreta e uma chave pública utilizando uma semente que vem de um valor gerado aleatoriamente do hash da senha (também de um modo determinístico) ou da última chave secreta gerada.
Somente a chave pública é publicada como o endereço do usuário.
Minerador
O minerador pega a lista de transações pendentes e cria um novo bloco contendo as transações. Por configuração, cada bloco tem no máximo duas transações dentro dele.
Montando um bloco novo:
- Da lista de transações não confirmadas, se seleciona transações candidatas que não estão no blockchain e já não foram selecionadas;
- Pega as duas primeiras transações da lista de transações candidatas;
- Adiciona uma nova transação contendo a taxa a ser entregue para o endereço no minerador (1 Satoshi por transação);
- Adiciona uma nova transação contendo o prêmio de 50 moedas para o endereço do minerador;
- Prova-o-trabalho para esse bloco.
Prova-de-trabalho:
A prova-de-trabalho é feito calculando os 14 primeiros valores hexadecimais de um hash de uma transação e o incremento do nonce até que isso atinga a dificuldade mínima necessária. A dificuldade aumenta exponencialmente (elevado a 5) a cada 5 blocos criados. Em torno do bloco 70 o Naivecoin começa a gastar cerca de 50 segundos para gerar um bloco novo utilizando essa configuração. Todos esses valores podem ser alterados.
A expressão this.blockchain.getDifficulty()
retorna o valor hexadecimal da dificuldade do índice de bloco atual. Esse valor é calculado ao elevar a dificuldade por 5 a cada 5 blocos.
A expressão block.getDifficulty()
retorna o valor hexadecimal dos primeiros 14 bytes do hash do bloco e o compara com a dificuldade aceitada no momento.
Quando o hash é gerado atinge a dificuldade mínima, ele retorna o bloco.
Nó
O nó contem um lista de outros nós conectados e faz todo o trabalho de troca de mensagem entre os nós, incluindo:
- Recebe novos nós e verifica o que fazer com isso;
- Recebe novos blocos e verifica o que fazer com isso;
- Recebe novas transações e verifica o que fazer com isso;
O nó transmite para todos os nós todas as informações que recebe, a não ser que ele não utilize a informação, quando por exemplo, o nó/transação/blockhain já existe localmente.
Uma responsabilidade extra é pegar a quantidade de confirmações existentes para uma transação. O nó faz isso perguntando para todo nó conhecido se a dada transação já existe em seus blockchains.
Confira o projeto:
É isso!