Создаем Blockchain с нуля на Python

автор

Самый быстрый способ узнать, как работает блокчейн — это создать его.

Вы здесь, потому что, как и я, немного помешаны на криптовалюте. Кроме этого, вы явно хотите узнать, как работает блокчейн — фундаментальная технология, связанная с криптовалютой.

Однако, понимание блокчейн — не самое простое дело, по крайней мере, так было в моем случае. Я прошел через тонны видеороликов, изучал руководства и постоянно разочаровывался из-за слишком маленького количества примеров.

Я люблю учиться на практике. Это лучше всего показывает суть дела на уровне кода, что запоминается лучше всего. Если у вас похожее мнение, то в конце статьи вы получите рабочий блокчейн с твердым пониманием того, как они работают.

Перед тем, как начать…

Помните, что блокчейн — это неизменная, последовательная цепочка записей, каждая часть этой цепочки называется блоками. Они могут содержать транзакции, файлы или любой вид данных, который вам угоден. Однако важный момент заключается в том, что они связаны вместе хешами.

Если вы не знаете, что такое хеш, то вот вам статьи:

На кого нацелена данная статья?

В целом, вы уже должны более-менее свободно читать и писать основы Python, наряду с пониманием работы запросов HTTP, так как мы будем говорить о блокчейне на HTTP.

Что нам нужно? Убедитесь, что у вас установлен Python 3.6+ (а также pip). Вам также нужно будет установить Flask и замечательную библиотеку Requests:

И да, вам также нужен будет HTTP клиент, такой как Postman или cURL. В целом, что-нибудь подойдет.

Исходный код статьи

Вот здесь вы можете ознакомиться с исходным: https://github.com/dvf/blockchain

Шаг 1: Создание блокчейна

Открывайте свой любимый редактор текста, или IDE, лично я предпочитаю PyCharm. Создайте новый файл под названием blockchain.py. Мы используем только один файл, но если вы запутаетесь, вы всегда можете пройтись по исходному коду.

Скелет блокчейна

Мы создадим класс блокчейна, чей конструктор создает начальный пустой лист (для хранения нашего блокчейна), и еще один — для хранения транзакций. Вот чертёж нашего класса:

Наш класс Blockchain отвечает за управление цепью. Он будет хранить транзакции, а также иметь несколько вспомогательных методов для внесения новых блоков в цепь. Начнем с работы с несколькими методами.

Как выглядит блок?

Каждый блок содержит индекс, временной штамп (время unix), список транзакций, доказательство (об этом позже) и хеш предыдущего блока.

Вот пример того, как выглядит один блок:

С этого момента, понимание цепи должно быть раздельным — каждый новый блок содержит внутри себя хеш предыдущего блока. Это принципиально важно, так как этим обеспечивается неизменность блокчейна: если злоумышленник взломает предыдущий блок, то все остальные блоки будут содержать неправильные хеши.

Имеет ли это смысл? Если нет — то вам нужно уделить время, чтобы понять и осознать это, так как мы говорим о фундаментальном принципе работы блокчейна.

Внесение транзакций в блок

Нам нужен будет способом внесения транзакций в блок. Наш метод new_transaction() отвечает за это, и он достаточно прямолинейный:

После того, как new_transaction() внесет транзакцию в список, он вернет индекс блока, в которой должна будет быть внесена транзакция — а именно следующая. В будущем, это будет полезно для пользователя, отправляющего транзакцию.

Создание новых блоков

После того, как мы получили экземпляр блокчейна, нам нужно посадить в него блок генезиса — первый блок без предшественников. Нам также нужно внести “пруф” в наш блок генезиса, который представляет собой результат майнинга (доказательства проведенной работы). Мы рассмотрим майнинг позже.

В дополнению к созданию блока генезиса в конструкторе, мы также выкатим методы для new_block(), new_transaction() и hash():

Код выше должен быть достаточно ясным — я внес несколько комментариев и документацию, чтобы все было понятно. Мы почти закончили с скелетом нашего блокчейна. Однако на данный момент, вам наверное интересно, как создаются новые блоки?

Понимание подтверждения работы

Алгоритм пруфа работы (Proof of Work, PoW) — это то, как новые блоки созданы или майнятся в блокчейне. Цель PoW — это найти число, которое решает проблему. Число должно быть таким, чтобы его тяжело было найти, но легко подтвердить (говоря о вычислениях) кем угодно в интернете. Это главная задача алгоритма.

Рассмотрим простой пример, чтобы получить лучшее представление.

Скажем, что хеш того или иного числа х, умноженного на другое число должен заканчиваться нулем. Таким образом, hash(x * y) = ac23dc…0. Для этого упрощенного примера, представим что x = 5. Как это работает в Python:

Решение здесь следующее: y = 21, так как созданный хеш заканчивается нулем:

В биткоине, такой алгоритм называется Hashcash. И он особо не отличается от приведенного выше примера. Это алгоритм, который поколение майнеров (читай, отдельная раса) пытается решить, чтобы создать новый блок. В целом, сложность определяется количеством символом, которые рассматриваются в строке. Майнеры неплохо вознаграждаются за решение задачи получением коина в транзакции.

Реализация базового PoW

Давайте реализуем аналогичный алгоритм для нашего блокчейна. Наше правило будет аналогично указанному ранее:

Найдите число «p«, которое хешировано с предыдущим созданным решением блока с хешем содержащим 4 заглавных нуля.

Чтобы скорректировать сложность алгоритма, мы можем изменить количество заглавных нулей. В нашем случае, 4 — достаточно. Вы узнаете, что внесение одного ведущего нуля создает колоссальную разницу во времени, необходимом для поиска решения (майнинга).

Наш класс практически готов, так что мы можем начать взаимодействовать с ним через HTTP запросы.

Шаг 2: Блокчейн как API

Здесь мы задействуем фреймворк под названием Flask. Это макро-фреймворк, который заметно упрощает сопоставление конечных точек с функциями Python. Это позволяет нам взаимодействовать с нашим блокчейном в интернете при помощиHTTP-запросов.

Мы создадим три метода:

  • /transactions/new для создания новой транзакции в блоке;
  • /mine, чтобы указать серверу, что нужно майнить новый блок;
  • /chain для возвращения всего блокчейна

Настройка Flask

Наш “сервер” сформирует единый узел в нашей сети блокчейна. Давайте создадим шаблонный код:

Краткое объяснение того, что мы только что добавили:

  • Строка 15: Создание экземпляра узла. Можете больше узнать о Flask здесь;
  • Строка 18: Создание случайного имени нашего узла;
  • Строка 21: Создание экземпляра класса Blockchain;
  • Строки 24-26: Создание конечной точки /mine, которая является GET-запросом;
  • Строки 28-30: Создание конечной точки /transactions/new, которая являетсяPOST-запросом, так как мы будем отправлять туда данные;
  • Строки 32-38: Создание конечной точки /chain, которая возвращает весь блокчейн;
  • Строки 40-41: Запускает сервер на порт: 5000.

Конечная точка транзакций

Вот так запрос транзакции должен будет выглядеть. Это то, что пользователь отправляет в сервер:

Так как мы уже обладаем методом класса для добавления транзакций в блок, дело осталось за малым. Давайте напишем функцию для внесения транзакций:

Конечная точка майнинга

Конечная точка майнинга — это часть, где происходит магия, и это просто! Для этого нужно сделать три вещи:

  • Подсчитать PoW;
  • Наградить майнера (нас), добавив транзакцию, дающую нам 1 коин;
  • Слепить следующий блок, внеся его в цепь.

Обратите внимание на то, что получатель замайненого блока — это адрес нашего узла. Большая часть того, что мы здесь сделали, это просто взаимодействие с методами в нашем классе Blockchain. С этого момента, мы закончили, и можем начать взаимодействовать с нашим blockchain на Python.

