Блокчейн (blockchain) – это технология распределенного хранения данных в одноранговой сети в виде непрерывной последовательности блоков взаимосвязанных с помощью алгоритма хеширования. Давайте подробнее познакомимся с этой технологией и рассмотрим пример ее реализации на языке программирования C#.

Что такое Блокчейн?

Как я уже писал выше блокчейн это распределенное хранилище данных. То есть, блокчейн можно представить себе как непрерывную последовательность зависящих друг от друга блоков, содержащих как полезную информацию, так и дополнительные служебные данные, которые гарантируют достоверность хранимой информации. Главной особенностью данной технологии является распределенность (децентрализация) – не существует единого центрального хранилища данных, каждый пользователь хранит у себя полную копию всей информации. Каждый экземпляр хранилища синхронизуется с другими по заданным системой правилам. Второй важной особенностью блокчейна является ее криптозащищенность. Под этим подразумевается использование специализированных алгоритмов хеширования данных, которые гарантируют их целостность и неизменность. Это особенно важно, если брать во внимание, что не существует эталона данных, каждый экземпляр является полноценным и равным другому. Поэтому очень важно защищать данные от внесения изменений. Давайте чуть подробнее рассмотрим принцип работы технологии блокчейн.

Как работает Блокчейн?

Вводимые данные «упаковываются» в специализированные блоки. В данном блоке содержится дополнительная служебная информация, которая необходима для обеспечения целостности и неизменности введенных данных, а также для работы самой цепочки блоков. Сама по себе цепочка блоков очень похожа по структуре на односвязный список. Подробнее данная структура данных описана в статье Связный список (Linked List) C#. Блок данных содержит следующие важные поля:

  • Data – поле, в котором сохранены сами полезные данные. Это может быть как простая строка, так и более сложные структуры.
  • CreatedOn – дата и время создания блока данных. Важно использовать универсальное UTC время, чтобы не возникало конфликтов из-за разных часовых поясов.
  • Hash – уникальный ключ, созданный на основе хранимых данных блока с помощью специализированной хеш-функции, которая подразумевает только одностороннее шифрование.
  • PreviousHash – указатель на предыдущий хеш-блока. Это необходимо для связывания блоков в единую цепочку.

Также возможно использование и дополнительных данных, в данном случае также использовались следующие поля:

  • User – данные о пользователе, создавшем блок
  • Algorithm – используемый алгоритм хеширования
  • Version – версия блока
Блокчейн (blockchain)

Блокчейн (blockchain)

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

Что такое хеширование?

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

Рассмотрим пример самой элементарной хеш-функции, это суммирование всех цифр числа. Например, если на вход мы получим число 73, то хешем данного числа будет 7 + 3 = 10. Это очень простая операция, которая позволяет получить хеш. Но вот обратное преобразование однозначно выполнить невозможно, так как существуют многие числа, дающие такой же результат: 64, 37, 181, 11116 и так далее.

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

Реализация Блокчейн на языке C#

Обратите внимание, что в данной статье рассматривается прототип приложения, а не готовый к использованию продукт. Код далеко не идеален, и не весь изначально задуманный функционал реализован, но для знакомства с технологией блокчейн этого более чем достаточно. Изначально, данный проект реализовывался в рамках ежегодного соревнования среди разработчиков департамента CRM компании ООО «Норбит» (New Year Coding Challenge 2018).

Вот краткая формулировка задачи: необходимо разработать защищенное хранилище информации на базе технологии блокчейн. Решение должно обеспечивать хранение информации без возможности модификации (кроме полного стирания всех данных). Решение должно позволять хранить произвольный набор данных в виде строки размером до 5 МБ. Кроме того, необходимо реализовать ролевую модель пользователей и возможность разделения хранимых данных по типу.

Приложение разделено на несколько проектов.

  • BlockchainData — уровень данных. Я заранее предусмотрел возможность расширения возможных средств для сохранения данных. В данном случае используется единственная реализация сохранения в базу данных MS SQL с использованием EntityFramework.
  • Blockchain — уровень логики. Здесь реализованы все основные классы и логика работы приложения.
  • BlockchainService — WCF-служба, предоставляющая REST API интерфейс для клиентских приложений.
  • BlockchainExplorerDesktop — WinForms-приложение для просмотра данных хранимых в блокчейн.
  • BlockchainExplorerWeb — MVC ASP.NET приложение для просмотра данных хранимых в блокчейн.

Рассмотрим подробнее реализацию основной библиотеки классов Blockchain.dll.

Реализация цепочки блоков Chain

Итак, основным классом библиотеки является класс Chain.cs. Он взаимодействует с уровнем данных использую принципы инверсии управления и внедрения зависимостей. Подробнее про это можно прочитать в статье Инверсия управления и Внедрение зависимостей (IoС & DI). Таким же образом реализован подход к алгоритму хеширования. Chain содержит в себе список всех блоков в хранилище данных, а также подсписки для разделения блоков по типам (блоки данных, блоки с данными о пользователях, блоки с данными о IP адресах серверов). При создании экземпляра класса цепочки блоков выполняется обращение к глобальной сети для синхронизации с другими хостами. Также выполняется получение блоков из локального хранилища. На данный момент не реализовано корректного алгоритма слияния этих двух цепочек. Выбирается просто выбирается наиболее длинная цепь. Если не было получено ни локальной, ни глобальной цепочки, то выполняется создание новой цепочки состоящей из одного генезис-блока. Генезис блок — это единый начальный блок цепочки. Он всегда генерируется по одинаковым правилам у всех экземпляров блокчейн. После создания экземпляра цепочки выполняется ее полная проверка на корректность, чтобы удостовериться, что не было внесено никаких изменений.

