Криптографические основы блокчейн-технологий. [Евгения Александровна Ищукова] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]

Евгения Александровна Ищукова,
Сергей Петрович Панасенко,
Кирилл Сергеевич Романенко,
Вячеслав Дмитриевич Салманов

Криптографические основы
блокчейн-технологий

Москва, 2022

УДК 004.338
ББК 65.050.253
И98

Ищукова Е. А., Панасенко С. П., Романенко К. С., Салманов В. Д.
И98 Криптографические основы блокчейн-технологий. – М.: ДМК Пресс,
2022. – 302 с.: ил.
ISBN 978-5-97060-865-4
Книга предназначена как для специалистов в области блокчейн-технологий,
так и для только начинающих интересоваться данной темой. Она освещает вопросы построения блокчейн-систем, не ограничиваясь применяемыми в них
криптографическими алгоритмами, но рассматривая также их основные механизмы, включая транзакции, принципы формирования блоков и сценарии достижения консенсуса в распределенных сетях. Теоретический материал книги
проиллюстрирован на примере нескольких криптовалютных платформ, базирующихся на блокчейн-технологиях.
Дизайн обложки разработан с использованием ресурса freepik.com

УДК 004.338
ББК 65.050.253

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

ISBN 978-5-97060-865-4

© Ищукова Е. А., Панасенко С. П., Романенко К. С.,
   Салманов В. Д., 2022
© Оформление, издание, ДМК Пресс, 2022

Оглавление

Предисловие ........................................................................................ 6
Введение ...............................................................................................7
Глава 1. Алгоритмы хеширования .................................................... 9
1.1 Основные понятия и определения ............................................................10
1.1.1 Структура алгоритмов хеширования .................................................10
1.1.2 Надстройки над алгоритмами хеширования.....................................14
1.2 Методы криптоанализа и атаки на алгоритмы хеширования.................18
1.2.1 Цели атак на алгоритмы хеширования ..............................................19
1.2.2 Атаки методом «грубой силы» ............................................................21
1.2.3 Словарные атаки и цепочки хеш-кодов .............................................22
1.2.4 Радужные таблицы...............................................................................26
1.2.5 Парадокс «дней рождения» и поиск коллизий ..................................27
1.2.6 Дифференциальный криптоанализ ...................................................30
1.2.7 Алгебраический криптоанализ ...........................................................34
1.2.8 Атаки, использующие утечки данных по побочным каналам ..........35
1.2.9 Другие виды атак .................................................................................35
1.3 Наиболее известные алгоритмы хеширования ........................................38
1.3.1 Алгоритмы семейства MD ...................................................................38
1.3.2 Алгоритмы семейства RIPEMD ...........................................................59
1.3.3 Алгоритмы семейства SHA ..................................................................69
1.3.4 Отечественные стандарты хеширования ...........................................84

Глава 2. Алгоритмы электронной подписи
на эллиптических кривых ............................................................... 91
2.1 Математические основы ............................................................................91
2.2 Эллиптические кривые...............................................................................95
2.2.1 Определение эллиптической кривой .................................................95
2.2.2 Основные операции над точками эллиптической кривой ...............96
2.2.3 Основные характеристики эллиптической кривой...........................99
2.2.4 Примеры эллиптических кривых .....................................................101
2.2.5 Задача дискретного логарифмирования в группе точек
эллиптической кривой ...............................................................................105
2.2.6 Альтернативные формы представления эллиптических кривых ... 107
2.3 Основные алгоритмы электронной подписи .........................................111
2.3.1 Алгоритм ECDSA ................................................................................111
2.3.2 ГОСТ Р 34.10–2012 .............................................................................112

4

 Оглавление
2.3.3 Некоторые особенности алгоритмов ECDSA
и ГОСТ Р 34.10–2012....................................................................................114
2.3.4 Алгоритм EdDSA..................................................................................116
2.3.5 Алгоритм BLS......................................................................................118

Глава 3. Основные принципы работы блокчейн-технологий.... 122
3.1 Базовые механизмы блокчейн-систем.....................................................123
3.1.1 Транзакции..........................................................................................123
3.1.2 Упаковка транзакций в блоки............................................................127
3.1.3 Применение деревьев Меркля при формировании блоков.............130
3.2 Механизмы консенсуса.............................................................................131
3.2.1 Консенсус доказательства работы Proof of Work..............................131
3.2.2 Консенсус доказательства владения долей Proof of Stake................135
3.2.3 Консенсус на основе решения задачи византийских генералов.....136
3.2.4 Другие механизмы достижения консенсуса.....................................137
3.3 Выстраивание цепочки блоков.................................................................139
3.3.1 Принципы формирования цепочки..................................................139
3.3.2 Ветвления цепочки блоков.................................................................142
3.4 Смарт-контракт..........................................................................................145
3.5 Основные виды блокчейн-систем............................................................148
3.5.1 Публичный блокчейн..........................................................................148
3.5.2 Приватный блокчейн..........................................................................149
3.6 Криптовалютные кошельки......................................................................150
3.6.1 Программы-кошельки........................................................................150
3.6.2 Аппаратные кошельки........................................................................152

Глава 4. Основные блокчейн-платформы....................................153
4.1 Биткойн......................................................................................................153
4.1.1 Введение в устройство блокчейн-системы Биткойн........................154
4.1.2 Особенности механизма консенсуса в системе Биткойн.................156
4.1.3 Форки в системе Биткойн..................................................................156
4.1.4 Транзакции..........................................................................................159
4.1.5 Кошельки в системе Биткойн.............................................................203
4.1.6 Создание и использование иерархических
детерминированных ключей......................................................................206
4.2 Эфириум.....................................................................................................209
4.2.1 Глобальное состояние.........................................................................209
4.2.2 Консенсус.............................................................................................210
4.2.3 Газ.........................................................................................................211
4.2.4 Адреса и кошельки..............................................................................212
4.2.5 Транзакции..........................................................................................213
4.2.6 Структура блока..................................................................................213
4.2.7 Эволюция системы Эфириум.............................................................214
4.2.8 Основная и тестовые сети платформы Эфириум.............................218
4.2.9 Запуск сети Эфириум..........................................................................219
4.2.10 Смарт-контракты в системе Эфириум............................................234
4.3 Hyperledger.................................................................................................245

Оглавление 

5

4.3.1 Основные особенности системы.......................................................245
4.3.2 Проекты экосистемы Hyperledger......................................................246
4.3.3 Архитектура Hyperledger Fabric.........................................................247
4.3.4 Пример смарт-контракта для Hyperledger........................................248
4.4 Обзор других платформ............................................................................253
4.4.1 EOSIO...................................................................................................253
4.4.2 Краткий обзор прочих блокчейн-платформ.....................................255
4.4.3 Обзор отечественных решений.........................................................257

Приложение 1. Таблицы констант алгоритмов хеширования.....261
П1.1 Таблица замен алгоритма MD2..............................................................261
П1.2 Индексы используемых в итерациях слов блока
сообщения алгоритма MD4.............................................................................262
П1.3 Константы алгоритма MD5.....................................................................263
П1.4 Константы алгоритма MD6.....................................................................267
П1.5 Константы алгоритмов семейства SHA-2..............................................268
П1.6 Раундовые константы алгоритмов семейства SHA-3...........................270
П1.7 Константы алгоритма ГОСТ Р 34.11–2012.............................................271

Список сокращений.........................................................................275
Перечень рисунков..........................................................................281
Перечень таблиц..............................................................................286
Перечень источников......................................................................289

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

Введение
С момента появления блокчейн-технологий прошло менее 15 лет, но их активное развитие в течение этого времени предопределило вхождение данных
технологий в весьма различные сферы деятельности.
Основной сферой применения блокчейн-технологий можно считать финансовую: они лежат в основе криптовалют, которые, похоже, уже достаточно
прочно вошли в нашу жизнь. Буквально в последний год мы могли наблюдать
всплеск интереса к криптовалютам после многократного удорожания основной из них – биткойна – в течение 2020 – начала 2021 года.
При этом постоянно модернизируются и совершенствуются как сама платформа Биткойн и лежащая в ее основе блокчейн-система, так и данные технологии в принципе. Их развитие способствует и развитию множества смежных
технологий и направлений: от криптографических алгоритмов до вычислительных ресурсов, применяемых пользователями подобных систем.
Помимо финансового сектора, блокчейн-технологии востребованы в различных системах государственных организаций, в частности:
 ведение различных реестров, например государственной регистрации
прав на недвижимое имущество, землю и т. п.;
 выпуск цифровых удостоверений личности на основе блокчейн-технологий;
 удаленное голосование, опробованное, в частности, в России в 2019
и 2020 годах.
Можно предполагать, что в дальнейшем приведенный выше, далеко не полный перечень применений блокчейнов будет только расширяться.
Отметим, что распределенные реестры были известны и ранее, но именно
блокчейн-технологии, обеспечивающие, с одной стороны, возможность модификации общих данных различными пользователями распределенных систем и,
с другой стороны, контроль целостности и непротиворечивости данных на основе
определенных правил, предопределили масштабное развитие подобных технологий и появление таких принципиально новых направлений, как криптовалюты.
Про блокчейн-технологии, особенно в части их применений в криптовалютах, издано достаточно много книг. Специфика этой книги в том, что при ее
создании мы изначально ставили своей целью рассмотреть и проанализировать именно криптографические механизмы, лежащие в основе блокчейн-технологий и предопределяющие их основные качества, способствующие столь
бурному развитию и предполагаемому в будущем широчайшему применению
данных технологий.
Понимая и разделяя интерес многих потенциальных читателей к криптовалютам, мы не обошли их стороной, но рассмотрели именно с точки зрения
реализованных в них криптоалгоритмов и прочих методов, обеспечивающих
технические составляющие безопасного использования криптовалют.

8

 Введение

В первой главе книги описаны алгоритмы хеширования, обеспечивающие
контроль целостности данных в блокчейне. Рассмотрены основные принципы
данных алгоритмов, возможные проблемы при их реализации и использовании, включая известные атаки на алгоритмы хеширования. Приведено подробное описание наиболее известных алгоритмов хеширования, включая используемые в распространенных блокчейн-платформах.
Вторая глава также посвящена криптографическим алгоритмам – на этот
раз алгоритмам электронной подписи, являющимся одним из важнейших элементов, обеспечивающих связь в цепочках данных блокчейна, и не только. Рассмотрены эллиптические кривые, лежащие в основе современных алгоритмов
электронной подписи и наиболее часто применяемые из данных алгоритмов.
Третья глава описывает базовые механизмы построения цепочек данных –
основы блокчейн-технологий. Значительная часть главы посвящена описанию
различных механизмов достижения консенсуса, легитимизирующих действия
пользователей с данными блокчейна.
Наконец, в последней главе рассмотрены примеры построения блокчейнплатформ на основе алгоритмов и методов, описанных в предыдущих главах.
В частности, дано подробное описание системы Биткойн, обеспечивающей
оборот одноименной криптовалюты, наиболее широко используемой в мире.
Надеемся, что изложенная в нашей книге информация оправдает ваши ожидания от книги, окажется интересной и принесет пользу в вашей деятельности.
Авторы выражают глубокую признательность известному специалисту по
прикладной криптографии Олегу Геннадьевичу Тараскину (компания Waves)
за предоставленную для публикации в данной книге главу 2, без материала
которой книга была бы неполной, а также за множество полезных замечаний,
позволивших значительно улучшить книгу.
Авторы также благодарны эксперту технического комитета по стандартизации «Криптографическая защита информации» (ТК-26) Сергею Васильевичу
Матвееву за предисловие к книге и ценные замечания по ее материалу.
Будем рады вашим письмам по изложенным в книге вопросам, а также замечаниям к содержимому книги и предложениям по ее возможному усовершенствованию. Адреса электронной почты для связи с авторами:
serg@panasenko.ru (Сергей Панасенко) и
jekky82@mail.ru (Евгения Ищукова).

Глава

1
Алгоритмы хеширования

Алгоритмы хеширования (или функции хеширования, хеш-функции) позволяют по определенным правилам выполнить свертку входных данных произвольной длины в битовую строку фиксированного размера, называемую хешкодом [15] (распространены также термины «хеш» или «хеш-значение»).
Фактически хеш-функции выполняют контрольное суммирование данных, которое может происходить как с участием некоего секретного ключа, так и без него.
Обычно алгоритмы ключевого хеширования представляют собой надстройки над алгоритмами хеширования, не использующими ключ. Однако существуют и такие хешфункции, которые изначально разрабатывались с учетом использования секретного
ключа в качестве дополнительного параметра преобразований.
Такое контрольное суммирование достаточно широко применяется в области защиты компьютерной информации, в том числе:
 для подтверждения целостности данных;
 для свертки данных перед вычислением или проверкой их электронной
подписи;
 в различных протоколах аутентификации пользователей;
 в процедурах генерации псевдослучайных последовательностей и производных ключей.
Алгоритмы хеширования применяются и с другими целями, не относящимися к задачам защиты информации. В частности, они применяются для вычисления уникальных идентификаторов данных и построения на их основе
хеш-таблиц, существенно ускоряющих поиск требуемых данных в больших
массивах. Однако в подобных случаях к алгоритмам хеширования предъявляются иные требования, чем при их криптографических применениях.
Алгоритмы хеширования активно используются в блокчейн-технологиях, наиболее часто – для свертки данных перед их подписанием электронной
подписью. В ряде случаев хеш-функции используются и с другими целями –
например, в биткойне для подтверждения проделанной работы используются
найденные (получаемые путем перебора) хеш-коды специального формата –
с количеством лидирующих битовых нулей не менее заданного.
Конкретные применения алгоритмов хеширования в технологиях блокчейна будут описаны в главах 3 и 4, а в данной главе рассмотрим подробно структуру алгоритмов хеширования, предъявляемые к ним требования, а также основные методы и результаты их криптоанализа.

10  Глава 1

1.1 Основные понятия и определения
Функции хеширования позволяют выполнить однонаправленное преобразование входного массива произвольного размера в выходную битовую строку
(хеш-код) фиксированного размера.
Криптографические хеш-функции должны обладать, как минимум, следующими свойствами [44, 202].
1. Хеш-код сообщения должен однозначно соответствовать сообщению
и должен изменяться при любой модификации сообщения.
2. Должно быть вычислительно сложно найти прообраз, т. е. такое сообщение M, хеш-код которого был бы равен заданному значению h:
h = f(M),
где f() – функция хеширования.
3. Должно быть вычислительно сложно найти второй прообраз, т. е. такое
сообщение M2, хеш-код которого был бы равен хеш-коду заданного сообщения M1:
f(M1) = f(M2).
4. Должно быть вычислительно сложно найти коллизию, т. е. такие два сообщения M1 и M2, хеш-коды которых были бы эквивалентны.

1.1.1 Структура алгоритмов хеширования
Хотя все множество алгоритмов хеширования достаточно разнообразно по структурам их формирования, наиболее известные алгоритмы хеширования основаны
на нескольких типовых структурах, которые рассмотрим в данном разделе далее.

Схема Меркля–Дамгорда
Многие из широко используемых алгоритмов хеширования имеют схожую
между собой структуру, которая была предложена в 1988–1989 гг. независимо
двумя известными криптологами: Ральфом Мерклем (Ralph Merkle) [177] и Айвеном Дамгордом (Ivan Damgård) [102]. Она получила название «схема Меркля–Дамгорда» (Merkle-Damgård construction) – см. рис. 1.1.
В соответствии со схемой Меркля–Дамгорда работают, в частности, алгоритмы
MD4 [210], MD5 [212] и SHA [125], а также отечественный стандарт хеширования
ГОСТ Р 34.11–2012 [15], которые будут подробно рассмотрены в этой главе далее.

IV

M0

M1

fb()

fb()

MN

...

fb()

HN

Рисунок 1.1. Схема Меркля–Дамгорда

Хешируемое сообщение M разбивается на блоки определенной длины (например, по 512 байт в алгоритме SHA-1) M0…MN. Выполняется (по-разному

Алгоритмы хеширования  11
в различных алгоритмах) дополнение сообщения M до размера, кратного данной длине блока (при этом количество блоков может увеличиться – см., например, описание алгоритма SHA далее).
Каждый i-й блок сообщения обрабатывается функцией сжатия fb() (функция
сжатия является криптографическим ядром алгоритма хеширования), причем
данная функция накладывает результат этой обработки на текущий (промежуточный) хеш-код Hi-1 (т. е. результат обработки предыдущих блоков сообщения):
Hi = fb(Hi-1, Mi).
Результатом работы алгоритма хеширования hash() (т. е. хеш-кодом сообщения M) является значение HN:
hash(M) = HN.
Начальное значение H-1 обычно является константным и различным в разных алгоритмах хеширования; оно часто обозначается как IV (от Initialization
Vector – вектор инициализации).

Алгоритмы хеширования на основе криптографической губки
Схема алгоритмов хеширования на основе «криптографической губки»
(cryptographic sponge) была предложена рядом криптологов в работе [69].
Авторами данной схемы являются Гвидо Бертони (Guido Bertoni), Джоан
Деймен (Joan Daemen), Михаэль Петерс (Michaël Peeters) и Жиль Ван Аске
(Gilles Van Assche).
Алгоритмы хеширования, использующие конструкцию криптографической
губки, содержат две фазы преобразований (рис. 1.2):
 «впитывание» (absorbing) – поблочная обработка входного сообщения,
в процессе которой внутреннее состояние алгоритма хеширования «впитывает» свертку данных очередного блока;
 «выжимание» (squeezing) – сжатие внутреннего состояния для извлечения из него результирующего хеш-кода.
Хеш-код

M

Состояние

Дополнение

0
0

...

+

f

+

f

...

...

+

f

f

f

...
Впитывание

...

Усечение

f

...
Выжимание

Рисунок 1.2. Фазы преобразований криптографической губки

Конструкция криптографической губки, по мнению ее авторов, имеет как
минимум следующие преимущества [69, 71]:

12



Глава 1

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

Надстройки над алгоритмами блочного шифрования
Существуют также варианты создания стойких однонаправленных хешфункций на основе алгоритмов блочного шифрования. В частности, в работе [202] подробно описаны 12 алгоритмов, представляющих собой хеширующие надстройки над блочными шифрами.
Рассмотрим две из таких надстроек более подробно. Первая из них – это алгоритм Матиаса–Мейера–Осиса (Matyas-Meyer-Oseas – MMO), функция сжатия
которого представлена на рис. 1.3. В данном алгоритме нижележащий блочный
шифр используется следующим образом [174]:
 блок хешируемых данных Mi подается на вход алгоритма шифрования
в качестве открытого текста (P);
 ключом блочного шифра (K) служит текущее состояние алгоритма хеширования Hi-1, обработанное некоторой функцией g() (см. далее);
 на результат шифрования (C) блока Mi на ключе g(Hi-1) накладывается
операцией XOR сам блок Mi, в результате чего получается новое состояние алгоритма хеширования Hi.
Mi

Hi-1

g()

K

P
Блочный шифр
C

+
Hi
Рисунок 1.3. Схема алгоритма MMO

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

Алгоритмы хеширования  13
а размер состояния алгоритма хеширования является фиксированным, в алгоритме MMO существует необходимость в функции g(), задача которой состоит
лишь в адаптации значения Hi-1 под требуемый размер ключа шифрования. Так
как функция g() не влияет на безопасность алгоритма, она может быть максимально простой и, например, выполнять только дополнение Hi-1 до требуемого
размера [246].
Формально алгоритм MMO представляется так:
Hi = E(Mi, g(Hi-1)) ⊕ Mi,
где E(P, K) – блочный шифр, шифрующий открытый текст P на ключе K.
Существует также вариант алгоритма MMO, который вместо операции XOR
использует сложение 32-битовых субблоков Mi и C по модулю 232. В работе [217]
такой вариант называется предпочтительным, в частности для алгоритмов
MD4 и MD5.
В качестве блочного шифра может использоваться также функция сжатия
какого-либо алгоритма хеширования, поскольку, как и блочный шифр, функции сжатия алгоритмов хеширования обычно принимают на вход два параметра (состояние и хешируемый блок данных вместо ключа шифрования и шифруемого блока), выдавая в качестве результата модифицированное состояние.
В этом случае описанные в данном разделе схемы можно рассматривать как
надстройки, усиливающие криптографические свойства нижележащего алгоритма хеширования.
Еще один вариант формирования алгоритма хеширования на основе блочного шифра – алгоритм Миягучи–Пренеля (Miyaguchi-Preneel), который отличается от предыдущего только тем, что предыдущее состояние алгоритма
хеширования также участвует в операции XOR с блоком сообщения и результатом его зашифрования (рис. 1.4) [174]:
Hi = E(Mi, g(Hi-1)) ⊕ Mi ⊕ Hi-1.
Mi

Hi-1

g()

K

P
Блочный шифр
C

+
Hi
Рисунок 1.4. Схема алгоритма Миягучи–Пренеля

14

 Глава 1

Аналогично предыдущей схеме, здесь также возможна (и рекомендуется для
тех же алгоритмов MD4 и MD5 [217]) замена операции XOR на операцию сложения субблоков операндов по модулю 232.

1.1.2 Надстройки над алгоритмами хеширования
В свою очередь, алгоритмы хеширования достаточно часто используются в качестве основы для различных кодов аутентификации сообщений (Message Authentication Codes – MAC).

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

Алгоритм
вычисления MAC

Сообщение

Сообщение

MAC

MAC
Вычисление и
проверка MAC

MAC

Ключ

Ключ

Рисунок 1.5. Схема использования MAC

Простейшие варианты создания MAC на основе произвольного бесключевого алгоритма хеширования выглядят так (при этом все три описанных далее
варианта были признаны небезопасными) [202, 203]:
1. «Секретный префикс» (secret prefix):
MAC(k, m) = hash(k || m),
где:
 k – ключ;
 m – сообщение;
 hash() – нижележащая бесключевая хеш-функция.

Алгоритмы хеширования  15
2. «Секретный суффикс» (secret suffix):
MAC(k, m) = hash(m || k).
3. «Конверт» (envelope):
MAC(k1, k2, m) = hash(k1 || m || k2),
где подключи k1 и k2 являются различными.
Более безопасным способом создания MAC на основе алгоритмов хеширования является алгоритм HMAC (Hash-based Message Authentication Code –
код аутентификации сообщения на основе хеширования), который позволяет
вычислять хеш-коды с использованием некоего секретного ключа с помощью
практически произвольного бесключевого алгоритма хеширования [163].
В основе алгоритма HMAC лежит функция хеширования, которая позволяет
вычислить код аутентификации сообщения следующим образом (см. рис. 1.6):
1. Размер ключа выравнивается с размером блока используемого алгоритма хеширования:
 если ключ key длиннее блока, он укорачивается путем применения
к нему используемого алгоритма хеширования hash (этот вариант не показан на рис. 1.6):
key = hash(key);
отметим, что размер выходного значения алгоритма хеширования
обычно много меньше размера блока хешируемых данных – например,
соответственно, 128 и 512 бит для алгоритма MD4; в [163] рекомендуется
минимальный размер ключа, равный размеру выходного значения алгоритма хеширования;
 если размер ключа меньше размера блока, то выровненный ключ k получается путем дополнения до размера блока нулевыми битами исходного
(или укороченного) ключа key.
2. Выровненный ключ k складывается по модулю 2 с константой C1, которая представляет собой блок данных, заполненный байтами с шестнадцатеричным значением 36; аналогичным образом ключ k также складывается с константой C2, которая представляет собой блок данных, заполненный байтами с шестнадцатеричным значением 5С:
ki = k ⊕ C1;
ko = k ⊕ C2.
3. Вычисляется хеш-код t от результата конкатенации модифицированного ключа ki и сообщения m:
t = hash(ki || m).
4. Выходным значением алгоритма HMAC является хеш-код от результата
конкатенации модифицированного ключа ko и полученного на предыдущем шаге значения t:
hmac = hash(ko || t).

16

 Глава 1

Таким образом, при вычислении HMAC используемый алгоритм хеширования применяется дважды; каждый раз с участием модифицированного ключа.
Размер выходного значения алгоритма HMAC равен размеру выходного значения алгоритма хеширования, а общая формула вычисления HMAC (без учета
выравнивания ключа) выглядит следующим образом:
HMAC(k, m) = hash((k ⊕ C2) || hash((k ⊕ C1) || m)).
Константа C2

Ключ k

+

Константа C1

Сообщение m

+

hash()

hash()

Результат

Рисунок 1.6. Вычисление HMAC

Алгоритмы, построенные с помощью HMAC, традиционно обозначают следующим образом:
 «HMAC-x», где «x» – используемый алгоритм хеширования, например
HMAC-MD4;
 «HMAC-x-k» в тех случаях, где выходное значение алгоритма хеширования может быть усечено (т. е. может использоваться частично); в данном
случае «k» – размер выходного значения алгоритма HMAC в битах; пример – алгоритм HMAC-SHA1-80 [163].
Стоит отметить, что быстродействие HMAC ненамного хуже, чем у используемой функции хеширования, что особенно заметно при хешировании длинных сообщений, поскольку само сообщение при использовании HMAC обрабатывается однократно [158].
Алгоритм NMAC (Nested Message Authentication Code – «вложенный» код
аутентификации сообщения) был предложен авторами алгоритма HMAC в работе [62]. Данный алгоритм позволяет использовать при аутентификации сообщений два ключа: k1 и k2. Формула вычисления NMAC крайне проста:
NMAC(k1, k2, m) = hash(k2, hash(k1, m)),

Алгоритмы хеширования  17
где второй параметр функции hash() обозначает хешируемое сообщение,
а первый параметр – нестандартный вектор инициализации, в качестве которого используется ключ (соответственно, k1 и k2 обозначают уже выровненные
до размера вектора инициализации ключи).
NMAC фактически является обобщением HMAC, поскольку ключи k1 и k2
можно использовать независимо, а можно представить как производные от
единственного ключа k, вычисляемые следующим образом [158]:
k1 = h(k ⊕ C1);
k2 = h(k ⊕ C2),
где h() – функция сжатия используемого алгоритма хеширования; в данном
случае применяется стандартный вектор инициализации алгоритма.
Схема NMAC приведена на рис. 1.7.
Ключ k2

Ключ k1

Сообщение m

hash()

hash()

Результат

Рисунок 1.7. Вычисление NMAC

Хеширование с использованием деревьев Меркля
Дерево Меркля (Merkle tree) представляет собой бинарное дерево, листья которого содержат хеш-коды блоков данных, а узлы содержат хеш-коды от конкатенации хеш-кодов дочерних листьев или узлов.
Данная структура была предложена Ральфом Мерклем в 1979 г. и запатентована в 1982 г. в патенте [178]. Общий вид дерева Меркля приведен на рис. 1.8.
Результирующий
хеш-код:

h2,0

Узлы дерева:

h1,0

h1,1

Листья дерева:

h0,0

h0,1

h0,2

h0,3

Блоки данных:

D0

D1

D2

D3

Рисунок 1.8. Дерево Меркля

18

 Глава 1

В таком дереве вычисление хеш-кодов должно выполняться «снизу вверх»
следующим образом:
1. Вычисление хеш-кодов данных для каждого листа дерева:
h0,i = hash(Di),
где:



2.

Di – i-й блок данных;
h0,i – хеш-код i-го блока данных;
hash – используемая функция хеширования.
Поочередное вычисление хеш-кодов узлов дерева, начиная с более нижних уровней до вычисления корневого хеш-кода, который является результирующим:
hj,k = hash(hj-1,2k || hj-1,2k+1),

где:
 ha,b – b-й хеш-код a-го уровня дерева;
 || – операция конкатенации.
Дерево Меркля активно применяется при хешировании данных в различных приложениях (включая ряд известных криптовалют – см. главу 4) в связи
с наличием у него ряда положительных свойств (по сравнению с хешированием всех данных за один проход, т. е. одним вызовом функции hash), включая
следующие:
 вычисление хеш-кодов с использованием дерева Меркля достаточно хорошо распараллеливается;
 при изменении одного из блоков данных не требуется пересчет хеш-кодов
всех данных – достаточно пересчитать только хеш-коды, соответствующие той ветви дерева, которой принадлежит измененный блок данных;
 для проверки целостности одного из блоков данных также не требуется
проверять целостность всех данных – достаточно проверить только соответствующую блоку ветвь дерева.
При большом количестве блоков данных пересчет одной ветви дерева по
сравнению со всеми данными блоков требует значительно меньшей трудоемкости, в частности в криптовалютных применениях различия в трудоемкости
могут достигать нескольких порядков.
Схожий с деревом Меркля структурный элемент применяется и непосредственно в ряде алгоритмов хеширования. В качестве примера таких алгоритмов можно привести алгоритм хеширования MD6, который будет подробно
рассмотрен в данной главе далее.

1.2 Методы криптоанализа и атаки на алгоритмы
хеширования
Рассмотрим в данном разделе цели, которые преследуют атакующие при анализе алгоритмов хеширования, а также существующие методы криптоанализа
и некоторые из основных результатов атак на хеш-функции.

Алгоритмы хеширования  19

1.2.1 Цели атак на алгоритмы хеширования
Опишем основные и промежуточные цели атак на алгоритмы хеширования.

Основные цели атак на алгоритмы хеширования
Нахождение коллизии
Коллизией (collision) является ситуация, в которой два различных сообщения
m1 и m2 имеют один и тот же хеш-код h = hash(m1) = hash(m2), где hash() – используемый алгоритм хеширования [242, 246].
Предотвратить существование коллизий принципиально невозможно, поскольку для n-битового алгоритма хеширования размер множества возможных
хеш-кодов составляет 2n, тогда как размер множества хешируемых сообщений
является бесконечным. Трудоемкость нахождения коллизий является одной из
основных характеристик криптостойкости хеш-функций: для стойкого алгоритма хеширования поиск коллизии должен требовать порядка 2n/2 операций
хеширования.
Существует также понятие мультиколлизии (multicollision) – множества из
r сообщений, каждое из которых при хешировании дает один и тот же результат [153, 188]. В работе [153] показано, что трудоемкость поиска мультиколлизии лишь незначительно (логарифмически) превышает трудоемкость поиска
коллизии для того же алгоритма хеширования. Мультиколлизии будут подробно описаны далее.
Нахождение прообраза
Атака, направленная на поиск прообраза (preimage attack), ставит своей целью
нахождение сообщения, хеш-код которого соответствует заданному хеш-коду
h. Существуют два типа таких атак [242, 246]:
 поиск первого прообраза (first preimage attack) – поиск сообщения m, для
которого hash(m) = h;
 поиск второго прообраза (second preimage attack) – при имеющемся сообщении m1 поиск другого сообщения m2, удовлетворяющего условию:
hash(m2) = hash(m1).
Считается, что алгоритм хеширования является стойким против данных
атак, если для поиска прообраза необходимо не менее 2n операций хеширования алгоритмом, вычисляющим n-битовые хеш-коды.
Атаки, направленные на поиск прообраза, существенно более опасны, чем
атаки, ставящие своей целью поиск коллизий, поскольку они могут быть использованы, в частности, для следующих целей [166]:
 компрометации схем проверки целостности;
 подделки электронной подписи;
 поиска паролей по их известным хеш-кодам.
Определение секретного ключа
Определение секретного ключа как цель атаки актуально для ключевых алгоритмов хеширования или ключевых надстроек над бесключевыми алгоритмами хеширования.

20  Глава 1

Промежуточные цели атак на алгоритмы хеширования
Нахождение near-коллизии
Near-коллизией называется пара сообщений, хеш-коды которых являются
почти эквивалентными, с различиями только в нескольких битах [72].
Само по себе обнаружение near-коллизии не может быть опасным. Однако поиск near-коллизий может использоваться в контексте различных атак на
хеш-функции. В качестве примера использования near-коллизии для поиска
реальной коллизии можно привести пример атаки на алгоритм SHA, направленной на поиск многоблочной коллизии и описанной в [73]. Поэтому считается, что криптографически стойкие хеш-функции должны также не быть подверженными поиску near-коллизий [174].
Нахождение псевдопрообраза
Псевдопрообраз (pseudo-preimage) – это сообщение m, для которого hash(IV’,
m) = h, где:
 h – заданный хеш-код;
 IV’ – начальный хеш-код, использованный при вычислении хеш-кода h.
Таким образом, псевдопрообраз отличается от первого прообраза тем, что
при его поиске не фиксируется начальный хеш-код, т. е. может быть использовано другое начальное значение, нежели прописанное в алгоритме хеширования (например, для алгоритма SHA начальное значение – это совокупность
исходных значений регистров A…E – см. описание алгоритма SHA в разделе 1.3). Значение IV’, используемое для поиска псевдопрообраза, может иметь
различные специфические свойства, существенно упрощающие поиск псевдопрообраза по сравнению с поиском прообраза (при стандартном начальном
значении) [166].
Как и near-коллизии, псевдопрообразы сами по себе не являются опасными (если не рассматривать заведомо ошибочные варианты реализации алгоритмов хеширования, позволяющие изменять или загружать начальные
значения), однако псевдопрообразы могут являться промежуточным шагом
других атак, в частности они могут быть успешно использованы в контексте
атак, посвященных поиску прообраза ([103, 166]).
Нахождение псевдоколлизии
По аналогии с псевдопрообразом, псевдоколлизия определяется как ситуация,
в которой два различных сообщения m1 и m2 имеют один и тот же хеш-код h
= hash(m1, IV1) = hash(m2, IV2), но, в отличие от «классической» коллизии, псевдоколлизия возникает при различных начальных значениях алгоритма хеширования: IV1 и IV2.
В работе [250] утверждается, что криптографические алгоритмы хеширования должны быть стойкими к поиску псевдоколлизий (т. е. трудоемкость поиска псевдоколлизии не должна быть ниже 2n/2 операций для n-битового алгоритма хеширования), поскольку нахождение псевдоколлизии также может
являться промежуточным шагом, снижающим итоговую трудоемкость различных атак на алгоритмы хеширования.
Рассмотрим далее различные методы криптоанализа алгоритмов хеширования и атаки на них, приводящие к достижению описанных выше целей.

Алгоритмы хеширования  21

1.2.2 Атаки методом «грубой силы»
Метод «грубой силы» (brute-force attack) предполагает перебор всех возможных вариантов каких-либо объектов (в частности, хешируемых сообщений) до
нахождения искомого значения (например, сообщения, соответствующего заданному хеш-коду).
Атаки методом «грубой силы» могут быть применены для достижения всех
возможных целей атак на алгоритмы хеширования:
 поиска коллизии;
 поиска прообраза;
 определения секретного ключа (для ключевых алгоритмов хеширования).
Рассмотрим последнюю из рассматриваемых целей. Пусть размер секретного ключа в битах равен b. Соответственно, существует 2b вариантов ключа.
Криптоаналитик должен методично перебрать все возможные ключи, т. е. (если
рассматривать b-битовую последовательность как число) применить в качестве ключа значение 0, затем 1, 2, 3 и т. д. до максимально возможного (2b – 1).
В результате секретный ключ обязательно будет найден, причем в среднем такой поиск потребует 2b/2, т. е. 2b-1 тестовых операций [34].
В качестве критерия корректности найденного ключа используется N пар
сообщений и их хеш-кодов. Поскольку при переборе ключей возможны коллизии, в [202] показано, что требуемое число пар для определения верного ключа
«несколько превышает» (slightly larger) значение b/n для n-битового алгоритма
хеширования.
Защита от атак методом «грубой силы» весьма проста – достаточно лишь
увеличить размер ключа: увеличение размера ключа на 1 бит увеличит количество ключей (и среднее время атаки) в 2 раза.
Существуют различные методы усиления эффективности атаки методом
«грубой силы», в частности [84]:
 атака методом «грубой силы» простейшим образом распараллеливается: при наличии, скажем, миллиона компьютеров, участвующих в атаке,
ключевое множество делится на миллион равных фрагментов, которые
распределяются между участниками атаки;
 скорость перебора ключей может быть во много раз увеличена, если
в переборе участвуют не компьютеры общего назначения, а специализированные устройства.
Следует учесть, что с развитием вычислительной техники требования к размеру секретного ключа постоянно возрастают. В качестве примера (применительно к симметричному шифрованию) можно привести тот факт, что в той же
работе [84] был рекомендован 90-битовый размер ключа в качестве абсолютно
безопасного (причем с 20-летним запасом) на конец 1995 г. В 2000 г. эксперты
посчитали безопасным с примерно 80-летним запасом использование ключей
размером от 128 бит [101].
В известной работе Даниэля Бернштейна (Daniel J. Bernstein) [66] указано,
что, несмотря на всю силу параллельных атак методом «грубой силы», криптографы уделяют данной проблеме достаточно мало внимания, допуская следующие ошибки при проектировании криптографических алгоритмов и протоколов и их реализации:

22

 Глава 1

 криптографы часто сильно преувеличивают реальную стойкость своих криптосистем из-за того, что рассматривают только последовательные (т. е. использующие один атакующий компьютер) атаки на
криптосистему, не уделяя внимания более сильным параллельным
атакам;
 в случае если криптосистема архитектурно зависима от решения, параллельным или последовательным атакам она должна противостоять, час­
то делается неверный выбор в пользу последовательных атак.
Атака методом «грубой силы» является «мерилом эффективности» других
атак – атака считается тем эффективнее, чем быстрее она достигает требуемой
цели по сравнению с атакой методом «грубой силы».

1.2.3 Словарные атаки и цепочки хеш-кодов
Словарная атака (dictionary attack) – это метод вскрытия какой-либо информации путем перебора возможных значений данной информации (здесь и далее
для определенности в качестве мишени словарных атак будемрассматривать
пароли пользователей, хранящиеся в виде их хеш-кодов; при этом область
применения словарных атак достаточно широка) [246].
Название атаки произошло благодаря тому, что основу множества перебираемых паролей изначально составляли слова какого-либо языка, а словарные
атаки эксплуатировали присущую большинству пользователей тенденцию
к использованию легко запоминаемых паролей, к которым часто относятся
различные слова и их варианты (например, замена части букв слова на похожие по написанию цифры или спецсимволы).
Словарные атаки часто классифицируют как один из вариантов атаки методом «грубой силы», поскольку здесь также выполняется перебор вариантов
пароля или ключа. По сравнению с методом «грубой силы» словарные атаки
осуществляют перебор по существенно меньшему (но наиболее вероятному
ввиду того, что пользователи нередко выбирают простые и легко запоминающиеся пароли) множеству возможных значений.
Далее рассмотрим некоторые варианты словарных атак и методы противодействия им.

Словарная атака, основанная на предварительных вычислениях
Смысл данного варианта словарной атаки состоит в предварительном вычислении хеш-кодов паролей, принадлежащих какому-либо множеству.
Путем такого вычисления атакующий получает таблицу соответствий входящих в множество паролей и их хеш-кодов (см. рис. 1.9). Для нахождения искомого пароля, соответствующего известному злоумышленнику хеш-коду h,
выполняется поиск хеш-кодов в таблице, в результате которого делается один
из следующих выводов:
 если в таблице нашлась запись, соответствующая искомому значению h,
то пароль p (для которого h = hash(p)) найден; при этом стоит учитывать,
что поскольку возможно возникновение коллизий, найденный пароль
может быть не искомым паролем, а паролем с эквивалентным искомому
хеш-кодом;

Алгоритмы хеширования  23
 если записи со значением h в таблице нет, то искомый пароль не принадлежит к множеству, покрываемому данной таблицей; можно продолжить
поиск с помощью других таблиц или с применением иных методов.
abaca
aback
abaction

hash
hash
hash

...
zygoma

5d12fdca
0a23647f
ca56ff12
...

hash

7dd412a4

Рисунок 1.9. Таблица паролей и хеш-кодов

Основным недостатком данной атаки является достаточно большой объем
памяти, требуемой на хранение таблицы, размер которого в битах n-битового
алгоритма хеширования можно оценить следующим образом:
V(T) = (n + k) * |P|,
где:
 P – множество паролей, покрываемое таблицей T;
 k – средний размер пароля в битах.

Цепочки хеш-кодов
Использование цепочек хеш-кодов (hash chains) – это улучшенный метод словарной атаки. Улучшение состоит в том, что при использовании цепочек на
хранение таблицы паролей и соответствующих им хеш-кодов требуется существенно меньше памяти, чем в случае классической словарной атаки.
Принцип действия цепочек хеш-кодов основан на введении дополнительной функции R(), с помощью которой любому хеш-коду h можно псевдослучайным образом сопоставить некий пароль p из множества паролей P [123, 144]:
p = R(h).
С помощью функции R() и функции хеширования hash() можно сформировать цепочки из паролей p1…pN и соответствующих им хеш-кодов h1…hN:
p1 → h1 → p2 → h2 →…→ pN → hN.

(1.1)

Простой пример функции R() приведен в [147]: если множеством паролей
является множество 6-значных десятичных чисел, то выходным значением
функции R() могут быть первые 6 цифр хеш-кода в десятичном представлении.
Исходные значения p1 для каждой цепочки могут выбираться по любому
принципу, в том числе генерироваться случайным образом в пределах покрываемого таблицей множества паролей.
Атакующий объединяет вычисленные таким образом цепочки в таблицу
(см. рис. 1.10). Принципиальным моментом является то, что для уменьшения

24

 Глава 1

требуемой для хранения памяти сохраняется не вся таблица, а лишь первое
и последнее значения (т. е. p1 и hN) для каждой строки.
abaca
trend
mary

hash
hash
hash

...
peace

R

5d12fdca

R

6fade4ac

R

67a97688

couple
come
further

...
hash

hash
hash
hash

f87df65a

...

sands

1abb67a1

...

reach

a3429904

...

etc

...
R

4fd769a3

afford

...
hash

hash

788a2c5d

hash

df34a456

hash

a63dd12a

...

a9112a3c

...
hash

shorten

...

c8a913cf

Рисунок 1.10. Таблица цепочек хеш-кодов

При необходимости найти пароль, соответствующий заданному хеш-коду hx,
атакующий вычисляет следующую цепочку:
hx → px+1 → hx+1 →…→ px+N-1 → hx+N-1.

(1.2)

Каждое из получаемых в процессе данных вычислений значений hx+i сравнивается с хранящимися в таблице значениями hN каждой строки. Если на каком-либо этапе обнаружены эквивалентные значения hx+i и hN, то дальнейшие
вычисления цепочки (1.2) не выполняются, а поиск требуемого пароля выполняется восстановлением данной строки таблицы, т. е. вычислением цепочки (1.1) до нахождения некоторого значения hj, равного hx. При нахождении такого значения искомый пароль определяется как значение pj данной цепочки.
Поскольку возможно возникновение коллизий, восстановление строки таб­
лицы может не приводить к нахождению требуемого значения hj (см. пример
на рис. 1.11; данный пример показывает также то, что коллизии уменьшают
покрываемое таблицей множество паролей). В этом случае атакующий продолжает вычисления цепочки (1.2) до нахождения следующего совпадения hx+i и hN.
Если вычисления цепочки (1.2) завершены, а совпадения не найдены, то атакующим делается вывод о том, что данная таблица не содержит искомый пароль.
...
afford

...
hash

...
peace

a9112a3c

...
R

...
hash

4fd769a3

yellow

...
hash

...
R

afford

3287acfe

...
R

...
hash

a9112a3c

reviewer

...
hash

...
R

yellow

d51a900a

...

...
hash

3287acfe

...

Рисунок 1.11. Пример коллизии в цепочке хеш-кодов

Значение N является параметром таблицы цепочек, от которого зависят две
важные характеристики таблицы:
 размер памяти для хранения таблицы цепочек обратно пропорционален
значению N при эквивалентном покрытии множества паролей (без учета
коллизий, вероятность которых нелинейно возрастает с ростом N);
 сложность нахождения требуемого пароля в таблице, которая возрастает
с ростом N.

Алгоритмы хеширования  25

Использование цепочек хеш-кодов уменьшенной длины
Как показано выше (см. рис. 1.11), возникновение коллизий может приводить к частичному совмещению строк таблицы, что, в свою очередь, приводит
к уменьшению покрытия множества паролей при заданных размерах таблицы [191, 246].
Вторая проблема – это «зацикленные» строки, возникающие из-за того, что
в процессе вычисления строки произошло совпадение паролей (текущего и одного из предыдущих) или хеш-кодов (т. е. для неких i и j какой-либо одной
строки pi = pj или hi = hj). Стоит отметить, что «зацикленные» строки могут быть
обнаружены на этапе вычислений и отброшены.
Использование нескольких таблиц меньшего размера вместо одной большой таблицы – это одно из направлений усиления таблиц цепочек хеш-кодов.
В каждой из таких таблиц используется своя функция преобразования из хешкода в пароль, т. е. в совокупности таблиц используются несколько функций
R1()…RT() (где T – количество таблиц).
В этом случае при возникновении коллизий в разных таблицах не происходит дальнейшего совмещения строк, поскольку в разных таблицах используются различные функции R(). Коллизия в одной таблице приведет к совмещению строк, но вероятность такой коллизии в T раз меньше по сравнению
с коллизией в разных таблицах.

Использование цепочек хеш-кодов переменной длины
Второе направление усиления таблиц цепочек хеш-кодов – это использование
строк переменной длины (но не превышающей некоего порогового значения L) [88]. Строка таблицы завершается при вычислении некоего специфического значения hi. Пример такого специфического значения – хеш-код, у которого значение первых двух байт равно нулю [147] (см. рис. 1.12). Следует,
однако, учесть, что пороговое значение L должно быть достаточно большим,
чтобы в подавляющем большинстве строк специфический хеш-код достигался. Если же такое значение после вычисления L хеш-кодов не достигается, то
вычисляемая строка отбрасывается, поскольку считается «зацикленной».
spoke
length
ode

hash
hash
hash

000012ca
6acf499a
a97688cd

R
R

pipe
medicine

hash
hash

752a65fd

...

john

hash

000056df

0000a342

...

Рисунок 1.12. Таблица цепочек переменной длины

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

26



Глава 1

Направление применения таблиц цепочек хеш-кодов активно развивается
(см., например, [146, 228]) наряду с радужными таблицами, подробно описанными далее.

1.2.4 Радужные таблицы
Радужная таблица (rainbow table) – это одно из направлений усиления цепочек
хеш-кодов с точки зрения повышения эффективности их применения.
В радужных таблицах также используется последовательность функций преобразования из хеш-кода в пароль, но каждая функция Ri() используется для
преобразования i-го хеш-кода hi в (i + 1)-й пароль pi+1. Таким образом, в таблице
последовательно применяются функции R1()…RN-1() (см. рис. 1.13).
abaca
trend
mary

hash
hash
hash

...
peace

5d12fdca
6fade4ac
67a97688

R1
R1
R1

...
hash

4fd769a3

couple
come
further

hash
hash
hash

...
R1

afford

f87df65a
1abb67a1
a3429904

R2
R2
R2

...
hash

a9112a3c

texas
school
blow

hash
hash
hash

...
R2

come

77f9ac1a
d7c907f1
93aa1cbd

R3
R3
R3

...
...
...

...
hash

1abb67a1

R3

...

Рисунок 1.13. Радужная таблица

Радужные таблицы обеспечивают более высокую скорость поиска паролей
по сравнению с описанными выше вариантами при эквивалентных размерах
(а также несколько более высокий процент покрытия множества паролей).
Две описанные выше проблемы таблиц цепочек хеш-кодов решаются радужными таблицами достаточно элегантно [147, 191, 246]:
 «зацикленные» строки невозможны в принципе, поскольку в любой
строке используется вся последовательность функций преобразования;
 коллизия приводит к совмещению строк только в случае, если она
возникает в одном столбце (вероятность чего в N раз меньше вероятности коллизии во всей таблице); следовательно, значения hN у таких
строк становятся эквивалентными – лишнюю строку можно отбросить
и пересчитать с другим начальным значением.
Поиск требуемого пароля в радужной таблице отличается от описанных
выше вариантов; он выполняется следующим образом:
1. Сначала ищется совпадение заданного хеш-кода hx с каким-либо из значений hN таблицы.
2. Если совпадение не обнаружено, то предполагается, что hx должно совпадать с каким-либо из hN-1. Следовательно, вычисляется
hx+1 = hash(RN-1(hx))
и сравнивается со значениями hN.
3. Если совпадение снова не найдено, предположение о местонахождении
hx в таблице «смещается» еще на один столбец влево, т. е. с hN сравнивается уже следующее значение:

Алгоритмы хеширования  27
hx+2 = hash(RN-1(hash(RN-2(hx)))).
4. И так далее, до нахождения соответствия. Если соответствие найдено, то
расчет искомого пароля выполняется с начала строки, как во всех описанных выше вариантах. Если же соответствие не найдено до завершения обработки предположения, что hx = h1, то считается, что искомый пароль в данной таблице отсутствует.
Название радужных таблиц произошло от аналогии каждой функции Ri() с каким-либо цветом, тогда таблица с длинными тонкими разноцветными полосками будет напоминать радугу [147]. Радужные таблицы активно используются для
атак на реальные криптосистемы – см., например, [134, 176, 192, 206]. Кроме того,
рассматриваются варианты комбинирования радужных таблиц с таблицами цепочек переменной длины, использующими специфические значения [146].
Стоит отметить, что радужные таблицы и описанные в предыдущем разделе
словарные атаки изначально предлагались к использованию для нахождения
ключей симметричного шифрования, т. е. в качестве паролей (например, в радужной таблице) фигурируют ключи, а в качестве хеш-кодов – результаты зашифрования константного блока открытого текста на конкретном ключе. Следовательно, функция R() в данном случае преобразует блок шифртекста в некий
возможный ключ.

1.2.5 Парадокс «дней рождения» и поиск коллизий
Парадокс «дней рождения» состоит в том, что для получения из множества,
содержащего N видов элементов, двух элементов одинакового вида требуется сделать O( N ) попыток (поэтому атаки, использующие парадокс «дней
рождения», называют также «атаками квадратного корня» [246]). Например,
в среднестатистической группе из 23 человек у двух человек совпадут дни
рождения с вероятностью, превышающей 50 %, что выглядит несколько удивительным (именно от этого примера произошло название парадокса «дней
рождения») [46]. Данный парадокс активно используется в криптоанализе.
Именно благодаря парадоксу «дней рождения» атака методом «грубой силы»
для поиска коллизии требует в 2n/2 раз меньше операций, чем для поиска прообраза.
Парадокс «дней рождения» может быть напрямую использован для атак на
хеш-функции, поскольку от него зависит трудоемкость, требуемая для нахождения коллизии. В [154] и [202] приведен пример следующей атаки, которую
может выполнить злоумышленник (данную атаку предложил Гидеон Юваль
(Gideon Yuval) еще в 1979 г.):
1. Злоумышленнику необходимо выдать поддельный документ за действительный документ, подписанный неким пользователем системы.
2. Злоумышленник генерирует r вариантов поддельного документа
и столько же вариантов оригинального документа (и в том, и в другом
случае под «вариантами» подразумеваются документы, идентичные по
смыслу, но различные в каких-либо деталях, незначительных в контексте документа, но приводящих к различным хеш-кодам). Значение r может быть вычислено с помощью парадокса «дней рождения»: например,
2n/2 для n-битового алгоритма хеширования.

28 

Глава 1

3. Злоумышленник выполняет поиск коллизий между оригинальными и поддельными документами. Если коллизия найдена, т. е. найдены оригинальный документ mx и поддельный документ fy, такие, что
hash(mx) = hash(fy), то злоумышленник предлагает пользователю подписать именно вариант оригинального документа mx.
4. Электронная подпись пользователя вместо документа mx прикрепляется
злоумышленником к документу fy. Подпись пользователя под подложным документом будет верна именно благодаря найденной злоумышленником коллизии.
Из-за наличия парадокса «дней рождения» выходное значение алгоритмов
хеширования должно быть достаточно большим – его размер в битах не менее,
чем в два раза, должен превышать размер, достаточный для успешного противодействия атакам методом «грубой силы», направленным на поиск коллизии.
Весьма интересную особенность парадокса «дней рождения» отметили авторы работы [63] Михир Белэр (Mihir Bellare) и Тадайоши Коно (Tadayoshi Kohno).
Они ввели понятие «регулярности» (amount of regularity) хеш-функций, которое фактически означает степень равномерности распределения возможных
сообщений по множеству выходных значений алгоритма хеширования. Приведенные выше оценки трудоемкости поиска коллизий справедливы только
для регулярных хеш-функций (т. е., другими словами, функций, в которых любые возможные хеш-коды встречаются одинаково часто). Для хеш-функций,
которые не отличаются регулярностью, трудоемкость поиска коллизии может
быть значительно ниже. Авторы данной работы предложили методики количественной оценки регулярности алгоритмов хеширования и оценки нижней
границы трудоемкости поиска коллизии в зависимости от регулярности.
В работе [202] Барт Пренель (Bart Preneel) выполнил анализ требуемого размера выходного значения хеш-функций, достаточного для противостояния
атакам, использующим парадокс «дней рождения», в том числе для противодействия злоумышленникам, обладающим «огромным» (до 64 терабайт) объемом памяти и большим количеством атакующих рабочих станций. В результате автор данной работы в 2003 г. делает вывод, что 160-битового хеш-кода
вполне достаточно с запасом, как минимум, на 20 лет вперед. В настоящее время данное значение уже не выглядит однозначно достаточным.
Для оценки криптостойкости алгоритмов хеширования криптоаналитики
часто используют различные методы поиска коллизий (в отличие от описанного выше метода, в данном случае достаточно найденной коллизии у незначащих (абстрактных) сообщений) и сравнивают криптостойкость алгоритма
с указанной выше теоретической – 2n/2 операций для n-битового алгоритма
хеширования.
«Классический» поиск коллизий выполняется путем выбора некоторых базовых значений хешируемых сообщений m0…mN, над которыми выполняется
вычисление цепочек хеш-кодов:
mi → hash(mi) → hash(hash(mi)) → …
Все значения каждой из цепочек записываются и сравниваются с уже записанными. Совпадение значений сигнализирует о нахождении коллизии.

Алгоритмы хеширования  29
Такой подход требует весьма серьезных ресурсов памяти для хранения всех
промежуточных результатов. В 1987 г. авторы работы [205] предложили усовершенствование данного метода, заключающееся в том, что хранению в памяти
подлежат не все промежуточные результаты вычислений, а только те из них,
которые имеют специфические значения (аналогично использованию специ­
фических значений в описанных выше словарных атаках); в качестве примера
специфических значений авторы [205] приводят значения с 20 нулевыми битами слева. При этом только специфические значения сохраняются и сравниваются (остальные значения отбрасываются), что на несколько порядков снижает требования данного метода к памяти.
Как и при классическом подходе, совпадение каких-либо специфических
значений сигнализирует о нахождении коллизии. Аналогично вычислению
цепочек хеш-кодов переменной длины в словарных атаках, в данном методе
поиска коллизий также устанавливается некое (весьма большое) пороговое
значение максимальной длины цепочки, при достижении которого цепочка
считается зацикленной и отбрасывается.
Несмотря на существенно сниженные требования к ресурсам метода со
специфическими значениями (по сравнению с классическим подходом), авторы работы [240] в 1994 г. утверждали, что «существующая технология поиска
коллизий мало применима на практике, пока она не может эффективно распараллеливаться». Они предложили эффективный метод распараллеливания
поиска коллизий, суть которого в следующем (см. рис. 1.14 из [240]):
 каждый участвующий в поиске компьютер обрабатывает свою цепочку
хеш-кодов, начиная с некоторого значения mi;
 при достижении специфического значения компьютер сообщает о нем
остальным участникам поиска (или в некий центр, управляющий поиском);
 другие участники поиска (или управляющий центр) сравнивают специ­
фические значения с вычисленными на более ранних этапах; совпадение свидетельствует о коллизии.

– начальные значения
– специфические значения

Рисунок 1.14. Параллельный поиск коллизий

Данный метод распараллеливания был испытан авторами на практике для
поиска коллизий в алгоритме хеширования MD5 [212], который будет рассмот­
рен далее в разделе 1.3.

30

 Глава 1

Отдельно рассмотрим мультиколлизии, которые могут быть эффективными
при проведении некоторых атак на алгоритмы хеширования и трудоемкость
поиска которых только незначительно превышает трудоемкость поиска обычных коллизий.
Мультиколлизии были предложены в 2004 г. в работе [153]. Опишем мультиколлизии, для чего введем следующие обозначения:
 f(a, b) – функция сжатия алгоритма хеширования, где a – предыдущее
значение переменных состояния, а b – обрабатываемый блок данных;
предполагается, что функция сжатия является слабой, т. е. в данном случае существует эффективный метод поиска коллизий для этой функции
сжатия;
 hi – значение переменных состояния на i-й итерации (см. далее);
 Bi и Bi’ – блоки сообщений, дающие коллизию на i-й итерации;
 t – количество итераций (определяется атакующим исходя из требований атаки).
Тогда алгоритм поиска мультиколлизий выглядит следующим образом:
1. В качестве начального значения переменных состояния h0 используется
стандартный вектор инициализации алгоритма.
2. В цикле по i от 1 до t выполняются следующие действия:
 определяется значение блоков Bi и Bi’, дающих коллизию функции сжатия:
f(hi-1, Bi) = f(hi-1, Bi’);
 текущим значением переменных состояния становится значение f(hi-1, Bi).
3. После выполнения цикла атакующий получает мультиколлизию – 2t сообщений с эквивалентным хеш-кодом (рис. 1.15), каждое из которых
имеет следующий вид:
(b1, …, bt, pad),
где bi – это одно из значений Bi и Bi’, а pad – стандартное дополнение сообщения.
B1
h0

B2
h1

B1'

Bt
h2

...

B2'

ht-1

ht
Bt'

Рисунок 1.15. Мультиколлизия

1.2.6 Дифференциальный криптоанализ
Данный метод атак был изобретен в 1990 г. известными израильскими криптологами Эли Бихамом (Eli Biham) и Ади Шамиром (Adi Shamir) и опубликован
в ряде их работ (см., например, [74, 75]). Однако не менее известный криптолог

Алгоритмы хеширования  31
Брюс Шнайер (Bruce Schneier) в своей книге [53] утверждает, что дифференциальный криптоанализ был открыт существенно раньше, но не появлялся в открытой печати. Тем не менее именно Бихам и Шамир до сих пор считаются
изобретателями дифференциального криптоанализа.
Дифференциальный криптоанализ в равной степени применим как против
алгоритмов симметричного шифрования (изначально дифференциальный
криптоанализ был предложен для атаки на стандарт шифрования США DES
(Data Encryption Standard – стандарт шифрования данных) [124]), так и против
алгоритмов хеширования.
Рассмотрим дифференциальный криптоанализ в применении к алгоритмам хеширования на примере криптоанализа алгоритма SHI1, выполненного
в работе [93].
SHI1 является упрощенным вариантом алгоритма хеширования SHA (подробное описание алгоритма SHA будет приведено в разделе 1.3), в итерации
которого все нелинейные элементы заменены на побитовую логическую операцию «исключающее или» (XOR).
Таким образом, итерация алгоритма SHI1 выглядит следующим образом
(рис. 1.16):
t = (a 15.
...

Wn-16

Wn-15

>>>1

Wn-14

...

Wn-7

...

>>>8

Wn-3

Wn-2

>>>19
>>6

+

+
+

Wn

...

>>>61

>>7

+

Wn-1

+

Рисунок 1.42. Расширение обрабатываемого блока в алгоритме SHA-512

Используемые здесь функции Sig0,512() и Sig1,512() определены так:
Sig0,512(x) = (x >>> 1) ⊕ (x >>> 8) ⊕ (x >> 7);
Sig1,512(x) = (x >>> 19) ⊕ (x >>> 61) ⊕ (x >> 6).
2. Содержимое регистров A…H копируется во временные переменные
a…h.
3. Выполняется 80 итераций (см. рис. 1.43), в каждой из которых следующим образом модифицируются переменные a…h:
T1 = h + Sum1,512(e) + Ch(e, f, g) + Wi + Ki,512 mod 264;
T2 = Sum0,512(a) + Maj(a, b, c) mod 264;
h = g;
g = f;
f = e;
e = d + T1 mod 264;
d = c;
c = b;
b = a;
a = T1 + T2 mod 264.

78

Глава 1


a

b

c

d

Sum0,512
+

e

f

g
Ch

+

Ki,512

Sum1,512

+

Wi

g

h

+

Maj

h

+

a

b

c

d

e

f

Рисунок 1.43. Итерация алгоритма SHA-512

Итерация SHA-512 весьма похожа на таковую в алгоритме SHA-256 с учетом
вычислений по модулю 264, а также изменений в используемых функциях Sumx,y:
Sum0,512(x) = (x >>> 28) ⊕ (x >>> 34) ⊕ (x >>> 39);
Sum1,512(x) = (x >>> 14) ⊕ (x >>> 18) ⊕ (x >>> 41).
Функции Ch() и Maj() аналогичны описанным ранее.
Константы Ki,512, различные для каждого раунда, приведены в приложении 1.
4. Значения регистров A…H складываются по модулю 264 с полученными
значениями переменных a…h соответственно.
Итоговый 512-битовый хеш-код – результат конкатенации 64-битовых регистров A…H.
SHA-384
Алгоритм SHA-384 выполняет практически те же преобразования, что и описанный выше алгоритм SHA-512. Отличия данных алгоритмов состоят в следующем:
1. Регистры A…H инициализируются другим набором констант (указаны
шестнадцатеричные значения):
Таблица 1.31. Начальное заполнение регистров алгоритма SHA-384
Регистр

Исходное значение

A

CBBB9D5DC1059ED8

B

629A292A367CD507

C

9159015A3070DD17

D

152FECD8F70E5939

E

67332667FFC00B31

F

8EB44A8768581511

G

DB0C2E0D64F98FA7

H

47B5481DBEFA4FA4

Алгоритмы хеширования  79
2. В качестве итогового 384-битового хеш-кода используется результат
конкатенации только первых шести 64-битовых регистров, а именно регистров A…F.
SHA-224
25 февраля 2004 г. вышло дополнение к стандарту FIPS 180-2 [128], включающее еще один алгоритм хеширования семейства SHA-2 – SHA-224.
Кроме того, дополнение к стандарту устанавливает, что в случае необходимости использования хеш-кодов некоторого размера, не предусмотренного
алгоритмом SHA-1 и алгоритмами семейства SHA-2 (но меньшего 512 бит),
следует использовать алгоритм семейства SHA-2 с ближайшим выходным значением (больше требуемого), после чего брать требуемое количество левых
битов результата. Однако такое применение стандарта разрешено только для
совместимости со старыми реализациями, в которых предусмотрен фиксированный размер хеш-кода. Данное «усечение» выходного значения может отрицательно повлиять на безопасность такой реализации и не разрешено для
ряда применений.
SHA-224 соотносится с алгоритмом SHA-256 так же, как соотносятся описанные ранее алгоритмы SHA-384 и SHA-512. Отличия SHA-224 от SHA-256 состоят
в следующем:
1. Регистры A…H инициализируются другими константами (указаны шестнадцатеричные значения):
Таблица 1.32. Начальное заполнение регистров алгоритма SHA-224
Регистр

Исходное значение

A

C1059ED8

B

367CD507

C

3070DD17

D

F70E5939

E

FFC00B31

F

68581511

G

64F98FA7

H

BEFA4FA4

2. В качестве итогового 224-битового хеш-кода используется результат
конкатенации только первых семи 32-битовых регистров, а именно регистров A…G.
SHA-512/224 и SHA-512/256
В более новой версии стандарта SHS [129] были введены два новых алгоритма
семейства SHA-2: SHA-512/224 и SHA-512/256.
Оба этих алгоритма образованы фактически усечением выходного значения
алгоритма хеширования SHA-512 (аналогично SHA-384), но с другими (различ-

80

 Глава 1

ными между собой и отличающимися от SHA-384 и SHA-512) начальными значениями регистров.
Результаты криптоанализа
На текущий момент не опубликовано атак в части поиска коллизий или прообразов на какой-либо из вариантов полнораундовых алгоритмов SHA-2. Однако
данное семейство алгоритмов подвержено некоторым менее значительным
проблемам, в частности возможности нахождения коллизий путем расширения хешируемых данных [246].
Кроме того, в экспертном сообществе достаточно давно существуют сомнения относительно криптостойкости алгоритмов семейства SHA-2 по причине
их значительного сходства со взломанными алгоритмами SHA-1/MD5 – см., например, [220].

Семейство алгоритмов SHA-3
В 2007 г. начался процесс пересмотра алгоритмов, лежащих в основе стандарта
SHS: Национальный институт стандартов и технологий NIST объявил о проведении открытого конкурса по выбору нового стандарта хеширования США
SHA-3 и опубликовал требования к алгоритмам хеширования, которые могли
участвовать в данном конкурсе [156].
SHA-3 должен был прийти на смену алгоритмам хеширования семейства
SHA-2. Конкурс SHA-3 подразумевал выбор наилучшего алгоритма хеширования по целому ряду критериев, основными из которых являлись криптостойкость алгоритма и его быстродействие.
В 2008 г. на конкурс SHA-3 было прислано 64 алгоритма хеширования. Основой
нового стандарта хеширования стал алгоритм Keccak [71], который был выбран
сообществом экспертов как наилучший из претендентов [190]. Авторы алгоритма
Keccak – Гвидо Бертони, Джоан Деймен, Михаэль Петерс и Жиль Ван Аске.
Драфт стандарта, описывающего семейство алгоритмов SHA-3, основанное
на алгоритме Keccak и включающее в себя алгоритмы хеширования SHA3-224,
SHA3-256, SHA3-384 и SHA3-512, вышел в мае 2014 г. [132].
Структура алгоритмов
Алгоритмы семейства SHA-3 построены по принципу криптографической губки; такая структура криптографических алгоритмов была предложена авторами алгоритма Keccak ранее [69] и рассмотрена в данной главе выше.
Алгоритм семейства SHA-3 выполняется в три этапа [70, 71, 132].
Этап 1. Инициализация алгоритма. В рамках данного этапа выполняется
обнуление внутреннего состояния алгоритма, дополнение входных данных
и их разбиение на блоки.
Входное сообщение разбивается на блоки, размер которых зависит от размера выходного значения конкретного из алгоритмов семейства SHA-3 следующим образом:
R = 1600 – 2 * n,
где:
 R – размер блока хешируемого сообщения в битах;
 n – размер выходного значения алгоритма SHA3-n.

Алгоритмы хеширования  81
Константа 1600 соответствует размеру внутреннего состояния алгоритма
в битах (см. далее).
Дополнение последнего блока выполняется следующим образом (как и, например, для алгоритма MD5, это может привести к появлению дополнительного блока данных):
 добавляется единичный байт (т. е. байт с шестнадцатеричным значением 01);
 добавляется единичный бит;
 добавляется при необходимости такое количество нулевых битов, чтобы
текущий размер последнего блока сообщения был равен R – 1;
 добавляется завершающий единичный бит.
Этап 2. Этап «впитывания», который включает в себя наложение текущего блока данных на внутреннее состояние и последующее перемешивание
состоя­ния алгоритма.
Внутреннее состояние алгоритма (изначально заполненное нулевыми битами)
S представляется в виде одномерного массива размером 1600 бит. В рамках данного этапа для каждого блока дополненного сообщения выполняются следующие
действия в соответствии со структурой криптографической губки (см. рис. 1.2):
1. На первые R бит состояния операцией XOR накладывается блок входных
данных.
2. Состояние обрабатывается функцией f(), которая является основой алгоритма и выполняет перемешивание его состояния.
В процессе преобразований, выполняемых функцией f(), состояние представляется в виде двумерного массива A размером 5×5, элементами которого
являются 64-битовые слова. Конвертация одномерного массива состояния S в
массив A производится следующим образом:
A[x, y] = S’[5y + x],
где S’ обозначает представление массива S в виде одномерного массива 64-битовых слов.
Функция f() выполняет 24 раунда, в каждом из которых производятся следующие действия:
C[x] = A[x, 0] ⊕ A[x, 1] ⊕ A[x, 2] ⊕ A[x, 3] ⊕ A[x, 4], x = 0…4;
D[x] = C[x - 1] ⊕ (С[x + 1] >>> 1), x = 0…4;
A[x, y] = A[x, y] ⊕ D[x], x = 0…4, y = 0…4;
B[y, 2x + 3y] = A[x, y] >>> r[x, y], x = 0…4, y = 0…4;
A[x, y] = B[x, y] ⊕ (~B[x + 1, y] & B[x + 2, y]), x = 0…4, y = 0…4;
A[0, 0] = A[0, 0] ⊕ RC[i],
где:
 B – временный массив, аналогичный по структуре массиву состояния;
 C и D – временные массивы, содержащие по 5 слов размером 64 бита каждое;
 r – массив, определяющий количество битов вращения для каждого слова состояния (см. [71]);

82

 Глава 1

 ~ – побитовый комплемент;
 >>> – операция побитового циклического сдвига вправо на указанное
количество битов;
 RC[i] – константа текущего раунда i (i = 0...23); константы RC приведены
в приложении 1;
 операции с индексами массива выполняются по модулю 5.
Данные операции (кроме операции наложения раундовой константы) показаны на рис. 1.44, где их последовательность обозначена цифрами.
A

B
5

+

&

~

3

+
C

>>>

+

1

4

r

2

+

>>>1

D

Рисунок 1.44. Схематичное изображение раунда функции f() алгоритма SHA-3

Отметим, что некоторые из операций допускают различные варианты реа­
лизации (в частности, операция вращения слов состояния на r бит), из которых в зависимости от ресурсов конкретной программной или аппаратной
платформы может быть выбран наиболее оптимальный. Раундовые константы
также являются вычисляемыми, что позволяет в случае крайне ограниченных
ресурсов не хранить таблицу констант (см. [132]).
Этап 3. «Выжимание», в процессе которого из внутреннего состояния вычисляется результирующий хеш-код алгоритма.
Хеш-код вычисляется путем нескольких вызовов функции f(), после каждого
из которых оно дополняется несколькими битами внутреннего состояния алгоритма. Данный этап состоит из выполнения следующих шагов:
1. Обозначим массив промежуточного выходного значения как Z; изначально данный массив пуст, т. е. имеет нулевой размер.
2. Строка Z дополняется R первыми битами состояния.

Алгоритмы хеширования  83
3. Если размер Z становится больше или равен n (т. е. размеру выходного
значения алгоритма), то Z усекается до требуемого размера, в результате
чего получается выходное значение и работа алгоритма завершается.
4. В противном случае выполняется функция f() и производится возврат
к шагу 2 данного этапа.
Отметим, что для алгоритмов SHA3-224, SHA3-256, SHA3-384 и SHA3-512
справедливо соотношение R ≥ n, поэтому этап «выжимания» сводится к однократному выполнению описанных выше шагов 1–3.
Помимо описанных выше четырех алгоритмов семейства SHA-3 с фиксированными размерами выходного значения, стандарт [132] описывает также
два алгоритма хеширования с переменным размером выходного значения:
SHAKE128 и SHAKE256 (где n в SHAKEn является не размером выходного значения, а определяет теоретический уровень криптостойкости алгоритма). Предполагается, что применение данных алгоритмов будет регулироваться последующими руководящими документами NIST.
Быстродействие алгоритмов
Еще в процессе проведения конкурса SHA-3 алгоритм Keccak показал стабильно высокое быстродействие на всех платформах, используемых для тестирования быстродействия криптографических алгоритмов в рамках проекта eBACS
(ECRYPT Benchmarking of Cryptographic Systems – сравнительный анализ криптосистем в рамках проекта ECRYPT) [110].
Эксперты NIST в [190] назвали Keccak наиболее быстрым в аппаратной реализации по сравнению как с SHA-2, так и с другими алгоритмами-финалистами конкурса SHA-3. Быстродействие Keccak в программной реализации также
посчитали приемлемым.
Кроме того, выбор Keccak в качестве основы нового стандарта хеширования предопределили наличие у него высокого запаса криптостойкости, его
гибкость, позволяющая адаптировать конструкцию алгоритма под различные
применения, а также простота и элегантность его структуры [95].
Отличия алгоритмов Keccak и SHA-3
По сравнению с описанным выше семейством алгоритмов SHA-3 оригинальный алгоритм Keccak [71] имеет ряд отличий, в частности:
 Keccak значительно более гибок – он имеет множество настраиваемых
параметров с целью обеспечения оптимального соотношения криптостойкости и быстродействия для определенного применения алгоритма
на определенной платформе; настраиваемыми величинами, в частности, являются: размер блока данных, размер состояния алгоритма, количество раундов в функции f() и др.;
 в алгоритме Keccak иначе выполняется дополнение последнего блока хешируемых данных.
Использование алгоритмов семейств SHA-3 и SHA-2
После появления семейства алгоритмов SHA-3 использование предыдущих
стандартов хеширования не было отменено. Как было сказано ранее, последняя на момент подготовки данной книги версия документа, описывающая
стандарты хеширования SHA-1 и SHA-2, вышла в 2012 г. [129].

84  Глава 1
При этом использование алгоритма SHA-1 при вычислении электронной
подписи было запрещено после декабря 2013 г., тогда как прочие применения SHA-1 и использование алгоритмов семейства SHA-2 в любых целях
разрешались без ограничений по времени [60]. На текущий момент предполагается, что в последующих руководящих документах NIST будут даны рекомендации по предпочтительному применению алгоритмов SHA-2 или SHA-3
в конкретных случаях.
Результаты криптоанализа
На текущий момент не опубликовано каких-либо атак на полнораундовые версии алгоритмов семейства SHA-3.

1.3.4 Отечественные стандарты хеширования
Данный раздел посвящен описанию отечественных стандартов хеширования,
которые широко используются в России. Предпринимаются также усилия по
международной стандартизации текущего стандарта хеширования РФ [26].

ГОСТ Р 34.11–94
Долгое время стандартом хеширования РФ был ГОСТ Р 34.11–94 [14]. Лежащий
в его основе алгоритм хеширования базируется на использовании отечественного стандарта шифрования ГОСТ 28147–89 [11] в качестве внутреннего блочного шифра.
Структура алгоритма
Алгоритм выполняется в три этапа. На первом этапе выполняется инициализация внутреннего состояния алгоритма следующим образом [14]:
M = M0;
H = H0;
Sum = 0;
L = 0,
где:







M0 – хешируемое сообщение;
M – текущая необработанная часть хешируемого сообщения;
H0 – стартовый вектор хеширования;
H – текущее 256-битовое значение хеш-функции;
Sum – текущее значение контрольной суммы;
L – текущее значение длины обработанной на предыдущих итерациях
части входной последовательности.
256-битовый стартовый вектор хеширования явным образом в стандарте
ГОСТ Р 34.11–94 не определен. Таким образом, значение H0 является дополнительным параметром алгоритма.
На втором этапе анализируется размер в битах |M| необработанной части
сообщения M; если значение |M| не превышает 256, осуществляется переход
к третьему этапу.

Алгоритмы хеширования  85
Затем необработанная часть сообщения разбивается на блоки по 256 бит путем разбиения входного сообщения на две части – Mp и Ms, одна из которых
(Ms) имеет размер 256 бит:
M = Mp || Ms.
После этого выполняются следующие действия с каждым из блоков Ms до
тех пор, пока размер необработанной части сообщения не станет меньше или
равным 256 бит:
H = χ(Ms, H);

L = L + 256 mod 2256;
Sum = Sum + Ms mod 2256;
M = Mp,
где в операции обновления контрольной суммы строка Ms конвертируется
в числовое значение (аналогичное действие выполняется и в третьем этапе),
а функция χ() представляет собой основное преобразование алгоритма – шаговую функцию хеширования, которая подробно описана далее.
На третьем этапе текущая необработанная часть сообщения M дополняется
слева битовыми нулями до 256 бит с получением 256-битовой строки M’; после
этого выполняются следующие действия:
L = L + |M| mod 2256;
Sum = Sum + M’ mod 2256;
H = χ(M’, H);
H = χ(L, H);

H = χ(Sum, H).

Значение H после выполнения последнего из перечисленных выше действий является выходным 256-битовым значением алгоритма хеширования.
Шаговая функция хеширования также выполняется в несколько этапов.
Входными данными функции χ() являются текущее значение хеш-функции H и
обрабатываемый 256-битовый блок Ms хешируемого сообщения (или другой
блок аналогичного размера на третьем этапе работы алгоритма).
На первом этапе генерируются ключи шифрования на основе входных данных.
Эта процедура выполняется с помощью следующей последовательности шагов:
1. Инициализируются внутренние переменные i, U, V:
i = 1;
U = H;
V = Ms.
2. Выполняются следующие вычисления:
W = U ⊕ V;
K1 = P(W),

86



где:



3.

Глава 1

W – временные переменные;
Kj, j = 1...4, – 256-битовые ключи внутреннего блочного шифра;
преобразование P() будет описано далее.
Инкрементируется индекс текущего ключа:
i = i + 1.

4. Если значение i превышает 4, то работа процедуры формирования ключей завершается; в противном случае выполняется переход к следующему шагу.
5. Осуществляется модификация внутренних переменных:
U = A(U) ⊕ Ci;
V = A(A(V));
W = U ⊕ V,
где:
 функция A() будет описана далее;
 Cj, j = 2...4, – константы, определенные следующим образом:
С2 = С4 =0256;
C3 = 180811602411608(0818)21808(0818)4(1808)4;
в данном случае запись dn обозначает битовую строку (подстроку) из
n повторяющихся битов со значением d.
6. Вычисляется значение следующего ключа:
Ki = P(W).
7. Выполняется переход к шагу 3.
Функция A() определена следующим образом:
A(X) = (x1 || x2) || x4 || x3 || x2,
где x1..x4 – 64-битовые подстроки 256-битовой строки X:
X = x4 || x3 || x2 || x1.
Преобразование P() трансформирует входную 256-битовую строку, рассматриваемую как последовательность 8-битовых слов ξ32 ||...|| ξ1, в строку
ξϕ(32) ||...|| ξϕ(1), где:
ϕ(i + 1 + 4 * (k – 1)) = 8i + k, i = 0...3, k = 1...8.
На втором этапе выполняется зашифрование текущего значения хешфункции H с помощью сформированных на предыдущем этапе ключей шифрования.
Для этого 256-битовое значение H рассматривается в виде четырех 64-битовых фрагментов, каждый из которых шифруется на отдельном ключе Ki,
i = 1...4, в режиме простой замены с помощью алгоритма блочного шифрова-

Алгоритмы хеширования  87
ния, определенного в стандарте ГОСТ 28147–89 [11]. В результате зашифрования получается 256-битовое значение S.
Поскольку в стандарте ГОСТ 28147–89 не определен узел замены алгоритма
шифрования, значение узла замены является дополнительным параметром
данного алгоритма хеширования.
На третьем этапе производится перемешивание текущих значений H, Ms и S
и формирование выходного значения шаговой функции хеширования.
Выходное значение функции χ() определяется следующим образом:
χ(Ms, H) = ψ61(H ⊕ ψ(Ms ⊕ ψ12(S))),

где:
 ψn() – n-я степень преобразования ψ();
 преобразование ψ() трансформирует 256-битовое входное значение,
рассматриваемое в виде 16-битовых фрагментов ƞ16 ||...|| ƞ1, в следующее
выходное значение аналогичного размера:
ƞ1 ⊕ ƞ2 ⊕ ƞ3 ⊕ ƞ4 ⊕ ƞ13 ⊕ ƞ16 || ƞ16 ||...|| ƞ2.

Результаты криптоанализа
В 2008 г. была опубликована работа [172], в которой были опубликованы следующие атаки на алгоритм ГОСТ Р 34.11–94:
 атака по нахождению коллизий с трудоемкостью 2105 операций;
 атака по поиску прообразов с трудоемкостью 2192 операций.
Обе атаки не имеют практической применимости.

ГОСТ Р 34.11–2012
В связи с необходимостью ввода нового стандарта хеширования с удовлетворяющим современным требованиям уровнем криптостойкости, а также с характеристиками, соответствующими новому отечественному стандарту элект­
ронной подписи ГОСТ Р 34.10–2012 (см. [13]), в августе 2012 г. старый стандарт
хеширования был заменен на ГОСТ Р 34.11–2012 [15].
Однако ГОСТ Р 34.11–94 продолжал действовать до 2018 г. включительно
в рамках переходного периода, в основном с целью обеспечения совместимости с разработанными ранее системами, основанными на его использовании [27].
ГОСТ Р 34.11–2012 разработан Центром защиты информации и специальной связи ФСБ России с участием ОАО «ИнфоТеКС» [15]. Альтернативное неофициальное название алгоритма, лежащего в основе стандарта ГОСТ Р 34.11–
2012, – «Стрибог».
Структура алгоритма
Алгоритм обрабатывает входное сообщение блоками по 512 бит. На верхнем
уровне структура алгоритма представляет собой незначительно модифицированную схему Меркля–Дамгорда [29], при этом выходное значение алгоритма
может иметь 256- или 512-битовый размер.
Выполнение алгоритма может бытьпредставлено следующими тремя этапами.

88

 Глава 1

Этап 1. Инициализация внутреннего состояния алгоритма. В рамках этого
этапа выполняются следующие действия:
h = IV;
N = Σ = 0,
где:
 h – 512-битовый регистр внутреннего состояния алгоритма;
 IV – вектор инициализации; его значение зависит от размера выходного значения алгоритма: IV заполнен битовыми нулями при вычислении
512-битового хеш-кода и заполнен 8-битовыми последовательностями
«00000001» при вычислении 256-битового выходного значения;
 N – 512-битовый счетчик обработанных данных;
 Σ – 512-битовый регистр, содержащий текущую сумму обработанных
данных по модулю 2512.
Этап 2. Поочередная обработка каждого блока сообщения, кроме последнего блока, если он является неполным, состоящая из следующих операций:
h = g(h, M, N);
N = N + 512;
Σ = Σ + M,

где:
 M – 512-битовый блок хешируемого сообщения;
 g() – функция сжатия, которая подробно будет описана далее;
 сложение выполняется по модулю 2512 (здесь и далее).
Этап 3. Дополнение последнего блока хешируемых данных и завершающие
преобразования.
Последний блок сообщения всегда дополняется единичным битом, а также, при необходимости, битовыми нулями до достижения размера, кратного
512 битам. Дополнение увеличивает количество блоков данных на один, если
размер сообщения изначально был кратен 512 битам.
После этого выполняются следующие финализирующие операции:
h = g(h, M, N);
N = N + (l mod 512);
Σ = Σ + M;

h = g(h, N, 0);
H = g(h, Σ, 0),

где l – размер хешируемого сообщения (до его дополнения).
В качестве результата работы 512-битового алгоритма ГОСТ Р 34.11–2012
берется значение H. Для 256-битового варианта алгоритма результатом являются старшие 256 бит значения H.
Функция сжатия g() основана на внутреннем блочном шифре E() и построена по обсуждавшейся ранее конструкции Миягучи–Пренеля [182] (на рис. 1.45

Алгоритмы хеширования  89
показана схема интерпретации конструкции Миягучи–Пренеля в алгоритме
ГОСТ Р 34.11–2012):
g(h, M, N) = E(k(h, N), M) ⊕ h ⊕ M,
где k() – функция вычисления псевдоключа блочного шифра (см. далее).

h
k()
E ()

M

+

Рисунок 1.45. Конструкция Миягучи–Пренеля в алгоритме ГОСТ Р 34.11–2012

Лежащий в основе функции сжатия 512-битовый блочный шифр E(K, M) выполняется в 12 раундов. M – это блок шифруемых данных, а K – псевдоключ,
который обновляется в каждом раунде алгоритма.
Фактически преобразования данных в рамках блочного шифра E() выполняются в два потока, в которых используется один и тот же набор преобразований:
 первый поток модифицирует псевдоключ;
 второй поток шифрует блок данных.
Структура блочного шифра E() приведена на рис. 1.46. В нем используется
следующая последовательность операций:
E(K, M) = X[K13]LPSX[K12] … LPSX[K2]LPSX[K1](M).
Cj-1
K1

X ()

S ()

P ()

L ()

Kj

M

X ()

S ()

P ()

L ()

X ()

Результат

Рисунок 1.46. Структура внутреннего блочного шифра алгоритма ГОСТ Р 34.11–2012

Х[t](a) – операция наложения псевдоключа (вычисление и модификация псевдоключа будут подробно описаны далее) путем суммирования по модулю 2:
Х[t](a) = t ⊕ a,

90

 Глава 1

где:
 a – текущее состояние блочного шифра, изначально a = M;
 в качестве t используется значение подключа текущего раунда.
S(a) – побайтовая замена состояния согласно таблице замен, приведенной
в приложении 1.
P(a) – перестановка байтов состояния согласно таблице, приведенной в приложении 1.
L(a) – умножение на матрицу А в поле GF(2). Матрица A приведена в приложении 1.
Раундовые псевдоключи Ki вычисляются следующим образом. Исходный
псевдоключ K1 зависит от текущего состояния алгоритма и счетчика N:
K1 = k(h, N) = LPS(h ⊕ N).
Дальнейшая модификация псевдоключей выполняется с использованием
тех же описанных выше преобразований:
Kj = LPS(Kj-1 ⊕ Cj-1),
где:
 j – номер раунда;
 Ci – раундовые константы согласно таблице, приведенной в приложении 1.
Результаты криптоанализа
После появления новый российский стандарт функции хеширования привлек
достаточно пристальное внимание специалистов в области защиты информации, в том числе криптоаналитиков. На текущий момент появилось несколько
работ по анализу алгоритма ГОСТ Р 34.11–2012, однако пока не предложено
каких-либо атак на данный алгоритм, близких к практической применимости.

Глава

2

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

2.1 МатеМатичесКие основы
Приведем в данном разделе определения основных математических терминов
и понятий, которые будут использованы в этой главе.
Определим понятие бинарной операции для множества S. Под S×S обозначим множество всех возможных упорядоченных пар (s1, s2), где s1и s2 принадлежат S.
Бинарная операция на множестве S – это отображение множества S×S
во множество S.
Самый простой пример бинарной операции на множестве – это множество
целых чисел Z с операциями сложения и умножения, которые являются бинарными операциями на Z.
Группой называется множество G c бинарной операцией *, если выполняются следующие условия:
1. Ассоциативность: для любых a, b, c ∈ G:
a * (b * c) = (a * b) * c.
2. Существует нейтральный элемент e ∈ G: для любого элемента a ∈ G:
a * e = e * a = a.

92

 Глава 2

3. Для любого a ∈ G существует обратный элемент a–1:
a * a–1 = a–1 * a = e.
Если также для любых a, b ∈ G выполняется a * b = b * a, то группа называется
абелевой, или коммутативной.
Если бинарная операция в группе обозначается как сложение, то a * b заменяется на a + b, нейтральный элемент e – на 0, а обратный a элемент a–1 – на
–a. Такая группа называется аддитивной. Группа же с операцией умножения,
определенная выше, называется мультипликативной.
Определение аддитивной группы G будет выглядеть так:
1. Ассоциативность: для любых a, b, c ∈ G:
a + (b + c) = (a + b) + c.
2. Существует нейтральный элемент 0 ∈ G: для любого элемента a ∈ G:
a + 0 = 0 + a = a.
3. Для любого a ∈ G существует обратный элемент –a:
a + –a = –a + a = 0.
Если также для любых a, b ∈ G выполняется a + b = b + a, то группа называется
абелевой, или коммутативной.
Для аддитивной группы сложение элемента a и элемента, обратного элементу b, чаще всего обозначают не как a + –b, а просто a – b.
Нейтральный элемент в мультипликативной группе называется единичным
элементом, или единицей, а в аддитивной группе – нулевым элементом, или
нулем.
Примеры групп:
1. Множество целых чисел Z с операцией сложения.
Очевидно, что сложение ассоциативно: для любых целых a, b, c выполняется
a + (b + c) = (a + b) + c.
Нейтральный элемент – это 0. Для каждого целого a имеется обратный элемент –a.
Кроме того, сложение коммутативно, поэтому Z с операцией сложения –
абелева группа.
2. Множество из двух целых чисел {–1, 1} c операцией умножения.
Ассоциативность выполняется. Нейтральным элементом является 1. Для –1
обратным является –1.
Операция коммутативна, поэтому это тоже абелева группа.
3. Множество классов вычетов по модулю n с операцией сложения: числа
{0, ... , n – 1} – остатки от деления на n образуют абелеву группу с нейтральным элементом 0.
4. Множество положительных вещественных чисел с умножением.
Ассоциативность выполняется, 1 – нейтральный элемент. Для любого целого
положительного a есть обратный элемент 1/a.
Кроме того, операция коммутативна, следовательно, группа – абелева.

Алгоритмы электронной подписи на эллиптических кривых  93
5. Множество классов вычетов по модулю простого числа p с операцией
умножения: числа {1, ... , p – 1} – остатки от деления на p – образуют абелеву группу с нейтральным элементом 1.
Группы делятся на конечные и бесконечные, в зависимости от числа элементов. Например, группы № 1 и № 4 из числа приведенных выше примеров – бесконечные, а группы № 2, 3 и 5 – конечные.
Число элементов группы G называется порядком группы и обозначается как
#G или |G|.
У групп № 1 и № 4 порядок бесконечен, у групп же № 2, 3 и 5 он равен 2,
n и p – 1 соответственно.
Подмножество H, состоящее из элементов группы G, называют подгруппой
группы G, если она сама является группой относительно групповой операции G.
Самой простой пример подгруппы – нейтральный элемент любой группы.
Кроме того, чисто формально любая группа содержит саму себя и является подгруппой по отношению к самой себе. Эти две подгруппы называют тривиальными подгруппами.
Рассмотрим в качестве примера группу классов вычетов по модулю 5 с операцией умножения: {1, 2, 3, 4}. Ее порядок равен 4. Она содержит следующие
подгруппы: {1}, {1, 4}, {1, 2, 3, 4}.
Теорема Лагранжа:
Порядок любой подгруппы H делит без остатка порядок содержащей ее группы G.
Эту теорему можно кратко записать таким образом:
#H | #G,
если H – подгруппа группы G.
Символ | в данном случае означает деление без остатка. Например, a | b означает, что a без остатка делит b.
Порядком элемента a мультипликативной группы называется такое минимальное натуральное число k, что:
ak

a ... a

нейтральный элемент.

k раз

Для аддитивной группы экспонента ak заменяется на ka:
нейтральный элемент
ka a ... a нейтральный
элемент(т. е. получается аналогичное определение
k раз

порядка элемента).
Например, порядок элемента –1 из группы {–1, 1} c операцией умножения
равен двум.
Если в группе порядка n существует хотя бы один элемент порядка n, то такую группу называют циклической. Такой элемент называют образующим элементом (или генератором группы).
Рассмотренные выше группы № 2, 3 и 5 – циклические. В группе № 2 генератором является –1, а в группе № 3 – любой взаимно простой c n элемент (т. е. не
имеющий с n общих делителей). Например, в группе № 3 генератором является

94

 Глава 2

число 1, поскольку, складывая 1, можно получить все элементы группы, начиная с 1 и заканчивая нейтральным элементом 0.
В группе № 5 генераторами являются элементы 2 и 3. Действительно, вся
группа № 5 – это множество {1, 2, 3, 4} c операцией умножения mod 5. Для степеней 2 имеем:
21 mod 5 = 2;
22 mod 5 = 4;
23 mod 5 = 3;
24 mod 5 = 1.
То есть степени элемента 2 «генерируют» всю группу. Порядок элемента 2
равен порядку группы.
Аналогично ведет себя элемент 3:
31 mod 5 = 3;
32 mod 5 = 4;
33 mod 5 = 2;
34 mod 5 = 1.
А степени 4 образуют подгруппу порядка 2:
41 mod 5 = 4;
42 mod 5 = 1.
Таким образом, 4 – генератор подгруппы {1, 4} группы {1, 2, 3, 4}, и его порядок равен двум. Также стоит отметить, что порядок подгруппы {1, 4} равен
двум, что является делителем числа четыре (порядка группы {1, 2, 3, 4}) в соответствии с теоремой Лагранжа.
Кольцом называют множество R с двумя бинарными операциями ∗ и +, такими, что:
1) множество R с операцией + представляет собой коммутативную группу;
2) операция * ассоциативна: для любых a, b, c ∈ R
a * (b * c) = (a * b) * c;
3) для любых a, b, c ∈ R:
a * (b + c) = a * b + a * c и (b + c) * a = b * a + c * a
(это свойство называют дистрибутивностью).
Кольцо называют коммутативным, если операция умножения ∗ является
коммутативной.
Самым простым примером кольца является множество целых чисел.
В качестве примера конечного кольца можно взять Z/Zn – множество классов
вычетов по модулю n с операциями сложения и умножения.
Полем называется коммутативное кольцо, в котором все элементы, кроме 0
(т. е. нейтрального элемента по сложению), имеют обратные элементы для
операции умножения.

Алгоритмы электронной подписи на эллиптических кривых  95
Множества рациональных, вещественных и комплексных чисел являются
полями.
Характеристикой поля называется порядок его единичного элемента, т. е.
минимальное натуральное число p, такое, что:
1 ... 1 0 .
p раз

Характеристика конечного поля всегда является простым числом.
Поле называется конечным (или полем Галуа), если оно состоит из конечного числа элементов. Число элементов конечного поля всегда равно степени его
характеристики.
Чаще всего для конечного поля из pn элементов, где p – характеристика, используют следующее обозначение:
GF(pn),
где GF означает Galois Field – поле Галуа, а pn – число элементов поля.
Нередко вместо GF(pn) используют обозначение Fpn.
Самый элементарный пример конечного поля – поле, состоящее из p элементов, где p – простое число. Такое поле называется простым полем (prime
field) и обозначается как GF(p) или Fp.
В качестве важного для криптографии примера конечного поля можно взять
Z/Zp – множество классов вычетов по модулю простого числа p с операциями
сложения и умножения.
Мультипликативная группа поля Fpn обозначается как Fp*n и состоит из всех
элементов поля, за исключением 0, т. е. нейтрального элемента по отношению
к операции сложения. |Fp*n | = pn – 1 (порядок Fp* равен
pn – 1).
n
Теорема:
Мультипликативная группа Fp* любого
конечного поля Fpn – циклиn
ческая.
Пусть G – группа с операцией ◦, а Gʹ – группа с операцией ▫.Отображение φ:
G → Gʹ, переводящее элементы G в элементы Gʹ, называется гомоморфизмом G
в Gʹ, если для любых a, b ∈ G выполняется:
φ(a◦b) = φ(a)▫ φ(b).

Если отображение φ еще и взаимно однозначно (т. е. любому элементу φ(a) из
Gʹ соответствует один-единственный a ∈ G), то оно называется изоморфизмом.

2.2 Эллиптические кривые
Идею использовать эллиптические кривые для построения криптографических схем предложили в 1985 г. Виктор Миллер (Victor Miller) и Нил Коблиц
(Neal Koblitz) независимо друг от друга.

2.2.1 Определение эллиптической кривой
Эллиптической кривой над полем F называется множество точек (x, y) – решений уравнения Вейерштрасса:
y2 + a1xy + a3 y = x3 + a2x2 + a4x + a6

(2.1)

96

 Глава 2

совместно с так называемой нулевой (бесконечно удаленной) точкой O (point
at infinity).
Константы a1, a2, a3, a4, a6 – это элементы поля F.
Выражение (2.1) представляет собой уравнение Вейерштрасса в общем виде
(generalized Weierstrass equation) в аффинных координатах. Если характерис­
тика поля F больше 3, то уравнение (2.1) может быть элементарно преобразовано в короткую форму (short):
y2 = x3 + ax + b.

(2.2)

Кривая должна иметь дискриминант Δ = 4a3 + 27b2, не равный 0. При Δ = 0
кривая называется сингулярной (не путайте с суперсингулярной – суперсингулярные кривые описаны далее) и не может быть использована в криптографических целях.
На практике кривые над полем с характеристикой, равной 2 (binary field), используются редко, несмотря на то что они все же присутствуют в ряде зарубежных стандартов. Для кривых над полями характеристики 3 существуют лишь
теоретические работы. Поэтому далее в нашей книге мы будем рассматривать
только кривые над полем с характеристикой, большей 3.
Кривая E над полем K чаще всего обозначается как E(K). В криптографических алгоритмах используют кривые над конечными полями. Кривая E над конечным полем Fp n (т. е. полем характеристики p, состоящем из pn элементов)
обозначается как E(Fp n) или E(GF(pn)) (данные обозначения эквивалентны, использование конкретного из них фактически зависит от того, как больше нравится авторам статей и книг обозначать конечное поле из pn элементов).

2.2.2 Основные операции над точками эллиптической кривой
Сложение точек
Опишем групповой закон сложения точек в форме Вейерштрасса.
Точки эллиптической кривой образуют коммутативную (абелеву) группу по
операции сложения, причем нулевая точка O играет роль нейтрального элемента группы: для любой точки Q имеет место равенство Q + O = O + Q = Q.
В представлении Вейерштрасса нулевая точка O не имеет каких-либо конкретных координат (x, y).
Пусть имеются две точки Q1 = (x1, y1) и Q2 = (x2, y2) кривой (2.2). Они считаются
равными, если равны их координаты, т. е. Q1 = Q2 если x1 = x2 и y1 = y2.
Их сумма, Q1 + Q2 точка Q3 = (x3, y3), вычисляется следующим образом:
1. В случае, когда Q1 ≠ Q2 (сложение точек):
 если x1 = x2 и y1 ≠ y2, то Q3 = O (поскольку Q1 обратна Q2: Q1 = –Q2 );
 если x1 ≠ x2, то:
x3 = λ2 – x1 – x2;
y  y
где    2  1 .
x2   x1

y3 = λ(x1 – x3) – y1,

Алгоритмы электронной подписи на эллиптических кривых  97
2. В случае, когда Q1 = Q2 (удвоение точки):
 если y1 = 0, то Q3 = O (поскольку в этом случае Q1 обратна самой себе:
Q1 + Q2 = 2 Q1 = O);
 если y1 ≠ 0, то:

3 x12  a
где   
.
2 y1

x3 = λ2 – 2x1;

y3 = λ(x1 – x3) – y1,

Геометрически сумму точек можно представить с помощью двух прямых,
пересекающих эллиптическую кривую, что показано на рис. 2.1 на примере
кривой y2 = x3 – 5x + 2 над полем действительных чисел R.

Рисунок 2.1. Геометрическое представление суммы точек эллиптической кривой

Первая прямая, проходящая через складываемые точки Q1 и Q2, пересекает
кривую в третьей точке –Q3, обратной их сумме. Для того чтобы получить Q3
из –Q3, достаточно поменять знак y-координаты точки –Q3, что геометрически выглядит как пересечение вертикальной прямой, проходящей через –Q3,
и кривой.
Когда складываемые точки обратны друг другу, прямая, которая проходит
через них, вертикальна и не пересекает кривую больше ни в одном месте. Этот
случай соответствует бесконечно удаленной точке.
Удвоение точки можно представить аналогично (рис. 2.2): через точку Q
проводим касательную и, найдя ее пересечение с кривой, получаем точку –2Q,
т. е. обратную точке 2Q. Осталось найти требуемую точку 2Q при помощи вертикальной прямой, как и в случае со сложением.

98

 Глава 2

Рисунок 2.2. Геометрическое представление удвоения точки эллиптической кривой

Когда удваиваемая точка обратна самой себе (т. е. координата y = 0), касательная вертикальна и не пересекает кривую, что соответствует бесконечно
удаленной точке.

Скалярное умножение
Скалярным умножением (scalar multiplication) числа k на точку P называется
k-кратное сложение точки P:

Q

P ... P

k P

k P.

k раз

Результат этой операции (точка Q) называется точкой кратности k.
Рассмотрим, как вычисляется результат скалярного умножения. Пусть мы
хотим умножить число k на точку P, т. е. узнать координаты следующей точки:
k P

P ... P .
k раз

Даже если число k небольшое, то вычислять k * P, складывая k раз точку P,
было бы крайне неэффективно. Самый простой алгоритм для вычисления k * P
называется бинарным. Данный алгоритм предусматривает выполнение скалярного умножения следующим образом.
Сначала представим k в бинарном виде. Пусть k – это m-битовое число:
m 1

k   ki  2i ,
i 0

где каждый из коэффициентов ki равен 0 либо 1.

Алгоритмы электронной подписи на эллиптических кривых  99
Тогда k * P тоже представим в бинарном виде:
m 1

k  P   ki  2i  P .
i 0

Из формулы очевидно, что можно последовательно, начиная с i = 0 до m – 1,
считать 2i ∗ P и прибавлять результат к общей сумме, если ki равно 1.
Псевдокод бинарного алгоритма выглядит так (результат вычислений будет
сохранен в переменной R):
R = 0
D = P
for i in range(m):
if k[i] == 1:
R = R + D
D = 2 ∗ D

Таким образом, при умножении случайного m-битового числа на точку будет выполнено m удвоений и в среднем m/2 сложений. Это значит, что бинарный алгоритм позволяет умножать точки на огромные числа.
К примеру, для кривой secp256k1 (будет рассмотрена далее), где для скалярного умножения используются 256-битовые числа, потребуется всего лишь
256 удвоений и около 128 сложений.
У данного алгоритма существует масса более быстрых алгоритмов-«конку­
рентов», которые можно использовать при практической реализации, но они
выглядят более сложно и не так интуитивно понятны, как бинарный метод.

2.2.3 Основные характеристики эллиптической кривой
Параметры и характеристики кривой
Опишем характеристики, определяющие основные свойства эллиптических
кривых.
Порядком группы точек кривой называется количество точек, лежащих на
этой кривой (т. е. решений уравнения кривой) плюс нулевая точка.
Порядком точки P называется минимальное число k, при умножении на которое получается нулевая точка, т.е. [k]P = O.
Как и любая другая группа, группа точек эллиптической кривой имеет подгруппы. Если порядок кривой – простое число, то их всего две и эти подгруппы
тривиальны – это нулевая точка и сама группа точек простого порядка. Как
правило, в криптосистемах используют только точки из подгруппы большого
простого порядка.
Количество точек кривой E над конечным полем, содержащим pn элементов, часто в литературе обозначается как #E(Fp n) или #E(GFp n)). Существует одно
весьма полезное представление этой величины:
#E(Fp n) = pn + 1 – t,
где целое число t называется следом Фробениуса (Frobenius trace). Отметим,
что оно целое, т. е. может быть нулевым, положительным или отрицательным.

100

 Глава 2

Известна теорема Хассе (Hasse’s theorem on elliptic curves):
След Фробениуса по абсолютной величине ≤ 2∗ pn .
Из теоремы следует, что количество точек эллиптической кривой над полем,
содержащим pn элементов, всегда лежит в относительно узком по сравнению
с pn интервале:

 

pn  1  2  pn  #E Fpn  pn  1  2  pn .
Это неравенство может быть полезно для грубой оценки количества точек.
Порядок группы точек кривой #E чаще всего имеет следующий вид:
#E= cofactor ∗ q,
где q – порядок простой подгруппы (prime subgroup order) – большое простое
число, а кофактор (cofactor) – как правило, небольшое целое число. q – это порядок именно той подгруппы, точки которой будут использоваться в криптографических алгоритмах.
Для практических криптографических приложений кривая описывается
при помощи набора параметров – криптонабора (paramset), состоящего из
следующих параметров:
1) конечное поле Fp n (т. е. значения p и n);
2) коэффициенты кривой a, b (для уравнения в форме Вейерштрасса);
3) порядок простой подгруппы (prime subgroup order) – большое простое
число q;
4) кофактор (cofactor);
5) базовая точка P (base point) – точка порядка q (точка P – генератор подгруппы, состоящей из q элементов; иными словами, q – это минимальное
натуральное число, на которое надо умножить точку P, чтобы получить
бесконечно удаленную точку: [q]P = O).
Есть еще один важный параметр, который определяет внутреннюю структуру группы точек кривой. Он называется j-инвариантом и вычисляется по
формуле:
j(E )  1728

4a3
,
4a3  27b2

где a и b – коэффициенты кривой E.
Если две кривые над одним и тем же полем изоморфны, то они имеют одинаковый j-инвариант. Обратное верно не всегда: у двух кривых над одним полем может быть одинаковый j-инвариант, притом что они не изоморфны.
Пусть есть эллиптическая кривая E: y2 = x3 + ax + b над полем F. Если мы выберем некоторое значение μ, не равное 0, из этого поля и вычислим aʹ = aμ4
и bʹ = bμ6, то получим изоморфную E кривую Eʹ: y2 = x3 + aʹx + bʹ.
Каждой точке (x, y) на E соответствует точка (xμ2, yμ3 ) на кривой Eʹ. Очевидно,
что после подстановки (xμ2, yμ3) в уравнение Eʹ произойдет сокращение μ6 и мы
получим уравнение y2 = x3 + ax + b. Также очевидно, что:

Алгоритмы электронной подписи на эллиптических кривых  101
j(E )  1728

4a 3
4( a  4 )3
4a3
=1728
=1728
=j(E ).
4a 3  27b2
4( a  4 )3  27(b 6 )2
4a3  27b2

Изоморфизм по определению сохраняет групповой закон сложения для элементов: если на E точка (x3, y3) равна сумме точек (x1, y1) и (x2, y2), то сумма образов (x1, y1) и (x2, y2) на Eʹ равна образу (x3, y3):
 на E:
(x1, y1) + (x2, y2) = (x3, y3);
 сумма образов на Eʹ:
(μ2 x1, μ3 x1) + (μ2 x2, μ3 y2) = (μ2 x3, μ3 y3).

Подсчет количества точек кривой

Первый алгоритм подсчета количества точек, более эффективный, чем прос­
той перебор, был разработан Рене Шуфом (René Schoof) в 1985 г., но он оказался неоптимальным для криптографических применений. В 1990-х годах на
его основе был создан гораздо более эффективный алгоритм SEA (Schoof-Elkies-Atkin), который позволяет весьма эффективно (за время порядка O(log4p))
решать эту задачу и реализован в ряде популярных систем компьютерной алгебры и библиотек.

2.2.4 Примеры эллиптических кривых
Проиллюстрируем сказанное выше на примерах эллиптических кривых.

Пример 1
Кривая E: y2 = x3 + x + 1 над конечным полем Fp = F19 (или GF(19) в другом обозначении).
Давайте немного поисследуем ее. Для начала вычислим значение #E (порядок группы точек).
Для «игрушечных» с точки зрения эллиптической криптографии полей, вроде
F19 (т. е. таких полей, число элементов которых можно легко перебрать), вычисление порядка группы точек кривой проще всего провести простым перебором.
Итак, коэффициенты a = 1 и b = 1. Конечное поле F19 содержит простое
число элементов – так называемое простое поле (prime field). Дискриминант
Δ = 4a3 + 27b2 = 4 + 27 (mod 19) = 31 (mod 19) = 12. Δ ≠ 0, следовательно, это –
эллиптическая кривая (а не сингулярная) и на ней выполняются групповые
операции сложения точек.
Давайте сначала выясним, какие точки она содержит. Очевидно, что для
этого надо перебрать возможные значения x из поля кривой и вычислить для
каждого правую часть уравнения кривой right = x3 + ax + b. Если right является
квадратичным вычетом, то решаем yравнение y2 = right и получаем пару точек
с одним x и противоположными y (т. е. (x, y) и (x, – y)).
p 1
a
2
Итак, для x = 0 значение x3 + x + 1 равно 1. Символ Лежандра    a mod p
p
 1 
равен    1, из чего следует, что 1 – квадратичный вычет по модулю 19.
 19 

102



Глава 2

Это означает, что уравнение y2 = 1 mod 19 имеет решения. Так как p = 3 mod 4,
то решения для y2 = right mod p находятся по следующим формулам:
y1  a

p 1
4

mod p ;

y2 = – y1 = p – y1.
Поэтому y1 = 1, y2 = –1 = 19 –1 = 18, следовательно, для x = 0 имеем два y: 1 и 18,
которые удовлетворяют уравнению. Таким образом, мы получили две точки
кривой: (0, 1) и (0, 18).
Проведя такие вычисления для всех x из поля F19 и отбросив повторы правой
части, мы получим все решения уравнения кривой:
(0, 1), (0, 18), (2, 7), (2, 12), (5, 6), (5, 13), (7, 3),
(7, 16), (9, 6), (9, 13), (10, 2), (10, 17), (13, 8), (13, 11),
(14, 2), (14, 17), (15, 3), (15, 16), (16, 3), (16, 16).
Добавив к полученному множеству бесконечно удаленную точку O, получим
всю группу точек кривой E. Ее порядок #E(F19) равен 21:
#E(F19 ) = 19 + 1 – t = 21.
То есть след Фробениуса t = –1 (как и должно быть по теореме Хассе:
1  2 19 ).
Проиллюстрируем сложение точек рассматриваемой кривой.
Возьмем точки Q1 = (10, 2) и Q2 = (10, 17). Чему равна их сумма Q1 + Q2?
Данные точки обратны друг другу: Q1 = –Q2, т. к. x1 = x2 = 10 и y1 = –y2 (последнее верно, поскольку 2 = –17 mod 19). В результате Q1 + Q2 = O, так как точки
являются взаимообратными.
Если же мы рассмотрим две какие-нибудь не обратные друг другу точки, например Q1 = (10, 17) и Q2 = (15, 3), то можем применить формулу сложения:



y2  y1 3  17

mod 19 = 1;
x2  x1 15  10

x3 = λ2 – x1 – x2 = 1 – 10 – 15 mod 19 = 14;

Следовательно:

y3 = λ(x1 – x3) – y1 = 1 * (10 – 14) – 17 = 17.
Q3 = Q1 + Q2 = (14, 17).

Рассмотрим также удвоение точки – для Q = (16, 3) вычислим 2Q:
3 x 2  A 3  162  1
 1

mod 19 = 11;
2y1
23
x3 = λ2 – 2x1 = 112 – 2 * 16 mod 19 = 13;

y3 = λ(x1 – x3) – y1 = 11 * (16 – 13) – 3 mod 19 = 11.

В результате 2 ∗ Q = (13, 11).

Алгоритмы электронной подписи на эллиптических кривых  103
Группа точек рассматриваемой кривой E – циклическая группа, т. е. в ней
существуют порождающие элементы (генераторы), при помощи скалярного
умножения на которые чисел от 1 до порядка группы можно получить все элементы группы.
Одна из таких точек – точка (0, 18). Умножая ее на числа от 1 до 21, мы получаем все точки кривой, включая O:
1 * (0, 18) = (0, 18);
2 * (0, 18) = (0, 18) + (0, 18) = (5, 13);
3 * (0, 18) = (5, 13) + (0, 18) = (15, 16);
4 * (0, 18) = (15, 16) + (0, 18) = (9, 6);
…;
21 * (0, 18) = (0, 1) + (0, 18) = O.
Кроме того, присутствуют еще две циклические подгруппы порядков 3 и 7
(по теореме Лагранжа порядками подгрупп могут быть только делители порядка основной группы):
{O, (2, 7), (2, 12)}
и
{O, (10, 2), (10, 17), (14, 2), (14, 17), (15, 3), (15, 16)}.

Пример 2
Кривая secp256k1, которая используется в подписи ECDSA транзакций в системах Биткойн и Эфириум: E: y2 = x3 + 7 над простым полем Fp, где характеристика
поля p = 2256 – 232 – 29 – 28 – 27 – 26 – 24 – 1.
В шестнадцатеричном представлении p имеет следующий вид:
p = FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFE FFFF FC2F.
Коэффициенты уравнения кривой:
a = 0;
b = 7.
Число всех точек на кривой secp256k1 (также в шестнадцатеричном виде):
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE
BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141.
Это простое число, из чего следует, что кофактор равен единице, т. е. #E = q.
Группа точек этой кривой, в отличие от кривой из предыдущего примера, по
теореме Лагранжа не содержит нетривиальных подгрупп.
Для secp256k1 базовая точка (генератор подгруппы) имеет следующие координаты в шестнадцатеричном представлении:

104



Глава 2
x = 79BE 667E F9DC BBAC 55A0 6295 CE87 0B07
029B FCDB 2DCE 28D9 59F2 815B 16F8 1798;
y = 483A DA77 26A3 C465 5DA4 FBFC 0E11 08A8
FD17 B448 A685 5419 9C47 D08F FB10 D4B8.

Легко видеть, что число точек кривой secp256k1 (по теореме Хассе это порядка 2256), как и любой другой кривой, которая используется в криптографических
алгоритмах, невозможно посчитать простым перебором, как в первом примере.
Для этого на практике используют алгоритм SEA (Schoof-Elkies-Atkin), который позволяет это делать за считанные секунды на обычном домашнем
компьютере, но, к сожалению, использует слишком сложную для этой книги
математику. Поэтому мы приводим лишь пример кода на SAGE, который его
использует «под капотом» и считает порядок кривой и порядок базовой точки:
p = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
a = 0
b = 7
x = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
y = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
E = EllipticCurve(GF(p), [a, b])
BasePoint = E(x, y)
pointOrder = BasePoint.order()
curveGroupOrder = E.order()
print("Order of group of points = " + hex(curveGroupOrder))
print("Order of BasePoint = " + hex(pointOrder))

Пример 3
Кривая bn254, применяемая во многих протоколах, основанных на спариваниях.
E: y2 = x3 + 2 над простым полем Fp.
Основные параметры кривой bn254:
 характеристика поля:
p = 2523 6482 4000 0001 BA34 4D80 0000 0008
6121 0000 0000 0013 A700 0000 0000 0013;
 j-инвариант = 0 (т. к. a = 0);
 порядок простой подгруппы:
q = 2523 6482 4000 0001 BA34 4D80 0000 0007
FF9F 8000 0000 0010 A100 0000 0000 000D;
 количество всех точек кривой: #E = q – простое, т. е. кофактор = 1;
 базовая точка:
x = 2523 6482 4000 0001 BA34 4D80 0000 0008
6121 0000 0000 0013 A700 0000 0000 0012;
y = 1.
Характеристика поля, порядок простой подгруппы и x-координата базовой
точки приведены в шестнадцатеричном представлении.

Алгоритмы электронной подписи на эллиптических кривых  105

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

Описание задачи
Как было показано ранее, задача умножения точки на число решается быстро
даже для больших чисел.
Теперь рассмотрим обратную задачу. К примеру, мы уже посчитали Q = [k]P.
Насколько будет сложно вычислить число k, зная только лишь точки P и Q?
Решение такой задачи называется дискретным логарифмированием
в группе точек эллиптической кривой. Задача нахождения дискретного логарифма в группе точек эллиптической кривой (Elliptic Curve Discrete Logarithm Problem, ECDLP) является вычислительно сложной, так же, как и задачи
факторизации (Factoring) и дискретного логарифмирования в мультипликативной группе конечного поля (Discrete Logarithm Problem, DLP), но, в отличие от последних, имеет экспоненциальную сложность решения (напомним,
что для задач Factoring и DLP существуют субэкспоненциальные алгоритмы). Как следствие криптосистемы, построенные на эллиптических кривых,
более эффективны – они имеют гораздо более короткие длины ключей и работают быстрее.
Самый эффективный на данный момент метод для решения ECDLP называется ро-методом Полларда (Pollard’s ρ-method). Данный метод был изначально разработан Джоном Поллардом в 1978 г. для решения DLP, но после появления эллиптических кривых в криптографии его стали использовать и для
решения ECDLP.
Этот вероятностный алгоритм имеет сложность, зависящую от порядка q
подгруппы, которой принадлежат точки P и Q. Он требует порядка q операций с точками.
То есть, к примеру, для решения ECDLP на рассмотренной ранее кривой
secp256k1, которая имеет порядок простой подгруппы q приблизительно 2256,
сложность «взлома» будет ~2128, что невозможно на существующем этапе развития вычислительной техники.
Таким образом, стоимость (трудоемкость) решения ECDLP ро-методом Полларда составляет приблизительно q операций.

Атака методом Поллига–Хеллмана
Отметим, что оценка уровня безопасности кривой при помощи q применима только для случая, когда q – простое число, т. е. точки P и Q лежат именно
в подгруппе простого, а не составного порядка. Для групп составного порядка
существует атака методом Поллига–Хеллмана (Pohlig-Hellman), которая сводит решение ECDLP в группе составного порядка к решению нескольких ECDLP
в подгруппах простого порядка этой группы.
Например, если q – не простое число, а произведение нескольких простых
чисел (q = q1 ∗...∗ qn), то стоимость решения ECDLP оценивается как сумма стои­

106

 Глава 2

мостей решения ECDLP в подгруппах порядка q1, ... ,qn и может оказаться практически возможным решить ECDLP для данной группы, если получится решить
ECDLP для каждой из n подгрупп (например, методом Полларда).
Очевидно, что использовать в криптосистемах такую подгруппу составного
порядка было бы бессмысленно с точки зрения соотношения производительности и безопасности.
Кроме того, есть пара семейств кривых, когда метод Полларда требует большого количества вычислений, но атакующий может применить гораздо более
эффективные методы для вычисления дискретного логарифма:
 аномальные кривые (anomalus curves);
 кривые с малой степенью вложения (embedded degree).

Аномальные кривые
Аномальные кривые – это кривые, число точек которых равно числу элементов
поля, т. е. #E(Fp n) = pn.
Быстрый полиномиальный алгоритм для решения ECDLP для аномальных
кривых был независимо открыт рядом криптографов в середине 1990-х годов.

Кривые с малой степенью вложения
Степенью вложения для кривой E(Fp n) с большой простой подгруппой порядка
q называется минимальное натуральное число c, такое, что q делит без остатка
pc∗n – 1.
Что это значит? pc∗n – 1 – это число элементов в мультипликативной группе
поля Fpc∗n (из поля Fpc∗n, которое содержит pc∗n элементов, выбрасываем 0 и получаем pc∗n – 1 элементов – мультипликативную группу поля Fpc∗n; эта группа

).
обозначается при помощи звездочки: Fpc∗n

Существование делителя q у числа pc∗n – 1 (т. е. у порядка группы Fpc∗n
) говорит нам о том, что в ней существует подгруппа порядка q. Известная атака
MOV (Menezes-Okamoto-Vanstone) использует спаривания Вейля (Weil pairing),
о которых будет рассказано далее, билинейно отображает точки кривой в эле∗

менты Fpc∗n
и, таким образом, сводит задачу ECDLP к решению задачи DLP в Fpc∗n.
Таким образом, сложность атаки определяется наиболее эффективным методом решения DLP. На настоящий момент таковым является метод решета
числового поля NFS (Number Field Sieve).
К кривым с малой степенью вложения, в частности, относятся так называемые суперсингулярные кривые (кривые, у которых след Фробениуса равен
0 mod p). Для любой суперсингулярной кривой степень вложения ≤ 6. Чем это
плохо? Поскольку сложность NFS существенно зависит от числа элементов
поля, то чем меньше c, тем проще считать дискретный логарифм.
Для кривых над простым полем характеристики p из теоремы Hasse следует,
что след Фробениуса t по абсолютной величине должен быть ≤ 2∗ p. Поэтому
для суперсингулярных кривых над простым полем он может быть равен только 0, из чего следует, что для них число точек равно #E(Fp) = p + 1.
Очевидно, что самым легким случаем для атакующего была бы степень вложения, равная 1. Это может быть при t = 2: #E(Fp n) = pn +1 – t = pn +1 – 2 = pn – 1
(поскольку q (порядок большой простой подгруппы) всегда делит #E(Fp n), порядок всей группы точек).

Алгоритмы электронной подписи на эллиптических кривых  107
Когда можно использовать такие кривые? Очевидно, что начиная c определенного значения степени вложения метод NFS потребует выполнения такого
же числа операций, как и ро-метод Полларда. В настоящий момент считается
безопасным использовать на практике степень вложения, равную 12. Примером кривой с c = 12 является рассмотренная ранее кривая bn254.
Стоит отметить, что благодаря последним достижениям в развитии метода NFS на самом деле теоретическая стоимость MOV-атаки для кривой
bn254 стала существенно меньше (2100), чем стоимость ро-метода Полларда
(2127 = q операций).

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

Скрученные кривые Эдвардса
Скрученная кривая Эдвардса (twisted Edwards curve) над полем Fp n, где p > 3,
имеет следующую форму:
ex2 + y2 = 1 + dx2y2,
где коэффициенты e и d – такие, что ed (e – d) ≠ 0.
То есть коэффициенты e и d – оба ненулевые и имеют разные значения. Это
условие несингулярности кривой, аналогичное условию 4a3 + 27b2 ≠ 0 для формы Вейерштрасса. Из его выполнения следует, что групповой закон будет выполняться для всех ее точек (кроме того, сингулярные кривые не считаются
эллиптическими).
Групповой закон сложения точек для кривых в данной форме выглядит так:

 x y  x y
y y  e x1 x2 
(x1 , y1 )  (x2 , y2 )=  1 2 2 1 , 1 2
.
 x1 x2 y1 y2 
 1 x1 x2 y1 y2 1
Нулевая точка имеет координаты, равные (0, 1), в отличие от формы Вейер­
штрасса, где она не имеет координат в принципе.
Элемент группы точек, обратный элементу (x, y), равен (–x, y), т. е.
(x, y) + (–x, y) = (0, 1).
j-инвариант EeE,d скрученной кривой Эдвардса c коэффициентами e, d:
j (E

E
e ,d



16 e 2  14 ed  d 2
) 
4
ed  e  d 



3

.

Легко видеть самое важное отличие от группового закона для уравнения
Вейерштрасса: для формы Эдвардса используется одна и та же формула как
для сложения, так и для удвоения точек.

108



Глава 2

Это существенно упрощает ее практическую реализацию и усложняет так
называемые атаки по побочным каналам (side-channel attack), когда противник, имея доступ к устройству, производящему вычисления, может воспользоваться разницей в мощности потребления (или излучения) энергии процессором при удвоении и сложении точек и узнать ключевую информацию (или, по
крайней мере, предположить с высокой долей вероятности).
Самый простой практический пример атаки по побочным каналам – это
SPA (Simple Power Analysis): противник измеряет мощность потребления
электроэнергии каким-либо устройством (например, смарт-картой), которое
производит умножение какого-либо секретного числа на точку (к примеру,
генерирует ключевую пару и в процессе вычисляет открытый ключ, умножая
закрытый ключ на базовую точку, – см. описание процедур генерации ключевых пар далее).
Если измерения достаточно точны, то атакующий, зная алгоритм скалярного умножения (к примеру, пусть это будет бинарный алгоритм, который мы
описали ранее), может понять, в какие моменты времени карта выполняет сложение (мощность больше), а в какие – удвоение (мощность меньше), и по этой
информации легко узнать все биты секретного числа.
Таким образом, очевидно, что форма Эдвардса – это весьма удобная для
прикладных криптографических применений форма эллиптической кривой.
Единственное ограничение – кривая Эдвардса всегда имеет порядок, который
делится на 4. Это означает, что не все кривые в форме Вейерштрасса можно
преобразовать в форму Эдвардса.
К примеру, это невозможно сделать для описанной выше secp256k1 – «главной» кривой систем Биткойн и Эфириум, т. к. она содержит простое число точек. Такая же ситуация с кривой bn254, ведь это тоже кривая простого порядка.
Обратное же преобразование возможно всегда: любую кривую из формы
Эдвардса ex2 + y2 = 1 + dx2y2 можно преобразовать в форму Вейерштрасса y2 =
x3 +ax +b, их коэффициенты (a, b) и (e, d) связаны следующим образом:
a = s2 – 3t2;
b = 2t3 – ts2,
где s и t зависят от e и d:
e d
;
4
ed
t
.
6
s

Пусть у нас есть точка (x, y) на скрученной кривой Эдвардса EeE,d с коэффициентами e и d. Как ее преобразовать в соответствующую ей точку (xʹ, yʹ) на
кривой в форме Вейерштрасса EaW,b ?
Подобное преобразование (EeE,d → EaW,b ) точки (x, y) в точку (xʹ, yʹ) выглядит
так:
 1 y
1 y 
 x  , y    s 1  y  t , s (1  y )x  .



Алгоритмы электронной подписи на эллиптических кривых  109
Опишем обратное преобразование ( EaW,b → EeE,d ). Пусть имеется точка (xʹ, yʹ)
на кривой EaW,b , и мы хотим получить на кривой EeE,d соответствующую ей точку
(x, y), тогда:
 x  t x  t  s 
 x , y    y , x  t  s  .



Кривые в форме Монтгомери
Кривая в форме Монтгомери имеет следующий вид:
By2 = x3 + Ax2 + x,
где коэффициенты A и B таковы, что B ≠ 0 и A2 ≠ 4 (это условие того, что данная
кривая несингулярна).
Порядок группы точек кривой в форме Монтгомери всегда делится на 4,
как и для кривых в форме Эдвардса. Из этого, в частности, следует, что кривые
простого порядка (например, secp256k1 или P-256) невозможно представить
в форме Монтгомери.
Обозначим кривую Монтгомери с коэффициентами A, B как E AM,B. Ее
j-инвариант равен:



j E

M
A ,B



A
 256

2

3



3

A2  4

.

Опишем групповой закон сложения точек для кривой в форме Монтгомери.
Сумма двух точек Q1 = (x1, y1) и Q2 = (x2, y2) есть точка Q3 = (x3, y3), такая, что:
x3 = Bλ2 + (x1 + x2) – A;
y3 = λ(x1 – x3) – y1,

где λ вычисляется различным образом при сложении и удвоении точек:
 случай Q1 ≠ Q2, сложение точек:
y y
  2 1;
x2  x1
 случай Q1 = Q2, удвоение точки:
3 x 2  2 Ax1  1
  1
.
2By1
Любая кривая в форме Монтгомери бирационально эквивалентна (birationally equivalent) некоторой скрученной кривой Эдвардса. Обратное также верно.
Это означает, что эти кривые связаны друг с другом рациональными выражениями над их полем.
В точности то же самое нельзя сказать про связь между формой Вейер­
штрасса и Монтгомери (или Вейерштрасса и Эдвардса), т. к. эквивалентные
кривые в форме Вейерштрасса существуют для любой кривой Монтгомери
(или Эдвардса), но не для любой кривой Вейерштрасса можно найти эквивалентную кривую Монтгомери (или Эдвардса), поскольку для этого необходимо

110



Глава 2

выполнение условия, что число точек делится на 4, а кривые Вейерштрасса могут иметь порядок группы, не делящийся на 4.
Получение коэффициентов в форме Эдвардса для кривой в форме Монтгомери (E AM,B → EeE,d ):
A  2
;
B
A  2
.
d
B
e

Преобразование точек в форму Эдвардса (x, y) → (xʹ, yʹ):
 x x 1 

 x  ,y    y ,x  1  .




Обратное предыдущему преобразование точки из формы Эдвардса в форму
Монтгомери (xʹ, yʹ) → (x, y):
 1  y

1  y



 x ,y    1  y  , 1  y  x   .

 

Пример кривой в форме Монтгомери и ее перевод в форму Эдвардса
Рассмотрим в качестве примера кривую Curve25519.
Это кривая в форме Монтгомери y2 = x3 + 486662x2 + x над простым полем
с характеристикой p = 2255 – 19.
Опишем далее основные параметры данной кривой:
 коэффициенты:
𝐴 = 486662;
B = 1;

 порядок простой подгруппы:
q = 2252+ 27742317777372353535851937790883648493;
 cofactor = 8;
 общее число точек #E кривой Curve25519:
#E= cofactor ∗ q = 8 ∗
(2 + 27742317777372353535851937790883648493)=
=2255 +221938542218978828286815502327069187944;
252

 генератор подгруппы:
x = 9;
y = 20AE 19A1 B8A0 86B4 E01E DD2C 7748 D14C
923D 4D7E 6D7C 61B2 29E9 C5A2 7ECE D3D9
(шестнадцатеричное значение).

Алгоритмы электронной подписи на эллиптических кривых  111
Если Curve25519 перевести в скрученную форму Эдвардса, то мы получим:
–x2 + y2 = 1 – (121665/121666)x2y2.
Эта кривая чаще всего в литературе обозначается как Edwards25519.

2.3 Основные алгоритмы электронной подписи
Рассмотрим наиболее часто применяемые (в том числе в блокчейн-платформах) алгоритмы электронной подписи на основе эллиптических кривых.

2.3.1 Алгоритм ECDSA
Elliptic Curve Digital Signature Algorithm (ECDSA) – алгоритм электронной
подпи­си на эллиптических кривых, который является одним из вариантов известного алгоритма Эль-Гамаля. По сути, ECDSA – это аналог широко известного алгоритма DSA (Digital Signature Algorithm), в котором мультипликативную
группу конечного поля заменили на группу точек эллиптической кривой.
Алгоритм ECDSA включен во многие стандарты (ISO, IEEE, NIST и т. д.)
и де-факто является наиболее часто используемым алгоритмом электронной
подпи­си в мире криптовалют.
ECDSA включен в текущий стандарт электронной подписи США, которым является выпущенный NIST документ FIPS 186-4 Digital Signature Standard [130].
Помимо ECDSA, в этом документе есть еще и алгоритмы DSA и RSA, которые
также являются стандартами подписи США.
В следующую версию стандарта, FIPS 186-5, NIST собирается добавить
подпись Шнорра на скрученных кривых Эдвардса – EdDSA (Edwards-curve
Digital Signature Algorithm); данный алгоритм будет рассмотрен далее.
Схема ECDSA использует параметры эллиптической кривой (коэффициенты
a и b, поле кривой Fp n, базовую точку P порядка q) и хеш-функцию Hash.
Fp n, поле кривой для ECDSA, на практике чаще всего состоит из простого числа элементов, т. е. из всех чисел из интервала от 0 до p –1, и поэтому называется
простым полем (prime field) и обозначается как Fp или GF(p).
В вышеупомянутом стандарте FIPS, помимо кривых над простыми полями,
присутствуют также и так называемые кривые над бинарными полями (binary
field), т. е. полями F2n, которые имеют характеристику, равную двум, и состоят
из 2n элементов. Но по различным причинам (в том числе из-за того, что они
более медленные) они редко используются в реальной жизни.
Опишем основные операции алгоритма ECDSA.

Генерация ключевой пары ECDSA
Вход: параметры кривой.
Выход: закрытый ключ – число d, открытый ключ – точка Q.
Последовательность действий:
1) генерация закрытого ключа d – случайного целого числа в интервале 0
< d < q;
2) вычисление открытого ключа Q – скалярное умножение закрытого ключа d на базовую точку P:
Q = [d]P.

112

 Глава 2

Создание подписи ECDSA
Вход: параметры кривой и хеш-функция Hash, подписываемое сообщение m,
закрытый ключ d.
Выход: подпись (r || s) – конкатенация чисел r и s, таких, что 0 < r < q, 0 < s < q.
Последовательность действий:
1) хеширование сообщения m и вычисление остатка от деления на q:
e = Hash (m) mod q;
2) генерация случайного целого числа k в интервале 0 < k < q и вычисление
точки C = [k]P;
3) вычисление r = Cx mod q, где Cx – x-координата точки C. Если r = 0, то возврат к шагу 2;
4) вычисление k–1, мультипликативно обратного к k по модулю q (т. е. k–1 –
это такое число, что k–1 k = 1 mod q);
5) вычисление s = k–1 (e + dr) mod q. Если s = 0, то возврат к шагу 2;
6) подписью считается пара (r || s).
Проверка подписи ECDSA
Вход: параметры кривой и хеш-функция Hash, сообщение m, подпись (r || s) сообщения m, открытый ключ Q.
Выход: подпись верна / подпись неверна.
Последовательность действий:
1) проверка значений r и s подписи на принадлежность интервалу от 1 до
q –1. В случае если хотя бы одно из них лежит вне интервала, подпись
считается неверной;
2) хеширование сообщения m и вычисление остатка от деления значения
хеша на q: e = Hash (m) mod q;
3) вычисление s–1, мультипликативно обратного к s по модулю q;
4) умножение значения e, полученного на шаге 2, на s–1: u = es–1 mod q;
5) умножение значения r из подписи на s–1: ν = rs–1 mod q;
6) вычисление точки R = [u]P + [ν]Q;
7) вычисление r' = Rx mod q, где Rx – x-координата точки R;
8) если rʹ равно значению r из самой подписи (r || s), то подпись верна, иначе –
подпись неверна.

2.3.2 ГОСТ Р 34.10–2012
Алгоритм, определенный в ГОСТ Р 34.10–2012 [13], является национальным
стандартом электронной подписи Российской Федерации.
В документе ГОСТ Р 34.10–2012 указывается, какие типы криптонаборов
можно использовать:
1) порядок простой подгруппы q: 2254 < q < 2256 и хеш-функция Стрибог (описана в разделе 1.3) с 256-битовым выходным значением;
2) порядок простой подгруппы q: 2508 < q < 2512 и хеш-функция Стрибог
с 512-битовым выходным значением.
Параметры кривых в самом тексте стандарта не указываются (их можно
найти в рекомендациях технического комитета по стандартизации ТК-26, на-

Алгоритмы электронной подписи на эллиптических кривых  113
пример в документе Р 1323565.1.024–2019 «Параметры эллиптических кривых
для криптографических алгоритмов и протоколов» [43]). В ГОСТ Р 34.10–2012
есть только тестовые примеры кривых, описывающие оба случая (так называемые «короткий» и «длинный» варианты), которые позволяют проверить, правильно ли была реализована подпись в коде программы.
Основное отличие ГОСТ Р 34.10–2012 от своего предшественника
ГОСТ Р 34.10–2001 [12] заключается в том, что в нем используется хеш-функция
Стрибог (определенная в ГОСТ Р 34.11–2012 [15]) вместо устаревшей, определенной в ГОСТ Р 34.11–94 [14]. Кроме того, в текущем стандарте появилась возможность использовать гораздо более безопасные (но, соответственно, более
медленные) криптонаборы с q: 2508 < q < 2512.
Таким образом, российский стандарт ЭП предлагает два уровня безопасности:
 если нам необходим 128-битовый уровень безопасности (т. е. для атакующего необходимо ~2128 операций, чтобы по открытому ключу найти
закрытый), то следует использовать криптонабор с кривой, у которой
порядок подгруппы q: 2254 < q < 2256 в сочетании с вариантом Стрибога
с 256-битовым выходным значением;
 в случае необходимости 256-битового уровня безопасности предполагается использовать кривую с q: 2508 < q < 2512 и Стрибог с 512-битовым
выходным значением.

Генерация ключевой пары ГОСТ Р 34.10–2012
Вход: параметры кривой.
Выход: закрытый ключ – число d, открытый ключ – точка Q.
Последовательность действий:
1) генерация закрытого ключа d, случайного целого числа в интервале
0 < d < q;
2) вычисление открытого ключа Q: скалярное умножение закрытого ключа d на базовую точку P:
Q = [d]P.

Создание подписи ГОСТ Р 34.10–2012
Вход: параметры кривой, подписываемое сообщение m, закрытый ключ d.
Выход: подпись (r || s) – конкатенация чисел r и s, таких, что 0 < r < q, 0 < s < q.
Последовательность действий:
1) хеширование сообщения m и вычисление остатка от деления значения
хеша на q: e = Hash (m) mod q, где Hash (m) – хеш-функция Стрибог. Если
e равно 0, то e присваивается значение 1;
2) генерация случайного целого числа k в интервале 0 < k < q и вычисление
точки C = [k]P;
3) вычисление r = Cx mod q, где Cx – x-координата точки C. Если r = 0, то возврат к шагу 2;
4) вычисление s = (ke + dr) mod q. Если s = 0, то возврат к шагу 2;
5) подписью считается пара (r || s).

114

 Глава 2

Проверка подписи ГОСТ Р 34.10–2012
Вход: параметры кривой, сообщение m, подпись (r || s) сообщения m, открытый
ключ Q.
Выход: подпись верна / подпись неверна.
Последовательность действий:
1) проверка значений r и s подписи на принадлежность интервалу от 1 до
q – 1. В случае если хотя бы одно из них лежит вне интервала, то подпись
считается неверной;
2) хеширование сообщения m и вычисление остатка от деления значения
хеша на q: e = Hash (m) mod q, где Hash (m) – хеш-функция Стрибог. Если
e равно 0, то e присваивается значение 1;
3) вычисление ν = e–1 mod q;
4) вычисление z1 = sν mod q, z2 = (–rν) mod q;
5) вычисление точки C = [z1]P + [z2]Q;
6) вычисление r' = Cx mod q, где Cx – x-координата точки C;
7) если rʹ равно значению r из подписи (r || s), то подпись верна, иначе –
подпись неверна.

2.3.3 Некоторые особенности алгоритмов ECDSA
и ГОСТ Р 34.10–2012
Требования к генерации используемых случайных чисел
При создании практических реализаций ECDSA и ГОСТ Р 34.10–2012 необходимо особое внимание уделять качественной генерации случайных чисел не
только для генерации ключевых пар, но и для создания подписи (т. е. числа k
на шаге 2).
К примеру, для ECDSA широко известны случаи, когда в результате ошибок
программистов в функции подписи иногда генерировалось одно и то же значение k. Это приводило к возможности по двум подписям разных сообщений
вычислить закрытый ключ.
Действительно, пусть у нас есть две такие подписи ECDSA для разных сообщений m1 и m2. Тогда они будут выглядеть так: (r || s1) и (r || s2), т. е. у них будут
одинаковые r.
Таким образом, атакующему, для того чтобы получить приватный ключ d,
надо решить систему двух уравнений в поле Fq с двумя неизвестными d и k:
1
 s1  k  e1  dr mod q 
,

1
 s2  k  e2  dr mod q 

что достаточно элементарно.
Эта ошибка в реализации не раз «всплывала» на практике. Вот два самых
известных случая: такую ошибку хакеры нашли в игровой приставке Sony PlayStation 3 в 2010 г., что позволило ее взломать, а в 2013 г. похожая история случилась с рядом кошельков системы Биткойн (исследователи обнаружили, что
в некоторых подписях транзакций повторялись значения r).

Алгоритмы электронной подписи на эллиптических кривых  115
Кроме того, необходимо отметить, что отдельные реализации ECDSA
и ГОСТ Р 34.10–2012 могут оказаться уязвимыми даже для случаев, когда это
на первый взгляд не совсем очевидно.
Предположим, что генератор случайных чисел – качественный, но его реа­
лизация позволяет атакующему измерить число единиц в бинарном представлении k (например, это может произойти в результате атаки по времени
выполнения – timing attack). Тогда, накопив несколько сотен подписей и зная,
сколько битов было равно единице в разных k для каждого случая подписи,
атакующий может восстановить закрытый ключ.

Сжатие точек для кривых в форме Вейерштрасса
Для экономии пространства можно представить точку не в виде координат
x, y, а в сокращенной форме: в виде x-координаты и одного бита, как показано
далее.
Сжатие точки
Вход: точка (x0, y0) кривой y2 = x3 + ax + b над полем Fp.
Выход: (x0, bit).
Последовательность действий:
1) если y – четное число, то bit = 1;
2) иначе, т. е. если y – нечетное число, bit = 0.
Значение bit необходимо для однозначного восстановления y, т. к., зная
только координату x0, можно подставить ее в правую часть уравнения и решить
его относительно неизвестного y в поле Fp:
y2 = x03 + ax0 + b.
Поскольку решений всегда будет два: y1 и p – y1, то, чтобы определить, какое
из них соответствует изначальной точке (т. е. равно y0), нужен бит четности
(y1 и p – y1 не могут быть одновременно четными или нечетными).
Восстановление точки
Вход: сжатая точка (x0, bit), кривая y2 = x3 + ax + b над полем Fp.
Выход: точка (x0, y0).
Последовательность действий:
1) решаем уравнение y2 = x03 + ax0 + b mod p;
2) в зависимости от значения bit и четности решений y1 и p – y1 считаем изначальной точкой либо (x0, y1), либо (x0, p – y1).
Проще всего уравнение y2 = d над Fp решается, если p = 3 mod 4. В этом случае
его решения равны:
d

p+1
4

mod p

и
p  d

p 1
4

mod p.

Многие кривые, например secp256k1, имеют именно такой модуль p = 3 mod 4.

116



Глава 2

Формат представления сжатых точек может различаться в различных реализациях алгоритма ЭП. Например, в системе Биткойн применяется следующий
формат сжатия: бит четности кодируется как байт со значением 2 для четных
y и 3 для нечетных, который конкатенируется с x (т. е. сначала идет этот байт,
а потом 32 байта x).
Следует отметить, что четность – не единственный способ для кодировки y. К примеру, в бите можно закодировать «знак» y, т. е. интервал, где лежит
y: y ≤ (p – 1)/2 или y > (p – 1)/2. Ведь если взять любую точку (x, y), то y-координата
обратной ей точки (x, p – y) будет всегда по разные стороны середины диапазона (0, p – 1) (за исключением точки с y = 0).

2.3.4 Алгоритм EdDSA
EdDSA представляет собой детерминированную подпись Шнорра на скрученных кривых Эдвардса.
Детерминированность EdDSA заключается в том, что при подписи одинаковых сообщений на одном закрытом ключе всегда будет получаться одинаковая
подпись. Это важное отличие EdDSA от недетерминированных подписей типа
DSA, ECDSA, ГОСТ Р 34.10–2021 и др., где подобная ситуация означает серьезные проблемы в реализации и создает возможность утечки закрытого ключа
(т. к. тогда его легко посчитать, имея лишь две подписи от разных сообщений).
Алгоритм был создан международной группой криптографов (T. Lange,
D. Bernstein и др.) в 2011 г. и описан в статье [68].
EdDSA позиционируется как более безопасная и быстрая альтернатива ECDSA, т. к. он использует кривые Эдвардса и не требует генерации случайных чисел в процессе подписи, что позволяет избежать атак, аналогичных атакам на
ECDSA, которые были описаны выше (т. е. EdDSA по сравнению с ECDSA имеет
более высокую устойчивость к side-channel атакам и ошибкам/сбоям в генераторах случайных чисел).
Де-факто данный алгоритм электронной подписи на практике является стандартом и используется в очень большом количестве самых разных систем. Его детальное описание с тестовыми векторами можно найти
в RFC 8032 [152].
Известны также различные модификации алгоритма EdDSA. Одна из них –
алгоритм XEdDSA, который используется в мессенджере Signal.
В отличие от EdDSA, XEdDSA – это недетерминированный алгоритм ЭП, который основан на кривых Монтгомери. Он использует случайные числа, но
безопасность в данном случае не так критично зависит от качества генератора
случайных чисел, как в ECDSA, поскольку в XEdDSA случайные числа нужны
скорее для защиты от атак по побочным каналам.

Генерация ключевой пары EdDSA
Вход: параметры скрученной кривой Эдвардса:
 коэффициенты кривой;
 точка B – генератор подгруппы большого простого порядка q;
 кофактор – 2c (т. е. общее число точек кривой равно 2cq);
 поле кривой – Fp,

Алгоритмы электронной подписи на эллиптических кривых  117
а также хеш-функция Hash с 2b-битовым размером выходного значения, где
2b–1 > p (т. е. любой элемент поля Fp или точка в сжатом виде (y и бит знака x)
помещаются в b бит).
Выход: закрытый ключ – число d, открытый ключ – точка A.
Последовательность действий:
1) генерация закрытого ключа d, случайной последовательности длиной
b бит;
2) хеширование d: Hash(d);
3) представление выходного значения хеш-функции в бинарном виде
(h0, h1, … , h2b–1), где hi – i-й бит;
4) вычисление целого числа a из битов первой половины этой битовой последовательности (игнорируя ее первые c и последние 2 бита):
b 3

a  2b 2  2i hi;
i c

5) вычисление открытого ключа A:
A = [a]B.

Создание подписи EdDSA
Вход: параметры кривой, хеш-функция Hash, подписываемое сообщение m, закрытый ключ d.
Выход: подпись (R || s) – конкатенация точки R и числа s (где s: 0 ≤ s < q, а точка
R – в сжатом виде).
Последовательность действий:
1) вычисление r = Hash(hb ||…|| h2b–1|| m) mod q, где hb ||…|| h2b–1 – правая половина результата хеширования d;
2) вычисление R = [r]B;
3) вычисление s = (r + Hash(R || A || m) ∗ a) mod q, где точки R и A – в сжатой
форме;
4) подписью считается пара (R || s).

Проверка подписи EdDSA
Вход: параметры кривой и хеш-функция Hash, сообщение m, подпись (R || s) сообщения m, открытый ключ A.
Выход: подпись верна / подпись неверна.
Последовательность действий:
1) проверка, что R лежит на кривой, а s – в интервале 0 < s < q. Иначе –
подпись неверна;
2) вычисление h = Hash(R || A || m) mod q;
3) проверка равенства [2сs]B = [2с]R + [2сh]A;
4) если верно это равенство, то подпись верна, иначе подпись неверна.

Некоторые особенности алгоритма EdDSA
Если в результате ошибки в реализации или сбоя в вычислениях на шаге 1 подписи злоумышленник получит две подписи для разных сообщений с одним и тем

118

 Глава 2

же значением r, то он сможет посчитать закрытый ключ (как и в случае c атакой
на ECDSA), решив систему двух линейных уравнений с двумя неизвестными.
Действительно, пусть (R || s1) – подпись сообщения m1, (R || s2) – подпись сообщения m2.
Зная A, (R || s1), m1, (R || s2), m2, атакующий составляет и решает систему с неизвестными a и r:
s1

r

Hash R A m1 * a mod q

s2

r

Hash R A m2 * a mod q

– и получает закрытый ключ a.
Сжатие точки кривой в форме Эдвардса (y-координата и бит) отличается от
такового для кривой в форме Вейерштрасса (x-координата и бит).
На практике EdDSA чаще всего используется с кривой Edwards25519 и хешфункцией SHA-512. Если уровня безопасности в 128 бит кривой Edwards25519
недостаточно, то можно использовать кривую Edwards448 с уровнем безопасности ~224 бита:
x2 + y2 = 1 + (39082/39081)x2 y2 mod p,
где модуль кривой p = 2448 – 2224 – 1.
Порядок простой подгруппы для данной кривой:
q = 2446 – 8335 DC16 3BB1 24B6 5129 C96F DE93
3D8D 723A 70AA DC87 3D6D 54A7 BB0D,
где вычитаемое приведено в шестнадцатеричном виде.
Кофактор: 4 (т. е. 2c = 4).
Описание обоих кривых, как в форме Эдвардса, так и в форме Монтгомери,
есть в RFC 7748 [164].

2.3.5 Алгоритм BLS
Алгоритм электронной подписи BLS (Boneh-Lynn-Shacham) появился в начале
двухтысячных [87]. Данный алгоритм основан на спариваниях (см. далее) и обладает рядом весьма полезных свойств, которые отсутствуют у подписей из
семейств Эль-Гамаля и Шнорра, описанных ранее.
Основными параметрами алгоритма BLS являются:
 спаривание e;
 хеш-функция Hash2Point, отображающая сообщения m в точки группы G1.
Спаривание (pairing) e: G1 × G2 → GT представляет собой отображение пар,
состоящих из элементов групп G1 и G2, в группу GT . Коммутативные группы G1,
G2, GT имеют большой простой порядок q.
Спаривание e обладает следующими свойствами:
1) свойством билинейности, что означает выполнение равенств:
e(P + Q, R) = e(P, R) ∗ e(Q, R);
e(P, R + Q) = e(P, R) ∗ e(P, Q);

Алгоритмы электронной подписи на эллиптических кривых  119
2) свойством невырождености, которое означает, что для любого элемента
P ∈ G1 существует такой элемент Q ∈ G2, что e(P, Q) ≠ 1, и наоборот: для
любого Q ∈ G2 существует P ∈ G1 такой, что e(P, Q) ≠ 1.
В криптографических алгоритмах обычно используются группы G1 , G2, GT:
 G1 – подгруппа порядка q кривой E1 над полем Fp;
 G2 – подгруппа порядка q кривой E2 над полем Fpk, являющимся расширением поля Fp (где степень расширения k равна степени вложения (embedded degree), которая определяется тем, что q делит pk – 1, т. е. в мультипликативной группе поля Fpk есть подгруппа порядка q);
 GT – мультипликативная подгруппа порядка q поля Fpk.
Из первого из описанных выше свойств следует, что:
e([a]P, [b]Q) = e(P, [b]Q)a = e(P, Q)ab = e(P, [a]Q)b = e([b]P, [a]Q).

Генерация ключевой пары BLS
Вход: параметры спаривания:
 параметры кривой E1(Fp), содержащей подгруппу G1 простого порядка q
с базовой точкой P1;
 параметры кривой E2(Fpk), содержащей подгруппу G2 простого порядка q
с базовой точкой P2.
Выход: закрытый ключ – число d, открытый ключ – точка Pub ∈ G2.
Последовательность действий:
1) генерация закрытого ключа d, случайного целого числа в интервале
0 ≤ d < q;
2) вычисление открытого ключа Pub: скалярное умножение закрытого
ключа d на базовую точку P2 ∈ G2:
Pub = [d]P 2.

Создание подписи BLS
Вход: параметры кривой E1, хеш-функция Hash2Point, подписываемое сообщение m, закрытый ключ d.
Выход: подпись σ – точка ∈ G1.
Последовательность действий:
1) хеширование m: H = Hash2Point(m), H ∈ G1;
2) вычисление σ = [d]H.

Проверка подписи BLS
Вход: параметры спаривания и хеш-функция Hash2Point, сообщение m,
подпись σ сообщения m, открытый ключ Pub.
Выход: подпись верна / подпись неверна.
Последовательность действий:
1) хеширование m: H = Hash2Point(m), H ∈ G1;
2) проверка равенства: если e(H, Pub) = e(σ, P2), то подпись верна. Иначе –
подпись неверна.

120

 Глава 2

Выше был приведен случай, когда открытый ключ – «большой» т. к. точка
Pub ∈ G2, а подпись σ – «маленькая» т. к. из G1.
Иногда может быть удобнее сделать наоборот; для этого нужно поменять G1
и G2 местами: при генерации пары получим точку из G1, а при подписи заменяем хеш-функцию Hash2Point(m) с выходом – точкой из G1 на хеш-функцию
с выходом – точкой из G2. В результате получатся «маленький» открытый ключ
и «большая» подпись.

Агрегация подписей BLS
Вычисление спариваний – относительно медленная (в десятки раз) по сравнению с умножением точки на число операция. Давайте познакомимся с необычными свойствами BLS, которые в некоторых случаях делают ее вне конкуренции по сравнению с более быстрыми дальними «родственниками» по
эллиптической линии (ECDSA, EdDSA и т. д.).
Если есть n подписей различных сообщений на разных ключах, то это можно
представить как множество (σ1, Pub1, m1), …, (σn, Pubn, mn). Все эти подписи можно объединить в одну:
(σ1, ..., σn) → σ.

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

   i .
i 1

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

e  , P   e  H i , Pubi  ,
i 1

где:
 Hi = Hash2Point(mi);
 Pubi – открытый ключ, соответствующий сообщению mi;
 P – базовая точка подгруппы G2, т. е. P2, если подпись – «маленькая» (т. е.
все σi ∈ G1); иначе, при σi ∈ G2, P равна базовой точке P1 ∈ G1.
Итак, для проверки агрегированной подписи из n разных сообщений требуется вычислить n + 1 спариваний. Очевидно, что это быстрее, чем проверять по
отдельности все n BLS подписей, что потребует 2n спариваний.
В случае если все сообщения mi одинаковые, то стоимость проверки сокращается до двух спариваний. Действительно, пусть в этом случае все хеш-коды
Hi равны H, тогда благодаря свойству билинейности спариваний правая часть
уравнения проверки выглядит следующим образом:

Алгоритмы электронной подписи на эллиптических кривых  121
n


e
H
,
Pub


e
H
,
Pubi  .



i


i 1
i 1


n

Таким образом, надо проверить равенство всего двух спариваний:
n


e  , P   e  H , Pubi  .
i 1



При использовании этого алгоритма агрегации необходимо помнить, что
в чистом виде он уязвим к атаке типа «ключ мошенника» (rogue key), когда злоумышленник при помощи открытого ключа Pub1 другого участника формирует
используемый в атаке ключ такого вида:
Pub2 = [x]P – Pub1,
где x – случайное число, 0 < x < q , а P – базовая точка, принадлежащая подгруппе, к которой принадлежат открытые ключи.
Далее злоумышленник хеширует сообщение m: H = Hash2Point(m) и вычисляет σ = [x]H. После этого, предъявив σ, он может утверждать, что ничего не подозревающий владелец ключа Pub1 тоже подписал сообщение m, ведь
e(σ, P) = e([x]H, P) = e(H, [x]P) = e(H, Pub1 + Pub2).
Для того чтобы предотвратить такую атаку, можно потребовать от владельца каждого ключа предъявлять доказательство с нулевым разглашением (Zero
Knowledge Proof) того, что он знает реальный закрытый ключ, который соответствует его открытому ключу (т. е. что он обладает числом d, таким, что его
открытый ключ Pub = [d]P).

Глава

3

Основные принципы работы
блокчейн-технологий
В последнее время мы наблюдаем стремительный рост цифровых технологий.
Все больше на слуху актуальность использования блокчейн-технологий и распределенного реестра. Иногда обе эти технологии отождествляют.
Однако это неправильно. Блокчейн действительно является разновидностью распределенного реестра, но не каждый распределенный реестр является блокчейном. В общем случае распределенный реестр – это база данных,
распределенная между разными сетевыми узлами. Каждый узел принимает
и обрабатывает информацию независимо от других узлов. Кроме того, узлы
голосуют за обновления и принимают их большинством голосов в соответствии с определенным правилом (консенсусом). Как только консенсус достигнут, распределенный реестр обновляется, и последняя согласованная версия
реестра сохраняется в каждом узле.
Блокчейн (от англ. blockchain) дословно переводится как цепочка блоков
и представляет собой технологию построения распределенной базы данных,
в которой все данные связаны между собой по определенному принципу. Данные в блокчейне (в отличие от распределенного реестра) не могут быть удалены. Кроме того, все данные в блокчейн-системе объединяются в блоки, и уже
блоки связываются в единую цепь с помощью консенсуса.
Первым и самым известным применением технологии блокчейн стала
криптовалюта биткойн (bitcoin). Впервые мир услышал о ней в 2008 году, когда
Сатоши Накамото (Satoshi Nakamoto) опубликовал статью «Bitcoin: A Peer-toPeer Electronic Cash System» [187], что можно перевести как «Биткойн: система
цифровой пиринговой наличности». Русcкоязычный перевод данной статьи
можно найти по ссылке [30]. Именно в работе Сатоши Накомото впервые описывались подходы к построению блокчейн-технологий.
Отметим, что до сих пор среди широкой общественности идут споры о том,
что скрывается за именем Сатоши Накомото. Есть разные версии, среди которых в том числе версия о том, что группа разработчиков ядра платформы
Биткойн взяла себе такой псевдоним.
Далее в этой главе мы рассмотрим основные структуры и механизмы, используемые в современных блокчейн-системах. Начнем с общего представления
структур безотносительно их использования. Это связано с тем, что каждая блокчейн-система по-разному определяет наполнение: какие данные и какого объема

Основные принципы работы блокчейн-технологий  123
будут записаны в базу данных, какие механизмы консенсуса будут использованы
для синхронизации узлов блокчейна, как часто происходит выработка блока, –
все это зависит от предназначения блокчейн-системы и от ее архитектуры.
Блокчейн-система не обязательно реализует криптовалюту. Это могут быть
любые системы, в которых требуется распределенное хранение данных. Например, можно использовать блокчейн для совершения сделок с недвижимостью. В этом случае все действия пользователей системы будут записаны в единый реестр. Никто уже не сможет удалить эти данные. Так же, как никто не
сможет их подменить или, наоборот, сказать, что таких действий не совершал.
После того как мы рассмотрим основные принципы работы блокчейн-систем, мы перейдем к более подробному рассмотрению наиболее известных
блокчейн-платформ, таких как Биткойн, Эфириум, Hyperledger, EOS. Блокчейнплатформы реализованы с использованием основных механизмов блокчейна,
но каждая по-разному. При этом они предоставляют пользователям возможность использовать свою архитектуру либо просто для работы с блокчейн-сетью, либо для разработки собственных блокчейн-решений.

3.1 Базовые механизмы блокчейн-систем
3.1.1 Транзакции
Транзакция – это единичная запись в базе данных блокчейна. Это может быть
запись о совершенном пользователем действии, запись о начислении пользователю денег (например, за то, что он создал блок, как это происходит для
механизма консенсуса Proof of Work, но об этом позже), запись о каких-то изменениях параметров системы и т. д.
Транзакция состоит из нескольких полей. В каждое поле записывается информация определенного типа. Например, в качестве полей могут быть записаны имя пользователя или адрес пользователя в блокчейн-сети, идентификатор
транзакции, временная метка, инструкции, по каким правилам обрабатывать
транзакцию, и т. д.
Содержимое транзакций зависит от предназначения блокчейн-системы
и архитектуры ее разработки. При проектировании будущей системы разработчик должен учитывать тот факт, что транзакции, попавшие в блокчейн, уже
никогда не смогут быть удалены оттуда. Об этом необходимо помнить при проектировании блокчейн-систем. Если транзакции будут объемными, а система
будет насчитывать много тысяч пользователей, то такая база данных в скором
времени обретет очень большой размер, что будет затруднять работу с ней.
В общем виде транзакцию можно представить так, как это сделано на рис. 3.1.
Данные
отправителя

[Дополнительные
данные]

Содержание
транзакции

Подпись

Рисунок 3.1. Общий вид транзакции

На рис. 3.1 мы можем видеть четыре основных поля. Данные отправителя – это те параметры, которые позволяют идентифицировать пользователя
в блокчейн-системе.

124

 Глава 3

В самом простом варианте в качестве параметра идентификации пользователя в сети может выступать открытый ключ пользователя. Однако в большинстве систем в целях экономии памяти для идентификации используется не сам
открытый ключ пользователя сети, а некоторая его производная (например,
хеш-код ключа).
Более того, для криптовалютных систем вообще рекомендуется каждый раз
использовать новую пару ключей. В этом случае затрудняется прослеживание
действий одного пользователя и, как следствие, трудно определить, каким количеством денег владеет тот или иной пользователь системы.
В зависимости от назначения транзакции она может содержать некоторые
дополнительные данные, такие как метка времени, время блокировки транз­
акции, специальные служебные поля и флаги, а также указание на то, какими
алгоритмами необходимо выполнить проверку подписи в транзакции.
Содержание транзакции также зависит от предназначения системы. Если
это криптовалютная система, то данные транзакции, скорее всего, будут содержать так называемые входы (денежные источники для совершения транз­
акции) и выходы (между которыми распределяются денежные источники), как
показано на рис. 3.2.
Индентификатор
транзакции

[Дополнительные
данные]

Входы

Выходы

Подпись

Рисунок 3.2. Пример структуры криптовалютной транзакции

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

[Дополнительные
данные]

Выходы

Подпись

Рисунок 3.3. Криптовалютная транзакция, содержащая только выходы

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

Основные принципы работы блокчейн-технологий  125
Как правило, в блокчейн-системе у каждого пользователя есть пара ключей, соответствующих используемому алгоритму электронной подписи. Открытый ключ (или публичный ключ, от англ. Public key) пользователя является идентификатором пользователя блокчейн-системы или применяется для
формирования адреса пользователя. В любом случае пользователь должен сообщить свой открытый ключ всем пользователям, с которыми он планирует
взаимодействовать.
Закрытый ключ (или приватный ключ, от англ. Private key) генерируется
пользователем при регистрации в системе. Ответственность за хранение закрытого ключа ложится полностью на плечи пользователя. Если закрытый
ключ будет утерян, пользователь не сможет совершать действия в блокчейнсистеме, так как утратит способность вырабатывать электронную подпись, соответствующую его открытому ключу.
Именно так, потеряв носитель с закрытым ключом или вследствие поломки носителя, многие пользователи теряют доступ к своим биткойнам (которые
в декабре 2020 года показали рекордный рост стоимости, а в первой половине
2021 г. данный рекорд был значительно превзойден).
Подпись транзакции записывается в специально отведенное для этого поле.
Существуют блокчейн-системы, в которых подписывается не вся транзакция,
а только та информация, которая имеет критически важное значение. Делается
это для того, чтобы не перегружать блокчейн-систему лишней работой.
Также важно отметить, что в настоящей главе мы рассматриваем базовые
принципы построения отдельных элементов блокчейн-системы. В реальной
жизни эти механизмы могут быть значительно сложнее. Например, в системе
Биткойн сохраняется не только электронная подпись, но и набор инструкций
для ее проверки.
На рис. 3.4 представлена общая схема формирования и проверки подписи
транзакции.
В качестве примера, проиллюстрированного на рис. 3.4, мы рассмотрим
некоторую транзакцию. Будем считать, что у создателя транзакции есть пара
ключей: открытый ключ Kpub и закрытый ключ Kpriv. При этом пользователь, создающий данную транзакцию, должен иметь возможность предоставить свой
открытый ключ тем пользователям или узлам, которые будут выполнять проверку подписи.
Итак, для того чтобы сформировать подпись, из всех полей транзакции сначала по заданному алгоритму формируется хеш-код H(m), а затем применяется
алгоритм электронной подписи, где данный хеш-код подписывается с использованием закрытого ключа автора транзакции Sig = Signing(H(m), Kpriv). Таким
образом формируется подпись Sig, которая и помещается в самое последнее
поле транзакции.
Для проверки подлинности транзакции выполняются следующие действия.
Прежде всего из всех полей транзакции, за исключением поля «Подпись», вычисляется хеш-код по заданному алгоритму H(m). Далее производится верификация подписи (Verification) на основе полученного хеш-кода H(m), значения
подписи Sig и открытого ключа отправителя Kpub. Алгоритм проверки напрямую зависит от используемого алгоритма электронной подписи.

126

 Глава 3
Закрытый ключ отправителя Kpriv

Формирование подписи
H(m)

Идентификатор
транзакции

Sig = Signing(H(m), Kpriv)

[Дополнительные
данные]

Содержание
транзакции

Подпись
Sig

[Дополнительные
данные]

Содержание
транзакции

Подпись
Sig

Проверка подписи
Идентификатор
транзакции

H(m)

Verification(Sig, H(m), Kpub)
Открытый ключ отправителя Kpub

Рисунок 3.4. Формирование и проверка подписи транзакции

Если подпись транзакции оказалась верной, это значит, что транзакция прошла проверку. То есть, во-первых, все данные, содержащиеся в транзакции, не
изменились с момента ее подписания, и, во-вторых, транзакция действительно сформирована пользователем, которому принадлежит закрытый ключ Kpriv,
соответствующий открытому ключу Kpub.
Более подробную информацию о процедурах создания и проверки элект­
ронной подписи в соответствии с различными алгоритмами можно найти во
второй главе настоящей книги.
В виде транзакции может быть представлен не только денежный перевод
или некоторое действие в системе, но и так называемый смарт-контракт.
Смарт-контракт – это тоже транзакция, которая имеет свой идентификатор,
подпись и инструкции, в соответствии с которыми программный код этого
контракта будет выполняться на виртуальной машине (конечно, это применимо только для тех блокчейн-систем, которые поддерживают работу со смартконтрактами). Смарт-контракты будут подробно описаны далее.
Все созданные и подписанные транзакции распространяются по блокчейнсети и попадают в так называемый мемпул (от memory pool – «пул памяти»),
в котором они ожидают попадания в формируемые блоки.
Необходимо упомянуть, что чем больше пользователей в блокчейн-системе, тем больше транзакций формируется пользователями и тем сложнее становится транзакциям попасть в блоки. Поэтому создатели транзакций могут
предлагать некоторое вознаграждение (комиссию) тому пользователю, который поместит транзакцию в новый блок. Чем выше комиссия, назначенная за

Основные принципы работы блокчейн-технологий  127
транзакцию, тем скорее транзакция попадет в блок (на примере системы Биткойн более подробно описано в главе 4).

3.1.2 Упаковка транзакций в блоки
Мемпул представляет собой область памяти, которая разворачивается на узлах, занимающихся созданием блоков. Как было сказано выше, в мемпул попадают сформированные транзакции для их последующей упаковки в блоки.
Прежде чем поместить транзакцию в свой мемпул, узел должен выполнить
ряд проверок, чтобы убедиться в валидости транзакции. В частности, узлу необходимо:
 проверить, что транзакция составлена правильно, соответствует синтаксису построения транзакций, содержит все обязательные поля и они заполнены правильными параметрами;
 если это финансовая транзакция, проверить, что списки входов и выходов транзакции существуют и не являются пустыми (бывают описанные
далее исключения); также проверяется, что сумма входов и выходов совпадает;
 убедиться, что размер транзакции в байтах меньше, чем максимально
допустимый размер блока (транзакции могут иметь различный размер);
 проверить, что данная транзакция ранее не была уже добавлена в пул
или в ранее сгенерированный блок; отклонить транзакцию, если она уже
есть в пуле или в системе;
 для финасовой транзакции проверить, чтобы ни один из ее входов не
ссылался на выход другой транзакции в пуле; в противном случае отклонить транзакцию;
 если транзакция использует выход Coinbase-транзакции (такие транз­
акции описаны далее), необходимо убедиться, что соответствующая
Coinbase-транзакция имеет минимум 100 подтверждений;
 если комиссия за транзакцию слишком мала для включения ее в блок,
узел может отклонить транзакцию.
Это основной, но далеко не полный перечень проверок, которые выполняются при добавлении транзакции в мемпул. Количество проверок напрямую
зависит от используемой блокчейн-системы и ее назначения.
Из мемпула транзакции собираются в блок. Размер блока зависит от того,
какие настройки прописал разработчик. Например, в системе Эфириум возможно создание пустого блока, если транзакции не произошло. Кроме того,
обязательно устанавливается верхний предел размера блока. Как правило, размерность блока задается в байтах.
Если в мемпуле находится небольшое количество транзакций, проблем
у майнера (создателя блока) не возникает. И наоборот, чем больше транзакций
в системе, тем сложнее майнерам создать блок, содержащий все транзакции.
При этом майнер не обязан добавлять в блок все ожидающие в мемпуле транзакции. В криптовалютных блокчейн-системах основным критерием
попадания транзакции в блок может являться размер вознаграждения: чем
большее вознаграждение майнеру прописано в виде комиссии за добавление
транз­акции в блок, тем быстрее эта транзакция попадет в блок.

128

 Глава 3

Транзакции, не попадающие в блок, накапливаются в пуле. Проблема накоп­
ления транзакций в пуле решается так: если для общего объема транзакций,
например, в 1 КБ общая сумма комиссий за все эти транзакции не превышает
некоторого минимального установленного порога, то эти транзакции удаляются из пула.
Если пул заполнился полностью, то также происходит автоматическое удаление транзакций, которые имеют малую комиссию. После этого в мемпул добавляются только те транзакции, у которых размер комиссии не меньше определенного порогового значения. Это позволяет бороться с переполнения­ми
мемпула, в том числе в результате различных вариантов атак на блокчейнсистему.
Узел, являющийся майнером и формирующий блоки цепочки, выбирает из
мемпула транзакции, которые он хотел бы добавить в блок. Сколько он будет
выбирать транзакций, зависит от размера блока, с которым он работает. Это
значение меняется от системы к системе. Если это новая система, в ней мало
пользователей, действия совершаются не очень часто, то в такой системе могут
формироваться пустые блоки из одной транзакции.
Например, если посмотреть на первые блоки в системе Биткойн или Эфириум, то можно увидеть, что все первые блоки содержат только одну транзакцию – вознаграждение майнеру за добытый блок.
Система также может отслеживать среднее количество транзакций в мемпуле и на основе этого автоматически устанавливать размер блока. Кроме
того, система может настраивать размер блока в зависимости от объема (веса)
транз­акций в мемпуле.
После того как все транзакции выбраны и проверены, из них начинает формироваться блок. Первое, что происходит с выбранными транзакциями, – из
них формируется дерево Меркля (описано в следующем разделе). Дерево Меркля используется по той причине, чтобы если возникнет необходимость пересобрать блок (добавив или удалив из него транзакцию), это можно было бы
сделать быстро и просто.
С помощью специальных обозревателей, например блокчейн-эксплорера (Blockchain Explorer), можно посмотреть состояние мемпула для наиболее
популярных криптовалют в различное время. При этом можно посмотреть
статис­тику как в объемах занимаемой памяти, так и в количестве транзакций.
Если сопоставить график изменения количества транзакций в мемпуле
с графиком изменения стоимости биткойна, можно увидеть, что эти графики поразительным образом похожи. Чем выше стоимость криптовалюты, тем
больше операций пользователи стремятся совершить, тем больше становится
объем мемпула (рис. 3.5).
Данные на рис. 3.5 получены с использованием соответствующих сервисов
блокчейн-эксплорера [32, 47], приведенные графики актуальны на сентябрь
2021 г.

Основные принципы работы блокчейн-технологий  129

Рисунок 3.5. Количество транзакций в мемпуле (вверху) и график стоимости биткойна (внизу)

130



Глава 3

3.1.3 Применение деревьев Меркля
при формировании блоков
Дерево Меркля и его использование в качестве надстройки над алгоритмами хеширования были описаны ранее в разделе 1.1.2. Дерево Меркля
(или дерево хешей) широко используется в блокчейн-системах, в частности
оно применяется в широко известных криптовалютных системах Биткойн
и Эфириум.
Принцип построения дерева в данных системах следующий. Листьями дерева являются хеш-коды всех транзакций, которые требуется собрать в единый блок. Хеш-коды транзакций группируются попарно, и из каждой пары
вырабатывается новый хеш-код. Если количество транзакций в блоке нечетное, то последняя транзакция дублируется.
Далее алгоритм повторяется, пока не останется один хеш-код, который называется корнем дерева Меркля (или корнем Меркля – Merkle root). Этот корень будет помещен в блок для проверки его целостности.
Что нам дает этот алгоритм? Допустим, что пока мысобирали блок, мы обнаружили, что в это же время другой майнер успел создать блок, в котором
задействована одна из наших транзакций. Это делает наш блок невалидным.
Следовательно, нам нужно удалить задействованную транзакцию из блока
и пересчитать хеш-код блока. Но благодаря дереву Меркля нам не нужно пересчитывать все хеш-коды, а необходимо пересчитать только хеш-коды тех
ветвей дерева, в которых использовалась удаленная транзакция.
На рис. 3.6 и 3.7 показаны примеры формирования дерева Меркля в случаях, когда возникают непарные значения. Например, если в блок помещается шесть транзакций, то на третьем уровне дерева Меркля останется хешзначение h9 без пары и его необходимо будет продублировать (рис. 3.6). А в
случае с пятью транзакциями в правой ветке дважды останется транзакция
без пары, и каждый раз ее необходимо будет дублировать (h5 и h8).
h1=h(A)

h2=h(B)

h3=h(C)

h7=h(h1,h2)

h4=h(D)

h8=h(h3,h4)

h10=h(h7,h8)

Корень Меркля

h5=h(E)

h6=h(F)

h9=h(h5,h6)

h11=h(h9,h9)

h12=h(h10,h11)

h9 дублируется
для получения h11

Рисунок 3.6. Дерево Меркля для шести транзакций в блоке

Основные принципы работы блокчейн-технологий  131
h1=h(A)

h2=h(B)

h3=h(C)

h6=h(h1,h2)

h4=h(D)

h7=h(h3,h4)

h9=h(h6,h7)

Корень Меркля

h5=h(E)

h5 дублируется
для получения h8

h8=h(h5,h5)

h10=h(h8,h8)

h11=h(h9,h10)

h8 дублируется
для получения h10

Рисунок 3.7. Дерево Меркля для пяти транзакций в блоке

Дерево Меркля позволяет клиентам системы самостоятельно проверять,
была ли транзакция включена в блок, на основе значения корня Меркля из заголовка блока и списка промежуточных хеш-кодов дерева.
Например, чтобы убедиться, что для схемы на рис. 3.6 транзакция D была
добавлена в блок, клиенту требуется только копия хеш-кодов h3, h6 и h10 в дополнение к корню Меркля, все остальные вычисления клиент выполнит самостоятельно. При этом клиенту не нужно ничего знать ни о каких других
транзакциях. Если бы все пять транзакций в этом блоке были максимального
размера, для загрузки всего блока потребовалось бы более 500 000 байт, но для
загрузки трех хеш-кодов и заголовка блока требуется всего 140 байт.
Две самые популярные на сегодняшний день платформы – Биткойн и Эфириум – используют дерево Меркля для упаковки транзакций в блок. При этом
для платформы Эфириум используется сразу три дерева Меркля: одно для
транзакций, второе для квитанций (квитанции содержат данные о результате выполнения транзакций, здесь речь идет о транзакциях смарт-контрактов)
и третье дерево – это дерево состояний.

3.2 Механизмы консенсуса
Под консенсусом мы будем понимать некоторое общее правило, установленное для конкретной блокчейн-системы, в соответствии с которым происходит
подтверждение записей в цепочке блокчейна для всех узлов сети.
В настоящий момент насчитывается более десятка различных консенсусов.
Пожалуй, самым распространенным и самым известным является консенсус
доказательства работы, который называется Proof of Work (PoW). Ниже мы рассмотрим основные принципы работы для самых известных консенсусов.

3.2.1 Консенсус доказательства работы Proof of Work
Идея, лежащая в основе механизма консенсуса с доказательством работы
(от англ. Proof of Work), заключается в следующем. Цепочка блоков совместно
поддерживается анонимными одноранговыми узлами в сети, поэтому требу-

132



Глава 3

ется, чтобы каждый блок имел доказательство, что в его создание был вложен
значительный объем работы.
Это необходимо для того, чтобы никто не мог изменить (быстро пересчитать и переписать) исходную цепочку блоков. Объединение блоков в цепочку
делает невозможным изменение транзакций, включенных в любой блок, без
изменения всех последующих блоков. В результате сложность модификации
конкретного блока увеличивается с каждым новым блоком, добавленным в цепочку, что усиливает эффект доказательства работы.
Вообще, понятие Proof of Work появилось задолго до появления блокчейнсистем вообще и системы Биткойн в частности. В 1992 году в работе [109] Синтия Дворк (Cynthia Dwork) и Мони Наор (Moni Naor) сформулировали следующую идею: «Чтобы получить доступ к общему ресурсу, пользователь должен
вычислить некоторую функцию: достаточно сложную, но посильную; так можно защитить ресурс от злоупотребления» [51].
В качестве подобных сложных вычислительных функций могут выступать
такие математические задачи, как факторизация числа (разложение произведения на два простых сомножителя), вычисление квадратного корня по модулю простого числа и некоторые другие.
Так, в качестве одной из самых распространенных задач для механизма консенсуса в блокчейн-системах (особенно это касается криптовалютных систем)
выступает задача поиска хеш-кода заданной сложности. В качестве сложности
обычно записывается некоторое количество нулей в начале или в конце последовательности. Так как символы хеш-кода можно представить в виде цифр
шестнадцатеричного числа, то в этом случае добавление каждого нуля к заданной сложности приводит к увеличению сложности в 16 раз.
Можно провести следующий эксперимент. Давайте откроем любой онлайн-калькулятор хеш-кодов, например https://emn178.github.io/online-tools/
sha256.html. Выберем алгоритм хеширования, например SHA-256. Введем
строку для расчета и получим ее хеш-код.
Если в качестве входных данных ввести имя Евгения (ввод с клавиатуры осуществляется в текстовом формате, после ввода данные представляются в двоичном виде в соответствии с ASCII-кодом), то получается следующий хеш-код
(в шестнадцатеричном виде):
5229cb8cc8c8cbb99a7b72e8efea0c195d22776ecf785029f0447f1957db01c8.
Теперь будем добавлять к имени в конце некоторое число, начиная с нуля, т. е.
фактически совершать перебор значений. Наша цель – определить такое число, при
котором в первом символе хеш-кода появится шестнадцатеричный 0 (табл. 3.1).
Таблица 3.1. Поиск заданного хеш-кода
Входное
сообщение

Хеш-код

Евгения

5229cb8cc8c8cbb99a7b72e8efea0c195d22776ecf785029f0447f1957db01c8

Евгения0

de13ecd2329fd4411017ff070b66cc3555e292152753b150e9cf764efef1b5c7

Евгения1

471054ff614d89f8d1da9be0f78418bdb680b6a2f5bc657f004e353f66ad328a

Основные принципы работы блокчейн-технологий  133
Окончание табл. 3.1
Входное
сообщение

Хеш-код

Евгения2

9193132b097df8235d07a9111467972a3eb33aef3e14fd62f5e963041eea8b8b

Евгения3

3a639225de56433daf47abe7dbd1402638c9c7e756332433ce9ef3483c31acf7

Евгения4

22e4c278433387b13a82e3648af0bdc58aa83e2feb802bb46b6199e4b15
4a295

Евгения5

d157086eccf9d6bb0ea5034f573feee725329aec94d9ddf57efc5370c1d1858c

Евгения6

769238c39df11de59fca27e30c19593a9b1cfcab0fcc7b6fe8fd699693d88262

Евгения7

9047c5aeb419178d9ebc8299e2105fa061f960110499aba4002fdb5f6e6372d9

Евгения8

2f87f217d553427c30343959e7914e0782b61b595d36615a40d9b9b1947e314e

Евгения9

9e3d1c0b899a75f7bcb887f9c83e6040218a413cf00b9e88120d5cdbfb729764

Евгения10

6cc831ace5b5b5229abeedf82f76cc08534dc1ef4ee9d538657ee159d507f782

Евгения11

b7907ba5187088149d0ba5533943022b761c98e63ae582948d6cf668646263ec

Евгения12

18d1f89f5dc276919540a88ece6bbd15668b749865b6578eed42f5f4fece9032

Евгения13

95034d35f70625f8038e3ccc4a895adab2591c4f0a020d623a4c1ae0bda0a5fb

Евгения14

bde845b11de00f37eea7d67bed4423c1967c43fa2675c7d98490ce8496825cb2

Евгения15

6c2c293ed376fd8fee31ea6170f3f31e3c9dc33bd398f59382ff77e63f806bd9

Евгения16

438622cf4acd310eaee112fb7f07f54772cd9710232a5d56876209334b04608d

Евгения17

882de9a5691be9ffe02bb5045cd2fcf6b37ec1e8c1636ac08a0e00ee1ccb9c1e

Евгения18

5b61c77dd9eedf930204e1703175f304b67c9de24effa1c88d61b241a79db1c4

Евгения19

06b604a3245191a36e112902fb85cb18250084dd97cf22ceaa2895e257fbf56d

Из табл. 3.1 видно, что при добавлении к имени значения 19 получился
хеш-код, у которого первый разряд равен нулю. Если мы продолжим поиски
и зададимся при этом целью найти значения, при которых хеш-код образует
2, 3, 4 нуля и т. д., мы получим результат, представленный в табл. 3.2.
Таблица 3.2. Поиск хеш-кодов разной сложности
Входное
сообщение

Хеш-код

Евгения19

06b604a3245191a36e112902fb85cb18250084dd97cf22ceaa2895e257fbf56d

Евгения1391

00b99a7114904a96f023a2bec6918b529c6e01c44998e8ef6d5b3545e465a6cd

Евгения7058

0006cf8d40169151aec82c19a9020786e77207c817731e3810fe31315abe69cc

134



Глава 3
Окончание табл. 3.2

Входное
сообщение

Хеш-код

Евгения
115446

0000bf801395c06cfb7a36b642fa9557ab91cf9dbdd6b5435929eaca5519f12f

Евгения
1501312

000004cd357c487d3053abab217f622772dd92b9757f61ae6798308e3fab8f5c

Евгения
14458172

0000006cb31ed737fd7a37f8d456ff8742037eb329eebc378325f374694e713d

Евгения
40211489

000000005c305d46291004f0299ff5c5a638c4f76400b7d5a3ff55c7ee66ea2c

Таким нехитрым способом мы рассмотрели механизм работы консенсуса
Proof of Work. Сложность работы определяется количеством нулей в искомом
хеш-коде. Чем больше нулей, тем сложнее поиск. Найденное значение, при
котором достигнута требуемая сложность, называется nonce. Процесс поиска
такого хеш-кода называется майнингом, а пользователь или узел, который выполняет майнинг, называется майнером.
Ряд известных криптовалют используют в своей архитектуре механизм
консенсуса Proof of Work: Биткойн, Эфириум, Litecoin, Monero, Zcash, Dogecoin
и многие другие.
У процесса майнинга для консенсуса Proof of Work есть ряд недостатков. Вопервых, этот процесс требует больших вычислительных ресурсов и, как следствие, большого потребления электроэнергии. Не секрет, что в соответствии
с законом Мура вычислительные ресурсы стремительно развиваются. Следовательно, должна увеличиваться сложность майнинга и потребляемая энергия.
Во-вторых, зная, какой алгоритм хеширования используют наиболее популярные криптовалютные блокчейны, производители выпускают специализированные устройства, которые справляются с решением задач майнинга гораздо
быстрее, чем это может сделать процессор обычного персонального компьютера.
Изначально для решения задач майнинга использовали графические процессоры (например, NVIDIA CUDA), а потом и вовсе стали выпускать интегральные
схемы специального назначения – ASIC (Application-Specific Integrated Circuit).
Как было показано ранее, механизм консенсуса Proof of Work является неэффективным из-за большого количества потребляемой энергии. Кроме того,
задачи майнинга усложняются с каждым годом, и поэтому майнеры уже давно
не майнят поодиночке, а объединяются в так называемые пулы. При этом существует возможность того, что пулы объединятся и тогда их вычислительный
ресурс позволит осуществить атаку 51 %, пересчитав всю цепочку блокчейна
и внеся в нее какие-то изменения.
Атака 51 % заключается в следующем: если в руках у злоумышленника сосредоточится больше вычислительных ресурсов, чем у всей остальной сети
(больше хотя бы на 1 %), тогда злоумышленник может построить новую цепочку блоков в соответствии со всеми правилами блокчейн-системы. В силу того,
что злоумышленник обладает более мощным вычислительным ресурсом, рано
или поздно он получит цепочку, длина которой будет больше основной цепи.

Основные принципы работы блокчейн-технологий  135
И в соответствии с правилами блокчейн-системы все пользователи системы
будут вынуждены ее принять как единственно истинную.
С другой стороны, когда блокчейн-система насчитывает большое количество
честных игроков, такая ситуация вряд ли случится. Честные игроки следят за
размерностью пула майнеров и предпринимают меры по снижению его активности при приближении к отметке 50 % [24]. Чем дольше существует блокчейнсеть и чем больше пользователей она насчитывает, тем надежнее сохраненная
в ней информация.
На базе консенсуса Proof of Work создан ряд протоколов, которые модернизируют подход Proof of Work и решают некоторые вопросы.
В качестве примера можно привести GHOST (Greedy Heaviest Observed Subtree – «самое тяжелое наблюдаемое поддерево») – алгоритм на базе Proof of
Work, упрощенная версия которого используется в платформе Эфириум.
Особенность GHOST в том, что алгоритм ориентируется не на самую длинную цепочку, а на количество блоков в дереве, образуемом текущей цепочкой.
Учитывается не только длина самой цепочки, но и блоки на разных ее высотах, т. е. в расчете показателей каждой цепочки фактически участвует дерево
и количество блоков, которое в нем находится. Это позволяет на более раннем
этапе выявить корректную цепочку блоков [91].

3.2.2 Консенсус доказательства владения долей
Proof of Stake
Это, пожалуй, второй по популярности консенсус. Более того, некоторые блокчейн-системы, использующие консенсус PoW, смотрят в сторону перехода на
Proof of Stake (PoS).
Впервые идея консенсуса Proof of Stake появилась в 2011 году в одном из постов известного форума bitcointalk.com. Спустя год, в 2012 году, она была реа­
лизована в криптовалюте, которая тогда называлась PPCoin, а сейчас известна
под именем PeerCoin.
В консенсусе Proof of Stake нет майнеров, но есть валидаторы. Валидаторы –
это пользователи блокчейн-системы, обладающие определенным ресурсом
(валютой). Чем больше у пользователя ресурсов, тем больше к нему доверия
и тем больше шансов, что он сможет создать новый блок.
Валидатор должен заблокировать на время часть своих ресурсов. Это своего
рода ставка, которая принесет вознаграждение в том случае, если блок, созданный валидатором, будет добавлен в блокчейн-сеть. При этом вознаграждение
пропорционально ставке. Так что чем выше заблокированная ставка, тем больше полученная валидатором комиссия.
С одной стороны, получается, что консенсус PoS удобнее, чем PoW, поскольку очевидна экономия вычислительного ресурса и электроэнергии. С другой
стороны, участие в создании новых блоков требует наличия у валидатора больших средств. А это значит, что включиться в процесс майнинга могут только те
участники, которые либо давно пользуются блокчейн-сетью и успели накопить
необходимый ресурс, либо состоятельные члены, которые могут позволить
себе вступить в игру с большим стартовым капиталом.
В случае с PoS для атаки 51 % пользователю придется скупить 51 % всех существующих монет. Это, в свою очередь, приведет к быстрому росту цен. Но,

136

 Глава 3

с другой стороны, возникает вопрос: зачем осуществлять атаку на сеть, в которую вложено большинство средств данного пользователя?
К популярным валютам, использующим механизм консенсуса Proof of Stake,
относятся такие, как Vcash, Peercoin, Stratis, BitBay, Qtum и Waves.
Для консенсуса Proof of Stake существует уязвимость, которая называется
«Нечего терять» (Nothing at Stake). Пользователи, которым нечего терять, могут
начать поддерживать любые блоки (в том числе и злонамеренные). Для решения этой проблемы пытаются придумать различные алгоритмы.
Известно, что платформа Эфириум давно планирует переключиться с консенсуса PoW на консенсус PoS. Однако для того, чтобы это сделать, разработчикам сначала необходимо обойти проблему «Нечего терять». Для этого предлагается использовать специальный протокол Каспер (Casper) [117]. Данный
протокол отслеживает нечестных игроков системы и блокирует их, обнуляя
совершенные ранее ими действия.

3.2.3 Консенсус на основе решения
задачи византийских генералов
Задача византийских генералов широко используется в различных протоколах, в том числе и криптографических. Суть задачи сводится к следующему.
При ведении боевых действий различными армиями командуют различные
генералы. От слаженности действий генералов зависит успех их армии.
Для координации действий генералы получают указания из центра. Если все
генералы отдают единогласный приказ «В атаку!», то их армии выигрывают
сражение. Если генералы отдают единогласный приказ «Отступать!», то они
сохраняют свою армию и имеют время для дальнейшего маневра. Но если генералы начинают отдавать разные (несогласованные) приказы, то такая армия
проигрывает сражение. Фактически она будет уничтожена по частям.
Несогласованность действий генералов может иметь две причины: либо
генерал является предателем и не выполняет приказ штаба, либо кто-то намеренно изменил послание для генерала, и он получил ложный приказ. Для
того чтобы генералы имели возможность согласовывать свои действия, было
предложено несколько различных схем. Именно эти схемы и легли в основу
консенсусов для блокчейн-сетей. Рассмотрим их более подробно.
Алгоритм консенсуса, основанный на классической задаче византийских
генералов (Byzantine Fault Tolerance, BFT) [107], сводится к тому, что система
продолжит устойчивую работу даже в случае, когда один или несколько узлов
будут принимать решения, не совпадающие с решением большинства узлов.
Различают несколько вариаций, которые описаны далее.

Практическая задача византийских генералов
Алгоритм, основанный на практической задаче византийских генералов (Practical Byzantine Fault Tolerance, PBFT) [92], – это один из первых алгоритмов,
который был разработан для решения сформулированной задачи.
Идея заключается в том, что в системе существует ряд валидаторов, которые
принимают решение о подтверждении транзакции (по сути, администраторы
системы) [8]. Каждый валидатор должен выполнить ряд проверок и опросить

Основные принципы работы блокчейн-технологий  137
по очереди все другие узлы, чтобы принять решение, действительна транзакция или нет. Для того чтобы произошло подтверждение, необходим положительный ответ от 2/3 общего пула участников.
Принятое решение пересылается в сеть, с тем чтобы другие валидаторы получили к нему доступ. Консенсус достигается на основании ответов всех валидаторов. Такой алгоритм работы эффективен при низких задержках сети. Однако работа алгоритма усложняется при росте количества участников сети, так
как каждое сообщение влечет за собой множество других запросов и проверок.
В настоящий момент практическая задача византийских генералов PBFT используется в следующих системах: Hyperledger, Chain, Dispatch.

Федеративное византийское соглашение
Федеративное византийское соглашение (Federated Byzantine Agreement,
FBA) [138] является другим способом решения поставленной задачи.
Основа метода заключается в том, что каждый доверенный узел отвечает
за свою собственную цепочку. Поэтому он проверяет все сообщения, для того
чтобы установить истину. Такой подход позволяет расширять сеть, пользователи могут без проблем присоединиться к сети.
Валидаторы могут быть назначены владельцами сети или выбираться из существующих пользователей системы. В любом случае, по федеративному византийскому соглашению транзакции подтверждаются определенным количест­
вом валидаторов, выбранных из активных в данный момент пользователей.
В настоящий момент федеративное византийское соглашение используется
в таких системах, как Stellar и Ripple.

Делегированная задача византийских генералов
Делегированная задача византийских генералов (Delegated Byzantine Fault
Tolerance, DBFT) [97] в полной мере реализует классическую задачу генералов. При этом система продолжает работать даже в том случае, если некоторые
участники находятся офлайн.
Делегированная модель была предложена как вариант модели BFT, для которой возникают проблемы скорости верификации при увеличении числа
пользователей системы. В данном случае назначаются валидаторы (те, кому
делегируются права), которые проверяют транзакции, прежде чем переслать
их другим узлам системы. Если делегированный участник окажется скомпрометирован, то остальные участники системы легко могут заменить его, делегировав на его роль другого участника.
Несмотря на то что протокол ориентирован на открытое использование, делегирование полномочий все же ведет к некоторой централизации.
Наиболее известным случаем применения протокола DBFT считается платформа NEO. Однако при этом в платформе NEO внесена модификация в алгоритм консенсуса таким образом, что участники системы не только выбирают (делегируют) валидаторов, но, кроме того, получают некоторый токен как
часть доли от дохода выбранного ими валидатора.

3.2.4 Другие механизмы достижения консенсуса
Коротко рассмотрим прочие механизмы достижения консенсуса.

138

 Глава 3

Консенсус на основе доказательства деятельности
Механизм консенсуса на основе доказательства деятельности Proof of Activity
(PoA) использует элемент централизации.
Для проверки транзакций и блоков используются определенные выбранные
узлы системы. Это, с одной стороны, дает большую пропускную способность
системе. С другой стороны, уходит в сторону от основного постулата блокчейна – децентрализации.
Как следствие консенсус PoA можно использовать только в частных (приватных) блокчейнах. Тем не менее данный механизм реализован в тестовой сети
платформы Эфириум, которая называется Kovan [19].

Консенсус на основе доказательства сжигания монет
Консенсус на основе доказательства сжигания монет Proof of Burn используется, например, в криптовалюте Slimcoin.
В основе консенсуса Proof of Burn лежит фактически та же идея, что и в основе консенсуса Proof of Work, – необходимо доказать сообществу свою приверженность сети. Только если в случае с Proof of Work пользователи делают
это с помощью сжигания большого количества энергии и майнинга с использованием специального оборудования, то в случае с Proof of Burn пользователи
доказывают свою приверженность, фактически растрачивая свои деньги.
Под сжиганием понимается отправка денег на несуществующий адрес сети,
то есть туда, где их никто не сможет потратить. Считается, что, сжигая свои монеты, пользователи как бы вкладываются в виртуальную майнинговую мощность. Таким образом, чем больше монет сжег пользователь, тем больше виртуальных ресурсов он получил и тем быстрее он сможет создать блок.

Консенсус на основе использования направленного ациклического графа
Направленный ациклический граф (Directed Acyclic Graph, DAG) работает без
учета структуры блоков самого блокчейна. Он асинхронно проверяет транзакции. По принципу DAG построены такие криптосистемы, как Iota, Hashgraph,
Raiblocks/Nano.
Направленный ациклический граф состоит из направленных ребер, которые не имеют циклов. Подобные графы применяют, в частности, в технологии
хеш-графов (hashgraph), которые позиционируются как улучшенный аналог
блокчейн-систем [58].
Одним из самых известных механизмов консенсуса на основе направленного ациклического графа является консенсус Tangle, предложенный для
Iota [200].
Для того чтобы иметь возможность сделать отправку транзакции в общую
сеть, вы должны подтвердить две предыдущие транзакции. Фактически проверка два к одному должна дать уверенность в стабильности работы системы.
В то же время теоретически существует возможность проведения атаки, если
какой-то узел сможет сгенерировать треть от всех транзакций. Поэтому для
предотвращения подобных атак в системе Iota была предусмотрена повторная
проверка специальным координатором системы. При этом разработчики системы говорят о том, что это вынужденная мера, которая будет устранена, как
только сеть станет достаточно большой.

Основные принципы работы блокчейн-технологий  139

Консенсус на основе доказательства времени ставки
Еще одной из разновидностей консенсуса является консенсус на основе доказательства времени ставки (Proof of Stake Time, PoST), который определяет валидаторов не в соответствии с их текущим состоянием, а в соответствии с тем
временем, в течение которого они этим состоянием владеют. То есть в данном
случае чем дольше пользователь является держателем ресурсов, тем больше
у него шансов создать блок.
Одним из примеров использования консенсуса на основе доказательства
времени ставки является система Vericoin.

Делегированное доказательство доли владения
Делегированное доказательство доли владения (Delegated Proof of Stake, DpoS)
является еще одной разновидностью консенсусов на основе владения.
Несмотря на схожие названия, DpoS сильно отличается от консенсуса PoS.
В DpoS владельцы больших состояний не выполняют самостоятельно проверку
блоков и транзакций. Они участвуют в выборе делегатов, которые будут выполнять проверку.
Количество делегатов может различаться в разное время и в среднем находится в диапазоне от 20 до 100. При этом делегаты периодически переизбираются. Так как делегатов сравнительно немного, то система работает достаточно быстро.
Использование консенсуса делегированного доказательства доли владения
DpoS характерно для EOS, BitShares, Steemit.

Транзакция как доказательство доли
Транзакция как доказательство доли (Transaction As Proof of Stake, TAPoS) – механизм, который является по своей сути функцией программного кода платформы EOS.
Алгоритм построен таким образом, что для каждой транзакции в системе
используется хеш-код последнего блока цепочки, что в свою очередь позволяет
избежать повтора транзакций в сети и отследить, не находится ли транзакция
в форке от основной цепочки. Все это сделано для того, чтобы противостоять
возможным атакам злоумышленников.
Помимо перечисленных выше консенсусов, существуют и другие, такие как,
например, консенсус на основе доказательства веса Proof of Weight (PoWeight)
или консенсус на основе доказательства важности (Proof of Importance). Однако
данные консенсусы пока не имеют широкого распространения и выдвигаются
больше как предположения. Хотя, несомненно, находятся блокчейн-системы,
которые пытаются реализовать такие консенсусы.

3.3 Выстраивание цепочки блоков
3.3.1 Принципы формирования цепочки
Основным элементом структуры блокчейн-системы является блок. Единичные действия пользователей, записанные в виде транзакций, попадают в блокчейн-систему не поодиночке, а скомпонованными группами – блоками.

140

 Глава 3

То, как формируется блок, зависит от каждой конкретной блокчейн-системы.
Так, например, если в сети нет активных пользователей, то могут формироваться пустые блоки, а могут не формироваться и находиться в ожидании поступ­
ления транзакций. Транзакции, которые поступают от пользователей, могут
упаковываться в блоки по мере поступления (если это не финансовая блокчейн-система), а могут выбираться из общего пула транзакций в соответствии
с суммой назначенной комиссии (если это криптовалютная блокчейн-система).
Для облегчения работы с блоком для всех транзакций вычисляется корень
дерева Меркля (как было описано ранее), который и помещается в блок. За счет
такого подхода во время майнинга не нужно вырабатывать хеш-коды от всех
транзакций, достаточно обработать все служебные поля, которые в том числе
содержат корень дерева Меркля.
Максимальный размер блока может варьироваться от системы к системе.
Обычно задается максимально допустимый размер в байтах. В этом случае
в блок каждый раз может быть помещено разное количество транзакций, так как
объем одной транзакции может значительно меняться в зависимости от количества данных (например, от количества входов и выходов в системе Биткойн).
В любом случае структура блока имеет некоторые схожие механизмы, одинаковые для всех блокчейн-систем. Блоки должны быть связаны в единую цепь,
которую незаметно нарушить злоумышленнику будет очень проблематично.
Для этого используется один из механизмов консенсуса, разобраный выше.
В данном разделе при рассмотрении принципов формирования цепочки
блоков мы будем анализировать механизм консенсуса, основанный на доказательстве работы, то есть механизм Proof of Work. Рассмотрим цепочку блоков,
представленную на рис. 3.8.
000...0000

000...efbc

000...5b3d

000...22ce

[Служебная
информация
Номер блока 0]

[Служебная
информация
Номер блока 1]

[Служебная
информация
Номер блока 2]

[Служебная
информация
Номер блока 3]

Транзакции

Транзакции

Транзакции

Транзакции

nonce

nonce

nonce

nonce

000...efbc

000...5b3d

000...22ce

000...536f

Рисунок 3.8. Пример построения цепочки блоков

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

Основные принципы работы блокчейн-технологий  141
В зависимости от блокчейн-системы принцип работы консенсуса PoW может различаться. Разработчики устанавливают среднее время, в течение которого должен быть сформирован новый блок. Так, например, в системе Биткойн
каждый блок формируется раз в примерно 10 минут. При этом система периодически производит проверку времени выработки блока и в случае необходимости усложняет (увеличивает число нулей) или, наоборот, упрощает (уменьшает число нулей) решаемую задачу.
Так как со временем вычислительные ресурсы совершенствуются и наращивают производительность, то на примере биткойна можно увидеть, как изменилась сложность вычисляемого хеш-кода, притом что скорость формирования одного блока осталась неизменной – все те же 10 минут.
Проиллюстрируем различную сложность формирования блоков для разных
периодов времени. На рис. 3.9 изображен одиннадцатый блок (нумерация идет
от нуля) системы Биткойн [5], созданный 9 января 2009 года. Для его формирования требовался хеш-код всего с восемью нулями.

Рисунок 3.9. Одиннадцатый блок системы Биткойн

Для сравнения на рис. 3.10 показан один из относительно недавних блоков.
Блок с номером 717 000 был создан 3 января 2022 года, и его сложность составила 19 нулей [6].
Как было сказано выше, добавление одного нуля увеличивает сложность
в 24 раза (так как хеш-коды представлены в шестнадцатеричном виде). Таким
образом, увеличение сложности с 8 до 19 нулей увеличило общую сложность
в 244 раза!

142

 Глава 3

Рисунок 3.10. Блок системы Биткойн № 717 000

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

3.3.2 Ветвления цепочки блоков
Форки
В предыдущих разделах мы рассмотрели, как формируются транзакции, как
транзакции упаковываются в блоки и как блоки выстраиваются в цепочку.
Логичным образом возникает вопрос: а что случится, если два совершенно разных майнера одновременно создадут блок, который может быть добавлен в цепочку? То есть они сформируют блок по всем правилам: соберут транзакции и определят nonce, который позволит получить требуемое количество нулей. Что тогда?
А тогда возникает ситуация, которая в терминологии блокчейн-систем называется форк, то есть «вилка». Фактически система разделится на две абсолютно правильные цепочки.

Основные принципы работы блокчейн-технологий  143
Те участники системы, которые получат первыми блок первого пользователя, проверят его и добавят в свою цепочку. Но тогда, получив блок второго
пользователя, они забракуют его, так как хеш-код предыдущего блока не будет
равен тому значению, которое использовалось для выработки блока.
Наоборот произойдет с теми участниками системы, которые первыми получат блок, сформированный вторым пользователем. Они проверят его и добавят в свою цепочку. Но тогда, получив блок первого пользователя, они забракуют его.
Таким образом возникнет два совершенно правомочных состояния системы.
Система продолжит жить в расщепленном состоянии до тех пор, пока одна из
цепочек не станет длиннее. В этом случае все остальные узлы после проверки
должны будут принять ту цепочку, в которой оказалось большинство блоков.
Открытым остается вопрос: а что будет с теми транзакциями, которые были
помещены в блоки второй цепочки, той, что в итоге оказалась в меньшинстве?
Здесь есть два варианта решения вопроса.
В первом случае все транзакции будут признаны недействительными
и пользователям придется формировать такие транзакции заново. Второй
вариант – проверить, какие из этих транзакций смогли попасть в более длинную цепочку (ведь майнеры, формируя блоки, могут выбирать одни и те же
транз­акции), и вернуть в мемпул все те транзакции, для которых время жизни
еще не истекло и которые на данный момент не попали в основную цепочку.
На рис. 3.11 приведен пример форка.
000...0000

000...efbc

000...5b3d

000...22ce

[Служебная
информация
Номер блока 0]

[Служебная
информация
Номер блока 1]

[Служебная
информация
Номер блока 2]

[Служебная
информация
Номер блока 3]

Транзакции

Транзакции

Транзакции

Транзакции

nonce

nonce

nonce

nonce

000...efbc

000...5b3d

000...22ce

000...536f

000...5b3d

000...fd34

000...427a

[Служебная
информация
Номер блока 2]

[Служебная
информация
Номер блока 3]

[Служебная
информация
Номер блока 4]

Транзакции

Транзакции

Транзакции

nonce

nonce

nonce

000...fd34

000...427a

000...52de

Рисунок 3.11. Пример форка для блокчейн-цепочки

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

144

 Глава 3

известное непредумышленное расщепление для системы Биткойн содержало
в цепочке 6 блоков.
В системе Биткойн достаточно подождать, пока сформируется еще 100 блоков, для того чтобы можно было потратить монеты, полученные за создание
блока. Если мы знаем, что время формирования одного блока в среднем составляет 10 минут, то можно посчитать, что временной интервал между тем,
когда будут получены деньги за создание блока в системе Биткойн, и тем, когда
их можно начать тратить, составит в среднем 16 часов и 40 минут.
На остальные транзакции это условие не распространяется. Но всегда необходимо помнить о возможности существования форков и отката назад. Поэтому для верности лучше выждать какое-то количество блоков, чтобы точно быть
уверенным, что транзакция закрепилась в сети и ею можно распоряжаться.
В другой известной блокчейн-сети Эфириум ситуация с форками обстоит
совсем по-другому. Там время создания одного блока исчисляется не минутами, а секундами, поэтому большую часть времени сеть Эфириум находится
в расщепленном состоянии. Однако благодаря четко отлаженным алгоритмам
работы для узлов сети проблем обычным пользователям это не доставляет.

Софтфорки и хардфорки
Известен случай, когда в результате ошибки в смарт-контракте проект The
DAO лишился более 60 миллионов долларов [20], после чего сообщество
пользователей сети Эфириум разделилось на два лагеря. Одни хотели вернуться к первоначальному состоянию системы. Другие хотели, чтобы совершенные ими операции за время существования проекта The DAO остались
действительными.
В итоге было решено произвести форк цепочки Эфириум, в результате чего
было образовано два блокчейна: Ethereum и Ethereum Classic [49]. И здесь мы
подходим к другому понятию – хардфорк, или жесткое разделение.
Таким образом, форки в блокчейне могут быть также вызваны намеренным
изменением программного кода, отвечающего за работу системы. При этом
различают два вида форков: софтфорки и хардфорки.
В случае софтфорка программный код меняется незначительно. При софтфорке не требуется полная переконфигурация всех узлов сети, то есть можно
не производить обновление программного обеспечения. При этом обычно сохраняется обратная совместимость данных.
Это означает, что новые транзакции и блоки создаются по обновленным
правилам, но для проверки ранее созданных блоков учитываются старые правила. Также это означает, что новые введенные правила легко можно отменить
и вернуться к старой версии работы.
Обычно софтфорк применяется в том случае, когда требуется внести какиелибо исправления или усовершенствования в работу блокчейн-сети, но при
этом сохранить ее основные механизмы. Таким образом, софтфорки практически являются одним из штатных механизмов работы блокчейн-платформ.
Второй разновидностью форков является хардфорк. Хардфорк заключается
в серьезном изменении кода, вследствие чего узлы со старым программным
обеспечением перестают распознавать новые транзакции и блоки.
Хардфорк означает, что изменяются различные параметры формирования

Основные принципы работы блокчейн-технологий  145
цепочки, но в первую очередь изменяются правила формирования транзакций
и блоков. Это означает, что программное обеспечение, которое предназначено
для обработки исходной цепочки, не сможет далее поддерживать и обрабатывать цепочки, которые будут созданы в результате хардфорка.
Именно поэтому узлам блокчейн-системы так важно вовремя производить
обновление программного обеспечения. Использование узлов с устаревшим
программным обеспечением (необновленных узлов) может привести к распространению неверной информации и к возникновению одного из вариантов
разветвления цепочки.
Это, в свою очередь, может привести пользователей к серьезным финансовым потерям. Так, например, необновленные узлы могут пересылать и принимать транзакции, которые обновленными узлами считаются недействительными, и, следовательно, данные транзакции никогда не попадут в блоки
основной цепочки.
И наоборот, необновленные узлы могут забраковать валидные блоки основной цепочки и, как следствие, отказаться их ретранслировать. Все это будет приводить к потере или искажению информации. Хардфорк применяется
обычно тогда, когда разработчики хотят создать свою, новую криптовалюту на
основе одной из существующих.
Обзор известных софтфорков блокчейн-платформ Биткойн и Эфириум будет дан в главе 4 настоящей книги. Там же будут описаны механизмы обнаружения и противодействия нежелательным ветвлениям блокчейн-цепочки.

3.4 Смарт-контракт
Считается, что идея создания смарт-контракта (или «умного контракта», от
анг­лийского слова smart – умный) была впервые предложена Ником Сабо
(Nick Szabo) в 1997 году (по некоторым источникам, идея возникла еще
в 1996 или в 1994 году, но официальная статья была опубликована только
в 1997 г.) [232].
Идея состояла в том, что некий программный код будет отвечать за соблюдение условий сделки разными сторонами договора. Однако на тот момент не
существовало механизмов, готовых надежно обеспечить подобную идею. Все
изменилось с появлением блокчейн-систем. Достоинством блокчейн-системы
является тот факт, что, однажды попав в блокчейн, информация остается там
навсегда и никто не может внести в нее изменения.
Широко о смарт-контрактах заговорили с появлением и широким внедрением платформы Эфириум, где специально был продуман механизм помещения подобных контрактов в блокчейн. На сегодняшний день существует целый
ряд платформ, в которых возможна реализация смарт-контрактов.
Давайте рассмотрим более подробно, что же из себя представляет смартконтракт. В силу того, что в настоящий момент отсутствуют международные
правовые основы по использованию и применению блокчейн-технологий
вообще и смарт-контрактов в частности, в трактовках различных государств
можно встретить различные определения. Ассоциация IPChain в своем докладе по смарт-контрактам собрала более десятка определений для понятия
смарт-контракт в трактовке разными странами [150].

146

 Глава 3

В любом случае, все трактовки сходятся в одном мнении. Смарт-контракт
должен выполняться программно и автоматически проверять заданные для
него условия.
Однако многие ошибочно полагают, что смарт-контракт следит за наступ­
лением какого-либо события и срабатывает в случае его наступления. Например, родители хотят подарить своему ребенку (скажем, сыну Роману) на совершеннолетие некоторую сумму денег, которая сейчас есть на счете одного
из родителей (для определенности: на счете у папы). Общими словами мы можем сформулировать контракт так: «Дата рождения Романа 18 июля 2004 года.
Я (папа) перевожу N монет на счет контракта. Когда Роману исполнится 18 лет,
то перевести со счета контракта на счет Романа N монет».
Контракт подобного рода, помещенный в блокчейн-систему, не будет отслеживать текущую дату, для того чтобы перевести на счет Романа указанную
сумму денег. Данное событие произойдет только в том случае, если Роман обратится к смарт-контракту с запросом «Я Роман, мне исполнилось 18 лет, переведите, пожалуйста, деньги».
В этом случае контракт проверит, во-первых, что к нему действительно обращается Роман (это можно сделать путем проверки электронной подписи), вовторых, действительно ли с даты рождения Романа прошло 18 лет (здесь тоже
есть разные способы проверки, но самым распространенным способом является сравнение времени по юникстайму (Unix time – система времени, принятая
в операционных системах семейства Unix), а также сопоставление с длиной цепочки блоков, так как выработка блоков происходит систематически с определенной частотой). Если проверки будут пройдены успешно, то контракт создаст
транзакцию по переводу денежных средств со счета контракта на счет Романа.
В принципе, смарт-контракт может быть применим там, где в процесс принятия решений вовлечено более одного человека. Многие запросы от бизнессообщества могут быть реализованы с применением смарт-контрактов. Рассмотрим некоторые из них в качестве примеров.
Пример первый. Система купли-продажи, причем не важно, чего. Есть продавцы, готовые что-то продать, и есть покупатели, готовые купить. Покупатель вносит свои деньги, которые получает контракт (не продавец). Продавец
передает объект покупки покупателю. После подтверждения факта покупки
деньги со счета контракта переходят на счет покупателя. В контракте могут
быть предусмотрены различные условия. Например, скидки в зависимости от
объема продажи.
Пример второй. Система доставки скоропортящихся продуктов. Есть поставщик скоропортящихся товаров. Получатель товара вносит свои деньги на
оплату поставки, которые получает контракт (не поставщик). Товар можно
перевозить только с соблюдением особых требований (например, влажность
и температура). На всем пути транспортировки специальные датчики снимают
показатели окружающей среды и записывают в блокчейн. При поступлении
товара получателю цена рассчитывается автоматически с учетом показателей
датчиков. Так, если условия транспортировки не были нарушены, то получателю товара будет выставлена полная стоимость. Если же условия были нарушены, то цена может снизиться либо получатель вообще сможет отказаться от
получения, возможно, испорченного продукта.

Основные принципы работы блокчейн-технологий  147
Отличительной особенностью смарт-контрактов является автоматическая
проверка условий и уход от посредников. Хотя в случае возникновения спорных ситуаций могуттребоваться услуги арбитра – третьей независимой стороны, которая призвана решить возникший конфликт. Кроме того, контракт хранится в блокчейн-цепочке. Это значит, что он не может быть утерян и изменен,
что повышает доверие к нему и, с одной стороны, повышает его надежность.
С другой стороны, контракт – это все-таки программный код. Чем больше
объем кода, тем больше вероятность возникновения в этом коде ошибок. Существуют методы тестирования программных функций. Но и здесь все зависит
от человеческого фактора: будут ли при тестировании предусмотрены все возможные ситуации развития событий?
Так как чаще всего контракт связан с денежными операциями, то ошибка
программиста в данном случае может привести к большим потерям, связанным со многими пользователями блокчейн-сети. Наглядным подтверждением
этому служит упомянутая в предыдущем разделе история, связанная с ошибкой в смарт-контракте проекта The DAO, которая привела к потере более
60 миллионов долларов.
Вообще, принципы работы и реализации смарт-контрактов различаются
в зависимости от используемых блокчейн-платформ. Первые попытки реализовать смарт-контракты были предприняты для самой первой и самой известной криптовалюты – биткойна. Однако возможности смарт-контракта для системы Биткойн весьма ограничены (например, невозможно создание циклов,
т. е. они не являются полными по Тьюрингу) и в большинстве своем основываются на принципах построения и обработки транзакций определенного вида,
поэтому в данной системе нельзя реализовать все требуемые функции.
Для построения и обработки транзакций в системе Биткойн используется
сценарный (скриптовый) язык Script, предназначенный для обработки заданной последовательности действий. Более подробно способы реализации
смарт-контрактов для системы Биткойн будут рассмотрены далее в соответствующей главе.
Наибольшую популярность и распространение смарт-контракты получили с появлением блокчейн-системы Эфириум. В этой системе изначально
все было продумано для того, чтобы иметь возможность создавать смартконтракты [91].
Сам смарт-контракт является формой транзакции, которая помещается
в блокчейн. При этом обязательными атрибутами такой транзакции являются
два открытых (публичных) ключа. Один ключ формируется создателем конт­
ракта, а второй ключ формируется самим контрактом и фактически указывает
уникальный адрес контракта в системе Эфириум.
Любой пользователь системы Эфириум может обратиться к адресу контракта и тем самым инициировать его исполнение. Для исполнения контракта используется специальная виртуальная машина Эфириум (Ethereum Virtual Machine, EVM). Именно она интерпретирует код контракта, написанный на языке
Solidity, проверяет условия и формирует ответные транзакции как результат
работы контракта.
В смарт-контрактах системы Эфириум нельзя добавлять новые функции,
пос­ле того как контракт был активирован. Однако у программистов может оста-

148



Глава 3

ваться возможность изменять или удалять некоторые функции. Это возможно
только при указании в коде контракта специальной функции SELFDESTRUCT.
Важно помнить, что контракт в системе Эфириум не может сам по себе формировать транзакции или вызывать другие контракты. Он может это делать
только после того, как он был вызван (инициирован) одним из пользователей
системы.
Очень понятно и наглядно о механизмах работы смарт-контрактов рассказано в статье [21].
Смарт-контракты можно создавать и для других платформ. Так, например,
для платформы EOS контракты создаются на языке C++ или Rust. Во многом
функционал контрактов для платформы EOS может быть соотнесен с функцио­
налом системы Эфириум. Однако тот факт, что у этих платформ есть сущест­
венные отличия в принципах работы (консенсус, плата за транзакции и т. д.),
влечет за собой различные плюсы и минусы.
Поэтому выбор платформы и языка разработки должен напрямую зависеть от
желаний и возможностей заказчика. Подробный сравнительный анализ функциональных возможностей для EOS и Эфириум можно найти в статье [114].
Еще одна распространенная платформа – это приватный корпоративный
блокчейн Hyperledger Fabric. Эту платформу нельзя в полной мере назвать
децентрализованным блокчейном. С ее помощью можно создавать частные
(приватные) сети для сообществ, в которых достаточно высокий уровень доверия.
Платформа поддерживает создание смарт-контрактов на общеизвестных
языках, таких как Golang, JavaScript, Java. Смарт-контракт в терминах платформы называется чейнкодом (от англ. chaincode). В чейнкоде определяются правила формирования транзакций в зависимости от текущих условий. При этом
выполнение смарт-контракта производится одновременно на нескольких независимых узлах с проверкой полученных в итоге результатов.

3.5 Основные виды блокчейн-систем
Современные блокчейн-системы делятся на публичные (открытые) и приватные (частные), которые рассмотрим далее.

3.5.1 Публичный блокчейн
Как это легко понять из названия, публичные блокчейн-системы находятся
в открытом доступе, то есть представляют собой открытую сеть. Это означает,
что вся информация, записанная в блокчейне, находится в открытом доступе. Любой пользователь системы может просматривать, читать и записывать
данные в цепочке блоков, и эти данные доступны всем, в том числе тем, кто не
является непосредственным участником системы.
Так, например, вы можете просмотреть содержимое блоков для систем Биткойн или Эфириум с помощью, в частности, упоминавшегося ранее обозревателя блокчейн-эксплорер (https://www.blockchain.com/ru/explorer), даже не
являясь при этом непосредственным участником системы.
Публичный блокчейн является полностью децентрализованным, а значит,
ни один участник системы не может контролировать формирование новых

Основные принципы работы блокчейн-технологий  149
данных в системе или изменять уже имеющиеся данные. К отличительным
чертам публичного блокчейна можно отнести следующие:
 функционирование по принципу распределенного реестра: все узлы
в цепочке блоков имеют право участвовать в проверке транзакций;
 открытое чтение и запись данных: любая участвующая сторона может
читать, писать и просматривать данные в цепочке блоков;
 неизменность: после проверки записи ее нельзя изменить или удалить.
Публичный блокчейн целесообразно использовать в государственных секторах, таких как здравоохранение и образование. Например, медицинские
учреждения могут использовать технологию блокчейн, чтобы вести учет всех
своих операций. Врачи и другие специалисты могут добавлять данные о пациентах, стоимости лечения и других расходах.
При этом данные могут быть просмотрены всеми участниками блокчейна,
что обеспечивает прозрачность, однако однажды добавленные данные не могут быть изменены. Также необходимо помнить о необходимости защиты персональных данных. А это значит, что, например, пациенты или пользователи
в таких системах должны быть обезличены.
Одним из важных недостатков публичного блокчейна является значительная вычислительная мощность, необходимая для поддержки крупномасштабного распределенного реестра. Например, как было описано ранее, для достижения консенсуса каждый узел в сети должен решить сложную ресурсоемкую
криптографическую проблему, называемую доказательством работы (консенсус PoW), чтобы гарантировать синхронизацию всех узлов.

3.5.2 Приватный блокчейн
Самым главным различием между публичным и приватным (частным) блокчейном является определение того круга лиц, которому разрешено участвовать в работе сети, формировать новые блоки (то есть фактически выполнять
протокол консенсуса) и обеспечивать общую работу реестра. Если к публичному блокчейну может присоединиться любой желающий, то для приватного
блокчейна требуется приглашение, которое должно быть подтверждено либо
самим владельцем (инициатором) сети, либо набором правил, установленных
инициатором сети.
Компании, которые используют приватный блокчейн, обычно создают сеть
с контролем доступа, то есть заранее определяют, кому разрешено участвовать в сети и какие при этом транзакции может создавать данный участник
сети. Механизм контроля доступа к сети может варьироваться: существующие
участники могут определять будущих участников, регулирующий орган может
выдавать лицензии на участие или вместо этого решения может принимать
консорциум. При этом доступ к информации в определенных транзакциях будут иметь только те участники, которые указаны в данной транзакции, остальные участники не будут иметь к ней доступа.
Подобные блокчейн-системы также обеспечивают бóльшую масштабируемость с точки зрения пропускной способности транзакций. Наглядным примером реализации приватной блокчейн-инфраструктуры является платформа
Hyperledger Fabric от Linux Foundation, разработанная специально для удовлетворения корпоративных запросов.

150

 Глава 3

3.6 Криптовалютные кошельки
Для того чтобы хранить приватный и публичный ключи (т. е. чтобы фактически иметь доступ к имеющимся криптовалютным средствам), существуют
кошельки. Кошельки делятся на «горячие» и «холодные». Первый тип кошелька
позволяет мгновенно потратить ваш ресурс. Второй обеспечивает только хранение монет и ключей.
Кошелек может быть полноценным узлом системы, т. е. программой, которая
целиком выкачивает блокчейн-цепочку и с помощью которой пользователь, как
полноценный узел, всесторонне взаимодействует с блокчейн-системой. А может быть легковесный кошелек, который не содержит в своем составе полной
цепи блокчейна и обращается к доверенным узлам для совершения операций.
Программные кошельки могут быть как мобильными, так и десктопными
приложениями. Обычно они являются легковесными и используют безопасную передачу данных для формирования какой-либо транзакции.
Кошельки бывают также аппаратного типа (например, в виде USBустройства). Такие аппаратные кошельки обеспечивают надежное хранение
секретного ключа за счет использования многофакторной аутентификации.

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

Основные принципы работы блокчейн-технологий  151
адресами, после чего по запросу пересылать скрипт или адрес открытого ключа, используя одну из записей базы данных.
Чтобы избежать повторного использования ключей, программа должна отслеживать уже использованные ключи. Также программа должна отслеживать,
сколько осталось неиспользованных ключей, и вовремя восполнять их запас.
Это возможно сделать с использованием родительского открытого ключа для
создания дочерних открытых ключей.
Основным преимуществом программ-кошельков с полным спектром функций является легкость их применения. Нет необходимости иметь дополнительные средства для хранения ключей или распределения транзакций. Одна
программа делает все, что нужно пользователю, чтобы получать, хранить
и тратить монеты.
Однако в то же время у полнофункциональных кошельков есть и существенный недостаток, связанный с тем, что они хранят закрытые ключи на устройстве, подключенном к интернету. Компрометация таких устройств – нередкое
дело, и подключение к интернету позволяет легко передать злоумышленнику
закрытые ключи от взломанного устройства, а вместе с этим и права на управление всеми непотраченными выходами транзакций.
Чтобы защитить пользователя от кражи, многие программы-кошельки включают в себя функцию, позволяющую пользователям возможность шифровать
файлы кошелька, содержащие закрытые ключи. Это защищает закрытые ключи,
когда они не используются, но не может защитить от атаки, предназначенной
для захвата ключа шифрования или чтения расшифрованных ключей из памяти.
Для повышения безопасности закрытые ключи могут быть сгенерированы
и сохранены отдельной программой-кошельком, работающей в более безопасной среде. Эти кошельки могут быть использованы только для подписи и должны работать совместно с программой-кошельком, которая взаимодействует
с блокчейн-сетью.
Программы-кошельки, предназначенные только для подписи транзакций,
обычно используют детерминированное создание ключей. Это означает, что
из исходной (родительской) пары «открытый–закрытый ключ» в последующем создаются дочерние пары вида «открытый–закрытый ключ».
При первом запуске программа-кошелек, предназначенная только для
подписи транзакций, создает родительскую пару ключей и отправляет соответствующий родительский открытый ключ в программу-кошелек, работающую с сетью (сетевой кошелек).
Сетевой кошелек использует родительский открытый ключ для получения дочерних открытых ключей, при необходимости помогает их распространять, отслеживает выходы, потраченные на эти открытые ключи, создает неподписанные транзакции, расходующие эти выходные данные, и передает неподписанные
транзакции в программу-кошелек, которая предназначена только для подписи.
После необязательного шага проверки программа-кошелек, предназначенная для подписи, использует родительский закрытый ключ для получения соответствующих дочерних закрытых ключей и подписывает транзакции, возвращая подписанные транзакции обратно в сетевой кошелек.
Затем сетевой кошелек транслирует подписанные транзакции в блокчейнсеть [80].

152



Глава 3

3.6.2 Аппаратные кошельки
Аппаратные кошельки представляют собой устройства, предназначенные для
подписания транзакций. В отличие от автономных программ, которым требуется отдельное автономное устройство, не подключенное к сети, аппаратные
кошельки представляют собой специальное небольшое устройство, напрямую
подключаемое к сетевому кошельку. При этом обеспечивается безопасность
связи за счет встроенных защитных механизмов, а самому пользователю нет
необходимости передавать данные вручную.
Рабочий процесс пользователя с использованием взаимодействующих между собой аппаратного и сетевого кошельков выглядит примерно так [80]:
1. С помощью аппаратного кошелька создаются родительские закрытый
и открытый ключи. Аппаратный кошелек подключается к устройству
с сетевым кошельком, чтобы последний мог получить родительский открытый ключ.
2. С помощью сетевого кошелька открытые ключи должны распространяться по сети для получения оплаты. Когда на кошелек будет получена
какая-либо сумма и возникнет необходимость ее потратить, в программе сетевого кошелька вводятся данные транзакции, затем подключается аппаратный кошелек. При получении соответствующей команды
от пользователя сетевой кошелек автоматически отправляет сведения
о транзакции в аппаратный кошелек.
3. На экране аппаратного кошелька отображаются сведения о транзакции,
которые необходимо просмотреть и проверить. Некоторые аппаратные
кошельки могут запрашивать кодовую фразу или ПИН-код. Аппаратный
кошелек подписывает транзакцию и загружает ее в сетевой кошелек.
4. Сетевой кошелек получает подписанную транзакцию от аппаратного кошелька и транслирует ее в сеть.
Основным преимуществом аппаратных кошельков
является их высокий уровень безопасности в сравнении с полнофункциональными программными кошельками.
Пример аппаратного кошелька модели Ledger
Nano X приведен на рис. 3.12.
При этом опять же главным недостатком аппаратных кошельков является неудобство их использования. Конечно, аппаратный кошелек проще в использовании, чем автономная программа-кошелек.
Однако пользователю необходимо, во-первых, потратить средства на то, чтобы приобрести такой кошелек.
Во-вторых, на пользователя ложится ответственность
за хранение такого кошелька, потому что каждый раз,
когда ему необходимо будет выполнить перевод денег, кошелек должен быть ему доступен.
Рисунок 3.12. Аппаратный кошелек Ledger
Nano X

Глава

4
Основные
блокчейн-платформы

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

4.1 Биткойн
Биткойн (англ. Bitcoin) представляет собой денежную единицу – наиболее известную и распространенную на текущий момент криптовалюту.
Это же название является синонимом для открытого программного обес­
печения, которое было разработано для данной криптовалюты. Сегодня в понятие «биткойн» также вкладывается определение глобальной компьютерной
сети, которая обеспечивает работу и поддержку программного обеспечения
для стабильной работы с одноименной криптовалютой.
Один биткойн обозначается как 1 BTC и может быть разделен на
100 000 000 единиц [251]. Самая малая единица биткойна называется сатоши
(Satoshi) в честь его создателя. Соответствие доли биткойна и количества сатоши представлено в табл. 4.1.
Таблица 4.1. Соответствие доли биткойна и количества сатоши
BTC (биткойн)

Satoshi (сатоши)

Альтернативное название и обозначение

0.00000001

1

0.00000010

10

0.00000100

100

0.00001000

1000

0.00010000

10 000

0.00100000

100 000

1 mBTC, миллибиткойн, мбит, миллибит

0.01000000

1 000 000

1 cBTC, центобиткойн, битцент

0.10000000

10 000 000

1.00000000

100 000 000

1 μBTC, микробиткойн, юбит, микробит

154



Глава 4

4.1.1 Введение в устройство блокчейн-системы Биткойн
Биткойн представлет собой открытую (публичную) блокчейн-сеть, в которой хранятся упорядоченные транзакции с указанием времени их создания. Каждый узел
в сети Биткойн хранит ту цепочку блоков, которую он предварительно проверил.
Считается, что когда узлы хранят одинаковый набор блоков, то они находятся в консенсусе. В биткойн-сети используется консенсус доказательства работы, рассмотренный в предыдущей главе книги.
На рис. 4.1 показано упрощенное представление цепочки блоков биткойна.
Транзакции блока обрабатываются с помощью дерева Меркля (деревья Меркля
были описаны ранее), в результате чего вырабатывается корень Меркля, который также записывается в заголовок блока. Помимо корня Меркля, заголовок
также содержит хеш-код от заголовка предыдущего блока.
Блок 1
Заголовок

Блок 2
Заголовок

Блок 3
Заголовок

Стартовое
значение

Хеш-значение от
заголовка
предыдущего
блока

Хеш-значение от
заголовка
предыдущего
блока

Дополнительные
данные

Дополнительные
данные

Дополнительные
данные

nonce

nonce

nonce

Корень Меркля

Корень Меркля

Корень Меркля

Транзакции Блока 1

Транзакции Блока 2

Транзакции Блока 3

Рисунок 4.1. Упрощенное представление цепочки блоков биткойна

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

Основные блокчейн-платформы  155
В качест­ве функции хеширования дважды используется алгоритм SHA-256,
подробно описанный в первой главе книги. И выходы каждой транзакции привязаны к ее идентификатору.
Получается, что все транзакции, которые записаны в биткойн-сети, могут
иметь один из двух статусов: уже потраченный выход или еще не израсходованный выход (обозначается как UTXO, от англ. Unspent Transaction Output).
Чтобы перевод денег в биткойн-сети был действительным, все входы транзакции должны находиться в статусе неизрасходованных (UTXO).
При проверке валидности транзакции обязательно проверяется выполнение
условия, что сумма всех выходов не превышает суммы всех входов. Если это условие не выполняется, то транзакция отклоняется. Если же потраченная сумма оказывается меньше, чем составляет общая сумма входов, то есть два пути расходования остатка: остаток может быть переведен на адрес инициатора транзакции
(то есть как бы осуществляется перевод сдачи) либо остаток может использоваться как комиссия тому майнеру, который замайнит данную транзакцию в блок.
На рис. 4.2 показан пример трат и поступлений для 7 транзакций. На вход
самой первой транзакции с номером 0 поступил перевод в 1000 сатоши. Далее
каждая транзакция тратит на 100 сатоши меньше. Таким образом, вознаграждение за каждую из этих транзакций добавит майнеру 100 сатоши.
Транзакция 3
Txid(3)
Вход 0

50
сатоши

Транзакция 4
Txid(4)

Транзакция 0
Txid(0)

Выход 0

Вход 0

Вход 0

2
са 00
то
ши

1000
сатоши

4
са 00
то
ши

Вход 0

Вход 0

Вход 1

са 10
то 0
ш
и

Транзакция 1
Txid(1)

1
са 50
то
ши

Выход 0

Транзакция 7
Txid(7)

Выход 0

50
сатоши
UTXO

Выход 0

Выход 1
Транзакция 5
Txid(5)

Выход 0

Выход 1

5
са 00
то
ши

Транзакция 2
Txid(2)

1
са 50
то
ши

Вход 0

Выход 0

Вход 0

50
сатоши
UTXO

Выход 0

Выход 1

200
сатоши

Транзакция 6
Txid(6)
Вход 0

Выход 0

100
сатоши
UTXO

Рисунок 4.2. Пример связей между поступлениями и тратами для нескольких транзакций

156



Глава 4

По рис. 4.2 можно видеть, что для трех транзакций под номерами 5, 6 и 7 остались неизрасходованные выходы, которые, соответственно, содержат 50, 100
и 50 сатоши.
Следует отметить, что комиссия (вознаграждение) за каждую транзакцию
автоматически вычитается из суммы транзакции и затем добавляется к общей сумме комиссии майнера. Так было не всегда. До определенного времени транзакции в биткойн-сети обрабатывались равновероятно и совершенно
бесплатно.

4.1.2 Особенности механизма
консенсуса в системе Биткойн
В биткойн-сети используется консенсус доказательства работы PoW, который
ранее был рассмотрен в третьей главе. Сложность генерации нового блока задается количеством нулей, которые необходимо получить в вырабатываемом
хеш-коде. При этом структура блока в биткойн-сети организована так, что в заголовоке блока есть специальное поле nonce, которое легко получить.
Для доказательства работы вырабатывается хеш-код только для 80-байтового заголовка блока. Таким образом, тот факт, что сам блок имеет большой
объем и содержит большое количество транзакций, не оказывает влияния на
скорость получения хеш-кода заданной сложности.
Если же осуществляется добавление дополнительных транзакций в блок, то
при этом достаточно пересчитать хеш-коды для дерева Меркля и определить
его корень. Таким образом обеспечивается доказательство работы, проделанной узлом, сгенерировавшим блок.
Пересмотр сложности генерации блока происходит через каждые 2016 блоков. Для этого используются временны́е метки из заголовков блоков, по которым определяется, за какое время были получены последние 2016 блоков.
Идеальным считается значение 1 209 600 секунд, то есть блок формируется
один раз в 10 минут.
При этом если по какой-то причине для генерации последних 2016 блоков
потребовалось менее двух недель, то сложность генерации хеш-кода будет автоматически скорректирована путем такого увеличения, которое (в теории)
позволяет сгенерировать следующие 2016 блоков ровно за две недели. Если же
генерация 2016 блоков займет больше двух недель, то сложность автоматически понизится аналогичным образом.
Согласно данным с сайта [79], в реализации ядра биткойн-сети была допущена ошибка, по которой временны́е метки учитываются только для последних 2015 блоков, что вносит небольшие искажения при расчете среднего времени выработки одного блока.

4.1.3 Форки в системе Биткойн
Сколько блоков может быть в цепочке и как образуется форк
Все блоки в цепи биткойна имеют свой номер, который для структуры биткойн-цепочки называется «Высота». Стартовый генезис-блок имеет высо-

Основные блокчейн-платформы  157
ту, равную 0. Это можно проверить, перейдя на сайт по адресу https://www.
blockchain.com/explorer?view=btc_blocks и указав номер блока 0 (рис. 4.3).
Дальше, последовательно переключая блоки, можно видеть, как с каждым
блоком высота увеличивается на 1.

Рисунок 4.3. Первый блок биткойн-цепочки

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

158

 Глава 4

Рисунок 4.4. Примеры форков

Как же биткойн-цепь справляется с данной проблемой? Рассмотрим этот вопрос подробнее.
Майнер, сформировав новый блок, отправляет информацию о блоке в общую сеть. Каждый узел сам решает, какой из полученных блоков ему принять.
Обычно обрабатывается тот блок, который узел получил первым. Таким образом на разных узлах могут сформироваться разные цепочки блоков. Та цепочка, которая оказывается длиннее других, и будет считаться основной.
Обычно расщепление бывает на уровне 1–2 блоков и эта ситуация легко разрешается (верхнее изображение на рис. 4.4).
Появление нетипичной ситуации с форком большой длины может возникнуть тогда, когда часть майнеров пытаются атаковать сеть, применив, например, атаку 51 %. Пример долгосрочного форка приведен на нижнем изображении рис. 4.4.
Так как ситуация с расщеплением цепочки является обычной для блокчейнсети, то очень часто сформированные блоки на разных узлах могут иметь одинаковое значение высоты, но при этом разное содержание. Поэтому не рекомендуется использовать высоту блока в качестве глобального уникального
идентификатора. Вместо этого на блоки обычно ссылаются по хеш-кодам их заголовков (часто с обратным порядком байтов и в шестнадцатеричном формате).

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

Основные блокчейн-платформы  159
тере или искажению информации. Хардфорк применяется обычно тогда, когда
разработчики хотят создать свою, новую криптовалюту.
Ядро платформы Биткойн (Bitcoin Core) содержит модуль, предназначенный для обнаружения хардфорков. Делается это на основе анализа заголовков в цепочке блоков. Узлы постоянно взаимодействуют друг с другом, проверяя правильность построения цепочки в соответствии с консенсусом, а также
определяя, у кого цепочка блоков длиннее. Если при анализе выясняется, что
у соседнего узла намного больше заголовков для цепочки блоков (по крайней
мере, больше, чем на 6), то такой узел посылает предупреждение, что необновленный узел не может переключиться на наиболее подходящую цепочку
блоков. Если установлено соответствующее программное обеспечение, то узел
может автоматически произвести обновление.
Изменения, вносимые в правила работы блокчейн-системы, могут быть активированы различными способами. В течение первых двух лет существования системы Биткойн было сделано несколько софтфорков путем небольших
изменений в клиенте с сохранением обратной совместимости. При этом вносимые изменения начинали действовать сразу после обновления.
Несколько софтфорков, таких как, например, BIP30 (о BIP будет рассказано
далее), начали свою работу не сразу, а по достижении определенного дня.
В этом случае новое правило работы блокчейн-системы начинает применяться либо в заранее установленное время, либо при достижении определенной высоты блока.
Такие форки, начинающие свою работу при достижении определенного
условия, известны как активируемые пользователем софтфорки (UASF, User
Activated Soft Forks), поскольку они зависят от наличия достаточного количест­
ва пользователей (узлов) для обеспечения соблюдения новых правил.
Более поздние софтфорки начинали вступать в действие тогда, когда большая часть (от 75 % до 100 %) суммарной вычислительной мощности майнингового оборудования (хешрейта) сигнализирует о своей готовности к применению новых правил. Как только сигнальный порог будет пройден, все узлы
начнут применять новые правила. Такие форки известны как софтфорки, активируемые майнером (MASF, Miner Activated Soft Forks), поскольку их активация зависит от майнеров.
Предложения по улучшению системы Биткойн (Bitcoin Improvement
Proposal, BIP) BIP16, BIP30 и BIP34 были реализованы как изменения, которые
могли привести к софтфоркам. Улучшение BIP50 описывается как случайный
хардфорк, разрешенный путем временного понижения возможностей обновленных узлов, и как преднамеренный хардфорк, когда временное понижение
версии было удалено. В документе от Гэвина Андресена (Gavin Andresen) [56]
описывается, как могут быть реализованы будущие изменения правил.

4.1.4 Транзакции
Запись транзакций в блоки
Для системы Биткойн нельзя создать пустые блоки, то есть блоки, не содержащие транзакций. Каждый блок должен содержать как минимум одну транз­
акцию. Если других действий в системе не производилось, то в блоке будет

160



Глава 4

содержаться единственная транзакция, которая начисляет вознаграждение
создавшему ее майнеру. При запуске системы данное вознаграждение составляло 50 биткойнов.
С использованием любого сервиса по просмотру блоков биткойн-цепочки, например расположенного по адресу https://www.blockchain.com/
explorer?view=btc_blocks, можно увидеть, что первые созданные блоки преимущественно содержали только эту единственную транзакцию. Такая транз­акция
называется транзакцией генерации блока. Помимо вознаграждения за созданный блок, она также может содержать комиссии, которые пользователи уплачивают за включение других транзакций в блок.
Так, например, в блоке с высотой 100 содержится всего одна транзакция, она
же транзакция генерации блока. Других транзакций в блоке нет, и потому майнер не получает комиссию (см. рис. 4.5).

Рисунок 4.5. Транзакции в блоке биткойна с высотой 100

А в блоке с высотой 200 000, помимо транзакции генерации блока, содержится еще 387 дополнительных транзакций. Поэтому майнер получает комиссию
за транзакции, помещенные в блок (рис. 4.6).

Рисунок 4.6. Транзакции в блоке биткойна с высотой 200 000

Основные блокчейн-платформы  161
Как было сказано ранее, при возникновении непотраченного остатка UTXO
алгоритмы биткойн-системы работают таким образом, что не дают его потратить до тех пор, пока после возникновения этого остатка не будет получено
100 блоков.
Это сделано для того, чтобы избежать так называемой двойной траты, а также для того, чтобы убедиться, что неизрасходованная транзакция попала в основную цепочку блоков (а не в цепочку-форк).
Технически майнеру не обязательно искать такой блок, в котором были бы
дополнительные транзакции. Однако в погоне майнера за дополнительным
вознаграждением в виде комиссии за транзакции, помещенные в блок, количество транзакций в блоке может достигать нескольких тысяч. В этом случае
суммарная комиссия может содержать десятые доли биткойна.
На рис. 4.7 приведен пример блока биткойна с высотой 599 999. Здесь вознаграждение за полученный блок составляет 12,5 BTC, а комиссия за 3394 транз­
акции – 0,248659028 BTC.

Рисунок 4.7. Данные блока биткойна с высотой 599 999

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

162



Глава 4

ичной транзакции. Такое представление транзакции хешируется для создания
идентификатора транзакции (обозначается как Idtx или txid).
Из всех идентификаторов транзакций строится дерево Меркля путем объединения каждого txid с другим txid и последующего хеширования их вместе.
Если число идентификаторов нечетное, то последний идентификатор дублируется, то есть хешируется с копией самого себя.
Есть одно важное «но»! В 2012 году было выяснено, что реализация, которую
система Биткойн использует для вычисления корня Меркля в заголовке блока,
ошибочна в том, что можно легко создать несколько списков хеш-кодов, для
которых будет получен один и тот же корень Меркля.
Если в блок поместить транзакции с идентичными идентификаторами txid,
то существует вероятность того, что дерево Меркля будет иметь коллизию с похожим блоком, в котором такие транзакции встречаются в одном экземпляре.
Например, вычисления корня Меркля для трех транзакций ([A, B, C]) и корня
Меркля для четырех транзакций ([A, B, C, C]) будут давать одинаковый результат (рис. 4.8). Это потому, что на каждой итерации хеш-функция дополняет
свой промежуточный список хеш-кодов последним хеш-кодом, если список
имеет нечетную длину, чтобы сделать его четной длины.
h1=h(A)

h2=h(B)

h3=h(C)

h4=h(h1,h2)

h3=h(C)

h5=h(h3,h3)

h6=h(h4,h5)

h1=h(A)

h2=h(B)

h4=h(h1,h2)

h3=h(C)

h5=h(h3,h3)

h6=h(h4,h5)

Рисунок 4.8. Пример коллизии для дерева Меркля

Получается, что для любого блока с нечетным количеством транзакций очень
просто подобрать коллизию: необходимо удвоить последний элемент [81].
В случае использования честного программного обеспечения и честного
взаимодействия со стороны всех участников биткойн-системы наличие возможных коллизий не влечет за собой сбоев в работе. Однако в случае нечестной
игры или применения модифицированного программного обеспечения может
получиться так, что в блоки попадут транзакции с расходованием одних и тех
же монет (двойное расходование), при этом правильно построенные блоки без
двойного расходования средств будут признаваться недействительными.
Это можно использовать для форка блокчейна, в том числе для глубоких атак
с двойным расходованием средств. Именно такая уязвимость была замечена
в 2012 году и внесена в общий список под названием CVE-2012-2459 [98]. Проб­
лема была исправлена Гэвином Андресеном путем отклонения блоков с повторяющимися транзакциями и предотвращения их хеширования вообще.

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

Основные блокчейн-платформы  163
Каждая транзакция состоит из нескольких частей. При этом транзакция
может быть простой или сложной в зависимости от того, из каких средств
складывается итоговая сумма перевода и для кого предназначен платеж
(или платежи).
Кроме того, для криптовалют, таких как биткойн, характерно свойство появления новых денег с каждым новообразованным блоком в цепочке. Каждый
раз, когда майнер находит новый блок, он получает вознаграждение в виде
какого-то количества биткойнов.
Первоначально вознаграждение за добытый блок составляло 50 BTC. Однако
в систему Биткойн введена функция сокращения вознаграждения вдвое каждые 4 года. Данная процедура называется халвинг (от англ. half – половина).
Таким образом, в 2012 г. произошло сокращение вознаграждения до 25 BTC,
в 2016 г. – до 12,5 BTC, в 2020 г. – до 6,25 BTC.
Транзакция вознаграждения имеет структуру, которая отличается от других
транзакций, при этом отличаются и правила формирования таких транзакций.
В описании биткойн-системы такие транзакции называются Coinbase, то есть
транзакции, которые вырабатываются самим ядром системы.
У Coinbase-транзакции фактически нет входов и есть только один выход.
Данной транзакцией майнеру начисляются новые монеты системы. Транзакции Coinbase могут быть созданы только майнерами биткойнов, и они являются исключением из многих правил, определенных для обычных транзакций.
Первоначально Coinbase-транзакции содержали только сумму вознаграждения. Это характерно для всех первых блоков. При этом за саму Coinbaseтранзакцию комиссия не взимается. Пример подобной транзакции для блока
с высотой 10 показан на рис. 4.9.

Комиссии нет

Тип транзакции

Су

мм
а

Адрес майнера

Сумма
вознаграждения

Рисунок 4.9. Coinbase-транзакция в блоке биткойна с высотой 10

С тех пор, как в систему Биткойн ввели комиссию за майнинг транзакции,
Coinbase-транзакция выполняет майнеру перевод, величина которого соответствует сумме вознаграждения за добытый блок и сумме всех комиссий за
транзакции, помещенные в блок. Комиссия за саму Coinbase-транзакцию попрежнему не взимается. Пример такой транзакции для блока с высотой 300 000
показан на рис. 4.10.

164



Глава 4

Су

мм
а

Комиссии нет

Тип транзакции

Сумма
вознаграждения

Адрес майнера

Рисунок 4.10. Coinbase-транзакция в блоке биткойна с высотой 300 000

Все остальные транзакции в биткойн-системе обязательно имеют как минимум один вход (показывает, откуда у пользователя появились деньги) и один
выход (показывает, куда пользователь эти деньги тратит). Основная структура
транзакции в биткойн-системе представлена на рис. 4.11.

Основная
часть
Транзакции 0

Основная
часть
Транзакции 2

Идентификатор
транзакции

Идентификатор
транзакции

Входы

Входы

Выходы

Выходы

Основная
часть
Транзакции 1

Подпись

Идентификатор
транзакции

Входы

Выходы

Подпись

Подпись

Основная
часть
Транзакции 3

Идентификатор
транзакции

Входы

Выходы

Подпись

Рисунок 4.11. Связь входов и выходов в транзакциях

Если мы посмотрим на транзакцию через любой интернет-обозреватель
(например, Blockchain Explorer), то сможем увидеть все входы и выходы транз­
акции. Рассмотрим для примера одну из первых транзакций в блоке с высотой 718 300 (рис. 4.12). Эта транзакция была создана 12 января 2022 года, содержит 14 входов и 2 выхода. Причем можем заметить, что все входы получены
с одного адреса и имеют примерно равноценное значение. Если внимательно
посчитать, то можно увидеть, что сумма всех входов соответствует сумме выходов и комиссии.

Основные блокчейн-платформы  165
Комиссия

Сумма выходов

Входы

Выходы

Рисунок 4.12. Входы и выходы транзакции в блоке биткойна с высотой 718 300

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

Рисунок 4.13. Выходы транзакции в блоке биткойна с высотой 718 300

166



Глава 4

Каждый вход тратит биткойны или доли биткойна (сатоши), которые были
получены в предыдущем выходе. Каждый выход может быть использован
только один раз. Для неиспользованных выходов устанавливается статус неизрасходованной транзакции (UTXO). Так, на рис. 4.13 выход с индексом 1 имеет
статус UTXO.
Когда вы проверяете свой баланс в биткойн-кошельке и видите, например,
значение 1000 биткойнов, то это на самом деле означает, что сумма всех ваших
неизрасходованных выходов транзакций составляет 1000 биткойнов.

Скриптовый язык для обработки транзакций
Для организации правильной работы по проверке и использованию транзакций
используется специальный стековый язык Script (скрипт). С помощью прос­тых
команд этого языка выстраиваются инструкции, в соответствии с которыми необходимо формировать транзакцию и проверять валидность транзакции.
Для начала рассмотрим основные команды, которые используются в языке
Script (табл. 4.2). В таблице приведен далеко не полный перечень команд для
языка Script. Однако нам будет достаточно знать данные команды, для того
чтобы разобраться с правилами построения транзакций для биткойн-системы.
С полным перечнем команд для языка Script можно ознакомиться в [222].
Таблица 4.2. Основные команды языка Script
Команда

Вход

Выход

Описание

OP_0

Нет

Пустое
значение

В стек помещается пустой массив
байтов

N/A (определяется
по коду операции)

Нет

Данные

Следующие за этой командой байты
данных (от 1 до 75 в зависимости от
кода операции) помещаются в стек

OP_1

Нет

1

В стек помещается значение 1

OP_2 – OP_16

Нет

2–16

В стек помещается одно из значений
от 2 до 16

OP_IF

IF [утверждение]

Если верхнее значение стека не
ложно, то утверждение выполняется;
верхнее значение стека удаляется

OP_VERIFY

Истина
или Ложь

Ничегоили
ошибка выполнения

Если вершина стека соответствует утверждению Ложь, то помечает транз­
акцию как поврежденную; верхнее
значение стека удаляется

OP_RETURN

Нет

Ошибка выполнения

Помечает транзакцию как недействительную; начиная с версии 0.9 ядра
системы Биткойн стандартным способом добавления дополнительных данных к транзакции является добавление вывода с нулевым значением; при
этом записывается операция OP_RETURN, за которой следуют данные

Основные блокчейн-платформы  167
Окончание табл. 4.2
Команда

Вход

Выход

Описание

OP_DUP

х

хх

Дублирует верхнее значение стека

OP_EQUAL

x1
x2

Истина или
Ложь

Возвращает 1, если входы точно равны, и 0 в противном случае

OP_EQUALVERIFY

x1
x2

Ничего или
ошибка выполнения

То же, что и OP_EQUAL, только после
этого запускается операция OP_VERIFY

OP_SHA256

x

Hash(x)

Вход хешируется с использоваием
алгоритма SHA-256

OP_HASH160

x

Hash(x)

Вход хешируется дважды: сначала
с использоваием алгоритма SHA-256,
а затем с использованием алгоритма
RIPEMD-160

OP_CHECKSIG

sig
pubkey

Истина или
Ложь

Все содержимое транзакции хешируется; подпись sig должна пройти
верификацию для полученного хеша
и открытого ключа pubkey; если проверка пройдена успешно, то возвращается 1, иначе возвращается 0

OP_CHECKSIGVERIFY

sig
pubkey

Истина или
Ложь

То же, что и OP_CHECKSIG, только
после этого запускается операция
OP_VERIFY

OP_CHECKMULTISIG

X
sig1
sig2

sigx
pubkey1
pubkey2

pubkeyx

Истина или
Ложь

Сравнивает первую подпись с каждым открытым ключом в соответствии
с алгоритмом проверки подписи
ECDSA; затем вторую и т. д.; процесс
повторяется до тех пор, пока не будут
проверены все подписи или пока не
останется открытых ключей; если все
подписи действительны, возвращается 1, иначе 0

Команды, записанные друг за другом, будут помещаться в стек (и обрабатываться) друг за другом. Так, например, запись «COM1 COM2» будет означать,
что в стек будет сначала помещена команда COM1, а затем команда COM2. Таким образом команда COM1 будет находиться на дне стека, а команда COM2 –
на его вершине.

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

168

 Глава 4

веряет синтаксис транзакции, правильность построения и соответствие всем
установленным шаблонам. Этот тест называется IsStandard, и транзакции, которые проходят его, называются стандартными транзакциями.
И наоборот, нестандартные транзакции – это те транзакции, которые не
прошли проверку и могут быть приняты узлами, не использующими настройки
основного ядра системы Биткойн (Bitcoin Core) по умолчанию. Если они включены в блоки, значит, они избежали проверки IsStandard и будут обработаны.
Задачи, которые решает стандартный тест транзакций, состоят в следующем:
 предотвращение атак на систему Биткойн, связанных с генерацией
и распространением вредоносных транзакций;
 запрет создания пользователями таких транзакций, которые в будущем
могут усложнить добавление новых функций в состав транзакций.
Например, как было описано выше, каждая транзакция включает в свой состав номер версии. При этом если пользователи начнут произвольно изменять
номер версии, она станет бесполезной в качестве инструмента для внедрения
обратно несовместимых функций.
Каждая транзакция содержит в своем составе входы и выходы (как минимум
один выход). При этом выходы содержат специальное поле, которое называется скриптом открытого ключа (Pkscript). Скрипт открытого ключа содержит
набор инструкций на языке Script (из тех, что приведены в табл. 4.2), по которым необходимо будет выполнить проверку валидности транзакции, когда
кто-нибудь захочет использовать эту транзакцию в качестве входа.
Выходы формируются из входов. Поэтому выход содержит скрипт открытого ключа (Pkscript), который был ему задан при формировании траты, а также
содержит специальное поле скрипта подписи (Sigscript). Скрипт подписи содержит набор параметров, которые необходимо использовать для выполнения
проверки в соответствии со скриптом открытого ключа.
Начиная с версии ядра 0.9 (Bitcoin Core 0.9), которая появилась в 2014 году,
можно выделить следующие стандартные транзакции:
 транзакция на основе платы за хеш открытого ключа (P2PKH);
 транзакция на основе платы за хеш скрипта (P2SH);
 транзакция на основе коллективной подписи (Multisig);
 транзакция на основе использования только открытого ключа (PubKey);
 транзакция с нулевыми данными.

Транзакция на основе платы за хеш открытого ключа
Схема, при которой выход одной транзакции передает свои деньги скрипту
открытого ключа, а затем вход другой транзакции может потратить эти деньги
с использованием правильных значений в скрипте подписи, называется PayTo-Public-Key-Hash (P2PKH), что дословно можно перевести как «плата за хеш
открытого ключа». Рассмотрим эту схему более подробно.
На рис. 4.14 представлены фрагменты двух транзакций, при этом часть полей, не влияющая на понимание концепции образования транзакции, опущена.
Транзакция может содержать несколько входов и выходов. У каждого входа и выхода есть свой индекс. На рис. 4.14 показано, что в транзакциях будет
представлено m входов и n выходов.

Основные блокчейн-платформы  169
Выход всегда содержит некоторую сумму, которую пользователь перечисляет некоему адресату. Фактически в качестве такого адресата выступает скрипт
открытого ключа (Pkscript), который будет описан далее. Сумму, назначенную
в данном выходе, сможет потратить тот, кто пройдет аутентификацию со стороны скрипта открытого ключа.
Выход1
Идентификатор
транзакции 1

Входы
1..m

Адрес
отправителя

Адрес
Идентификатор
Сумма
транзакции 2 отправителя

Сумма

Скрипт открытого
ключа (Pkscript)

Скрипт открытого Скрипт подписи
ключа (Pkscript)
(Sigscript)

Выходы
2..n

Входы
2..m

Подпись

Выходы
1..n

Подпись

Вход1

Рисунок 4.14. Преобразование выхода во вход

Фактически скрипт открытого ключа представляет собой набор условий, которые должны быть выполнены, а также обеспечивает автоматическую проверку соответствия данным условиям предоставляемых ему данных.
Формируемый вход, помимо всех основных полей выхода, из которого он
образован, также содержит в своем составе скрипт подписи (Sigscript). Скрипт
подписи содержит те данные, которые проверяются механизмами скрипта открытого ключа. Смысл заключается в том, что потратить данный выход сможет
тот, кто может доказать, что знает закрытый ключ, соответствующий хешированному открытому ключу.
По правилам биткойн-системы транзакции не хранят открытый ключ пользователя в открытом виде. Это сделано в том числе для того, чтобы у злоумышленников было как можно меньше шансов восстановить закрытый ключ, от
которого зависят активы пользователя. Однако транзакция содержит слепок
открытого ключа, образованный с использованием хеш-кода.
Для того чтобы Алиса смогла перевести Бобу деньги, Боб должен быть зарегистрирован в системе. Регистрация в системе подразумевает, что для Боба
должна быть сгенерирована пара ключей: закрытый и открытый.
В системе Биткойн используется алгоритм электронной подписи на основе
эллиптических кривых (ECDSA) с кривой secp256k1 [90] (данный алгоритм
подробно описан в главе 2). Закрытый ключ для ECDSA с кривой secp256k1
представляет собой 256 бит случайных данных. Копия этих данных детерминированно преобразуется в открытый ключ. В силу того, что данное преобразование можно надежно повторить позже, открытый ключ самому пользователю хранить не обязательно, достаточно знать закрытый ключ.
Выработанный открытый ключ хешируется. Полученный в результате хешкод сокращает длину ключа, а также скрывает само значение открытого ключа.
Уменьшение длины ключа упрощает ввод его значения вручную.

170

 Глава 4

Пользователь, который вырабатывает хеш-код, всегда может повторить данный процесс, зная исходные данные и алгоритм хеширования. Поэтому ему не
обязательно хранить вычисленный хеш-код.
Итак, схема работает следующим образом. Алиса хочет перевести Бобу некоторую сумму. На компьютере Боба хранится его закрытый ключ. На компьютере Боба из закрытого ключа вырабатывается открытый ключ, а затем его
хеш-код. Именно этот хеш-код Боб должен сообщить Алисе, для того чтобы она
смогла перевести Бобу деньги.
Используя полученный хеш-код открытого ключа, Алиса формирует скрипт
открытого ключа для создаваемой транзакции, помещая в него полученный
хеш-код открытого ключа (см. рис. 4.15).

Компьютер Боба

Компьютер Алисы

Хеш открытого
ключа

Хеш открытого
ключа

Полный
открытый ключ

Закрытый
ключ

Транзакция 1
Хеш открытого
ключа (копия)

Сеть

Рисунок 4.15. Создание P2PKH для получения платежа

Обычно хеш-код открытого ключа записывается в кодировке биткойн-адресов, которые представляют собой строки в кодировке base58, содержащие номер версии адреса, хеш-код и контрольную сумму для выявления опечаток.
Адрес может быть передан через любой носитель, и его можно дополнительно закодировать в другой формат, например QR-код, содержащий сообщение
вида “«bitcoin:» URI”, где URI (Uniform Resource Identifier) – это унифицированный идентификатор (адрес) ресурса.
Если адрес был дополнительно закодирован, то после его получения Алиса должна декодировать его обратно в стандартный хеш-код. После этого она
может создать транзакцию для отправки денег Бобу. Для этого она должна создать стандартный выход транзакции P2PKH (см. рис. 4.15), содержащий скрипт
открытого ключа.
Алиса формирует транзакцию и отправляет ее в сеть. После того как транзакция
будет замайнена, она добавляется в цепочку блоков. Сеть классифицирует выходы
транзакции как неизрасходованный выход транзакции (UTXO), а программное
обеспечение кошелька Боба отображает его как баланс, который можно потратить.
Когда через некоторое время Боб решит потратить свои неизрасходованные
выходы, он должен будет создать новый вход (по принципу, показанному на

Основные блокчейн-платформы  171
рис. 4.14) и в этом входе указать ссылку на данные выхода транзакции Алисы.
Затем Боб должен создать скрипт подписи, то есть набор тех данных, которые
пройдут все проверки, заданные Алисой в скрипте отрытого ключа (рис. 4.16).
Компьютер Боба
Скрипт подписи
Закрытый
ключ
Полный
открытый ключ

Подпись

Полный
открытый ключ

Транзакция 1
Скрипт открытого
ключа выхода

Скрипт открытого
ключа выхода

Сеть
Рисунок 4.16. Разблокирование выхода P2PKH для следующей траты

Фактически Боб должен поместить в скрипт подписи два компонента: свою
подпись, сделанную для данных транзакции закрытым ключом Боба в соответствии с алгоритмом ECDSA, а также свой полный открытый ключ. При этом
хеш-код открытого ключа из Транзакции 1 (см. рис. 4.14) также перепишется
из соответствующего выхода Транзакции 1.
Проверка данных параметров впоследствии осуществляется скриптом открытого ключа и состоит в следующем:
1. Из полного открытого ключа Боба будет получен его хеш-код. Он должен
совпасть с хеш-кодом открытого ключа, помещенным в скрипт подписи.
Это подтвердит тот факт, что деньги действительно предназначены тому
пользователю, которого указала Алиса.
2. Будет выполнена проверка подписи с использованием алгоритма ECDSA
и кривой secp256k1, чтобы убедиться в том, что Боб действительно владеет
закрытым ключом, из которого был выработан данный открытый ключ.
Проверка подписи Боба не просто доказывает, что Боб знает свой закрытый ключ, но также не позволяет кому бы то ни было внести изменения в состав самой транзакции. Поэтому транзакцию можно спокойно распространять
в одноранговой сети, не опасаясь подделки.
При создании подписи подписывается вся транзакция, за исключением
полного открытого ключа и самой подписи транзакции. Эти данные (полный
открытый ключ и подпись транзакции) помещаются в скрипт подписи, после
чего Боб отправляет сформированную транзакцию майнерам через одноранговую сеть.

172



Глава 4

Каждый одноранговый узел и майнер независимо друг от друга проверяют
транзакцию, прежде чем передавать ее дальше или пытаться включить ее в новый блок транзакций.
P2PKH – это наиболее распространенная форма скрипта открытого ключа,
используемая для отправки транзакции на один или несколько адресов системы Биткойн. Для формирования скрипта открытого ключа и скрипта подписи
используется стандартный набор инструкций в соответствии с табл. 4.3.
Таблица 4.3. Основные скрипты сценария «Плата за хеш открытого ключа»
Скрипт открытого ключа OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG
Скрипт подписи



Скрипт подписи того пользователя, который намеревается потратить деньги, проверяется, и его данные (подпись с использованием алгоритма ECDSA
с кривой secp256k1 и полный открытый ключ) записываются перед началом
скрипта открытого ключа. В результате в соответствии с табл. 4.3 образуется
следующая последовательность:
OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG

Чтобы проверить, действительна ли транзакция, скрипт подписи и скрипт
открытого ключа проверяют последовательно все элементы, начиная со скрипта подписи Боба и заканчивая скриптом открытого ключа Алисы, последовательно помещая данные в стек (рис. 4.17).
Инструкции и данные, которые Алиса записала в скрипт открытого ключа в Транзакции 1
OP_DUP

OP_HASH160

PkHash

OP_EQUALVERIFY

CHECKSIG

Данные, которые Боб записал в скрипт подписи при формировании входа для Транзакции 2
Sig

PubKey

Sig

PubKey

OP_DUP

Sig

PubKey

Sig

OP_HAS160

PkHash

OP_EQUALVERIFY

PkHash

PubKey

PkHash

PubKey

PubKey

PkHash

Sig

Sig

PubKey

CHECKSIG

PubKey

Sig

Рисунок 4.17. Схема последовательной проверки всех элементов

Sig

True

Основные блокчейн-платформы  173
Подпись, которую Боб сформировал для входа Транзакции 2 (Sig), а также
полный открытый ключ Боба (PubKey) просто помещаются в стек и используются для проверки (шаги 1 и 2 на рис. 4.17).
Скрипт открытого ключа Алисы выполняет операцию OP_DUP (шаг 3 на
рис. 4.17). OP_DUP помещает в стек копию данных, находящихся в данный момент наверху, – в этом случае создается копия открытого ключа, предоставленного Бобом.
Следующая выполняемая операция – OP_HASH160 (шаг 4 на рис. 4.17). Эта
операция вырабатывает хеш-код из тех данных, которые в настоящий момент
находятся в вершине стека, – в данном случае это открытый ключ Боба. Таким
образом создается хеш-код для открытого ключа Боба (шаг 5 на рис. 4.17), который Боб сообщил Алисе для последующей проверки.
Получается, что в этот момент в верхней части стека находятся две копии
хеш-кода ключа Боба (одна была записана в стек открытого ключа Алисы,
а вторая выработалась с помощью инструкции OP_HASH160).
Следующая инструкция в скрипте открытого ключа Алисы – это инструкция
OP_EQUALVERIFY (шаг 6 на рис. 4.17). Фактически данная инструкция состоит
из двух шагов: проверки OP_EQUAL и подтверждения OP_VERIFY.
На первом шаге OP_EQUAL проверяет два значения в вершине стека (сравнивает хеш-коды открытого ключа Боба). После этого сравниваемые хеш-коды
удаляются из вершины стека, а вместо них записывается 1, если сравнение выполнено успешно, и 0 в противоположном случае.
На втором шаге OP_VERIFY выполняется проверка значения, записанного
в вершину стека. Если в вершине находится значение 0, то дальнейшая проверка транзакции не проводится и алгоритм прекращает свою работу. Иначе,
если значение в вершине стека равно 1, оно выталкивается из стека и проверка
продолжается.
Следующая инструкция в скрипте открытого ключа Алисы – это OP_
CHECKSIG (шаг 7 на рис. 4.17). С помощью данной инструкции проверяется
подпись, предоставленная Бобом, с использованием открытого ключа Боба
(который на предыдущем шаге прошел аутентификацию).
Если проверка подписи с использованием открытого ключа выполняется
успешно, то в вершину стека помещается значение 1 (true), иначе помещается
значение 0 (false) (шаг 8 на рис. 4.17). Именно это значение определяет в конечном итоге, признается транзакция действительной или нет.
На рис. 4.18 представлен пример перехода выхода одной транзакции во вход
другой транзакции. В данном примере выход № 15 транзакции 27e9943d2197d
a7578a24a46272700b54d93175e1220b72207665b909a666a57 преобразуется в нулевой вход транзакции c1b7b41cc7bc40fe528a08528ae150e2fdab6aa8501dd100a
c64b194241cf9b2.

174

 Глава 4
Скрипт открытого
ключа

Адрес пользователя, которому
предназначен выход

Суммы выхода и
входа совпадают

Скрипт подписи

Рисунок 4.18. Пример преобразования выхода во вход по принципу P2PKH

Транзакция на основе платы за хеш скрипта
Скрипты открытых ключей создаются отправителями транзакций, которых
мало интересует, как работает этот скрипт. Получатели же транзакций заинтересованы в правильности выполнения всех проверок, и поэтому они могут
попросить отправителя использовать определенный скрипт открытого ключа.
Однако ссылки на пользовательские сценарии открытых ключей менее удобны, чем короткие адреса биткойнов, и не было стандартного способа передачи
их между программами до широко распространенной реализации ныне устаревшего платежного протокола BIP70 [57].
Чтобы решить эти проблемы, в 2012 году были созданы транзакции вида
Pay-To-Script-Hash (P2SH), что дословно можно перевести как «Плата за хеш
скрипта». Такие транзакции позволяют отправителю создать скрипт открытого ключа, который в свою очередь содержит второй скрипт, который называется скрипт погашения (Redeem Script).
Принцип формирования транзакции вида P2SH (рис. 4.19) очень похож на
формирование P2PKH. Боб создает скрипт погашения с любым сценарием проверки, который он хочет, хеширует скрипт погашения и предоставляет Алисе
хеш-код скрипта погашения. Алиса создает выход в стиле P2SH, содержащий
хеш-код скрипта погашения Боба.

Основные блокчейн-платформы  175
Компьютер Боба
Хеш скрипта
погашения

Скрипт
погашения
Полный
открытый ключ

Компьютер Алисы
Хеш скрипта
погашения

Транзакция 1
Хеш открытого
ключа
(хеш скрипта
погашения)

Сеть

Закрытый
ключ

Рисунок 4.19. Создание P2SH для получения платежа

Когда Боб хочет потратить деньги из выхода Алисы, он предоставляет свою
подпись вместе с полным скриптом погашения в скрипте подписи (рис. 4.20).
В остальном же работа по проверке скрипта погашения аналогична работе по
проверке скрипта открытого ключа, и Боб может потратить деньги, если после
проверки скрипт возвращает значение true.
Компьютер Боба
Скрипт подписи
Закрытый
ключ

Полный скрипт
погашения

Транзакция 1
Скрипт открытого
ключа выхода
(хеш скрипта
погашения)

Подпись
Полный скрипт
погашения

Скрипт
открытого
ключа выхода
(хеш скрипта
погашения)

Сеть
Рисунок 4.20. Разблокирование выхода P2SH для следующей траты

176



Глава 4

Хеш-код скрипта погашения имеет те же свойства, что и хеш-код скрипта
открытого ключа, поэтому его можно преобразовать в стандартный формат
адреса системы Биткойн с одним небольшим изменением, чтобы отличить его
от стандартного адреса. Таким образом, формирование адреса для транзакции
P2SH выполняется так же просто, как и для транзакции P2PKH.
Хеш-код тоже скрывает любые открытые ключи, используемые в скрипте
погашения, поэтому транзакции P2SH так же безопасны, как и транзакции
P2PKH.
P2SH используется для обработки транзакции в соответствии со скриптом
погашения.
Каждый из стандартных скриптов открытого ключа может использоваться
как скрипт погашения P2SH, за исключением самого P2SH. Начиная с версии
ядра 0.9.2 (Bitcoin Core 0.9.2) транзакции P2SH могут содержать любой действительный код, записанный в скрипт погашения, что делает механизм P2SH
гораздо более гибким и позволяет экспериментировать со многими новыми
и сложными типами транзакций.
Чаще всего механизм P2SH используется для стандартного сценария открытого ключа с несколькими подписями, а вторым по распространенности является протокол для открытых активов (Open Assets). P2SH также обеспечивает
удобный метод хранения текста в цепочке блоков, поскольку он позволяет хранить до 1,5 КБ текстовых данных.
Для формирования скрипта открытого ключа и скрипта погашения используется стандартный набор инструкций в соответствии с табл. 4.4.
Таблица 4.4. Основные скрипты сценария «Плата за хеш скрипта»
Скрипт открытого ключа

OP_HASH160 OP_EQUAL

Скрипт подписи

[sig] [sig …]

Такая организация скрипта открытого ключа отлично подходит для старых
узлов в том случае, если хеш-код скрипта соответствует скрипту погашения.
Однако после активации софтфорка новые узлы будут выполнять дополнительную проверку скрипта погашения. Они извлекут скрипт погашения из
скрипта подписи, декодируют его и применят извлеченные инструкции для
проверки оставшихся элементов стека (часть [sig] [sig …]).
Следовательно, чтобы получатель мог использовать транзакцию P2SH, отправитель должен предоставить действительную подпись или ответ в дополнение к правильному скрипту погашения. Этот последний шаг аналогичен
шагу проверки в сценариях P2PKH или коллективной подписи (см. далее),
в которых начальная часть скрипта подписи ( [sig] [sig ...]) действует как
скрипт подписи, а скрипт погашения действует как скрипт открытого ключа.
На рис. 4.21 представлен пример перехода выхода одной транзакции во вход
другой транзакции по принципу P2SH, где выход № 9 транзакции 8998bb9c15
4000e6d1ac9b4452550fe35b287c2cbe618d1cea70dc1555270078 преобразуется во
вход № 2 транзакции 41eb724cbca90b6d918513befe0a42149bb17e0e7422ca87d7
2615f0b6437f3c.

Основные блокчейн-платформы  177
Скрипт открытого
ключа

Адрес пользователя, которому
предназначен выход

Суммы выхода и
входа совпадают

Скрипт подписи

Рисунок 4.21. Пример преобразования выхода во вход по принципу P2SH

Транзакция на основе коллективной подписи
Хотя механизм коллективной подписи сейчас обычно используется для транз­
акций с несколькими подписями, этот базовый скрипт может использоваться
для запроса нескольких подписей, перед тем как можно будет потратить неизрасходованный выход UTXO.
В скрипте с несколькими подписями используется формула вида m-из-n,
где m – это минимальное количество подписей, которое должно соответствовать открытому ключу, а n – это количество предоставленных открытых ключей. И m, и n в формате скрипта должны быть представлены кодами операций
с OP_1 по OP_16 в соответствии с желаемым номером.
Из-за ошибки в исходной реализации системы Биткойн, которую нельзя
исправить из-за проблем совместимости (они неизбежно возникнут в случае
исправления), операция «OP_CHECKMULTISIG» забирает из стека на одно значение больше, чем указанное значение m, поэтому список подписей в скрипте
подписи должен начинаться с предварительного значения (OP_0), которое будет указано, но не будет использовано.
Скрипт подписи должен предоставлять подписи в том же порядке, в каком
соответствующие открытые ключи появляются в скрипте открытого ключа
или скрипте погашения (табл. 4.5).

178



Глава 4

Таблица 4.5. Основные скрипты сценария «Коллективная подпись»
Скрипт открытого ключа

[B pubkey] [C pubkey ...] OP_CHECKMULTISIG

Скрипт подписи

OP_0 [Bsig] [Csig …]

Фактически это получается не отдельный тип транзакции, а использование
механизма P2SH (два раза из трех возможных), как показано в табл. 4.6.
Таблица 4.6. Альтернативное представление сценария «Коллективная подпись»
Скрипт открытого ключа
Скрипт активации
Скрипт подписи

OP_HASH160 OP_EQUAL
OP_
CHECKMULTISIG
OP_0

Пример транзакции с мультиподписью приведен на рис. 4.22. Здесь показан
выход № 0 для транзакции 60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06
cd3aabd013ebcdc4bb1, который был потрачен в транзакции 23b397edccd3740a
74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63, образуя единственный
вход данной транзакции.
Скрипт открытого
ключа

Суммы выхода и
входа совпадают

Скрипт подписи

Рисунок 4.22. Пример преобразования выхода во вход по принципу мультиподписи

Основные блокчейн-платформы  179

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

OP_CHECKSIG

Скрипт подписи



На рис. 4.23 приведен пример выхода, преобразованного во вход по принципу «Только открытый ключ» для одних из ранних транзакций (блок с высотой 170, созданный в январе 2009 года). На рис. 4.23 показан выход № 0 для
транзакции ea44e97271691990157559d0bdd9959e02790c34db6c006d779e82fa5ae
e708e, который был потрачен в качестве входа № 0 транзакции f4184fc596403b
9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16, образуя единственный
вход данной транзакции.
Скрипт открытого
ключа

Адрес пользователя, которому
предназначен выход

Суммы выхода и
входа совпадают

Скрипт подписи

Рисунок 4.23. Пример транзакции по принципу «Только открытый ключ»

180



Глава 4

Транзакция с нулевыми данными
Не получил широкой известности тот факт, что в системе Биткойн сегодня
можно совершать не только денежные платежи, но и отправлять какие-либо
данные вместо криптовалюты. Такие транзакции называются транзакциями
с нулевыми данными (NullData).
В системе Биткойн транзакции с нулевыми данными появились с обновлением системы в марте 2014 года и с переходом к ядру системы версии 0.9.0
(Bitcoin Core 0.9.0) [78]. Чтобы сохранить данные в блокчейне, вы просто отправляете транзакцию, но вместо того, чтобы отправлять ее на другой биткойн-адрес, вы отправляете транзакцию с нулевыми данными с выходом нулевого значения. Получается, что один из выходов содержит не сумму и адрес,
а сообщение и нулевую сумму.
Для обрабатывающих узлов нет необходимости сохранять такие транзакции
в списке транзакций с непотраченными выходами (UTXO). Тем не менее разработчики системы обращают внимание пользователей на то, что предпочтительнее хранить лишние данные вне блокчейна, чтобы не раздувать его.
Правила консенсуса позволяют добавлять в блок транзакции с ненулевыми
данными до максимально допустимого размера сценария открытого ключа
(10 000 байт), при условии что все транзакции соответствуют всем остальным
правилам консенсуса (таким как, например, отсутствие отправки данных размером более 520 байт).
Тем не менее для версий ядра Bitcoin Core 0.9.x-0.10.x по умолчанию будут
формироваться и обрабатываться транзакции, которые содержат нулевые данные размером до 40 байт и только один выход, в котором указана сумма в 0 сатоши (табл. 4.8).
Таблица 4.8. Основные скрипты сценария «Транзакция с нулевыми данными»
Скрипт открытого ключа

OP_RETURN

Скрипт подписи

Транзакции с нулевыми данными не могут быть потрачены,
поэтому скрипт подписи отсутствует

До того, как в ядро системы биткойн была введена проверка совпадения
суммы входа и суммы выхода, транзакция с нулевыми данными позволяла потратить в качестве входов некоторую сумму биткойнов, но при этом не формировать выход. Фактически деньги оказываются замороженными. То есть они
были потрачены, но больше никогда не могут быть использованы.
Так, например, транзакция eb31ca1a4cbd97c2770983164d7560d2d03276ae
1aee26f12d7c2c6424252f29, созданная 29 марта 2013 года, имеет два входа на
0.075 BTC и 0.05 BTC соответственно, что на начало января 2022 года составляло примерно 400 000 рублей. При этом выход транзакции содержит скрипт
открытого ключа OP_RETURN с суммой 0 ВТС (рис. 4.24).

Основные блокчейн-платформы  181

Рисунок 4.24. Пример транзакции с нулевыми выходами (заморозка биткойнов)

182



Глава 4

Еще одним ярким примером транзакции с нулевым выходом является транз­
акция, в которой после команды OP_RETURN записывается некоторое послание в формате UTF-8. Так, например, в транзакции 8bae12b5f4c088d940733dcd
1455efc6a3a69cf9340e17a981286d3778615684, созданной 30 июня 2014 года, выход № 0 не содержит денежного перевода. Зато он содержит послание, которое
в соответствующей кодировке гласит «charley loves heidi», что значит «Чарли
любит Хайди» (рис. 4.25).

Рисунок 4.25. Пример транзакции с нулевыми выходами (любовное послание)

В версии ядра Bitcoin Core 0.11.x размерность транзакции по умолчанию
была увеличена до 80 байт, при этом остальные правила остались прежними.
В версии Bitcoin Core 0.12.0 размер транзакции по умолчанию был увеличен до
83 байт. При этом было снято ограничение на размерность одной транзакции.
Главным оставалось условие не превышать общую размерность блока.
Параметр конфигурации --data carrier size для ядра Bitcoin Core позволит
вам установить максимальное количество байтов в транзакциях с нулевыми данными, которые вы планируете формировать и отправлять. Пример по
формированию таких транзакций с использованием специальной библиотеки
Python Bitcoin Library [204] для языка Python можно найти в статье [239].

Нестандартные транзакции
Если в выходных данных транзакции содержится что-либо, кроме стандартного значения скрипта открытого ключа, то узел ядра системы Биткойн (Bitcoin
Core) с настройками, заданными по умолчанию, не будет принимать, пересылать и майнить такую транзакцию.
Если вы создадите скрипт погашения, захешируете его и будете использовать полученный хеш-код как выход для транзакции P2SH, блокчейн-сеть будет видеть только хеш-код и, как следствие, она будет считать выход действительным, независимо от того, что выявляет скрипт погашения.

Основные блокчейн-платформы  183
Такой механизм позволяет производить оплату нестандартным скриптам,
а с версии ядра Bitcoin Core 0.11 можно потратить почти все действующие
скрипты погашения.
Исключение составляют скрипты, в которых используются неназначенные
коды операций NOP; эти коды операций зарезервированы для будущих софтфорков. Транзакции с такими кодами операций могут быть обработаны и замайнены только узлами, которые не соответствуют стандартной политике для
работы мемпула (пула памяти).
В исследовательской работе [77] авторы провели поиск нестандартных
транзакций и определили десять их видов, характерных для системы Биткойн
в разное время существования (табл. 4.9).
Таблица 4.9. Основные виды нестандартных транзакций
Вид
транзакции

Скрипт открытого ключа

Год возникновения

Pay to Public
Key Hash 0

OP_DUP OP_HASH160 0 OP_EQUALVERIFY OP_CHECKSIG

2011

P2PKH NOP

OP_DUP OP_HASH160 OP_EQUALVERIFY
OP_CHECKSIG OP_NOP

2011, 2014

OnlyHash

Только хеш

2011–2014

P2Pool Bug

OP_IFDUP OP_IF OP_2SWAP OP_VERIFY OP_2OVER OP_DEPTH

2012

CLTV

OP_CHECKLOCKTIMEVEIRFY OP_DROP

2012

OP_MIN OP_
EQUAL

OP_MIN 3 OP_EQUAL

2012

Pay to Hash
(P2H)

OP_HASH160 OP_
EQUALVERIFY

2012–2015

UnLocked (UL)

Пустой скрипт

2015

OP_RETURN
ERROR

OP_RETURN ERROR

2016–2017

OP_2 OP_3
ERROR

OP_2 OP_3 ERROR

2017–2018

Так, например, в ноябре 2011 г. биржа Mt.Gox стала жертвой ошибки в своем программном обеспечении (ПО) [17], вследствие чего 2609.36304319 BTC
было отправлено на адрес биржи с использованием Pay to Public Key Hash 0.
Эти деньги остались навсегда замороженными в системе Биткойн. На январь
2022 года эта сумма составляет 8,5 миллиарда рублей.
Сайт Blockchain.com (с помощью которого, в частности, сделана большая
часть рисунков в настоящей книге) на январь 2022 года не отображает адреса,
для которых используются нестандартные транзакции, но просто выводит сообщение «Невозможно декодировать выходной адрес». Пример нестандартного выхода для транзакции 6d5088c138e2fbf4ea7a8c2cb1b57a76c4b0a5fab5f4c188
696aad807a5ba6d8 показан на рис. 4.26.

184



Глава 4

Рисунок 4.26. Пример нестандартной транзакции

Однако альтернативный ресурс blockchair.com отображает адреса с нестандартными транзакциями и позволил определить адрес биржи Mt.Gox, на который были выведены деньги. Адрес имеет нестандартный вид s-272edf45031dd
498e7b3ae89e11ff21b (рис. 4.27).

Рисунок 4.27. Кошелек s-272edf45031dd498e7b3ae89e11ff21b

Кроме того, ресурс blockchair.com позволяет получить специальную выписку
по кошельку, в которой видны все совершенные транзакции. По адресам данных транзакций можно проверить, что все они соответствуют нестандартному
скрипту Pay to Public Key Hash 0. На рис. 4.28 приведен фрагмент выписки, полную выписку можно получить по ссылке [2].

Основные блокчейн-платформы  185

Рисунок 4.28. Выписка по кошельку s-272edf45031dd498e7b3ae89e11ff21b

Ресурс bitcoin-supply.com отображает как самые большие, так и последние
потери в системе Биткойн. Здесь можно получить информацию о номере блока, в котором произошла утрата, идентификатор и сумму транзакции.
Здесь же стоит упомянуть о том, что в ранних версиях системы Биткойн не
проводилось отслеживание создания двойных идентичных транзакций. Самы-

186



Глава 4

ми подверженными этой уязвимости оказались Coinbase-транзакции, по которым майнер получает вознаграждение за созданный блок.
Так как Coinbase-транзакция не содержит в своем составе ссылок на предыдущие выходы и формируется по одному и тому же принципу, то известно, по крайней мере, два случая, когда одинаковая транзакция была создана и начислена
майнеру. Так, в блоки 91812 и 91842 добавлена одна и та же Coinbase-транзакция
d5d27987d2a3dfc724e359870c6644b40e497bdc0589a033220fe15429d88599
(рис. 4.29). Кроме того, в блоки 91722 и 91880 также добавлена одна и та же
транзакция e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585
fb468 (рис. 4.30).

Рисунок 4.29. Дублирующая транзакция для блоков 91812 и 91842

Рисунок 4.30. Дублирующая транзакция для блоков 91722 и 91880

Основные блокчейн-платформы  187
Фактически произошло то, что майнер получил в качестве вознаграждения
дважды одну и ту же транзакцию, то есть по факту вместо 100 BTC получил всего 50 BTC. Один из разработчиков системы Биткойн Рассел О’Коннор (Russell
O’Connor) считает, что в случае с дублирующими транзакциями злоумышленник может удалять прошлые транзакции других пользователей из реестра [22].
Для того чтобы иметь возможность отслеживать дублирующие транзакции,
в 2012 году были введены улучшения BIP30 [248] и BIP34 [55].
Важно отметить, что стандартные транзакции предназначены для защиты
именно биткойн-сети, а не для того, чтобы защитить пользователя от совершения ошибок. Начиная с версии ядра системы Биткойн Bitcoin Core 0.9.3 стандартные транзакции также должны соответствовать следующим условиям:
1. Транзакция должна быть завершена: либо ее время блокировки должно
быть в прошлом (или меньше, или равно текущей высоте блока), либо все ее
порядковые номера должны иметь шестнадцатеричное значение FFFF FFFF.
2. Размер транзакции должен быть меньше 100 000 байт. Это примерно
в 200 раз больше, чем типичная транзакция P2PKH с одним входом и одним выходом.
3. Каждый из скриптов подписи транзакции должен быть меньше 1650 байт.
Этого достаточно, чтобы выполнить проверку 15 транзакций с несколькими подписями вида P2SH с использованием сжатых открытых ключей.
4. Открытые транзакции с несколькими подписями, которые не являются
транзакциями типа P2SH и для которых требуется более трех открытых
ключей, в настоящее время являются нестандартными.
5. Задача скрипта подписи транзакции заключается в том, чтобы помес­
тить данные в стек для выполнения проверки. Он не может использовать
новые коды операций, за исключением тех кодов операций, которые используются для помещения данных в стек.
6. Выход транзакции не может быть меньше, чем 1/3 от того количества сатоши, которое необходимо потратить в качестве комиссии за транзакцию.
В настоящее время эта сумма составляет 546 сатоши для транзакций вида
P2PKH или P2SH. Исключение составляют стандартные транзакции с нулевыми данными, у которых выход должен содержать нулевые сатоши.

Время блокировки и порядковый номер
Поле, в которое записано время блокировки транзакции (Locktime, в исходном
коде ядра Bitcoin Core это поле называется nLockTime), может подписываться
с любым типом хеш-кода, то есть подписывается всегда. Поле Locktime показывает самое раннее время, когда транзакция может быть добавлена в цепочку блоков.
Locktime позволяет подписывающим сторонам создавать транзакции с временной блокировкой, которые станут действительными только в будущем, давая подписавшимся сторонам возможность изменить свое мнение.
Если кто-либо из подписывающих сторон в транзакции с блокировкой (первой транзакции) передумает, он может создать новую (вторую) транзакцию без
блокировки. Вторая транзакция будет использовать в качестве одного из своих
входов один из входов первой транзакции. Это сделает первую транзакцию недействительной, если вторая транзакция будет добавлена в цепочку блоков до
истечения срока блокировки.

188

 Глава 4

Необходимо соблюдать осторожность перед истечением срока действия временной блокировки. Одноранговая сеть позволяет блокировать время за два
часа до истечения реального времени, поэтому транзакция времени блокировки может быть добавлена в цепочку блоков за два часа до того, как ее временная
блокировка официально истечет. Кроме того, блоки не создаются с гарантированными интервалами, поэтому любую попытку отменить ценную транзакцию
следует предпринимать за несколько часов до истечения срока блокировки.
Предыдущие версии ядра Bitcoin Core использовали функцию, которая не
позволяла подписывающим транзакциям использовать вышеописанный метод отмены транзакций. Но необходимая часть этой функции была отключена
с целью предотвращения атаки типа «отказ в обслуживании».
Как следствие используемой ранее функции сейчас используются четырехбайтовые порядковые номера для обозначения каждого входа/выхода. Порядковые номера должны были позволить нескольким подписывающим сторонам
прийти к соглашению об обновлении транзакции: когда они завершили обновление транзакции, они могли договориться установить порядковый номер
каждого входа равным четырехбайтовому беззнаковому максимуму (шестнадцатеричное значение FFFF FFFF), что позволяло добавить транзакцию в блок,
даже если ее временная блокировка еще не истекла.
Даже сегодня установка всех порядковых номеров в значение FFFF FFFF (как
это происходит по умолчанию в Bitcoin Core) может отключить временную
блокировку. Поэтому если вы хотите использовать время блокировки, то, по
крайней мере, один вход должен иметь порядковый номер ниже максимального. Поскольку порядковые номера не используются сетью для каких-либо
других целей, установки любого порядкового номера в ноль достаточно для
включения времени блокировки.
Само время блокировки представляет собой 4-байтовое целое число без знака, которое можно проанализировать двумя способами:
 если значение числа меньше 500 миллионов, то время блокировки соответствует высоте блока; транзакцию можно добавить в любой блок, имеющий эту высоту или выше;
 если значение числа больше или равно 500 миллионам, то время блокировки рассчитывается с использованием формата времени эпохи Unix
(количество секунд, прошедших со времени 0:00:00 1 января 1970 года;
в настоящее время эта величина больше 1,395 миллиарда); транзакция
может быть добавлена к любому блоку, время блока которого больше
времени блокировки.
Отследить время блокировки можно с использованием различных сервисов.
Например, сервис blockchair.com покажет вам все транзакции, которые в текущий момент находятся в пуле.
Можно настроить параметры отображения и вывести на экран интересующие поля транзакции, в том числе поле «Время блокировки». Также есть функция сортировки от большего к меньшему и наоборот.
Так, мы можем видеть, что на рис. 4.31 представлены транзакции или вообще без блокировки (в поле блокировки стоит значение 0), или указана высота
блока (как правило, следующего блока по отношению к текущему); на рис. 4.31
представлены данные, актуальные на 15 января 2022 года.

Основные блокчейн-платформы  189

Рисунок 4.31. Транзакции по первому типу блокировки

Если же попробовать отсортировать транзакции по полю «Время блокировки»
от большего к меньшему, то можно видеть на рис. 4.32, что самая первая транз­
акция имеет время блокировки, записанное в соответствии со временем Unix.
Значение 1 642 282 318 указывает на время 16.01.2022 г. 00:31:58 в соответствии
с часовым поясом Москва (+03:00). Выполнить подобный перевод можно с использованием любого онлайн-сервиса по конвертации времени, например [33].

Рисунок 4.32. Транзакции по второму типу блокировки

190



Глава 4

Комиссия за транзакцию
За то, что транзакция попадает в блок, может взиматься комиссия. Величина
комиссии зависит от количества подписанных байтов транзакции.
Комиссия за каждый байт рассчитывается на основе текущего спроса на
пространство в добытых блоках, при этом плата увеличивается по мере увеличения спроса. Комиссия за каждую транзакцию, попавшую в блок, начисляется
тому майнеру, который этот блок создал. Каждый майнер устанавливает свое
значение минимальной комиссии за каждую транзакцию, которую он будет
принимать и пытаться замайнить.
Также существует концепция так называемых «высокоприоритетных транз­
акций». К высокоприоритетным транзакциям относятся транзакции, которые
долгое время хранились в блокчейн-цепочке.
Считается, что длительный период хранения отражает величину приоритета транзакции. С таких транзакций комиссия не взимается. Однако каждый
майнер сам решает, будет он работать с бесплатными транзакциями или нет.
До версии ядра Bitcoin Core 0.12 для таких высокоприоритетных транзакций
в каждом блоке было зарезервировано 50 КБ, однако теперь по умолчанию
установлено значение 0 КБ.
Всем незамайненным транзакциям присваивается приоритет на основе
суммы их комиссии за байт. При этом обработка начинается с более высокооплачиваемых транзакций. Транзакции добавляются в блок последовательно,
пока не будет заполнено все доступное пространство.
Начиная с версии ядра Bitcoin Core 0.9 комиссия стала обязательным атрибутом транзакции. Для того чтобы транзакция передавалась по сети, требовалось уплатить минимальную комиссию (в настоящее время 1000 сатоши). Любая транзакция, оплачивающая толькоминимальную комиссию, может долгое
время стоять в очереди ожидания, прежде чем в блоке будет достаточно свободного места для включения такой транзакции.
Величина комиссии автоматически вычитается из всех замайненных транз­
акций блока и добавляется к сумме вознаграждения майнера. На рис. 4.33 показана комиссия для блока 715 000, созданного 21 декабря 2021 года.

6,25

+0,1

3504

Рисунок 4.33. Комиссия для блока 715 000

850

Основные блокчейн-платформы  191
Отследить сумму комиссии можно с использованием различных сервисов.
Так же, как и со временем блокировки, сервис blockchair.com покажет вам все
транзакции, которые в текущий момент находятся в пуле.
Можно настроить параметры отображения и вывести на экран интересующие поля транзакции, в том числе поле «Комиссия», а затем отсортировать
транзакции по ее величине, например по убыванию (рис. 4.34).

Рисунок 4.34. Транзакции с различной стоимостью комиссии в майнинг-пуле

Выход сдачи
Поскольку каждая транзакция тратит неизрасходованные выходы предыдущих
транзакций (UTXO) и поскольку такие выходы можно потратить только один раз,
то сумма всех неизрасходованных выходов, помещенная в транзакцию, должна
быть или потрачена, или передана майнеру в качестве комиссии за транзакцию.
При этом очень редко получается так, что сумма неизрасходованных выходов
точно соответствует той сумме, которую необходимо заплатить. Поэтому большинство транзакций содержат так называемый выход сдачи (change output).
Выход сдачи представляет собой обычный выход. Его особенность заключается в том, что в качестве адреса получателя данного выхода сдачи указывается пользователь, осуществляющий перевод. Таким образом ему в виде UTXO
вернется обратно сдача от совершенной операции.
Пример транзакции со сдачей приведен на рис. 4.35, он относится к ранним
транзакциям системы Биткойн. Транзакция 25252ecd1a0fffbb294898d831cd5fe
901ab7c4e6e181e3b9a889bf3640a6dc2 была создана 17 ноября 2014 года пользователем 122BNoyhmuUt9G9mdEm3mN4nb73c1UgNKt. По рис. 4.35 видно, что
часть денег от суммы входов была переведена пользователю 16TBqpEFpivLtDR
AtmNnCxEdJW1coodiia. Остальная сдача в количестве 110 BTC вернулась пользователю 122BNoyhmuUt9G9mdEm3mN4nb73c1UgNKt.

192

 Глава 4

Рисунок 4.35. Пример транзакции с выходом сдачи

Как избежать повторного использования ключей
В транзакции и отправитель, и получатель раскрывают друг другу все открытые
ключи или адреса, применяемые в транзакции. Это позволяет любому человеку использовать общедоступную цепочку блоков для отслеживания прошлых
и будущих транзакций с применением тех же открытых ключей или адресов
какого-либо пользователя.
Если один и тот же открытый ключ часто используется повторно, как это
происходит, когда люди используют биткойн-адреса – 20-байтовые дважды хешированные открытые ключи по формуле RIPEMD-160(SHA-256(public_key)) –
в качестве статических платежных адресов, другие люди могут легко отслеживать историю поступлений и трат такого пользователя, включая знание о том,
сколько денег доступно в настоящий момент адресу данного пользователя.
Несмотря на то что блокчейн является открытой системой, такая открытость
ставит под угрозу пользователей сети Биткойн, делает их уязвимыми.
Поэтому считается, что правильно – использовать каждый открытый ключ
ровно дважды: один раз для получения платежа и один раз для его расходования. В таком случае пользователь может получить значительную финансовую
конфиденциальность.
Более того, использование новых открытых ключей или уникальных адресов при приеме платежей или создании выходов сдачи можно комбинировать
с другими методами, для того чтобы еще сильнее запутать следы и предотвратить возможность использования цепочки блоков для отслеживания того, как
пользователи получают и тратят свои деньги.
Предотвращение повторного использования ключей также может обеспечить
дополнительную защиту от потенциальных атак по реконструкции закрытых
ключей из открытых ключей (однако вероятность практической реализации подобных атак для используемого алгоритма ECDSA крайне мала) или по сравнению подписей (возможно сегодня при определенных обстоятельствах, описанных далее, с предположительным использованием в контексте более общих атак).
Уникальные (не используемые повторно) адреса для транзакций типа P2PKH
и P2SH защищают от первого типа атак, сохраняя открытые ключи ECDSA
скрытыми (хешированными) до тех пор, пока не будут израсходованы монеты,
отправленные на эти адреса в первый раз, поэтому атаки фактически бесполезны, если они не могут восстановить закрытые ключи в течение одного-двух
часов, пока транзакция не будет надежно встроена в цепочку блоков.
Уникальные (не используемые повторно) закрытые ключи защищают от
второго типа атак путем создания только одной подписи для каждого закрытого ключа, в результате чего злоумышленники никогда не получают последую-

Основные блокчейн-платформы  193
щую подпись для использования в атаках на основе сравнения. Существующие
атаки, основанные на сравнении, сегодня практичны только тогда, когда при
подписании используется недостаточная энтропия для генерации случайных
чисел или когда используемая энтропия раскрывается некоторыми средствами, такими как атаки по побочным каналам. Пример описания подобной атаки
был приведен ранее в разделе 2.3.3.
Подводя итог, можно сказать, что как для обеспечения конфиденциальности транзакций, так и для их безопасности в общем разработчики системы
Биткойн рекомендуют избегать повторного использования открытого ключа
и адресов в сети Биткойн.

Гибкость транзакции
Ни один из типов подписываемого хеш-кода в системе Биткойн не защищает скрипт подписи, оставляя возможность злоумышленнику применить атаку
типа «отказ в обслуживании». Данное свойство называется «гибкостью транз­
акции» (Transaction Malleability).
Скрипт подписи содержит подпись в соответствии с алгоритмом ECDSA,
которая не может подписывать сама себя. Это позволяет злоумышленникам
вносить нефункциональные изменения в транзакцию (в те поля, которые не
подлежат подписи), при этом сама транзакция остается действительной.
Например, злоумышленник может добавить некоторые данные в скрипт
подписи, которые будут удалены до обработки предыдущего скрипта открытого ключа.
Хотя модификации нефункциональны (то есть они не меняют входы и выходы, которые используются в транзакции), они тем не менее влияют на изменение хеш-кода транзакции. Так как идентификатор транзакции (txid) является
значением хеш-кода этой транзакции, то ее создатель будет пытаться использовать исходный идентификатор для дальнейшего управления выходами. Однако
если злоумышленник внесет изменения в транзакцию и, как следствие, изменит
идентификатор транзакции, создатель транзакции этого сделать уже не сможет.
Подобная ситуация не является проблемой для большинства биткойн-транз­
акций, которые предназначены для немедленного добавления в цепочку блоков. Однако это становится проблемой, когда выходные данные транзакции
изменяются до того, как транзакция добавляется в цепочку блоков. Разработчики сети Биткойн постоянно работают над снижением гибкости транзакций
среди стандартных типов транзакций.
Так, при использовании улучшения BIP62 предлагаются изменения в правилах проверки действительности транзакций системы Биткойн, чтобы сделать
гибкость транзакций невозможной [249].
Еще одним вариантом улучшения является BIP141: Segregated Witness [168], которое поддерживается ядром Bitcoin Core и было активировано в августе 2017 года.
Улучшение BIP141 определяет новую структуру, называемую «свидетельство»
(witness), которая фиксируется в блоках отдельно от транзакций дерева Меркля.
Эта структура содержит данные, необходимые для проверки действительности
транзакции. В частности, в эту новую структуру перемещены скрипты и подписи.
Получается, что теперь не входы и выходы содержат данные для проверки,
а специально отведенное для этого поле witness. Помимо того что теперь зло­

194



Глава 4

умышленник не может внести изменения в сами инструкции, сократился и общий объем, занимаемый одной транзакцией. Таким образом, в настоящий момент блок биткойна может вмещать около 2700 вместо 1650 транзакций [52].
Это значительно повысило пропускную способность сети Биткойн.
С введением Segregated Witness поменялась форма скрипта открытого ключа для транзакций типа транзакций P2PKH и P2SH (важно отметить, что поменялась форма записи, но не смысл самих проверок).
Так, вместо P2PKH теперь используется форма транзакции P2WPKH (Pay-ToWitness-Public-Key-Hash – «Плата за свидетельство хеша открытого ключа»).
Для транзакции P2WPKH скрипт открытого ключа содержит значение 0, за которым следует 20-байтовый хеш-код ключа, скрипт подписи остается пустым,
а поле witness содержит подпись и открытый ключ [37]. Основные поля транз­
акции P2WPKH приведены в табл. 4.10.
Таблица 4.10. Основные поля транзакции P2WPKH
Скрипт открытого ключа

OP_0

Скрипт подписи

Пустое поле

Witness



На рис. 4.36 представлен пример транзакции типа P2WPKH, где выход № 0
транзакции 1d8149eb8d8475b98113b5011cf70e0b7a4dccff71286d28b8b4b641f94f
1e46 превращается во вход № 24 транзакции 5944d4ff0e5620c7933c9a3c15c21f2
dd33a6f8192a54109711a8a374a6bf1e3.
Номер
выхода

Скрипт открытого
ключа

Номер
входа

Адрес пользователя, которому
предназначен выход

Пустой скрипт
подписи

Суммы входа и
выхода совпадают

Поле Witness с набором
инструкций

Рисунок 4.36. Пример перехода выхода во вход для транзакции типа P2WPKH

Основные блокчейн-платформы  195
Аналогичным образом в качестве транзакции P2SH теперь используется
форма транзакции P2WSH (Pay-To-Witness-Script-Hash – «Плата за свидетельство хеша скрипта»). Для транзакции P2WSH скрипт открытого ключа содержит значение 0, за которым следует 32-байтовый хеш скрипта witness, скрипт
подписи остается пустым, а поле witness содержит в качестве префикса нулевое значение, за которым следуют подпись, открытые ключи и инструкция
для проверки (могут быть варианты как для одной подписи, так и для мультиподписи) [37].
Поле witness для транзакции P2WSH делится на две части. Первая часть содержит список подписей, с помощью которых доказывается владение монетами. Во второй части размещается скрипт witness (Witness Script), содержимое
которого определяет правила траты.
Основное отличие заключается в том, что содержимое скрипта теперь указывается в момент траты монет (при создании входа), а в момент отправки
монет (при создании выхода) указывался только хеш-код от скрипта witness.
Основные поля транзакции P2WSH приведены в табл. 4.11.
Таблица 4.11. Основные поля транзакции P2WSH
Скрипт открытого ключа

OP_0

Скрипт подписи

Пустое поле

Witness

0 < 1 2 OP_CHECKMULTISIG>

На рис. 4.37 представлен пример транзакции типа P2WSH, где выход № 0
транзакции af5c739ff9b1ff8921bb7aced0d473570d6ed7bed0171b4f216bd526951c
0cf0 превращается во вход № 0 транзакции 55e16683f14b4ae4b4652c8b459127
cb8823c06ed030c1349fded95ab1bbbf0b.
Так как новый формат отличается от старого, то для обратной совместимости (чтобы старые сервисы и кошельки могли обрабатывать новые транзакции) используется специальная структура, которая позволяет сформировать
транзакцию, обладающую свойствами Segregated Witness с одной стороны, но
не отличается от структуры P2SH с другой стороны [37]. Основные поля транз­
акции P2WSH с обратной совместимостью приведены в табл. 4.12.

196

 Глава 4
Номер
выхода

Скрипт открытого
ключа

Номер
входа

Адрес пользователя, которому
предназначен выход

Пустой скрипт
подписи

Суммы входа и
выхода совпадают

Поле Witness с
набором инструкций

Рисунок 4.37. Пример перехода выхода во вход для транзакции типа P2WSH
Таблица 4.12. Транзакция P2WSH с обратной совместимостью
Скрипт открытого ключа

OP_HASH160 OP_EQUAL

Скрипт подписи

OP_0

Witness



На рис. 4.38 представлен пример транзакции типа P2WSH с обратной совместимостью, где выход № 0 транзакции превращается во вход № 0 другой
транзакции.
Гибкость транзакций также влияет на отслеживание платежей. Интерфейс
системы Биткойн позволяет отслеживать транзакции по их идентификатору,
но если этот идентификатор изменится из-за изменения транзакции, то может
показаться, что транзакция исчезла из сети.
Опыт работы с системой Биткойн по отслеживанию транзакций показывает, что самым предпочтительным способом отслеживания транзакции является отслеживание с помощью выходов UTXO, так как их нельзя изменить без
признания транзакции недействительной. Также необходимо учитывать, что
если транзакция действительно исчезает из сети и ее необходимо перевыпус­
тить, то ее следует повторно выпустить таким образом, чтобы сделать потерянную транзакцию недействительной. Самый действенный метод в данном

Основные блокчейн-платформы  197
случае – убедиться, что новая транзакция использует все те же выходы, которые потерянная транзакция использовала в качестве входов.
Номер
выхода

Скрипт открытого
ключа

Номер
входа

Адрес пользователя, которому
предназначен выход

Скрипт
подписи

Суммы входа и
выхода совпадают

Поле Witness с
набором инструкций

Рисунок 4.38. Пример перехода выхода во вход для транзакции типа P2WSH с обратной
совместимостью

Смарт-контракты в системе Биткойн
Смарт-контракт представляет собой транзакцию особого типа. Такие транзакции в системе Биткойн используются для обеспечения соблюдения финансовых соглашений. Предназначение контрактов в первую очередь заключается
в том, чтобы избежать участия посредников при совершении сделок.
Контракт условного депонирования и арбитраж
Рассмотрим пример контракта между некими продавцом и покупателем [99].
У Алисы есть товар, и она хочет продать его Бобу. У Боба есть деньги, и он хочет
купить товар Алисы. Но оба они не доверяют друг другу. В этом случае они могут использовать контракт, чтобы гарантировать, что Боб получит свой товар,
а Алиса получит оплату.
Самый простой вариант контракта: Алиса сможет потратить выход Боба
с указанной суммой только в том случае, если Алиса и Боб оба подпишут входную транзакцию. Это означает, что Алисе не заплатят, если Боб не получит свой
товар, и Боб не сможет оставить товар себе и получить при этом деньги Алисы.
Однако этот простой контракт будет работать в том случае, если Алиса и Боб
являются честными партнерами и у них не возникает споров. Поэтому для ре-

198



Глава 4

шения споров в контракт добавляется третий игрок – арбитр Кэрол, а также
создается контракт условного депонирования (эскроу).
Боб из своих денег создает выход, который можно потратить, только если
двое из трех человек подпишут вход. Теперь Боб может заплатить Алисе, если
все в порядке; Алиса может вернуть деньги Бобу, если возникнет проблема,
или Кэрол может выступить в качестве арбитра и решить, кто должен получить
деньги, если возникнет спор.
Чтобы создать выход с несколькими подписями (multisig), все участники конт­
ракта должны обменяться своими открытыми ключами. После этого покупатель
(Боб) создает следующий скрипт погашения мультиподписи P2SH (в скрипте не
показаны коды операций для помещения открытых ключей в стек):
OP_2 [Открытый ключ Алисы] [Открытый ключ Боба] [Открытый ключ Кэрол] OP_3
OP_CHECKMULTISIG

Коды операций OP_2 и OP_3 помещают в стек числа 2 и 3 соответственно.
OP_2 указывает, что для исполнения транзакции достаточно наличия двух
подписей; OP_3 указывает, что предоставляются три открытых ключа (без хеширования).
Данный скрипт представляет собой скрипт открытого ключа с несколькими подписями «2 из 3», в более общем смысле называемый скриптом открытого ключа m из n (где m – минимальное количество требуемых совпадающих
подписей, а n – количество предоставленных открытых ключей).
Алиса передает скрипт погашения Бобу, который проверяет, что в скрипт
включены все открытые ключи, включая открытый ключ арбитра Кэрол. Боб
хеширует скрипт погашения для создания скрипта погашения транзакции
P2SH и переводит деньги на счет этого скрипта погашения. Алиса видит, что
платеж добавляется в цепочку блоков, и после этого отправляет товар.
Допустим, при транспортировке товара с ним неосторожно обращались
и часть товара была повреждена. Боб требует полного возмещения денег, но
Алиса считает, что достаточно компенсировать 20 % от стоимости товара. Они
обращаются к Кэрол, чтобы решить проблему. Кэрол просит у Боба фото-свидетельство вместе с копией скрипта погашения, созданного Алисой и проверенного Бобом. Изучив доказательства, Кэрол считает, что возврата 30 % достаточно, поэтому она создает и подписывает транзакцию с двумя выходами,
один из которых переводит 70 % денег на открытый ключ Алисы, а второй
переводит оставшиеся 30 % на открытый ключ Боба.
В скрипт подписи Кэрол помещает свою подпись и копию нехешированного
скрипта погашения, созданного Алисой. Она передает копию незавершенной
транзакции Алисе и Бобу. Любой из них может завершить ее, добавив свою
подпись для создания следующего скрипта подписи (в скрипте не показаны
коды операций для отправки подписей и скрипта погашения в стек):
OP_0 [подпись Aлисы] [подпись Боба или Кэрол] [Скрипт погашения]

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

Основные блокчейн-платформы  199
Когда транзакция передается по сети, каждый узел сверяет скрипт подписи
с выходом транзакции P2SH, ранее сформированным Бобом, гарантируя, что
скрипт погашения действительно соответствует ранее предоставленному хешкоду скрипта погашения.
После этого выполняется проверка скрипта погашения, при этом в качестве
входных данных используются две подписи. Если скрипт погашения проходит
проверку, то два выхода транзакции отображаются в кошельках Алисы и Боба
как неизрасходованные остатки (UTXO).
В случае если Кэрол создала и подписала транзакцию, на которую ни Алиса,
ни Боб не согласны (например, Кэрол создала выход, который переводит все
деньги на ее счет), то Алиса и Боб могут найти нового арбитра и создать новую
транзакцию с новым скриптом погашения с несколькими подписями в формате 2 из 3. Это позволяет Алисе и Бобу не беспокоиться о том, что арбитр может
украсть их деньги.
Для удобства работы с такими типами контрактов используются специальные приложения. Например, ресурс BitRated [82] предоставляет интерфейс
службы арбитража с несколькими подписями, разработанный с использованием средств HTML/JavaScript на веб-сайте с лицензией GNU AGPL.
Канал микроплатежей
Рассмотрим другой сценарий [99]. Пусть Алиса делает для Боба сдельную работу. Например, она следит за работой сайта Боба: размещает новости, обновляет
контент, отвечает на обращения пользователей. Боб обещал платить Алисе за
каждое действие, совершенное ею на форуме.
Однако по факту оказалось, что Боб часто забывает заплатить ей. Поэтому
Алиса требует оплаты сразу после каждого выполненного действия. Боб отказывается пересылать транзакции за каждое выполненное действие, так как
в этом случае ему необходимо будет платить комиссию за каждую транзакцию.
В этом случае на помощь Алисе и Бобу приходит канал микроплатежей.
Боб запрашивает у Алисы ее открытый ключ, а затем создает две транзакции. Первая транзакция создает перевод 100 миллибиткойнов на выход транз­
акции P2SH, для которой скрипт погашения с мультиподписью 2 из 2 требует
подписей как Алисы, так и Боба.
Данная транзакция является транзакцией с долговым обязательством.
Трансляция этой транзакции в сеть позволит Алисе удерживать миллибиткойны Боба в заложниках в течение времени блокировки (в случае данного примера это 24 часа).
Боб пока сохраняет эту транзакцию у себя и создает вторую транзакцию.
Вторая транзакция возвращает все миллибиткойны первой транзакции (за вычетом комиссии за транзакцию; на рис. 4.39 показана схема без учета комиссии) обратно Бобу после 24-часовой задержки, установленной временем блокировки.
Данная транзакция является транзакцией возврата. Боб не может подписать транзакцию возврата самостоятельно, поэтому он передает ее Алисе на
подпись, как показано на рис. 4.39 (перевод рисунка с сайта [99]).

200

 Глава 4

Компьютер
Алисы

Боб: Пожалуйста, подпиши возврат № 1. Это полный
возврат по долговому обязательству, который не может
быть потрачен в течение 24 часов
Алиса: Я подписала. Пожалуйста, пришли мне долговое
обязательство, чтобы доказать, что ты пополнил счет

Компьютер
Боба
Возврат № 1
Боб: 100
Алиса: 0
Блокировка
времени

Боб: Вот долговое обязательство. Пожалуйста, начинай
работу
Долговое
обязательство
(2 подписи из 2)

Алиса: Я сделала работу. Пожалуйста, подпиши возврат
№ 2, уменьшив твой счет на 1
Боб: Подписал. Сейчас возврат Бобу 99, Алисе 1
Алиса: Я сделала еще работу. Пожалуйста, подпиши
возврат № 3, уменьшив твой счет еще на 1
Боб: Подписал. Сейчас возврат Бобу 98, Алисе 2
Алиса: ….
Боб: ….

Возврат № 45
Боб: 66
Алиса: 44
Нет блокировки

Алиса: Я сделала всю работу на сегодня. Я отправляю в
сеть транзакцию с возвратом № 45

Рисунок 4.39. Схема взаимодействия Алисы и Боба при формировании канала
микроплатежей

Алиса проверяет, что первая транзакция Боба имеет блокировку на 24 часа (а
значит, Боб не сможет потратить эту транзакцию в ближайшие сутки), подписывает ее и возвращает копию Бобу. Затем она спрашивает Боба о транзакции
с долговым обязательством.
Боб формирует вторую транзакцию, в которой он в качестве входных данных указывает выход первой транзакции, по-прежнему переводя на свой счет
полную сумму из транзакции номер 1. Алиса следит за тем, чтобы вход второй
транзакции совпадал с выходом первой транзакции.
Если Алиса сейчас подпишет эту транзакцию в сеть, то Бобу придется выждать 24 часа, прежде чем он сможет потратить свои деньги. Если Алиса ничего не сделает, Боб также сможет тратить свои деньги, после того как истекут
24 часа. По факту в этот момент Боб еще ничего не теряет (кроме, возможно,
небольшой комиссии за транзакцию).
Теперь каждый раз после того, как Алиса выполняет какую-то работу стоимостью 1 миллибиткойн, она просит Боба создать и подписать новую версию

Основные блокчейн-платформы  201
транзакции долгового обязательства. Вторая версия транзакции переводит
1 миллибиткойн Алисе, а остальные 99 – обратно Бобу.
У транзакции долгового обязательства нет времени блокировки, поэтому
Алиса может сразу подписать эту транзакцию и отправить в сеть. Но она этого
не делает, так как сделала еще не всю запланированную работу. Алиса и Боб
повторяют шаги работы и оплаты до тех пор, пока Алиса не закончит рабочий
день или пока не истечет срок действия временной блокировки.
После этого Алиса подписывает окончательную версию транзакции долгового обязательства и отправляет ее в сеть. Таким образом Алиса получает ту
сумму, которую она успела заработать, а Боб получает остаток. На следующий
день, перед тем как Алиса приступит к работе, они создадут новый канал
микро­платежей.
Если Алиса не успеет отправить в сеть блокчейна (включая процесс майнинга) свою транзакцию с переводом заработанных денег на свой счет
до того, как первая транзакция Боба будет разблокирована, то Боб может
успеть получить полный возврат своих денег. Это одна из причин, по которой каналы микроплатежей лучше всего подходят для небольших платежей. Если, например, у Алисы возникнут проблемы с интернетом ближе
к истечению срока действия временной блокировки, она не сможет отправить свою транзакцию в сеть и, как следствие, ее платеж может оказаться
недействительным.
Считается, что для больших платежей сумма комиссии ничтожно мала (по
сравнению с суммой перевода) и поэтому имеет смысл сразу отправлять транз­
акцию с переводом денег в сеть Биткойн.
На сайте bitcoinj [83] представлена библиотека для языка программирования Java, которая содержит полный набор функций микроплатежей. Пример
реализации микроплатежей с помощью данной библиотеки приведен в специальном руководстве [247].

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

202



Глава 4
Непотраченные
выходы Боба (UTXO)
50 BTC

Непотраченные выходы
Алисы (UTXO)
25 BTC

15 BTC

10 BTC

Входы

Непотраченные выходы
Кэрол (UTXO)
20 BTC

30 BTC

Выходы
50 BTC

50 BTC

50 BTC

Транзакция CoinJoin
Участник 1

Участник 3
Участник 2

Рисунок 4.40. Пример транзакции объединения монет

Рассмотрим более подробно транзакцию, пример которой приведен на
рис. 4.40. Алиса, Боб и Кэрол решают создать транзакцию CoinJoin. Каждый
из них подготавливает входы на общую сумму 50 BTC. Кроме того, каждый из
них готовит новые адреса в сети Биткойн (на рис. 4.40 это Участник 1, Участник 2 и Участник 3).
Эти адреса и данные входов передаются одному из участников соглашения –
фасилитатору. Допустим, фасилитатором является Алиса. Тогда Боб и Кэрол
должны передать Алисе данные своих входов и хеш-кодов открытых ключей.
Алиса создает транзакцию типа CoinJoin, расходующую каждый из UTXO на
три выхода одинакового размера. Затем Алиса подписывает транзакцию с использованием типа подписываемого хеш-кода SIGHASH_ALL, для того чтобы
никто не мог изменить входные или выходные данные.
Она передает частично подписанную транзакцию Бобу, который подписывает свои данные таким же образом. После этого Боб передает транзакцию Кэрол, которая также подписывает ее. После этого Кэрол отправляет транзакцию
в сеть Биткойн, для того чтобы она попала в блок.
Таким образом, получается, что никто, кроме Алисы, Боба и Кэрол, не знает
новые адреса друг друга и потому не может проследить дальнейшее расходование средств. Если выполнить несколько транзакций CoinJoin, то след будет запутан еще сильнее, что повысит конфиденциальность для средств участников
таких транзакций.
В рассмотренном выше варианте транзакции CoinJoin участники должны
будут выплатить некоторое вознаграждение за транзакции. Альтернативой
этому служит метод, когда несколько пользователей хотят совершить покупку
или перевод на одинаковую сумму денег. Тогда они могут для выходных транз­
акций вместо новых адресов участников сети использовать непосредственно
адреса своих продавцов.

Основные блокчейн-платформы  203
Чем больше пользователей принимает участие в сделке с транзакцией
CoinJoin, тем сложнее проследить историю переводов по сети Биткойн, но тем
больше пользователей будут посвящены в детали сделки.
Считается [4], что самой масштабной анонимной операцией с использованием
транзакций CoinJoin была транзакция на сумму 34,30693136 BTC (около 111,5 млн
рублей на январь 2022 года). Операция была приурочена к юбилейному событию –
10-летию со дня публикации White Paper биткойна [187]. Эта транзакция была создана 1 ноября 2018 года в 1:43 и имеет хеш-код 940ee6db84456b34a419112f71394f8
1c236873fbc5262ae2398e29a1171475f. Подробности приведены на рис. 4.41.

Рисунок 4.41. Большая анонимная транзакция CoinJoin

4.1.5 Кошельки в системе Биткойн
Помимо описанных в главе 3 видов кошельков, для системы Биткойн характерно использование кошельков-файлов, которые применяются для хранения
в цифровом виде в файле набора закрытых ключей.
Рассмотрим формат закрытого ключа. В системе Биткойн закрытый ключ
в стандартном формате – это 256-битовое число между значениями 1 и FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4140 (в шестнадцатеричном виде), что представляет собой почти весь диапазон значений
2256 – 1. Данный диапазон регулируется стандартом электронной подписи ECDSA
на основе эллиптической кривой secp256k1, который был рассмотрен в главе 2.

Формат импорта кошелька
Формат импорта кошелька (Wallet Import Format, WIF) используется для того,
чтобы сделать копирование закрытых ключей менее подверженным ошибкам.
Данный формат использует кодировку Base58Check [61] для закрытого ключа,
что значительно снижает вероятность ошибки копирования.
Преобразование закрытого ключа в формат импорта кошелька WIF может
быть выполнено следующим образом:
1. Возьмите закрытый ключ.
2. Добавьте перед ним байт со значением 80 (значения байтов приведены
в шестнадцатеричном виде) для адресов основной сети или со значением EF для адресов тестовой сети.

204

 Глава 4

3. Добавьте после него единичный байт (байт со значением 01), если закрытый ключ должен использоваться со сжатыми открытыми ключами.
Ничего не добавляется, если он используется с несжатыми открытыми
ключами.
4. Выполните хеширование в соответствии с алгоритмом SHA-256 (см. главу 1) для расширенного ключа.
5. Выполните хеширование по алгоритму SHA-256 для результата предыдущего хеширования.
6. Возьмите первые четыре байта второго хеш-кода SHA-256; они являются контрольной суммой.
7. Добавьте четыре байта контрольной суммы в конец расширенного (в соответствии с пп. 1 и 2) ключа.
8. Преобразуйте результат из байтовой строки в строку Base58, используя
кодировку Base58Check.
Этот процесс легко обратим с использованием функции декодирования Base58.
Существует также формат закрытого мини-ключа [181], который представляет собой метод кодирования закрытого ключа длиной менее 30 символов, позволяющий встраивать ключи в небольшое физическое пространство, такое как
физические токены биткойнов и более устойчивые к повреждениям QR-коды.

Формат BIP39
BIP39 описывает шаги, которые необходимо предпринять, чтобы превратить
приватный ключ в мнемоническую фразу. Это предложение стало стандартом
для кошельков [194].
Последовательность действий состоит из двух частей: генерации мнемоники и ее преобразования в бинарное начальное число. Позже это начальное
число можно использовать для создания детерминированных кошельков с использованием различных существующих методов.
Мнемонический код или предложение лучше подходят для взаимодействия
с человеком по сравнению с необработанными двоичными или шестнадцатеричными представлениями исходного числа кошелька. Человеческий мозг
легче запоминает осознанные слова, которые можно с чем-то ассоциировать,
нежели случайный набор букв и цифр. Мнемоническую фразу можно записать
на бумаге, легко запомнить или передать по телефону.
Рассмотрим основные принципы, которые лежат в основе построения мнемонической фразы (в соответствии с BIP39) и дальнейшей выработки ключа
с ее использованием.
Мнемоническая фраза может состоять из 12 или 24 слов. В зависимости от
этого используются различные параметры выработки. Мы далее будем записывать параметры в формате x/y, где х – параметр для мнемонической фразы
из 12 слов, а y – параметр для мнемонической фразы из 24 слов.
На текущий момент (середина 2022 г.) существует возможность задать мнемоническую фразу на одном из 10 языков: это английский, японский, корейский, испанский, китайский упрощенный, китайский традиционный, французский, итальянский, чешский и португальский языки [76].

Основные блокчейн-платформы  205
В каждом языковом словаре представлено 2048 слов. Несмотря на то что
строки в словаре пронумерованы от 1 до 2048, необходимо помнить о том, что
при двоичном представлении данных нумерация будет вестись от 0 до 2047.
Так, для словаря английского языка 11-битовая последовательность из всех
нулей (00000000000) будет соответствовать первому слову словаря – abandon,
а 11-битная последовательность из всех единиц (11111111111) будет соответствовать последнему слову zoo.
Для того чтобы сгенерировать мнемоническую фразу из 12/24 слов, используется следующий алгоритм [193] (рис. 4.42):
1. Сгенерировать 128/256-битовую случайную последовательность M.
2. Получить хеш-код Hash(M).
3. Составить 132/264-битовую последовательность, соединив 128/256 бит
последовательности M и первые 4/8 бит от хеш-кода Hash(M).
4. Разделить 132/264-битовую последовательность на 12/24 последовательностей по 11 бит.
5. Заменить каждую 11-битовую последовательность в соответствии со
словарем на соответствующее слово и получить мнемоническую фразу.
Сгенерировать случайную последовательность из 128/256 бит

Sha256

Случайная последовательность из 128/256 бит

Контрольная сумма – первые
4/8 бит хеш-кода

Последовательность из 132/264 бит разделяется на 12/24 11-битовых
последовательностей

01101101010
01110110101
11011011010
….
11111011101

874
949
1754
….
2013

hood
issue
swamp
….
wing

Мнемоническая фраза (12/24 слова):
hood issue swamp what tray combine rude enjoy sound fall
object wing

Рисунок 4.42. Преобразование случайной последовательности в мнемоническую фразу

206

 Глава 4

Для того чтобы на практике опробовать работу мнемонического конвертора или убедиться в правильности работы вашего ПО, можно использовать онлайн-калькулятор Иана Колмана (Ian Coleman) [183].
Мнемоническая фраза используется для расширения ключа путем применения функции получения ключа на основе пароля семейства PBKDF2 (PasswordBased Key Derivation Function 2).
Принцип действия данной функции прост и заключается в следующем: к мнемонической фразе добавляется соль, после чего к полученной последовательности
применяется хеш-функция HMAC-SHA-512, в результате образуется 512-битовый
хеш-код (сид-последовательность), из которого извлекается ключ (рис. 4.43) [48].
Мнемоническая фраза (12/24 слова):
hood issue swamp what tray combine rude
enjoy sound fall object wing

Соль (Salt)

PBKDF2 на основе использования HMAC-SHA512
(преобразуется 2048 раз)

512-битовая сид-последовательность

Рисунок 4.43. Расширение ключа с использованием мнемонической фразы

4.1.6 Создание и использование иерархических
детерминированных ключей
Иерархический детерминированный протокол создания и передачи ключей
(Hierarchical Deterministic protocol, протокол HD) значительно упрощает резервное копирование кошелька, устраняет необходимость в повторном обмене данными между несколькими программами, использующими один и тот
же кошелек, позволяет создавать дочерние учетные записи, которые могут работать независимо, дает каждой родительской учетной записи возможность
контролировать свои дочерние элементы, даже если дочерняя учетная запись
взломана, и делит каждую учетную запись на части с полным и ограниченным
доступом, чтобы ненадежным пользователям или программам было разрешено получать или отслеживать платежи, не имея возможности их тратить.
Протокол HD использует функцию создания открытого ключа ECDSA, point,
которая принимает большое целое число (закрытый ключ) и превращает его
в точку эллиптической кривой (открытый ключ):
public_key = point(private_key).
Благодаря принципу работы функции point (данная функция фактически
выполняет скалярное умножение базовой точки эллиптической кривой на за-

Основные блокчейн-платформы  207
крытый ключ) можно создать дочерний открытый ключ, объединив существующий (родительский) открытый ключ с другим открытым ключом, созданным из любого целочисленного значения i.
Этот дочерний открытый ключ является тем же самым открытым ключом,
который был бы создан функцией point, если бы вы добавили значение i к исходному (родительскому) закрытому ключу по модулю q, который равен порядку простой подгруппы группы точек эллиптической кривой, и применили
бы функцию point к полученному результату:
point((parent_private_key + i) mod q) == parent_public_key + point(i).
Это означает, что две или более независимые программы, которые согласовывают последовательность целых чисел, могут создать серию уникальных
пар дочерних ключей из одной родительской пары ключей без дальнейшего
взаимодействия. Более того, программа, которая распространяет новые открытые ключи для получения платежа, может делать это без какого-либо доступа к закрытым ключам, позволяя программе распространения открытых
ключей работать на потенциально небезопасной платформе, такой как общедоступный веб-сервер.
Дочерние открытые ключи также могут создавать свои собственные дочерние открытые ключи, повторяя операции по получению дочерних ключей:
point((child_private_key + i) mod p) == child_public_key + point(i).
При создании дочерних открытых ключей или последующих открытых
ключей предсказуемая последовательность целочисленных значений была бы
не лучше, чем использование одного открытого ключа для всех транзакций,
поскольку любой, кто знал один дочерний открытый ключ, мог найти все другие созданные дочерние открытые ключи из того же родительского открытого
ключа. Вместо этого можно использовать случайное начальное число для детерминированной генерации последовательности целочисленных значений,
чтобы связь между дочерними открытыми ключами была невидима для всех
без знания этого начального числа.
Протокол HD использует одно корневое начальное число для создания
иерархии дочерних и следующих за ними ключей с несвязанными детерминированно сгенерированными целыми числами. Каждый дочерний ключ
также получает детерминированно сгенерированное начальное число от
своего родителя, называемое чейнкодом, поэтому компрометация одного
чейнкода не обязательно нарушает целочисленную последовательность для
всей иерархии, позволяя главному чейнкоду продолжать быть полезным,
даже если, например, взламывается веб-программа распространения открытых ключей.
На рис. 4.44 показан принцип работы протокола HD. Данный протокол использует следующие входные значения:
 родительский закрытый ключ и родительский открытый ключ – это
обычные несжатые 256-битовые ключи ECDSA;
 родительский чейнкод – это 256 бит псевдослучайной последовательности;
 индекс – это 32-битовое целое число, определяемое программой.

208



Глава 4

Рисунок 4.44. Вычисление дочерних ключей

Как показано на рис. 4.44, родительский чейнкод, родительский открытый
ключ и индекс подаются на вход алгоритма хеширования HMAC-SHA-512
(см. главу 1). В результате работы данного алгоритма получается 512-битовое
значение, которое используется следующим образом:
 старшие 256 бит используются как дочерний чейнкод;
 младшие 256 бит используются в качестве целочисленного значения, которое должно быть объединено либо с родительским закрытым ключом,
либо с родительским открытым ключом, соответственно, для создания
либо дочернего закрытого ключа, либо дочернего открытого ключа:
child_private_key = parent_private_key + lefthand_hash_output mod p;
child_public_key = point(parent_private_key + lefthand_hash_output mod p)
или
child_public_key = point(child_private_key) = parent_public_key + point(lefthand_
hash_output).
Указание разных индексов приведет к созданию разных несвязанных дочерних ключей из одних и тех же родительских ключей. Повторение процедуры
для дочерних ключей с использованием дочернего чейнкода создаст несвязанные ключи второго поколения.
Так как для создания дочерних ключей требуется как ключ, так и чейнкод,
то ключ и чейнкод вместе называются расширенным ключом. Расширенный
закрытый ключ и соответствующий ему расширенный открытый ключ имеют
одинаковый чейнкод.
Родительский закрытый ключ и родительский чейнкод генерируются на основе случайных данных, как показано на рис 4.45.

128, 256 или 512
бит случайной
последовательности

ит

Хеш-функция
(512 бит)


25
256

бит

Закрытый
мастер-ключ

Открытый
мастер-ключ

Мастерчейнкод

Рисунок 4.45. Создание мастер-ключей

Расширенный
открытый
мастер-ключ

Расширенный
закрытый
мастер-ключ

Основные блокчейн-платформы  209
Начальное инициализирующее значение, из которого в дальнейшем извлекается ключ, представляет собой 128, 256 или 512 бит случайных данных
(в оригинальном описании системы Биткойн данное значение называется root
seed, то есть корневое начальное число). Начальное инициализирующее значение – это единственные данные, которые необходимо сохранить пользователю, чтобы в дальнейшем иметь возможность извлечь ключи с использованием
определенной программы-кошелька.
Как показано на рис. 4.45, начальное инициализирующее значение хешируется, в результате чего образуется хеш-код длиной 512 бит, из которого формируется закрытый мастер-ключ и мастер-чейнкод. Открытый мастер-ключ получается из закрытого мастер-ключа с помощью функции point. Полученный
открытый мастер-ключ вместе с мастер-чейнкодом образуют расширенный
открытый мастер-ключ.

4.2 Эфириум
После того как биткойн стал набирать обороты в своем развитии, его протоколы совершенствовались, появилась перспектива дальнейшего развития блокчейн-технологий. Так, в 2015 году в свет вышла новая криптовалютная система
Эфириум (Ethereum), продемонстрировавшая концептуально новый подход
к построению систем распределенных реестров.
Новизна заключалась в том, что теперь стало возможно дополнять транзакции вызовами различных функций. Начался период блокчейн-платформ со
смарт-контрактами.
В общем случае модель системы Эфириум можно представить следующим
образом:
 децентрализованные приложения;
 смарт-контракты, благодаря которым пользователи разрабатывают различные децентрализованные приложения;
 виртуальная машина (EVM) хранит в себе глобальное состояние системы, внутри которой циркулируют смарт-контракты;
 ноды (узлы), взаимодействуя между собой, формируют виртуальную машину;
 интернет, в котором существует несколько тысяч нод.

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

210



Глава 4

в сети контрактов изменяются. Это учитывается при добавлении новых блоков в цепочку.
Структура глобального состояния представляет из себя префиксное дерево
(Merkle Patricia tree), которое несколько отличается от обычного бинарного дерева Меркля. Префиксное дерево позволяет динамически добавлять информацию в его различные ветви, обозначая изменение состояния, но, как и с
бинарным деревом, для его пересчета и проверки целостности понадобится
пересчитать только интересующую ветвь [16].
Глобальное состояние можно представить, как показано на рис. 4.46.

Рисунок 4.46. Глобальное состояние сети Эфириум

При его формировании учитывается текущее состояние всех адресов и аккаунтов в сети. Учитываются следующие данные для каждого пользователя:
 Nonce – количество опубликованных пользователем контрактов;
 Balance – текущий баланс пользователя;
 Storage Hash – хеш-код всей информации об указанном адресе;
 Code Hash – хеш-код программного кода смарт-контракта, связанного
с данным адресом.
Когда транзакции изменяют состояние аккаунтов, изменяется и глобальное
состояние, его новый хеш-код добавляется в блок.

4.2.2 Консенсус
Ранее мы уже рассказывали о различных протоколах консенсуса в системах
распределенного реестра (см. главу 3). Теперь мы постараемся немного подробнее описать адаптированный вариант консенсуса на основе доказательства
работы PoW, используемый в системе Эфириум.
В основе механизма консенсуса PoW лежит майнинг. В сети Эфириум для поддержания работы механизма консенсуса PoW используется алгоритм Ethash [3].
Данный алгоритм был разработан для того, чтобы предотвратить майнинг с помощью устройств на основе специализированных микросхем (ASIC-устройств).
Ethash предполагает использование DAG-файла (на основе направленного
ацик­лического графа), который со временем увеличивается в размере, на 8 Мб
каждые 3000 блоков, представляющие так называемую «эпоху». Такой механизм
не позволит майнить на ASIC-устройствах, вынуждая использовать только графические процессоры, обладающие достаточной памятью для хранения DAG-файла.

Основные блокчейн-платформы  211
В настоящий момент размер файла преодолел порог в 4 Гб и целый ряд графических вычислителей оказался неактуальным в задаче формирования блока.
Таким образом, процесс майнинга на платформе Эфириум несколько отличается от типичного алгоритма в сети Биткойн, который был описан в главе 4 ранее.
Генерирующий блок узел выбирает случайное число nonce и вместе с пред­
обработанным заголовком предыдущего блока передает его на вход функции хеширования Keccak-256 (см. главу 1), полученный результат указывает
на 128-байтовый сектор в DAG-файле. После этого хеш-код и выбранный сектор смешиваются с помощью специальной функции mix, полученный результат укажет на новый сектор DAG-файла. Данная операция выполняется 64 раза,
после чего результат сжимается до 32 байт.
Полученное значение mixed_hash сравнивается с заданным значением сложности. Если оно больше порогового значения сложности, то выбирается новое
значение nonce, случайно или путем инкрементирования [31]. Если полученный результат удовлетворяет условию, то значение nonce для данного блока определено, доказательство работы выполнено и будет легко проверено
остальными участниками сети.
В ходе работы системы значение сложности вычисляется для каждого блока
заново по определенной формуле, зависящей от различных параметров, в том
числе от времени выработки предыдущего блока. Так, например, если текущий
блок был выработан намного быстрее, чем предыдущий, то для следующего
блока необходимо будет увеличить значение сложности, и наоборот. С помощью такого механизма время выработки блока стабилизируется и приводится
к среднему значению порядка 15 секунд.
На основе алгоритма Ethash работает не только платформа Эфириум,
но и альтернативные системы, такие как Ethereum Classic, Ubiq, Expanse.

4.2.3 Газ
Для формирования распределенной системы, которая бы умела работать со
смарт-контрактами, т. е. могла бы формировать транзакции с вызовами функций кода, написанного на Тьюринг-полном языке, понадобилось обезопасить
систему от различного типа попыток «заспамить» систему, экстренно увеличить в ней нагрузку и вывести из строя.
Для этого разработчиками была создана система оценки нагрузки каждой
транзакции на узлы системы. Подход рассматривает каждую транзакцию как
набор элементарных операций, которые могут быть выполнены на узле при
проверке транзакции в момент формирования блока. При этом каждая операция стала стоить определенное количество так называемого «газа» (gas).
Теперь перед пользователем будет стоять требование предоплаты вызовов
функций – обеспечить суммарное количество газа, необходимое для выполнения транзакции [137].
При формировании вызова пользователь указывает два параметра – GasLimit
и GasPrice. В случае если при проверке транзакции возникнет непредвиденное
или целенаправленное сверхвысокое количество операций, то их выполнение
будет прервано в момент, когда это число превысит максимальное разрешенное пользователем значение GasLimit.
В значении GasPrice отправитель транзакции указывает стоимость одной единицы газа в веи (wei – дробная единица основной криптовалюты системы Эфи-

212

 Глава 4

риум – эфира (ether или Eth); 1 веи равен 10-18 эфира). Чем это значение выше,
тем более привлекательна данная транзакция будет для майнеров и тем быстрее
она будет обработана и добавлена в блок – за каждую транзакцию, добавленную
в блок, майнеры получают дополнительное вознаграждение, комиссию.
Значение GasPrice * GasLimit будет характеризовать максимальную прибыль
от этой транзакции, однако размер комиссии будет высчитываться исходя из
реально потребленного количества газа [50].

4.2.4 Адреса и кошельки
В системе Эфириум у каждого пользователя существует его уникальный идентификатор – его адрес в сети. Этот адрес будет характеризовать отправителя
транзакции, а также использоваться для перевода средств. Пользователь может обладать несколькими адресами, переводить валюту с одного счета на другой, пользоваться каждым для отдельных целей и т. д.
Как и в системе Биткойн, адрес пользователя вычисляется из его открытого
ключа; в качестве адреса используются последние 20 байт результата хеширования открытого ключа пользователя алгоритмом хеширования Keccak-256.
Кошельком в данном случае называют программное обеспечение, которое помогает пользователю удобно одновременно управлять несколькими счетами. Кошелек защищен паролем пользователя и сохраняет счета пользователей в безопас­
ности. Одними из наиболее популярных кошельков являются официальный
кошелек системы MyEtherWallet, приложение Mist, мобильный кошелек jaxx и др.
Для работы с сетью пользователю необходимо располагать парой ключей –
закрытым и открытым. Пара ключей вырабатывается в соответствии с алгоритмом ECDSA на основе эллиптической кривой secp256k1 (см. главу 2). Открытый
ключ пользователя хешируется алгоритмом Keccak-256, после чего от результирующего хеш-кода берутся самые правые 20 байт, т. е. 40 шестнадцатеричных
символов, которые и будут являться идентификатором пользователя.
В момент создания закрытый ключ шифруется с помощью пароля пользователя с использованием симметричного алгоритма AES-256 [131] и сохраняется
на устройстве пользователя в зашифрованном виде.
Чтобы управлять своим счетом, пользователю необходимо располагать на
устройстве файлом со своим закрытым ключом, а также помнить пароль, для
того чтобы расшифровать его.
Помимо адресов пользователей, в системе присутствует не менее важный
набор адресов – адреса смарт-контрактов (каждый контракт, как и пользователь, обладает адресом, но, в отличие от пользователя, у контракта нет собственного закрытого ключа). В следующем разделе мы напишем подробнее
о транзакциях создания контракта. В этот момент генерируется новый адрес,
который устанавливается в ассоциацию с исполняемым кодом.
По аналогии с адресом пользователя адрес контракта обладает собственным
балансом. Это в том числе позволяет расширить функционал смарт-контрактов.
В остальном эти адреса принципиально различаются. Основным отличием пользовательского адреса от адреса контракта является возможность формирования
транзакций [1]. Логично, что пользовательский адрес может являться инициатором транзакций, контракт же, в свою очередь, выступает только исполнителем
и не может самостоятельно без внешнего вызова исполнять те или иные команды.

Основные блокчейн-платформы  213

4.2.5 Транзакции
Транзакции сети Эфириум можно разделить на два ключевых типа:
 транзакции вызова – включают в себя перевод криптовалюты от одного
пользователя другому или вызов функции уже опубликованного смартконтракта;
 транзакции создания смарт-контракта в сети – каждая такая транзакция
создает новую учетную запись и ассоциацию с ней программного кода;
подобные транзакции несут в себе несколько больше информации.
Для обоих типов транзакций есть целый ряд общих параметров:
 Nonce – количество транзакций, сформированное данным пользователем; поле должно являться уникальным и будет идентифицировать
транзакцию;
 GasPrice – указывает количество веи – стоимость, которую пользователю
необходимо будет заплатить за каждую единицу газа при выполнении
транзакции;
 GasLimit – предельное количество газа, которое пользователь готов заплатить за выполнение данной транзакции; в случае если выполнение
транзакции, например выполнение функции смарт-контракта, будет
требовать газа больше, чем указано в параметре GasLimit, то выполнение транзакции прервется, и состояние системы вернется к тому, которое было до начала выполнения данной транзакции;
 From – отправитель данной транзакции;
 Signature – электронная подпись транзакции, вычисленная по алгоритму
ECDSA с использованием кривой secp256k1 и хеш-функции Keccak-256.
Помимо этих параметров, каждый тип транзакции обладает собственным
набором информативных полей. В частности, для транзакций вызова указывается следующая информация:
 To – учетная запись, в отношении которой совершается транзакция, например пользователь-получатель перевода, либо смарт-контракт, чья
функция была вызвана данной транзакцией;
 Value – количество криптовалюты в веи, которое передается вместе с выполнением транзакции, например сумма перевода или необходимая
сумма для выполнения функции смарт-контракта;
 InputData – входные параметры вызова, например параметры для функции смарт-контракта.

4.2.6 Структура блока
Рассмотрим параметры блоков в сети Эфириум. Выше мы описали основной набор информации, который циркулирует в сети, какие данные хранятся
в блоках и для чего они предназначены, теперь же попробуем сделать некоторое обобщение и структурируем информацию.
Стандартный набор данных блока включает в себя следующее:
 Block Height (высота блока) – высотой принято называть текущую длину
цепочки, то есть количество блоков в ней;
 Timestamp – временна́я метка создания блока;

214



Глава 4

 Transactions – количество транзакций, добавленных в данный блок;
 Miner (получатель награды) – адрес пользователя в сети Эфириум, который создал блок и получил вознаграждение за него, а также комиссию от
транзакций – 160 бит, 40 шестнадцатеричных символов;
 Reward – награда, которую получит майнер за создание блока, – на текущий момент стандартная награда составляет 2 эфира за выработку
блока, а также к этому значению добавляется сумма комиссий за транз­
акции в блоке;
 Difficulty – сложность, определенная для данного блока;
 Size – размер блока в байтах;
 Gas Used – суммарное количество газа, израсходованное всеми транзакциями блока;
 Gas Limit – установленное ограничение на количество газа, суммарно
потребляемое транзакциями данного блока;
 Extra Data – дополнительные данные, которые могут быть внесены создателем блока.
Дополнительная информация, включаемая в блок:
 Hash – хеш-код данного блока;
 Parent Hash (родительский хеш) – хеш-код заголовка предыдущего блока;
 Ommers Hash / Sha3 Uncles (хеш дядей) – хеш-код списка «дядей» (описаны далее) для данного блока;
 State Root – корень состояния; ранее мы описывали построение дерева
состояния, в каждый блок включается хеш-код дерева в текущий момент,
после того как все транзакции будут добавлены в глобальное состояние;
 Transactions Root – хеш-код корня дерева всех транзакций, включенных
в данный блок;
 Receipts Root – корень дерева квитанций; для каждой транзакции после
ее успешного выполнения создается квитанция, в этой квитанции отображается, в какой блок была добавлена транзакция, хеш-код этой транз­
акции, какое количество газа она потребляет, какое количество потребляют суммарно все транзакции, которые уже добавлены в блок, и т. д.;
 Logs Bloom – журнал, содержащий различную служебную информацию
в форме логов;
 Nonce – случайное число, используемое алгоритмом Ethash для выработки хеш-кода текущего блока согласно консенсусу PoW;
 MixedHash – вместе со значением Nonce может использоваться для проверки выполнения условия консенсуса PoW.

4.2.7 Эволюция системы Эфириум
Ранее, в главе 3, было рассмотрено понятие форков. Описывались ситуации
с разветвлением системы в тех или иных случаях.
Для платформы Эфириум форки также актуальны. Обычные форки цепочки порождают ommits, или так называемых «дядей», которые были упомянуты при описании структуры блока. Дядями называют устаревшие блоки более
коротких цепочек, однако такие блоки не просто отбрасываются – в цепочку
транзакции этих блоков не попадают, однако их владельцам назначается не-

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

Frontier – «Рубеж» (блок № 1)
Цепочка была запущена 30 июля 2015 года, эту стадию принято называть
Frontier. Дальнейшая жизнь платформы видоизменялась от форка к форку.
Преобразования были как запланированными, так и вынужденными.
Спустя полтора месяца, 14 марта 2016 года, по достижении длины цепочки
в 200 000 блоков был выпущен пакет обновлений системы Ice Age («Ледниковый период»). Это было вызвано необходимостью усовершенствования безопасности системы и скорости ее работы.

Homestead – «Усадьба» (блок № 1 150 000)
14 марта 2016 года, спустя год после запуска сети, команда разработчиков
сделала решительный шаг – выпустила новую версию программного обеспечения, заявив о завершении тестирования сети и готовности к стабильной
работе. Этот релиз получил название Homestead и был активирован на блоке
с номером 1 150 000.

DAO Fork (блок № 1 920 000)
Использование смарт-контрактов набирало популярность, все больше пользователей становились владельцами цифровых активов. Компании стали выпус­
кать свои токены, вести бизнес с помощью криптовалютной площадки.
Наиболее популярной децентрализованной организацией в сети стала крауд­
фандинговая компания The DAO. Однако свою известность компания приобрела в ходе крупнейшего скандала за всю историю системы Эфириум [120].
Все дело в том, что злоумышленники нашли в используемом The DAO смартконтракте возможность неограниченного вывода средств на посторонний адрес.
Так, почти треть активов компании, по курсу того времени – 50 миллионов долларов, была украдена злоумышленником, чему никак не могла помешать система, ведь контракт был выполнен верно, а вина остается за его разработчиком.
Пытаясь найти компромисс в сложившейся ситуации, создатель сети Виталик Бутерин (Vitalik Buterin) предложил сформировать новый форк, разделив
сеть за некоторое время до кражи, чтобы транзакции злоумышленников были
забыты. Этот вариант решения выглядел одним из наиболее оптимальных, но
он стал причиной огромных разногласий и идеологических споров.
С одной стороны, необходимо было вернуть доверие к площадке и устранить действия хакеров, с другой – идеологически это нарушало принцип децентрализации сети, отсутствия в ней централизованного механизма управления, ведь контракт выполнил те действия, которые ему были предписаны.
В результате 20 июля 2016 г. еще совсем молодая система, оказавшись в столь
сложном положении, потеряла часть своих пользователей. Форк действительно
был создан, но его противники продолжили пользоваться оригинальным про-

216



Глава 4

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

Metropolis: Byzantium – «Византия» (блок № 4 370 000)
Еще в 2015 году были анонсированы планы по развитию проекта Эфириум.
Один из крупнейших апгрейдов был обозначен как Metropolis и включал в себя
несколько фаз развития.
Первой фазой стал софтфорк Byzantium, произошедший 16 октября 2017 года.
Форк предлагал обновление программного обеспечения платформы, которое
оптимизировало работу системы, увеличило скорость обработки транзакций,
а также сократило награду майнерам с пяти до трех Eth; был обозначен задел
на использование криптографических протоколов на основе доказательства
с нулевым разглашением zk-SNARK.

Metropolis: Constantinople – «Константинополь» (блок № 7 280 000)
Столь долгожданное новое обновление платформы было назначено на блок
№ 7 080 000 и было выполнено в Ropsten – тестовой сети Эфириум.
Обновление несло новые изменения, улучшающие масштабируемость системы, было уменьшено в 10 раз потребление газа, изменена его стоимость,
награда майнеру сократилась до 2 Eth. Кроме того, в Constantinople заложены
ключевые изменения в механизмы платформы, которые стали первыми шагами к переходу от консенсуса PoW к PoS, сделав консенсус системы на некоторое
время их гибридной версией.
Новое обновление, как всегда, было встречено пристальным вниманием, в том числе со стороны компаний, контролирующих информационную
безопас­ность и проводящих аудит информационных систем. Своевременно
была обнаружена критическая уязвимость, благодаря ей злоумышленники
могли бы получить доступ к счету любого пользователя [118]. Уязвимость быстро закрыли с помощью форка St. Petersburg и уже на высоте 7 280 000 блока
28 февраля 2019 года оба обновления были добавлены в один и тот же блок настоящей цепи, не давая воспользоваться существовавшей уязвимостью.
Стоит также заметить, что это был форк из числа хардфорков, по сути система вновь расслоилась пополам. Всем пользователям, желающим перейти на
новую версию форка, необходимо было заранее обновить программное обес­
печение, до момента формирования блока с «имплементацией», встраиванием новых параметров системы. В противном случае пользователи могли либо
навсегда остаться в старой версии системы, либо потерять часть средств, которыми успели воспользоваться после выхода обновления.

Istanbul – «Стамбул» (блок № 9 069 000)
Сеть получила новое обновление зимой 2019 года. Новшества вновь направлены на поддержку масштабирования, а также на сопротивление DoS-атакам. На
платформе появились все необходимые инструменты для работы с технологией zk-SNARK и взаимодействия с криптовалютой Zcash, предпосылки которых
появились еще в Constantinople. Платформа продолжает движение к консенсусу PoS [7].

Основные блокчейн-платформы  217

Berlin – «Берлин» (блок № 12 224 000)
Форк состоялся 15 апреля 2021 г. и включает следующие основные обновления [42]:
 оптимизация оценки газа для операций возведения в степень по модулю
и, как следствие, уменьшение стоимости выполнения ряда криптографических алгоритмов (например, проверки подписи RSA и выполнения
некоторых других функций);
 введен новый тип транзакций – «транзакции-оболочки» (Typed
Transaction Envelope), который делает все будущие типы транзакций обратно совместимыми благодаря возможности «запаковывать» в транз­
акции-оболочки другие транзакции, типы которых планируется добавить в будущем;
 увеличена стоимость (до трех раз) использования некоторых кодов операций, что защищает от атак типа «отказ в обслуживании» и повышает
скорость обработки транзакций.

London – «Лондон» (блок № 12 965 000)
Вышел 5 августа 2021 г.; основные изменения состоят в следующем:
 изменен принцип формирования комиссий за транзакции: от фактически аукционной формы произошел переход к вычисляемым комиссиям,
что должно снизить расходы на проведение транзакций;
 отложен запланированный ранее (и внедренный в систему Эфириум)
переход на консенсус PoS в рамках Ethereum 2.0.

Serenity – «Безмятежность» – Ethereum 2.0
В апреле 2020 года в тестовой сети совершенно нового Ethereum 2.0 Topaz был
сформирован первый блок новой цепочки. Позже, 1 декабря 2020 г., был выпущен genesis-блок оригинальной сети Ethereum 2.0 [39].
Сейчас сеть Ethereum 2.0 проходит различные тестирования, но в ближайшей перспективе планируется полный перевод пользователей и их активов на
новую площадку. До этого времени система второй версии будет играть лишь
тестовую роль.

Предложения по улучшению системы Эфириум
Немаловажное значение в эволюции системы Эфириум играют и предложения
по ее улучшению – EIP (Ethereum Improvement Proposals). По аналогии с BIP
для системы Биткойн периодически предлагаются новые улучшения системы
Эфириум EIP, и некоторые из них добавляются в цепочку, в частности [119]:
 EIP 1234 – вознаграждение для майнеров сокращается с трех до двух
Eth [221];
 EIP 145 – эффективность и скорость блокчейна увеличивается при помощи добавления переключающих механизмов к виртуальной машине
Эфириум [65];
 EIP 1052 – сеть начинает потреблять меньше энергии [151];
 EIP 1283 – снижается потребность в использовании внутренней валюты
блокчейна Эфириум – газа [233].

218

 Глава 4

4.2.8 Основная и тестовые сети платформы Эфириум
Как правило, все действия, транзакции, вызовы функций смарт-контрактов
выполняются в основной сети платформы (Mainnet). Система работает по всем
правилам, которые были описаны ранее. Курс криптовалюты применим именно для нее, все токены и активы имеют ценность именно в основной цепочке.
Однако, помимо основной цепочки, существует несколько альтернативных
ее версий. Такие цепочки называются тестовыми (testnets) и, как следует из
названия, применяются для тестирования разработчиками различных механизмов сети, отладки смарт-контрактов и т. д. Такие тестовые сети являются
глобальными и доступны всем пользователям интернета.
Кроме тестовых сетей, каждому разработчику доступно создание своей
собственной частной, или приватной, сети. Эта сеть будет строиться на кас­
томизированных разработчиком параметрах. Будут заданы необходимые
настройки, что опять же позволяет протестировать различные приложения
в тех или иных условиях работы системы. Такие сети чаще доступны в рамках
локальных сетей.
Каждая сеть характеризуется ее идентификатором (network ID). Так, например, основная сеть Эфириум имеет идентификатор 1, Ethereum Classic – 61.
Приватная сеть также должна иметь свой идентификатор, при запуске необходимо его указать.
Идентификатор не должен совпадать с идентификаторами других глобальных сетей. Со списком основных и тестовых глобальных сетей можно ознакомиться на сайте chainlist.org [94].
Наиболее популярными глобальными тестовыми сетями можно считать следующие:
 Ropsten Testnet (ID = 3) – сеть была запущена в 2016 году, позволяет
пользователям тестировать свои приложения и смарт-контракты [216].
Максимально полно дублирует механизмы текущего состояния основной сети, в том числе гибридный консенсус, время выработки блока и др.
В 2017 году сеть была подвержена спам-атаке, в связи с чем вышла из
строя на короткий срок, после чего была восстановлена с более безопасными параметрами;
 Rinkeby Testnet (ID = 4) – вместо PoW-консенсуса, применяемого основной сетью и сетью Ropsten, использует вариант консенсуса PoA. Это позволило сделать инструмент устойчивым к практически любого рода
спам-атакам [208].
При работе с тестовыми сетями возникает вопрос: как выполнять операции
в системе, ведь выполнение транзакции также требует оплаты комиссии, газа,
потраченного на выполнение команд. Вариант неограниченного баланса не
подошел бы в таких сетях, и системы постоянно были бы атакованы.
Для решения этого вопроса в тестовых системах существуют так называемые «краны» (faucets) – сервисы, периодически предоставляющие тес­
товые средства на указанный кошелек в сети. Так, например, кран сети
Ropsten [215] один раз в день предоставляет тестовые 0,3 Eth на уникальный
IP-адрес.

Основные блокчейн-платформы  219

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

MetaMask
MetaMask [179] представляет собой криптовалютный кошелек, который
устанавливается как расширение к браузеру. Он подходит как для работы
с глобальной сетью, так и для работы с приватными сетями, тестовыми
и частными.
Если вы первый раз создаете криптовалютный кошелек, то он предлагает вам сервис для хранения и управления несколькими адресами (рис. 4.47).
Например, у вас есть 5 адресов, тогда вы можете их хранить и распоряжаться
средствами в одном кошельке.

Рисунок 4.47. Установка криптовалютного кошелька MetaMask

Для того чтобы создать свой кошелек, необходимо ввести личные данные,
которые запрашивает расширение, после чего появится окошко с секретной резервной фразой (secret recovery phrase), пример которого приведен на
рис. 4.48.

220

 Глава 4

Рисунок 4.48. Выбор секретной фразы кошелька MetaMask

Секретная фраза позволяет выработать из случайного набора слов средство
восстановления ключа. Ее необходимо сохранить в удобное и защищенное
место. Далее платформа просит ввести заданную фразу, чтобы убедиться, что
пользователь ее запомнил.
После успешного входа можно приступать к работе с криптовалютным кошельком. Если вы хотите работать с сетью Mainnet – основной сетью платформы, то необходимо остаться в том окне, которое было загружено при входе. Мы
же в качестве примера приведем работу с тестовой сетью.
MetaMask по умолчанию поддерживает четыре тестовые сети:
 Ropsten;
 Kovan;
 Rinkeby;
 Goerli.
Будем работать с тестовой сетью Ropsten. Для этого необходимо в правом
верхнем углу в списке сетей выбрать сеть Ropsten. Однако при этом визуально
ничего не изменится: аккаунт и адрес остаются прежними.
Преимущество тестовой сети заключается в возможности получить тестовый эфир и с помощью полученных средств заниматься тестированием различных приложений, которые будут основаны на платформе Эфириум.
Перейдем на сайт Ropsten Faucet [215]. Как было сказано выше, это кран –
сервис, подвязанный к сети Ropsten, который выдает тестовые средства
(рис. 4.49). Нужно в появившемся окне указать адрес тестового кошелька и нажать на кнопку «Отправить тестовый Эфир» (Give me Ropsten ETH!).

Основные блокчейн-платформы  221

Рисунок 4.49. Получение эфира в тестовой сети

После нажатия запрос будет добавлен в очередь. Чтобы посмотреть информацию о зачисленных тестовых средствах, необходимо в кошельке MetaMask
нажать на меню (три вертикальные точки) и выбрать действие «Посмотреть на
Etherscan» (View Account on Etherscan) (рис. 4.50).

Рисунок 4.50. Просмотр информации о тестовых средствах

Etherscan [235] – один из сервисов, позволяющих отслеживать транзакции
и просматривать всю информацию о событиях, происходящих в сети Эфириума. Можно просмотреть информацию по конкретному адресу, блоку или
транз­акции. Важно отметить, что получать тестовые средства можно только
1 раз в сутки по одному IP-адресу.
На главной панели Etherscan можно увидеть заданный адрес, баланс счета,
транзакции и расширенную информацию (см. рис. 4.50).
В блоке с транзакциями указаны адреса отправителя и получателя, количест­
во эфира и налог за перевод средств. В тестовой сети соблюдаются все те же
правила, что и в главной сети.
Вернувшись к кошельку MetaMask, можно увидеть, что тестовый баланс обновился и составляет 0,3 Eth. Теперь можно оперировать полученным активом.
В частности, можно подключить USB-устройство для сохранения ключей, открыть новый счет и переводить деньги между счетами.
Однако при переводе между счетами будет сформирована транзакция,
и сеть будет просить комиссию за перевод так же, как если бы это было в главной сети. Комиссия при этом будет начисляться на счет майнера. При переводе

222

 Глава 4

средств можно менять скорость транзакции, что скажется на размере комиссии (меньше скорость – меньше комиссия).
Давайте создадим новый счет и попробуем перевести на него часть средств.
Для этого необходимо в правом верхнем углу нажать на управление аккаунтом
(кружочек) и выбрать действие «Создать счет» (рис. 4.51). Создадим «Счет 2».

Рисунок 4.51. Создание нового счета

Теперь у нас есть два счета, и мы можем с одного счета перевести на другой,
например, 0,1 Eth. При выборе соответствующего действия появляется окошко
с подтверждением транзакции, на котором мы видим (рис. 4.52):
 счет отправителя и счет получателя;
 количество средств, которое мы хотим перевести;
 количество газа (комиссия за перевод);
 скорость транзакции;
 итог.
Скорость транзакции можно изменить, изменится и количество газа (меньше скорость сделки – меньше газа придется заплатить отправителю).
Когда выполняется перевод между своими счетами, со счета отправки списывается также комиссия за перевод.
Кроме глобальных тестовых сетей, которыми пользуются пользователи со
всего мира, существует возможность организовать свою частную тестовую
сеть.
Для этого существуют различные средства и программное обеспечение,
о которых речь пойдет далее.

Основные блокчейн-платформы  223

Рисунок 4.52. Перевод средств между двумя счетами

Ganache
Ganache является одним из эмуляторов сети, построенной на платформе Эфириум. Это свободно распространяемое ПО, доступное для скачивания [136].
Для того чтобы начать работу с этой программой, создадим новую сеть,
щелк­нув по кнопке «New Workspace» (рис. 4.53).

Рисунок 4.53. Главное окно Ganache

224



Глава 4

В открывшемся окне можно указать название проекта и подключить сторонние проекты сервиса Truffle (trufflesuite.com), если вы создавали проекты
на этой платформе.
На вкладке «Server» находятся настройки сервера (рис. 4.54). По умолчанию
заданы следующие параметры:
 «Hostname»: 127.0.0.1 – Loopback Pseudo-Interface 1;
 «Port number»: 7545.
Для параметра «Network ID» необходимо указать какой-нибудь ID сети, для
того чтобы сети отличались друг от друга. У любой сети (тестовой или глобальной) тоже есть свои идентификаторы, поэтому рекомендуется поставить значение больше 0010.
Также на вкладке «Server» оставляем включенными переключатели
«Automine» и «Error on transactions failure» и выключенным переключатель
«Chain forking».

Рисунок 4.54. Вкладка «Server»

На вкладке «Accounts and Keys» (рис. 4.55) можно указать, какое количество
адресов с каким количеством средств должно быть создано в сети.
По умолчанию установлен баланс для всех создаваемых пользователей
в поле «Account default balance», равный 100 Eth. Также по умолчанию в поле
«Total accounts to generate» задано 10 аккаунтов.
Переключатель «Autogenerate HD mnemonic» отвечает за генерацию мнемонической фразы. Его можно отключить, тогда будет использоваться фраза,
указанная ниже, в поле. Переключатель «Lock accounts» также оставим выключенным. Необходимость блокировки аккаунтов будет рассмотрена далее.

Основные блокчейн-платформы  225

Рисунок 4.55. Вкладка «Accounts and Keys»

Вкладка «Chain» (рис. 4.56) используется для настройки самой цепочки.
Здесь задаются параметры «Gas limit» и «Gas price» в соответствующих полях.
Если будет разработан слишком большой контракт, то для того, чтобы загрузить его в сеть, будет требоваться несколько больший лимит газа, поэтому
можно увеличить значение параметра «Gas limit».
«Gas price» – это цена комиссии за переводы. Если поставить значение 0
в этом поле, то все действия в вашей сети будут бесплатными. Для первого
тестового запуска эти два параметра можно оставить по умолчанию.
Выпадающее меню «Hardfork» задает настройки сети. В разделе 4.2.7 обсуждались версии системы «Константинополь» и «Византия», в которых есть предварительные настройки и дополнительные функции, отсутствующие в более
ранних версиях системы. Если разработчику контракта необходимо посмот­
реть, как работают те или иные функции в новых форках, или посмотреть старые проекты, то существует возможность гибко подстроить систему под себя.
Вкладка «Advanced» содержит настройки приложения: работа с логами
и Google-аналитика (все переключатели можно оставить выключенными).
Вкладка «About» содержит краткую информацию о приложении.

226



Глава 4

Рисунок 4.56. Вкладка «Chain»

После того как все параметры для настройки Ganache заданы, можно сохранить рабочее пространство и перейти на вкладку «Accounts». На вкладке откроется 10 адресов, баланс каждого адреса составит 100 Eth (рис. 4.57).

Рисунок 4.57. Созданные аккаунты тестовой сети

Основные блокчейн-платформы  227
В небольшой панели под вкладками можно увидеть адрес сервера и номер
сети, на которых запущен эмулятор, а также остальные поля, которые были заполнены при настройке приложения.
Поле «Current block» показывает номер текущего блока, но таких блоков еще
нет в только что созданной цепочке. Есть единственный генезис-блок (начальный блок), высота которого равна 0.
Чтобы посмотреть информацию о блоке, можно просто нажать на него,
после чего мы можем просмотреть его хеш-код, дату и время создания, лимит газа и сколько газа было использовано (рис. 4.58).

Рисунок 4.58. Информация о блоке

Обратите внимание на то, что майнинг в Эфириуме работает несколько
иначе, чем в биткойне, и мы не видим нескольких нулей в начале хеш-кода
просматриваемого блока. Здесь иные требования, которые основываются на
вычислениях в графе.
Кратко опишем остальные элементы управления:
 вкладка «Transactions» содержит информацию о транзакциях. Пока их нет;
 параметр «Contracts» предназначен для загрузки контракта;
 вкладка «Events» отображает события;
 на вкладке «Accounts» есть также два следующих параметра: «Tx count»
и «Index». Параметр «Tx count» показывает количество транзакций, созданных с данного адреса. Параметр «Index» показывает порядковый номер адреса в системе;
 параметр «Show keys» (символ ключика) содержит всю ключевую информацию: адрес пользователя и приватный ключ (рис. 4.59). Приватный
ключ необходимо использовать только в целях разработки, никогда не
используйте его в открытом виде в публичных блокчейнах.

Рисунок 4.59. Информация о закрытом ключе

228

 Глава 4

Связь Ganache и MetaMask
Для того чтобы подключиться из приложения MetaMask к созданной эмулятором Ganache тестовой сети, необходимо в приложении MetaMask в списке
сетей выбрать «Добавить сеть», ввести имя сети, добавить URL (адрес сервера:
номер порта из приложения Ganache), идентификатор цепочки и обозначение
криптовалюты «eth» (рис. 4.60).

Рисунок 4.60. Загрузка в MetaMask данных сети Ganache

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

Geth
Geth представляет собой полноценный консольный клиент сети Эфириум со
всеми необходимыми настройками. Клиент Geth позволяет создавать частную
сеть, а также работать с реальной сетью Эфириум [143].
Необходимо установить клиент, после чего можно начинать с ним работу.
Работа происходит через ввод консольных команд в терминале.
Если в терминале запустить команду geth, то цепочка сети Эфириум будет
скачиваться в папку «С:\Пользователи\\AppData\Roaming\
Ethereum\chaindata\».
Для создания новых пользователей в тестовой сети необходимо ввести следующую команду:
geth account new --datadir

После ввода команды программа запрашивает ввод пароля. Этот пароль
будет нужен в дальнейшем, поэтому обязательно нужно его сохранить. После
ввода пароля на экран выводится адрес открытого ключа («Public address of the
key») и путь к секретному ключу («Path of the secret key file») (рис. 4.61).

Основные блокчейн-платформы  229

Рисунок 4.61. Создание нового аккаунта с помощью Geth

Если перейти по указанному адресу (зайти в только что созданный кошелек),
то можно увидеть данные кошелька, которые записны в JSON-формате (рис. 4.62).

Рисунок 4.62. Файл кошелька

Здесь указаны адрес и параметры криптографической системы, которые
показывают нам способ шифрования, параметры используемого алгоритма,
а также ряд других данных.
Использование шифрования и сохранение данных в зашифрованном виде –
это блокировка адреса или кошелька в системе, которая позволяет защитить
пользователя от исполнения команд посторонним лицом. Пока пользователь
не введет пароль, тем самым подтверждая, что он может расшифровать сообщение и получить закрытый ключ, он не сможет использовать данный ключ
для подписи транзакций.
Создадим файл генезис-блока, указав там необходимые настройки. Например, создадим при старте системы два адреса с разными балансами 1000
и 10 Eth (рис. 4.63).

230

 Глава 4

Рисунок 4.63. Файл генезис-блока

Для этого перейдем в терминал и введем следующую команду (рис. 4.64):
geth --datadir init

После успешного выполнения появится сообщение о том, что состояние генезиса успешно записано.

Рисунок 4.64. Инициализация генезис-блока

При этом появится папка «geth», в которой будет накапливаться цепочка.
Также будут созданы две папки: «chaindata» и «lightchaindata».
Чем они отличаются? Если вы хотите стать легким клиентом цепи, то вам достаточно подгрузить из интернета «lightchaindata» – это просто хеш-коды всех
блоков, облегченная версия цепочки, которая весит гораздо меньше основной,
что позволяет работать с цепочкой, но майнить блоки при этом нельзя.
Если же вы хотите полноценно работать с сетью, то для этого нужна папка
«chaindata». При инициализации в цепочке существует только нулевой блок,
который не содержит транзакций, поэтому эти две папки идентичны.
Теперь необходимо произвести инициализацию цепочки. Для этого необходимо в консоли ввести команду следующего формата:

Основные блокчейн-платформы  231
geth --rpc --rpcapi “eth, miner, personal, txpool, admin, web3” --datadir --rpcaddr “адрес” --networkid 15 --allow-insecure-unlock
--rpccorsdomain “*” console

В нашем случае команда запуска выглядит так (рис. 4.65):
geth --rpc --rpcapi “eth, miner, personal, txpool, admin, web3” --datadir
C:\test --rpcaddr “127.0.0.1” --networkid 15 --allow-insecure-unlock
--rpccorsdomain “*” console

Рисунок 4.65. Инициализация цепочки

Рассмотрим, что означают введенные команды и параметры:
 --rpc – протокол удаленного вызова команд;
 --rpcapi – указываем используемые модули geth;
 eth – отвечает за работу цепочки;
 miner – отвечает за работу с майнингом;
 personal – работа с определенным кошельком;
 txpool – отвечает за транзакции;
 web3 – предоставляет интерфейс разработки приложений (Application
Programming Interface, API) к нашей цепочке;
 --datadir – указываем, с какой цепочкой будем работать;
 --rpcaddr – адрес, с которым будет работать протокол (указываем адрес
из Ganache);
 --networkid 15 – идентификатор сети;
 --allow-insecure-unlock – упрощает работу с ключами;
 --rpccorsdomain – позволяет подключаться разным сервисам к нашей цепочке;
 console – запускает сервис в режиме консоли.
После запуска можем работать с консолью Geth, которая поддерживает синтаксис языка JavaScript. Можно пользоваться модулями, которые были подключены в --rpcapi.
Первый модуль – модуль eth. Для того чтобы посмотреть, какие команды для
него доступны, нужно в консоль ввести команду eth. (с точкой в конце) и быстро нажать два раза клавишу Tab, после чего откроется список доступных команд (рис. 4.66).

232



Глава 4

Рисунок 4.66. Команды модуля eth

Названия команд интуитивно понятны. Так, например:
 eth.blockNumber – номер текущего блока в цепочке;
 eth.accounts – список аккаунтов, которые есть в сети;
 eth.getBalance – запрос баланса какого-либо пользователя;
 eth.getBlock – получить информацию о блоке;
 eth.getHashrate – получить хешрейт;
 eth.getUncle – можно узнать дядей блока;
 eth.sendTransaction – отправить транзакцию;
 eth.sign – подписать транзакцию и т. д.
Для того чтобы посмотреть, какие аккаунты сейчас есть в сети, необходимо
ввести команду eth.accounts. После выполнения команды на экран будут выведены те адреса, которые находятся в рабочей папке (те же адреса, что и в
генезис-блоке) (рис. 4.67). Если генезис-блок был сформирован с ошибкой, но
инициализировал цепочку, то у этих адресов будет нулевой баланс.

Рисунок 4.67. Просмотр аккаунтов в сети

Основные блокчейн-платформы  233
Баланс двух аккаунтов на рис. 4.67 обуславливается значением, заданным
в генезисе. Для просмотра баланса первого по порядку (с индексом 0) аккаунта
необходимо ввести команду eth.getBalance(eth.accounts[0]) (так как accounts –
это список с адресами, то баланс адреса можно узнать, обратившись к нему по
его индексу). В консоли появится баланс выбранного аккаунта (рис. 4.68).

Рисунок 4.68. Просмотр баланса аккаунта по индексу

Второй способ посмотреть баланс – ввести команду eth.getBalance(«адрес»).
Есть также и третий способ, основанный на использовании JavaScript, – для
просмотра баланса следует создать какую-нибудь переменную и присвоить ей
значение, например eth.accounts[0] (рис. 4.69):
var User =

eth.accounts[0],

После этого передаем созданную переменную в команду:
eth.getBalance(User)

Если теперь в консоли просто ввести имя переменной User, то на экран будет
выведено значение адреса, которое в ней хранится.

Рисунок 4.69. Просмотр баланса аккаунта с помощью дополнительной переменной

Для просмотра команд модуля personal необходимо аналогичнымобразом
ввести в консоль его название (с точкой в конце) и два раза нажать Tab. Как и в
случае с eth, на экран будет выведен полный список команд (рис. 4.70).

Рисунок 4.70. Команды модуля personal

234



Глава 4

Функция personal.newAccount создает новый аккаунт (адрес). Так как это
функция, то в консоли при вводе данной команды нужно указать скобки:
personal.newAccount(). Если скобки оставить пустыми, то после выполнения
данной команды последует просьба придумать пароль, после ввода которого
будет выдан новый адрес.
Кроме того, можно сразу в функцию передать пароль, указав его в скобках
(обязательно в кавычках), например так (рис. 4.71):
personal.newAccount(“123”)

Рисунок 4.71. Создание нового аккаунта

Функция personal.unlockAccount используется для разблокировки аккаунта.
Модуль miner содержит две важные функции:
 miner.start() – функция, запускающая майнинг. После выполнения консоль обновляется, начинают появляться сообщения со статусом состояния блока. Если во время выполнения мы захотим узнать баланс пользователя, то после выполнения команды увидим, что баланс майнера
постоянно увеличивается;
 miner.stop() – функция, останавливающая процесс майнинга.

4.2.10 Смарт-контракты в системе Эфириум
Понятие смарт-контрактов было описано ранее в разделе 3.4. Рассмотрим далее специфику применения смарт-контрактов на платформе Эфириум.
Отметим, что смарт-контракты в системе Эфириум предоставляют значительно более широкие возможности по сравнению с рассмотренными ранее
смарт-контрактами в системе Биткойн.

Токены
На сегодняшний день существует несколько специализированных шаблонов
смарт-контрактов – токенов. Под токеном понимается цифровой актив, который принадлежит пользователю системы. Смарт-контракт содержит в себе необходимый набор функционала по работе с токеном.
Фактически под владением токеном понимается привязка определенного
значения числовой переменной к адресу пользователя. Стоимость такого цифрового актива будет варьироваться в зависимости от спроса на него и от того,
как владельцы каждого токена будут ценить владение им. Наиболее популярные токены соответствуют стандарту ERC-20 [116], который будет подробно
описан далее.
Использование шаблонного кода смарт-контракта позволяет отслеживать
токен различным сервисам, как криптокошелькам, так и биржам, обменникам.

Основные блокчейн-платформы  235
Один из наиболее популярных обменников – сервис Uniswap [238]. Сервис позволяет обменивать токены на фиатную валюту, криптовалюту или на другие
токены, автоматически рассчитывая стоимость обмена.

Описание токена ERC-20
Токен ERC-20 содержит в своем составе следующий набор функций:
 name() – имя токена, заданное владельцем токена при его выпуске;
 symbol() – символическое обозначение токена, заданное владельцем токена при его выпуске; в обозначении используются заглавные латинские
символы, например SHIB, WETH, DAI;
 decimals() – количество знаков после запятой, позволяет разделить токен
на дробные части; например, если значение равно трем, то каждый токен может быть разделен на 1000 составных частей. Обычно используется значение decimal = 18, по аналогии с соотношением Eth и wei;
 totalSupply() – общее количество токенов в системе;
 balanceOf(address _owner) – баланс токенов конкретного пользователя;
 transfer(address _to, uint256 _value) – перевод токенов с баланса одного
пользователя другому;
 approve(address _spender, uint256 _value) – разрешить другому пользователю тратить свои токены в пределах указанного количества;
 allowance(address _owner, address _spender) – количество токенов, которые пользователь разрешил тратить другому со своего баланса;
 transferFrom(address _from, address _to, uint256 _value) – перевести токены пользователя, доступ к которым был предоставлен, другому пользователю.
Обычно перед разработчиками становится задача увеличить функционал токена, добавить дополнительную бизнес-логику. Рассмотрим язык программирования Solidity, который обычно применяется для разработки смарт-контрактов.

Язык программирования Solidity
Язык Solidity был разработан в 2014 году. Язык является кросс-платформенным,
однако применяется преимущественно для смарт-контрактов платформы
Эфириум.
Solidity представляет собой объектно-ориентированный, статически типизованный JavaScript-подобный компилируемый язык программирования.
Спустя годы использования язык не имеет завершенной версии, и разработчики постоянно выпускают новые релизы, совершенствуя и развивая функцио­
нал [227].
Синтаксис и компиляцию кода поддерживают многие среды разработки
и редакторы кода, в их числе VisualStidio Code, Sublime Text, Atom, Remix IDE,
IntelliJ IDEA и др.
Структура кода
Рассмотрим основную структуру кода на языке Solidity.
В первой строке всегда указывается версия используемого компилятора:
pragma solidity (>) balances – словари, которые по ключу хранят значение;
 address – адрес пользователя или контракта, который представляет собой последовательность из 40 символов (20 байт).
Пользовательские структуры объявляются аналогично многим другим языкам программирования, например:
struct{
uint a;
uint b;
}

Поговорим о структурах немного подробнее. Рассмотрим пример исходных
данных системы, которые заданы в соответствии с табл. 4.13.
Таблица 4.13. Пример задания исходных данных системы
estate_id

owner

info

square

useful_square

present_
status

sale_status

(целое
число)

(адрес Eth)

(строка)

(целое
число)

(целое
число)

(True/False)

(True/
False)

0

0xfCC..90C

Чехова 2

150

130

False

False

1

0x148..44B

Фрунзе 9

70

60

True

False

Для хранения данных, соответствующих табл. 4.13, можно составить следующую структуру:
struct Estate {
uint estate_id;
address owner;
string info;
uint square;
uint useful_square;
bool present_status;
bool sale_status;
}

То есть для каждого поля в первой строке таблицы указывается тип данных,
который указан во второй строке таблицы.
Обращение к элементам структуры происходит следующим образом (рис. 4.73):

238



Глава 4

 если необходимо получить всю структуру, то просто указывается ее название;
 если необходимо получить конкретный элемент, то нужно указать его
в следующем формате: название_структуры[искомый элемент];
 если необходимо получить конкретный параметр элемента, то нужно
обратиться к нему в формате: название_структуры[искомый элемент].
параметр.

Рисунок 4.73. Обращение к элементам структуры

Функции
Функции объявляются с использованием ключевого слова function, после которого указывается имя функции, а также параметры (типы данных и имена
переменных, которые принимает функция) и атрибуты функции. Если функция будет возвращать какие-то значения, то типы данных этих значений необходимо указать в списке возвращаемых значений returns (см. рис. 4.74).

Рисунок 4.74. Структура функции

Основные блокчейн-платформы  239
Затем в фигурных скобках создается тело функции. Если функция возвращает значения, то их необходимо в теле функции вернуть с помощью команды
return . При этом важно соблюдать порядок возвратов с порядком типов, указанных в блоке returns.
Атрибуты функций приведены на рис. 4.74. Атрибут public позволяет вызвать функцию одного контракта из любого участка кода другого контракта,
а атрибут private разрешает вызов функции только в том контракте, в котором
функция находится.
Основные операторы
В языке Solidity есть сложные операторы, такие как if-else, циклы for, while,
конструкция require. Однако цикл while использовать не рекомендуется.
Конструкция require используется следующим образом: если условие выполняется, то осуществляется переход к следующей команде в теле этой конструкции; если условие не выполняется, то выполнение функции прекращается, выводится сообщение об ошибке, транзакция не формируется.
В ходе выполнения функции можно обращаться к параметрам транзакции.
Транзакция обозначается как msg, имеет два основных атрибута msg.sender
(адрес создателя транзакции) и msg.value (сумма транзакции в веи).
Если необходимо работать с криптовалютой, то при перечислении средств
с обычного аккаунта на кошелек смарт-контракта деньги перечисляются автоматически с заполнением поля msg.value при формировании транзакции.
Если криптовалюту нужно перечислить с кошелька контракта на кошелек
пользователя или кошелек другого контракта, то необходимо использовать
следующую конструкцию (при этом сумма перевода указывается в веи):
.transfer()

Также очень полезной функцией является block.timestamp(now). Это метка
времени в формате Unix time в момент создания блока с транзакцией (по времени блокчейн-системы). Значение имеет тип uint.

Среда разработки Remix IDE
Наиболее популярным средством для разработки смарт-контракта является
Remix IDE [207] – онлайн-среда разработки.
Среда разработки содержит тестовые контракты (слева на вкладке contracts).
Рассмотрим один из тестовых контрактов (рис. 4.75).
Представленный на рисунке контракт содержит переменную number и два
метода. При этом функция store задает значение поля number, а функция
retrieve возращает данное значение.
Среда разработки включает в себя различные виртуальные машины. Так, например, виртуальная машина Injected Web 3 может работать с расширением
MetaMask, которое было рассмотрено выше.

240

 Глава 4

Рисунок 4.75. Тестовый контракт

Выбрать необходимую машину можно в разделе environment, после чего автоматически произойдет сопряжение c расширением MetaMask и будет предложено выбрать и импортировать счет для работы (рис. 4.76).
Для того чтобы контракт оказался размещенным в сети Эфириум, необходимо выполнить функцию деплоя (deploy). После компиляции кода контракта на
специальной вкладке необходимо нажать кнопку «Deploy». При этом откроется
вкладка MetaMask с подтверждением операции. После успешного выполнения
в терминале среды разработки появляется зеленая галочка и сообщение о том,
что все выполнено успешно. Также мы сможем увидеть контракт на вкладке
Deployed contracts.
Есть еще один вариант подключения к цепочке – это использование Web 3
Provider, который связан с запущенным клиентом Geth. При нажатии на Web 3
Provider появляется всплывающее окно, в котором нам необходимо указать
адрес и порт.

Основные блокчейн-платформы  241

Рисунок 4.76. Выбор виртуальной машины

При попытке деплоить контракт может возникнуть ошибка, связанная с отсутствием доступа. Для решения этой проблемы необходимо в консоли клиента Geth разблокировать аккаунт следующим образом:
personal.unlockAccount(,
, )

Далее необходимо запустить майнинг командой miner.start(). После этого
можно вернуться в среду разработки и выполнить деплой контракта.

Пример разработки смарт-контракта
Покажем, как разработать смарт-контракт, на примере. Пусть перед нами стоит задача разработать систему перевода внутрисистемных средств с пароль-

242



Глава 4

ной защитой перевода – пользователь должен ввести корректный код для завершения перевода и получения средств.
В первую очередь указывается версия используемого компилятора, допускаются операторы сравнения. Стоит отметить, что не все команды программного кода будут обратно совместимы с устаревшими версиями компилятора.
pragma solidity ^0.5.0;

Как было сказано выше, контракт фактически представляет собой класс –
имеет атрибуты, конструктор и методы.
contract Change {


Как и другие языки, Solidity поддерживает пользовательский тип данных
в виде структур. Объявим структуру с балансом пользователя и его ролью в системе.
struct User {
uint balance;
bool role;
bool registered;
}

Доступ к структуре может быть получен с помощью соответствия (маппинга, словаря), которое по уникальному значению адреса пользователя будет
хранить информацию о нем. Атрибут public обеспечит получение доступа
к этим данным.
mapping(address => User) public users;

Маппинг не позволяет получить список заполненных уникальных ключей
(в данном случае – адресов), по любому ключу будет возвращена информация,
даже пустая. Для решения этой проблемы создадим список используемых ключей.
address[] public userlist;

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

Основные блокчейн-платформы  243
struct Transfer {
address adr_from;
address adr_to;
uint value;
uint category;
bytes32 pwhash;
uint time;
bool finished;
}

Заведем в системе массив переводов, каждый из которых будет доступен по
его индексу.
Transfer[] public transfers;

Также заведем список категорий для переводов.
string[] public categories;

Как и класс, смарт-контракт имеет конструктор или функцию инициализации – функцию, которая будет выполнена сразу при публикации контракта
в сети.
Зафиксируем в системе одного пользователя, администратора системы,
обозначим его стартовый баланс в 10 000 условных единиц. Добавим в список пользователей структуру с нужными параметрами и добавим этот адрес
к списку пользователей.
Конструктор выполняется от имени пользователя, который деплоит конт­
ракт, он будет распознан как msg.sender в этой функции. Также добавим несколько типов переводов в соответствующий список.
constructor() public {
users[msg.sender] = User(10000, true, true);
userlist.push(msg.sender);
categories.push("Личный перевод");
categories.push("Аренда жилья");
}

Создадим функцию регистрации нового пользователя. Если в значении
флага registered у пользователя уже установлено значение true, то выполнение
транзакции будет прервано. Если пользователь еще не был зарегистрирован,
то заполнятся необходимые структуры.
function create_user() public {
require(users[msg.sender].registered == false);
users[msg.sender] = User(0, false, true);
userlist.push(msg.sender);
}

Для реализации ролевой модели в системе добавим модификатор контроля прав администратора: если роль вызывающего функцию пользователя – не
администратор, то модификатор прервет дальнейшее выполнение функции.
modifier onlyAdmin() {
require(users[msg.sender].role, "error: you are not admin");
_;
}

244



Глава 4

Добавим функцию создания новой категории. С помощью модификатора
доступ к данной функции предоставлен только администратору. Новая категория добавляется в соответствующий список.
function create_category(string memory name) public onlyAdmin {
categories.push(name);
}

Определим функцию создания перевода. Функция проверяет объем средств
у пользователя, проверяет наличие в системе указанной категории, проверяет
существование в системе получателя перевода. Функция принимает в качестве
параметров адрес получателя, сумму перевода, категорию перевода и хеш-код
кодового слова.
Хеш-код может быть получен в клиентском приложении или высчитан пользователем самостоятельно в случае обращения непосредственно к контракту.
Используемый алгоритм хеширования – Keccak (см. раздел 1.3).
В случае успешного прохождения всех проверок будет создана новая структура с переводом, а баланс отправителя уменьшится на соответствующее значение. При создании перевода время выполнения будет равно нулю, флаг перевода будет равен false.
function create_transfer(address adr_to, uint value, uint category, bytes32
pwhash) public {
require(users[msg.sender].balance >= value, "error: not enought money");
require(users[adr_to].registered == true, "error: recipient is not
registered");
require(category>=0 && category