Шаг 3: Взаимодействие с нашим блокчейном

Вы можете использовать старый добрый cURL или Postman для взаимодействия с нашим API в сети.

Запускаем сервер:

Давайте попробуем майнить блок, создав GET-запрос к узлу http://localhost:5000/mine:

Теперь, давайте создадим новую транзакцию, отправив POST-запрос к узлу http://localhost:5000/transactions/new с телом, содержащим структуру нашей транзакции:

Если вы не пользуетесь Postman, тогда вы можете создать аналогичный запрос при помощи cURL:

Я перезапустил свой сервер и замайнил два блока, итого их количество 3. Давайте проверим всю цепочку, выполнив запрос к узлу http://localhost:5000/chain:

Шаг 4: Консенсус

Пока всё идет очень здорово. У нас есть базовый blockchain, который принимает транзакции и дает возможность майнить новые блоки. Но вся суть блокчейна в том, что они должны быть децентрализованными. А если они децентрализованы, каким образом мы можем гарантировать, все они отображают одну цепочку?

Это называется проблемой Консенсуса, так что нам нужно реализовать алгоритм Консенсуса, если нам нужно больше одного узла в нашей цепи.

Регистрация новых узлов

Чтобы мы смогли реализовать алгоритм Консенсуса, нам нужно найти способом дать узлу знать о существовании соседних узлов в цепи. Каждый узел в нашей цепи должен содержать регистр других узлов в цепи. Следовательно, нам понадобиться больше конечных точек:

  • /nodes/register для принятия список новых узлов в форме URL-ов;
  • /nodes/resolve для реализации нашего алгоритма Консенсуса, который решает любые конфликты, связанные с подтверждением того, что узел находиться в своей цепи.

Нам нужно будет изменить конструктор нашего Blockchain и привнести метод для регистрации узлов:

Обратите внимание на то, что мы использовали set() для хранения списка узлов. Это легкий способ убедиться в том, что внесение новых узлов является идемпотентным — это означает, что вне зависимости от того, сколько раз мы внесем определенный узел, он возникнет только один раз.

Реализация алгоритма Консенсуса

Как мы уже знаем, конфликт заключается в том, что один узел имеет другую цепь, связанную с другим узлом. Чтобы решить это, мы введем правило, где самая длинная и валидная цена является авторитетной. Другими словами, длиннейшая цепь сети де-факто является единственной. Используясь этот алгоритм, мы достигнем Консенсуса среди узлов в нашей сети.

Первый метод valid_chain() отвечает за проверку того, является ли цепь валидной, запустив цикл через каждый блок и проводя верификацию как хеша, так и пруфа.

Метод resolve_conflicts(), который запускает цикл через все наши соседние узлы, загружает их цепи и проводит проверку, как и в предыдущем методе. Если валидная цепь, длина которой больше, чем наша, мы заменяем нашу.

Давайте зарегистрируем две конечные точки нашего API, одну для внесения соседних узлов, а вторую — для решения конфликтов:

Теперь вы можете сесть за другой компьютер (если хотите), и развернуть разные узлы в своей сети. Также вы можете развернуть процессы при помощи различных портов на одном и том же компьютере. Я развернул еще один узел на своем компьютере, но на другом порте и зарегистрировал его при помощи моего текущего узла. Таким образом, я получил два узла: http://localhost:5000 и http://localhost:5001.

После этого я получил два новых блока в узле 2, чтобы убедиться в том, что цепь была длиннее. После этого, я вызвал GET /nodes/resolve в узле 1, где цепь была заменена нашим алгоритмом консенсуса:

И это была обертка… Найдите друзей и вместе попробуйте протестировать ваш блокчейн!

Подведем итоги

Надеюсь, эта статья вдохновила вас на что-нибудь новое. Я в восторге от криптовалют, так как я верю, что блокчейн радикально изменят наше представление об экономике, правительстве и учетных записях!

Вам может быть интересно

Scroll Up