Uma implementação de uma criptomoeda em menos de 1500 linhas de código

Conrado Quilles Gomes
TOTVS Developers
Published in
6 min readFeb 26, 2018

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
  • 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:

  1. 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;
  2. Pega as duas primeiras transações da lista de transações candidatas;
  3. Adiciona uma nova transação contendo a taxa a ser entregue para o endereço no minerador (1 Satoshi por transação);
  4. Adiciona uma nova transação contendo o prêmio de 50 moedas para o endereço do minerador;
  5. 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.

O nó contem um lista de outros nós conectados e faz todo o trabalho de troca de mensagem entre os nós, incluindo:

  1. Recebe novos nós e verifica o que fazer com isso;
  2. Recebe novos blocos e verifica o que fazer com isso;
  3. 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!

--

--