Содержание статьи
- Общая структура нашего блокчейна
- Функция подсчета хеш-суммы
- Файл block.h
- Файл block.cpp
- Конструктор для инициализации genesis-блока
- Конструктор для инициализации очередного блока
- Создаем genesis-блок
- Майним очередной блок
- Считаем хеш очередного блока
- Записываем в блок значение хеша предыдущего блока
- Читаем значение хеша блока из поля hash
- Файл blockchain.h
- Файл blockchain.cpp
- Инициализируем блокчейн
- Добавляем genesis-блок
- Добавляем очередной блок
- Заключение
Общая структура нашего блокчейна
Итак, наш блокчейн (как и положено) будет представлять собой цепочку из блоков, каждый из которых включает в себя следующее:
- номер блока
[index]
; - метка времени
[timestamp]
; - содержание транзакции
[transaction]
; - значение так называемого доказательства работы
[proof]
(о том, что это такое, чуть ниже); - значение хеш-суммы предыдущего блока
[previous hash]
; - значение хеш-суммы текущего блока
[hash]
.
В содержание транзакции мы включим отправителя денежных средств [sender]
, имя получателя этих средств [recipient]
и количество переданных денежных средств [amount]
. Для простоты в блок будем включать сведения только об одной транзакции.
Общая структура блока, таким образом, будет выглядеть вот так:
block = {
'index': 2,
'timestamp': 1527444458,
'transactions': [
{
'sender': "Petrov",
'recipient': "Ivanov",
'amount': 15418,
}
],
'proof': 4376,
'previous hash': "000bdf8cd989eb26be64a07b80a8cdcaf27476d8473efbde66c9dd857b94ab9",
'hash' : "00e86d5fce9d492c8fac40762fa3f4eee9a4ae4a17ee834be69aa05dff1309cc"
}
Помимо очередных текущих блоков, блокчейн должен включать в себя начальный (или первый) блок, с которого, собственно говоря, и начинается вся цепочка блоков. Этот блок называется genesis-блоком, и он, в отличие от текущих блоков, содержит в себе только номер (который всегда равен нулю), метку времени, какое-либо рандомное значение вместо хеш-суммы предыдущего блока (поскольку genesis-блок первый и предыдущего блока у него просто-напросто нет) и собственное значение хеш-суммы:
genesisblock = {
'index': 0,
'timestamp': 1527443257,
'random hash': "f2fcc3da79c77883a11d5904e53b684ded8d6bb4b5bc73370dfe7942c1cd7ebf",
'hash' : "3fe4364375ef31545fa13aa94ec10abdfdead26307027cf290573a249a209a62"
}
В целом наш блокчейн будет выглядеть следующим образом.
Функция подсчета хеш-суммы
Поскольку для подсчета хешей мы собрались использовать алгоритм «Стрибог» (про который мы уже писали), нам необходима соответствующая функция. Ее мы напишем, используя код из указанной статьи. Качаем его отсюда и подключаем в наш проект нужные файлы следующей строчкой:
# include "gost_3411_2012/gost_3411_2012_calc.h"
Саму функцию объявим так:
std::string get_hash_stribog(std::string str)
Далее объявляем структуру для хранения результатов подсчета хешей и выделяем для нее память:
...
TGOSTHashContext *CTX;
CTX = (TGOSTHashContext*)(malloc(sizeof(TGOSTHashContext)));
...
Поскольку в теле блока значения хеш-сумм представлены в виде строк, то функция должна получить на вход содержимое блока в виде строки. Для этого напишем следующее:
...
// Преобразуем строку входных данных к виду const char
const char *c = str.c_str();
// Формируем буфер для входных данных и копируем в него входные данные
uint8_t *in_buffer;
in_buffer = (uint8_t *)malloc(str.size());
memcpy(in_buffer, c, str.size());
...
Далее считаем хеш:
...
GOSTHashInit(CTX, HASH_SIZE);
GOSTHashUpdate(CTX, in_buffer, str.size());
GOSTHashFinal(CTX);
...
Поскольку выход функции тоже должен быть в виде строки, а рассчитанное значение хеша представлено в виде байтового массива, нам необходимо сделать соответствующее преобразование. Сделаем это следующим образом (HASH_SIZE
— длина хеш-суммы, 512 или 256 бит, выберем 256):
...
// Формируем буфер для выходных данных
char out_buffer[2 * (HASH_SIZE / 8)];
// Пишем в буфер значение хеш-суммы
for (int i = 0; i < HASH_SIZE / 8; i++)
sprintf(out_buffer + i * 2, "%02x", CTX->hash[i]);
// Не забываем освободить выделенную память
free(CTX);
free(in_buffer);
// Возвращаем строку с хеш-суммой
return std::string((const char *)out_buffer);
Функция по расчету хешей у нас готова, можно писать дальше.
Файл block.h
В этом файле опишем класс CBlock, в который войдет все, что нам нужно для создания блока (как очередного, так и genesis-блока). Однако прежде чем описывать сам класс, определим структуру, которая будет описывать транзакцию. Как мы уже решили, транзакция будет включать в себя три поля — отправитель, получатель и сумма транзакции:
struct STransaction {
std::string sender;
std::string recipient;
uintmax_t amount;
};
Теперь можно приступить к описанию нашего класса CBlock. В него входит public-секция, включающая два конструктора (тот, который без параметров, служит для инициализации genesis-блока, а тот, который с параметрами, — для инициализации очередных блоков); метод, создающий genesis-блок; метод, с помощью которого будет майниться очередной блок; метод, записывающий значение хеша предыдущего блока в нужное место текущего блока; метод получения значения хеша блока из соответствующего поля и private-секция со всеми необходимыми полями (номер блока, имя блока, метка времени и так далее) и одним методом подсчета хеш-суммы:
class CBlock {
public:
CBlock();
CBlock(uint32_t index_in, const std::string &in_name_block, const std::string &in_sender,
const std::string &in_recipient, uintmax_t in_amount);
void create_genesis_block();
void mine_block(uint32_t diff);
void set_previous_hash(std::string in_previous_hash);
std::string get_hash();
private:
uintmax_t index; // Номер блока
std::string name_block; // Имя блока
time_t time_stamp; // Метка времени
STransaction transaction; // Транзакция
uintmax_t proof; // Доказательство выполнения работы
std::string previous_hash; // Хеш предыдущего блока
std::string hash; // Хеш текущего блока
std::string calc_hash() const; // Метод подсчета хеша
};
Теперь можно написать реализацию всех указанных методов. Все это мы поместим в файл block.cpp
.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»