Освобождение блокчейна

Я получаю много вопросов о криптовалютах и ​​их механизме, блокчейне. Часто у меня возникают проблемы с лаконичным объяснением, поэтому я собираюсь написать пример блокчейна, чтобы лучше понять его сам. Эта статья - одна из трех, в которых мы поговорим о составных частях: цепочке блоков, одноранговой сети и майнинге. Я не буду делать из этого криптовалюту, просто блокчейн с какой-то абстрактной единицей стоимости.

Отказ от попыток создать настоящую криптовалюту упрощает ряд вещей. Нет никаких комиссий или вознаграждений (майнинг выполняется для чистого удовольствия). Транзакции не нужно суммировать, и пользователям не нужны традиционные кошельки. Что осталось после удаления этих вещей? Неизменяемый список «транзакций», который можно проверить и распространить.

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

Я буду использовать Spring Boot с WebFlux для HTTP-сервера, MongoDB для базы данных и Kafka для очереди сообщений, чтобы помочь в реализации одноранговой сети. Я не буду использовать все возможности MongoDB, даже индексы будут опущены в интересах краткости. Я не буду вдаваться в вопросы безопасности HTTP, любой сможет получить доступ ко всем конечным точкам. Если вы планируете использовать эти части для своей собственной криптовалюты, вам лучше подготовиться к настройке и защите от пуль.

Я буду широко использовать реактивную Java, поэтому, если вам нужно что-то освежить, вы можете прочитать мою статью Понимание реактивной Java.

Первое, что нужно сделать, это блок в цепочке блоков. В блоке есть несколько элементов данных; ссылка на предыдущий блок в цепочке, метка времени, одноразовый номер, который используется как часть доказательства работы, индекс и транзакция. Вот моя реализация блока:

Существует поле id, которое используется MonogDB для отслеживания всех сущностей. hash рассчитывается с помощью index, previousHash, timestamp, transactions и nonce. Я добавляю "увядание" для хеша, так как он должен быть вычислен после инициализации остальной части блока. Одноразовый номер обычно увеличивается для каждой итерации, пытаясь преодолеть трудность. Но в нашем случае трудностей нет, поэтому одноразовый номер всегда равен нулю. Я оставил эти части на месте, чтобы продемонстрировать, как их можно использовать, если бы мы захотели включить доказательство работы, которое было нетривиальным. Позже, когда мы займемся майнингом, мы подробнее рассмотрим доказательство работы.

Также существует «генезисный блок», который всегда является первым блоком в цепочке. Все его значения равны нулю. Когда наш сервер запускается, если база данных пуста, он вставит эту запись. Технически это должно происходить не иначе, как один раз на узел, когда вы его впервые запускаете. Но это должно быть частью правильности блокчейна. На данный момент у нас есть только один узел, мы снова поговорим об узлах, когда мы перейдем к одноранговой сети.

Сам по себе блок не очень полезен. Чтобы дать ему цель, нам нужно добавить к нему некоторые данные. Тогда полезность блока заключается в проверке данных. В нашем случае данные представляют собой список транзакций, а транзакция представляет собой очень простую пару элементов, входной контракт и выходной контракт. Если бы мы создавали криптовалюту, эти «контракты» были бы записью в бухгалтерской книге для перевода с одной учетной записи на другую. Мы используем термин «адрес» для обозначения учетной записи.

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

Ничего особенного. Хеш-значение рассчитывается на основе идентификатора транзакции и данных. Данные делятся на вход и список выходов, и теоретически входные данные должны равняться выходам. Но поскольку наш контракт - это всего лишь строка, обеспечить его соблюдение невозможно. Это просто еще один артефакт того, что это, возможно, начало криптовалюты. Входное значение подписывается закрытым ключом инициатора. Думайте об этом как об инициаторе, отправляющем что-то ценное по адресам в списке выходов.

Как эти две вещи сочетаются друг с другом? Пользователь может заключить транзакцию, но как его собрать в блок? Мы поговорим больше о майнерах позже, но достаточно сказать, что майнер вычищает все «свободные» транзакции и создает новый блок. Давайте посмотрим на некоторые методы, которые мы будем использовать для управления блокчейном. Вот мой класс Blockchain:

Это не так уж и много, большая часть из них связана с гигиеной блокчейна. Блок необходимо проверить по четырем условиям; он должен иметь индекс на единицу выше, чем последний блок в цепочке, он должен иметь хэш последнего блока в качестве его предыдущего хеш-значения, его собственный хеш должен воспроизводиться из данных, и он должен соответствовать уровню сложности. Поскольку у нас нет уровня сложности, всегда верно последнее. Может показаться странным, что мы проверяем, совпадает ли хэш, который, как можно было предположить, мы только что установили, с вновь вычисленным, но мы должны помнить, что в распределенной системе новый блок может быть добавлен партнером, поэтому мы нужно защищаться от неверных данных от них.

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

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

Этот контроллер имеет четыре конечных точки: block для получения всех блоков, block/last для получения последнего блока, block/{hash} для получения блока с определенным хешем и block/mine/{address} для того, чтобы притвориться майнером. Хотя последняя конечная точка относится к другому месту, как объяснено выше, я включаю ее сюда, потому что это была бы довольно скучная система, если бы все, что мы могли когда-либо увидеть, - это генезисный блок.

Затем есть контроллер транзакций. Обычно это используется для просмотра «свободных» транзакций, то есть транзакций, которые не являются частью блока. Опять же, я добавил конечную точку для добавления транзакции, хотя это должен делать только пользователь. Вот контроллер транзакции:

Здесь у нас есть только три конечных точки: transaction, которые получат все незакрепленные транзакции, transaction/{transactionId}, которые получат транзакцию с определенным идентификатором, и transaction (как POST), которые создадут новую транзакцию от имени фиктивного пользователя.

Чтобы запустить эти конечные точки, нам нужно запустить MongoDB и Zookeeper / Kafka. Вот docker-compose.yaml, который я использую для этого:

Запустив эти службы и запустив службу блокчейна, теперь мы можем визуализировать блокчейн. Во-первых, получение всех блоков раскрывает генезисный блок:

Теперь давайте совершим транзакцию:

Получите список незавершенных транзакций:

Майнить блок:

Снова получить все блоки:

Вы также можете проверить, нет ли больше незавершенных транзакций.

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

Весь код из этой статьи можно найти на моей странице GitHub здесь: