Introdução

Um blockchain é um livro que foi escrito de tal forma que atualizar os dados contidos nele torna-se muito difícil, alguns dizem que o blockchain é imutável e para todos os efeitos é correto, mas a imutabilidade sugere permanência e nada em um disco rígido poderia ser considerado permanente. Para Saber mais sobre o funcionamento do blockchain acesse nossa outra pagina aqui.

Iniciando o projeto

A primeira coisa que você gostaria de fazer é abrir um novo projeto em C ++, usando qualquer IDE C++ ou até mesmo um editor de texto funcionara. Vamos chamar o projeto de TestChainCH. Crie um arquivo chamado Block.h na pasta principal do projeto. Dentro do arquivo, adicione o seguinte código (se você estiver usando o CLion, coloque todo o código entre as linhas #define TESTCHAIN_BLOCK_H e #endif):

C++

#include <cstdint>
#include <iostream>
                              

Estas linhas acima dizem ao compilador para incluir as bibliotecas cstdint e iostream. Agora adicione esta linha abaixo dela:

C++

using namespace std;

Isso cria essencialmente um atalho para o namespace padrão, o que significa que não precisamos nos referir a declarações dentro do namespace padrão por seus nomes completos, por exemplo std::string, mas em vez disso use seus nomes abreviados, por exemplo string.

Um blockchain é composto de uma série de blocos que contêm dados e cada bloco contém uma representação criptográfica do bloco anterior, o que significa que é muito difícil alterar o conteúdo de qualquer bloco sem precisar alterar cada um dos subsequentes; portanto, onde o blockchain essencialmente obtém suas propriedades imutáveis.

Então, vamos criar nossa classe de bloco, adicionar as seguintes linhas ao arquivo de cabeçalho Block.h:

C++

              
class Block {
    public:
        string sPrevHash;

        Block(uint32_t nIndexIn, const string &sDataIn);

        string GetHash();

        void MineBlock(uint32_t nDifficulty);

    private:
        uint32_t _nIndex;
        int64_t _nNonce;
        string _sData;
        string _sHash;
        time_t _tTime;

        string _CalculateHash() const;
};
                          

Estamos chamando nossa classe de bloco (linha 1), seguido pelo modificador público (linha 2) e a variável pública sPrevHash (lembre-se de que cada bloco está vinculado ao bloco anterior) (linha 3). A assinatura do construtor (linha 5) usa três parâmetros para nIndexIn e sDataIn; Observe que a palavra-chave const é usada junto com o modificador de referência (&) para que os parâmetros sejam passados por referência, mas não possam ser alterados, isso é feito para melhorar a eficiência e economizar memória. A assinatura do método GetHash é especificada a seguir (linha 7) seguida pela assinatura do método MineBlock (linha 9), que toma um parâmetro nDifficulty. Especificamos o modificador privado (linha 11) seguido pelas variáveis privadas _nIndex, _nNonce, _sData, _sHash e _tTime (linhas 12–16). A assinatura para _CalculateHash (linha 18) também tem a palavra-chave const, para garantir que a saída do método não possa ser alterada, o que é muito útil ao lidar com um blockchain.

Agora é hora de criar nosso arquivo Blockchain.h na pasta principal do projeto.

Vamos começar adicionando essas linhas:

C++


#include <cstdint>
#include <vector>
#include "Block.h"

using namespace std;

Elas dizem ao compilador para incluir as bibliotecas cstdint e vector, bem como o arquivo de header Block.h que acabamos de criar, e cria um atalho para o namespace std.

Agora, vamos criar nossa classe Blockchain e adicionar as seguintes linhas ao arquivo de header Blockchain.h:

C++

              
class Blockchain {
    public:
        Blockchain();

        void AddBlock(Block bNew);

    private:
        uint32_t _nDifficulty;
        vector<Block> _vChain;

        Block _GetLastBlock() const;
};
                          

Assim como em nossa classe de bloco, estamos simplificando as coisas e chamando nossa classe de Blockchain (linha 1) seguida pelo modificador público (linha 2) e a assinatura do construtor (linha 3). A assinatura AddBlock (linha 5) usa um parâmetro bNew, que deve ser um objeto da classe Block que criamos anteriormente. Em seguida, especificamos o modificador privado (linha 7), seguido pelas variáveis privadas para _nDifficulty e _vChain (linhas 8–9), bem como a assinatura do método para _GetLastBlock (linha 11), que também é seguida pela palavra-chave const para denotar que saída do método não pode ser alterada.

Como blockchains usam criptografia, agora seria um bom momento para obter alguma funcionalidade criptográfica em nosso blockchain. Usaremos a técnica de hashing SHA256 para criar hashes de nossos blocos, vamos utilizar estes arquivos aqui.

Crie um arquivo de origem para o nosso bloco e salve-o como Block.cpp na pasta principal do projeto.

Comece adicionando essas linhas, que dizem ao compilador para incluir os arquivos Block.h e sha256.h que adicionamos anteriormente.

C++

#include "Block.h"
#include "sha256.h"

Siga estes passos para a implementação do nosso construtor de blocos:

C++

Block::Block(uint32_t nIndexIn, const string &sDataIn) : _nIndex(nIndexIn), _sData(sDataIn) {
    _nNonce = -1;
    _tTime = time(nullptr);
}

O construtor começa repetindo a assinatura que especificamos no arquivo de cabeçalho Block.h (linha 1), mas também adicionamos código para copiar o conteúdo dos parâmetros nas variáveis _nIndex e _sData. A variável _nNonce é configurada como -1 (linha 2) e a variável _tTime é definida como a hora atual (linha 3).

Vamos adicionar um acessador para o hash do bloco:

C++


string Block::GetHash() {
    return _sHash;
}

Nós especificamos a assinatura para GetHash (linha 1) e então adicionamos um retorno para a variável privada _sHash (linha 2).

Como você deve ter lido, a tecnologia blockchain tornou-se popular quando foi inventada para a moeda digital Bitcoin, já que a contabilidade é imutável e pública; o que significa que, quando um usuário transfere Bitcoin para outro usuário, uma transação para a transferência é gravada em um bloco no blockchain por nós na rede Bitcoin. Um nó é outro computador que está executando o software Bitcoin e, como a rede é peer-to-peer, pode ser qualquer pessoa em todo o mundo; Esse processo é chamado de "mineração", pois o proprietário do nó é recompensado com o Bitcoin cada vez que cria com sucesso um bloco válido no blockchain.

Para criar com sucesso um bloco válido e, portanto, ser recompensado, um minerador deve criar um hash criptográfico do bloco que deseja adicionar ao blockchain que corresponda aos requisitos de um hash válido naquele momento; isso é obtido contando o número de zeros no início do hash, se o número de zeros for igual ou maior que o nível de dificuldade definido pela rede que o bloco é válido. Se o hash não é válido, uma variável chamada nonce é incrementada e o hash é criado novamente; Esse processo, chamado Proof of Work (PoW), é repetido até que um hash seja produzido e válido.

Vamos adicionar o método MineBlock; aqui é onde a mágica acontece!

C++


void Block::MineBlock(uint32_t nDifficulty) {
    char cstr[nDifficulty + 1];
    for (uint32_t i = 0; i < nDifficulty; ++i) {
        cstr[i] = "0";
    }
    cstr[nDifficulty] = "\0";

    string str(cstr);

    do {
        _nNonce++;
        _sHash = _CalculateHash();
    } while (_sHash.substr(0, nDifficulty) != str);

    cout << "Block mined: " << _sHash << endl;
}

Começamos com a assinatura do método MineBlock, que especificamos no arquivo de cabeçalho Block.h (linha 1), e criamos uma matriz de caracteres com um comprimento maior que o valor especificado para nDifficulty (linha 2). Um loop for é usado para preencher a matriz com zeros, seguido pelo item de matriz final que recebe o caractere de terminador de cadeia (\0), (linhas 3-6) e, em seguida, a matriz ou cadeia de caracteres é transformada em uma cadeia padrão ( linha 8). Um loop do… while é usado (linhas 10–13) para incrementar o _nNonce e o _sHash é atribuído com a saída do _CalculateHash, a parte frontal do hash é então comparada com a sequência de zeros que acabamos de criar; se nenhuma correspondência for encontrada, o loop será repetido até que uma correspondência seja encontrada. Uma vez encontrada uma correspondência, uma mensagem é enviada ao buffer de saída para informar que o bloco foi minerado com êxito (linha 15).

Vamos adicionar o método _CalculateHash:

C++


inline string Block::_CalculateHash() const {
    stringstream ss;
    ss << _nIndex << _tTime << _sData << _nNonce << sPrevHash;

    return sha256(ss.str());
}

Iniciamos com a assinatura para o método _CalculateHash (linha 1), que especificamos no arquivo de cabeçalho Block.h, mas incluímos a palavra-chave inline que torna o código mais eficiente, pois o compilador coloca as instruções do método em linha sempre que o método é chamado; isso reduz as chamadas de método separadas. Um fluxo de seqüência de caracteres, em seguida, é criado (linha 2), seguido por acrescentar os valores para _nIndex, _tTime, _sData, _nNonce e sPrevHash para o fluxo (linha 3). Nós terminamos retornando a saída do método sha256 (dos arquivos sha256 que adicionamos anteriormente) usando a saída de string do fluxo de strings (linha 5).

Crie um arquivo de origem para o nosso blockchain e salve-o como Blockchain.cpp na pasta principal do projeto.

Adicione essas linhas, que dizem ao compilador para incluir o arquivo Blockchain.h que criamos anteriormente.

C++
#include "Blockchain.h"

Adicione estas linhas no arquivo para continuarmos com a implementação do nosso construtor blockchain:

C++


Blockchain::Blockchain() {
    _vChain.emplace_back(Block(0, "Genesis Block"));
    _nDifficulty = 6;
}

Começamos com a assinatura do construtor blockchain que especificamos em Blockchain.h (linha 1). À medida que os blocos são adicionados ao blockchain, eles precisam referenciar o bloco anterior usando seu hash, mas como o blockchain deve começar em algum lugar, temos que criar um bloco para o próximo bloco para referência, chamamos isso de um bloco de gênese. Um bloco de gênese é criado e colocado no vetor _vChain (linha 2). Em seguida, definimos o nível de _nDificuldade (linha 3) dependendo de quão difícil queremos fazer o processo de PoW.

Agora é hora de adicionar o código para adicionar um bloco ao blockchain, adicione as seguintes linhas:

C++


void Blockchain::AddBlock(Block bNew) {
    bNew.sPrevHash = _GetLastBlock().GetHash();
    bNew.MineBlock(_nDifficulty);
    _vChain.push_back(bNew);
}

A assinatura que especificamos em Blockchain.h para AddBlock é adicionada (linha 1), seguida pela configuração da variável sPrevHash para o novo bloco a partir do hash do último bloco no blockchain que usamos usando GetLastBlock e seu método GetHash (linha 2). O bloco é então extraído usando o método MineBlock (linha 3) seguido pelo bloco sendo adicionado ao vetor _vChain (linha 4), completando assim o processo de adicionar um bloco ao blockchain.

Vamos terminar esse arquivo adicionando o último método:

C++


Block Blockchain::_GetLastBlock() const {
    return _vChain.back();
}

Adicionamos a assinatura para _GetLastBlock de Blockchain.h (linha 1) seguida pelo retorno do último bloco encontrado no vetor _vChain usando seu método back (linha 2).

Vamos testar ?

Lembre-se do arquivo main.cpp? Agora é a hora de atualizá-lo, abri-lo e substituir o conteúdo pelas seguintes linhas:

C++


#include "Blockchain.h"

int main() {
    Blockchain bChain = Blockchain();

    cout << "Mining block 1..." << endl;
    bChain.AddBlock(Block(1, "Block 1 Data"));

    cout << "Mining block 2..." << endl;
    bChain.AddBlock(Block(2, "Block 2 Data"));

    cout << "Mining block 3..." << endl;
    bChain.AddBlock(Block(3, "Block 3 Data"));

    return 0;
}

Agora vamos compilar nosso exemplo, abra o terminal e execute o seguinte comando:



gcc -lstdc++     -o TestChainCH     -std=c++11     -x c++     main.cpp Block.cpp Blockchain.cpp sha256.cpp

Feito isto, teremos um arquivo do nosso projeto compilado, vamos executa-lo:

./TestChainCH

Ao executa o resultado no terminal sera algo como o retorno abaixo:


Mining block 1...
Block mined: 0000009eefd62ec1b5da6356f0fbbd0077f171727f6b046074d504412352872e
Mining block 2...
Block mined: 000000a33dff1d944e633f76b90620c4c9d2f8c524b76dcb1522a1bb9eff7a51
Mining block 3...
Block mined: 00000056d958fe3f8609010514d6c56f27cb25e6f01cc1f49ee4341ded4ce304