1. Visão geral
Blockchain é um livro-razão distribuído e imutável que facilita o processo de registro de transações e o rastreamento de ativos em uma rede empresarial. Um ativo pode ser tangível (uma casa, um carro, dinheiro, terras) ou intangível (propriedade intelectual, patentes, direitos autorais e criação de marcas) [1].
O objetivo desse artigo é que, ao construímos uma blockchain simples em Kotlin, tenhamos uma melhor compreensão de:
- Como uma blockchain funciona;
- Como funções hash e árvores de Merkle são utilizadas para manter e garantir a integridade da blockchain;
- Como algoritmos de criptografia assimétrica e assinatura digital são usados para implementar importantes features relacionadas à integridade, confidencialidade e autenticidade dos dados armazenados.
- Como o mecanismo de consenso chamado de Prova de trabalho (Proof-of-Work) funciona e como ele é utilizado no processo de mineração de novos blocos e mitigação de ataques de 51%;
- Como expor as funcionalidades de uma blockchain através de uma API HTTP escrita em Kotlin e Ktor.
Redes P2P (peer-to-peer) são um componente chave usado pela tecnologia blockchain para tornar a rede descentralizada. Porém, para manter este artigo não tão extenso, discutiremos sobre o assunto em uma futura publicação.
Iniciaremos apresentando importantes conceitos e, em seguida, implementaremos uma blockchain em um contexto de criptomoedas.
É importante pontuar que esta não será uma blockchain totalmente funcional e pronta para produção. Esta é uma implementação para fins educacionais, para ajudá-lo a entender como funciona uma blockchain e que servirá de base para futuras publicações.
O código fonte completo está disponível no GitHub.
1.1. Funções Hash
Uma função hash é um algoritmo que mapeia dados de comprimento variável para dados de comprimento fixo. Em geral, um hash é representado em base hexadecimal [2].
Funções hash possuem características como:
- Ser determinista: Um procedimento de hash deve ser determinístico - o que significa que, para um determinado valor de entrada, ele deve sempre gerar o mesmo valor de hash;
- Ser computacionalmente eficiente: Deve ser rápido calcular o valor de hash para qualquer entrada;
- Ser unidirecional: Não deve ser possível reverter um valor hash para obter a entrada original;
- Ser resistente à colisão: Deve ser computacionalmente difícil encontrar duas entradas diferentes de qualquer tamanho que resultem no mesmo hash;
Em blockchain, funções hash são usadas em processos de:
- Verificação de integridade dos dados;
- Assinaturas digitais;
- Mecanismo de consenso por Prova de trabalho (Proof-of-Work);
1.2. Árvores de Merkle
Árvores de Merkle são um tipo de estrutura no qual cada nó não folha da árvore contém valores de hash de seus próprios nós filhos. Desta maneira, a raiz é o hash de todos os dados contidos nas folhas [3].
Árvores de Merkle podem ser usadas para proteger qualquer tipo de dados armazenados, manuseados e transferidos dentro e entre computadores. Em blockchain, as árvores de Merkle podem ser utilizadas para garantir que blocos de dados recebidos de outros pares em uma rede P2P são recebidos intactos e inalterados, e até mesmo para verificar se os outros pares não mentem e enviam blocos falsos [4].
1.3. Criptografia assimétrica
A criptografia assimétrica (conhecida como criptografia de chave pública) é um ramo da criptografia onde uma chave secreta pode ser dividida em duas partes, uma chave pública e uma chave privada. A chave pública pode ser dada a qualquer pessoa, confiável ou não, enquanto a chave privada deve ser mantida em segredo (assim como a chave na criptografia simétrica). A criptografia assimétrica tem dois casos de uso principais: confidencialidade e autenticação [5].
A imagem abaixo representa o processo de transmissão de uma mensagem com confidencialidade. Em primeiro lugar, o remetente criptografa a mensagem com a chave pública do destinatário, o remetente pode então enviar a mensagem (criptografada) com segurança. O destinatário então recebe a mensagem e pode descriptografá-la usando sua chave privada.
1.4. Assinatura digital
Ao usar criptografia assimétrica, as mensagens podem ser assinadas com uma chave privada e, em seguida, qualquer pessoa com a chave pública pode verificar se a mensagem foi criada por alguém que possui a chave privada correspondente. Isso pode ser combinado com um sistema de prova de identidade para saber qual entidade (pessoa ou grupo) realmente possui essa chave privada, fornecendo autenticação [5].
1.5. Blocos
O nome blockchain vem do fato de que os dados são armazenados em blocos, e cada bloco é conectado ao bloco anterior, formando uma estrutura em cadeia. Com a tecnologia blockchain, você só pode adicionar (anexar) novos blocos à blockchain. Você não pode modificar ou excluir qualquer bloco depois que ele for adicionado à blockchain [6].
De forma simplificada, um bloco é composto pelas seguintes propriedades:
- Previous Hash: Hash de bloco anterior;
- Transactions: Lista de transações incluídas no bloco;
- Timestamp: Momento em que o bloco foi gerado;
- Nonce: Número arbitrário que os mineradores devem encontrar - essencialmente adivinhar - para produzir um hash dentro de um conjunto de hashes válidos. Esse processo faz parte do mecanismo de consenso por prova de trabalho (PoW), que iremos entender durante o andamento desse artigo;
- Hash: Um hash calculado a partir do conteúdo do bloco;
1.5.1. Hash do bloco
O hash do bloco é uma das propriedades mais importantes do bloco. Ele é calculado a partir de todos os dados do bloco. Isso significa que, se algo no bloco mudar, o hash original não será mais válido.
O hash do bloco também pode ser pensado como o identificador exclusivo do bloco. Usamos hashes de bloco para preservar a sua integridade e referenciar explicitamente o bloco anterior.
Com isso, quanto mais profundo o bloco estiver na blockchain, mais difícil será modificá-lo, pois exigiria modificações em cada bloco consecutivo.
1.5.2. Bloco Genesis
O bloco genesis é o primeiro bloco de uma blockchain. É a base na qual os próximos blocos serão adicionados para formar uma cadeia de blocos.
Cada bloco em uma blockchain armazena uma referência ao bloco anterior. No caso do Bloco Genesis, não há bloco anterior para referência.
1.6. Mecanismo de consenso por Prova de trabalho (Proof-of-Work)
Mecanismos de consenso (também conhecidos como protocolos de consenso ou algoritmos de consenso) permitem que sistemas distribuídos (redes de computadores) trabalhem em conjunto, cheguem a um consenso sobre o estado da rede e permaneçam seguros.
1.6.1 Mineração de novos blocos
Em blockchain, prova de trabalho é o algoritmo que define a dificuldade e as regras para o trabalho que os mineradores fazem. A mineração é o ato de adicionar blocos válidos (que inclui garantir que todas as transações presentes no bloco são válidas) à cadeia.
A prova de trabalho exige que os mineradores passem por uma intensa corrida de tentativa e erro para encontrar o "nonce" para um bloco através da realização de uma ampla gama de funções de hash com diferentes valores de "nonce".
O algoritmo de consenso de prova de trabalho define um bloco válido como aquele cujo valor de hash é menor que um determinado limite. A dificuldade determina o alvo para o hash. Quanto menor o alvo, menor o conjunto de hashes válidos.
Essa prova (desafio criptográfico) exige que os mineradores encontrem um resultado de uma função hash dentro desse conjunto de hashes válidos, que é computacionalmente difícil (mas factível) de ser descoberto, porém é facilmente verificável por ele e pelos demais mineradores e clientes da rede [7] [8].
O vencedor dessa corrida compartilha o novo bloco com o resto da rede e é recompensando pelo seu trabalho.
Em um contexto de criptomoedas, a recompensa é feita através da geração de novas moedas, que são transferidas para o minerador do bloco.
1.6.2 Segurança e consenso
Blockchains dependem de uma única fonte de verdade. E os utilizadores escolherão sempre a cadeia mais longa. A maior cadeia é aceita como válida pois ela teve o maior trabalho computacional realizado.
Quanto mais "trabalho" for feito, quanto maior a cadeia e quanto maior o número do bloco, mais certeza a rede pode ter do estado atual das coisas.
Para criar blocos maliciosos, ainda que válidos, o atacante precisaria de mais de 51% do poder de mineração da rede para superar todos os demais mineradores. Seria preciso muito poder computacional para poder executar essa quantidade de "trabalho" [7].
1.7. Transações
Em criptomoedas como o Bitcoin, as transações são compostas por dois componentes: entradas (inputs) e saídas (outputs).
Uma transação é, essencialmente, uma transferência de valor.
As entradas fornecem uma prova da origem dos valores transferidos e que são de propriedade do "remetente". As entradas sempre se referem a uma saída existente de uma transação anterior. As transações gastam saídas de transações anteriores e geram novas saídas que podem ser gastas por transações no futuro.
Assim como na estrutura do bloco, também iremos registrar o momento em que a transação foi criada e ter um identificador exclusivo (hash) dessa transação, que também garantirá sua integridade.
Com isso, uma transação pode ser representada pelas seguintes propriedades:
- Inputs: Lista de entradas da transação;
- Outputs: Lista de saídas da transação;
- Timestamp: Momento em que a transação foi criada;
- Hash: Um hash calculado a partir do conteúdo da transação;
2. Implementação em Kotlin
Após o entendimento de todos esses conceitos, chegou o momento de iniciarmos a nossa implementação em Kotlin.
IDE utilizada: IntelliJ IDEA 2022.1 (Community Edition)
Versão do Kotlin: 1.6.20
2.1. Função Hash
Assim como o Bitcoin, usaremos o algoritmo criptográfico SHA-256 para o cálculo de hash. Podemos implementá-lo em Kotlin através de uma extensão da classe String da seguinte forma:
2.2. Criptografia assimétrica e assinatura digital
A criptografia de curva elíptica (ECC) é uma técnica de criptografia assimétrica baseada na teoria da curva elíptica que pode ser usada para criar chaves criptográficas mais rápidas, menores e mais eficientes [9].
O ECC é uma alternativa ao algoritmo criptográfico Rivest-Shamir-Adleman (RSA) e é mais usado para assinaturas digitais em criptomoedas, como Bitcoin e Ethereum, bem como criptografia unidirecional de e-mails, dados e software [9].
O ECDSA é um esquema de assinatura digital criptograficamente seguro, baseado na criptografia de curva elíptica (ECC) [10] [11].
A seguir, iremos implementar funções responsáveis pela criação de pares de chaves, assinatura digital e alguns métodos auxiliares:
2.3. Estrutura das transações
O processo de armazenamento de transações na blockchain consiste, essencialmente, em criar transações (composta por Entradas e Saídas), assiná-las, incluí-las em um pool de transações a serem processadas e, a partir do processo de mineração, incluí-las nos blocos minerados.
2.4. Estrutura dos blocos
No mecanismo de consenso por prova de trabalho (Proof-of-Work), um bloco para ser considerado válido, além de ser composto por transações válidas, precisa que o hash gerado a partir do seu conteúdo atenda à um objetivo.
Para isso, é necessário resolver um desafio criptográfico. Esse processo consiste em encontrar um número arbitrário (nomeado como "nonce" e que faz parte do conteúdo do bloco).
Em nosso desafio, o valor de "nonce" encontrado deve permitir a geração de um valor hash com um certo número de 0s no início. A quantidade de 0s no inicio da hash determina a dificuldade de resolver o desafio criptográfico.
2.5. Armazenando a blockchain e validando a integridade dos blocos
Agora que criamos as classes relacionadas aos blocos e transações, vamos implementar uma blockchain.
Em nossa versão de blockchain, há algumas simplificações:
- Não existe um limite fixo para o número de transações que um bloco em uma blockchain pode conter;
- Cada novo bloco minerado, consome toda a lista atual de transações pendentes do pool de transações.
- O intervalo entre a geração de novos blocos não é gerenciado;
- O nível de dificuldade de mineração de novos blocos é fixo (Previamente estabelecido na geração do bloco genesis);
- Não há recompensa por bloco minerado e não há taxas ao enviar transações;
- Você pode pré-alocar valores a partir do bloco genesis para o dono (você) do par de chaves criadas na Wallet padrão (Iremos falar sobre a implementação da Wallet em breve);
Nossa Blockchain é considerada válida quando:
- Estiver vazia;
- Houver um único bloco e ele for válido;
- Houver mais de um bloco e iterarmos por cada bloco garantindo que:
- O bloco atual é válido;
- O bloco anterior é válido;
- Que o hash do bloco anterior é corretamente referenciado no bloco atual;
2.6. Wallet
O objetivo da carteira é criar uma interface mais simples para o usuário transacionar valores na blockchain.
O usuário deve ser capaz de:
- Criar uma nova carteira (Que irá gerar um par de chaves);
- Ver o saldo de sua carteira;
- Enviar moedas para outros destinatários (referenciados por suas publicKey);
2.7. API HTTP
Nesse momento, começaremos a criar uma API HTTP para nossa blockchain.
Primeiro, iremos representar nossa blockchain em formato JSON:
Note na representação acima, que no primeiro bloco (genesis), há uma pré-alocação de 1000 unidades. A partir do segundo bloco, as entradas das transações são mapeadas em saídas (usadas também como forma de troco). Caso o remetente não deseje enviar todo o valor para o destinatário, o troco será devolvido ao remetente).
A seguir, há uma descrição dos endpoints a serem implementados:
Method | URL | Description |
---|---|---|
GET | /blockchain/blocks | Get all blocks |
GET | /blockchain/blocks/{index} | Get block by index |
POST | /blockchain/blocks | Mine a new block |
GET | /blockchain/status | Blockchain status |
GET | /my_wallet | Get my wallet information |
POST | /my_wallet/transactions | Create a new transaction |
POST | /wallets | Create a new wallet |
2.7.1 Criando uma API HTTP em Kotlin e Ktor
Ktor é um framework Kotlin que pode ser usado para construir facilmente APIs HTTP.
Nossa aplicação em Ktor requer as seguintes dependências:
val ktorVersion = "2.0.1"
val logbackVersion = "1.2.11"
implementation("io.ktor:ktor-server-core:$ktorVersion")
implementation("io.ktor:ktor-server-netty:$ktorVersion")
implementation("io.ktor:ktor-server-html-builder:$ktorVersion")
implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-jackson:$ktorVersion")
implementation("ch.qos.logback:logback-classic:$logbackVersion")
Ao criarmos nossa API HTTP, é importante isolar os modelos relacionados às regras de negócio, dos que serão expostos através da API. Portanto, para cada estrutura criada anteriormente (Blockchain, Block, Transaction, Wallet, etc) foi criada uma representação a ser exposta em formato JSON.
Na camada de API, utilizamos a biblioteca Jackson, biblioteca poderosa e eficiente que trata da serialização e desserialização de objetos Java/Kotlin e suas representações JSON.
Em seguida, implementaremos a API em Ktor, sem tantas validações, com objetivo apenas de tornar acessível externamente a blockchain para fins de aprendizado:
3. Conclusão
Espero que você tenha gostado do artigo e agora tenha uma melhor compreensão de como uma blockchain funciona.
Em futuros artigos, iremos explorar assuntos relacionados à arquitetura de redes, mecanismos de consenso e como podemos tornar nossa blockchain descentralizada.
O código não é perfeito, então caso perceba algo a ser corrigido ou melhorado na implementação, sinta-se à vontade para contribuir com o projeto.
O código fonte completo está disponível no GitHub.
Obrigado :)
4. Referências
- https://www.ibm.com/br-pt/topics/what-is-blockchain
- https://pt.wikipedia.org/wiki/Fun%C3%A7%C3%A3o_hash
- https://www.gta.ufrj.br/ensino/eel878/redes1-2018-1/trabalhos-vf/blockchain/security.html
- https://pt.wikipedia.org/wiki/%C3%81rvores_de_Merkle
- https://cryptography.io/en/latest/hazmat/primitives/asymmetric/index.html
- https://www.oracle.com/br/blockchain/what-is-blockchain/
- https://ethereum.org/pt-br/developers/docs/consensus-mechanisms/pow/
- https://resources.infosecinstitute.com/topic/hash-functions-in-blockchain/
- https://www.techtarget.com/searchsecurity/definition/elliptical-curve-cryptography
- https://wiki.hyperledger.org/display/BESU/SECP256R1+Support
- https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm
- https://www.alibabacloud.com/blog/comprehensive-review-of-proof-of-work-consensus-in-blockchain_597042
- https://medium.com/@vasilyf/lets-implement-a-cryptocurrency-in-kotlin-part-1-blockchain-8704069f8580
- https://medium.com/@vasilyf/lets-implement-a-cryptocurrency-in-kotlin-part-2-wallets-and-transactions-7aa55239c0d9
- https://medium.com/programmers-blockchain/create-simple-blockchain-java-tutorial-from-scratch-6eeed3cb03fa
- https://medium.com/programmers-blockchain/creating-your-first-blockchain-with-java-part-2-transactions-2cdac335e0ce
- https://lhartikk.github.io/
- https://hackernoon.com/learn-blockchains-by-building-one-117428612f46
Top comments (0)