Реализация блока цепочки Block

Класс блок реализует структуру блока данных. Он содержит в себе все необходимые поля, которые должны быть сохранены в хранилище данных. При создании блока выполняется хеширование входных данных, а также хеширование всего блока, что позволяет гарантировать неизменность хранимых данных и метаданных. При выполнении хешированию учитываются следующие поля: версия, время создания блока, хеш предыдущего блока, хеш хранимых данных, хеш пользователя. Результат сохраняется в поле Hash блока, что позволяет легко проверить его корректность.

Реализация класса хранимых данных Data

Класс данных необходим больше для удобства работы. Он позволяет сохранять строковые данные, получать хеш хранимых данных, а также разделять хранимые данные по тип (данные о пользователях, данные о серверах, простые хранимые текстовые данные). Для корректного сохранения контента выполняется сериализация и десериализация в json формат.

Сериализация — это процесс преобразования объекта в поток байтов, десериализация — это обратный процесс, позволяющий сформировать из потока байтов готовый объект. Если сформулировать это более простым языком, мы выполняет сохранение всех значений объекта в специализированном тестовом формате json. Данная тема будет мной подробнее рассмотрена в одной из моих следующих статей.

Реализация класса пользователя User

Данный класс является также необходимым только для удобства работы с пользователями системы. Он хранит важные данные о пользователях, такие как логин, хеш пароля, права пользователя. Данные о пользователях также хранятся в сериализованном json формате, что позволяет легко сохранять и восстанавливать объекты.

Интерфейсы IHashable и IAlgorithm

Обратите внимание, что важной особенностью данной реализации является использование специального интерфейса IHashable, который должны реализовывать все классы, которые подвергаются хешированию (такие как Block, User, Data). Этот интерфейс реализует всего одно свойство и один метод. Свойство строковое Hash гарантирует, что у класса будет поле, в которое будет выполнено сохранение готового хеша объекта, а метод GetStringForHash возвращает строку, в которой хранятся все тестовые данные, важные для хеширования.

Данный интерфейс используется в другом интерфейсе IAlgorithm. Данный интерфейс используется для того, чтобы предоставить возможность дальнейшего расширения возможностей приложения, путем легкого добавления дополнительных алгоритмов хеширования. Данный интерфейс определяет один перегруженный метод GetHash, которвый возвращает либо хеш стоки, либо хеш класса, реализующего интерфейс IHashable.

В данный момент реализован один алгоритм хеширования Sha256 в соответствующем классе.

Для корректности работы классов, реализующих данные интерфейсы используется проектирование по контракту. Это позволяет задать предусловия и постусловия для всех классов, которые реализуют данные интерфейсы. Благодаря этому гарантируется минимальная проверка параметров у всех наследников. Подробнее данную тему можно изучить в статье Программирование по контракту (Code Contracts) в C#.

Реализация клиент-серверной части приложения

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

Уровень данных BlockchainData

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

Сейчас создана единственная реализация данного интерфейса с помощью работы с базой данных Microsoft SQL Server через ORM-систему EntityFramework 6. Данная реализация представлена в классе SqlDataProvider.

Служба WCF

Основным серверном контроллером системы является WCF служба. Именно она использует логику реализованную в библиотеке классов Blockchain. Она предоставляет несколько REST API интерфейсов, к которым обращаются клиентские приложения. Запросы выполняются в асинхронном режиме, что улучшает производительность системы. На данный момент реализована передача данных через GET, что не слишком правильно, но удобно при отладке приложения. Подробнее про службу WCF можно прочитать в статье Windows Communication Foundation (WCF) служба. В этой же статье рассматриваются возможности настройки подключения клиентских приложений к службе. Очень рекомендую ознакомиться, перед продолжением чтения.

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

Служба выполняет проверку и формирование полученных данный в экземпляры классов блоков соответствующего типа, и дальнейшая логика работы передается библиотеке классов Blockchain. Таким образом, служба является своеобразным посредником между клиентскими приложениями и основной бизнес-логикой системы.

Windows Form приложение BlockchainExplorerDesktop

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

ASP.NET MVC приложение BlockchainExplorerWeb

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

Заключение

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

Сразу поделюсь некоторой дополнительной информацией. В качестве эксперимента, при демонстрации проекта на конкурсе я использовал Azure. Вещь очень удобная, приложения публикуются без каких-либо проблем, базы данных создаются и отлично работают, службы тоже. Но я столкнулся с двумя проблемами. Первое, разница в часовых поясах. Серверное время у службы было австралийским, из-за этого были большие проблемы при хешировании, так как время менялось. Проблему еще сильнее усугубил EntityFramework, который некорректно сохранял время в UTC. Пришлось немного поплясать с бубном. Второе, Azure очень дорогой. За 1 месяц у меня ушло около трех тысяч рублей, на базу данных, службу и веб-приложение. Кстати, конкурс я успешно выиграл.

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

P.S. Присоединяйся в любой удобной для тебя социальной сети. Для меня очень важно оставаться с тобой на связи, ведь у меня есть еще много полезной информации о программировании для тебя, которой я хочу с тобой поделиться.
[DISPLAY_ULTIMATE_PLUS]

Рубрики: C#

shwan

Программист .NET

%d такие блоггеры, как: