Данный текст — продолжение серии статей, в которых я рассматриваю структуру (предположительно) готовящейся к выходу в этом году распределенной сети Telegram Open Network (TON). В предыдущей части я описал её самый базовый уровень — способ взаимодействия узлов между собой.
На всякий случай напомню, что к разработке этой сети я отношения не имею и весь материал почёрпнут из открытого (хотя и непроверенного) источника — документа (ещё к нему есть прилагающаяся брошюра, излагающая вкратце основные моменты), появившегося в конце прошлого года. Объем информации в этом документе, на мой взгляд, свидетельствует о его подлинности, хотя никаких официальных подтверждений тому нет.
Сегодня посмотрим на основной компонент TON — блокчейн.
Базовые понятия
Аккаунт (account). Некий набор данных, идентифицируемый 256-битным числом account_id (чаще всего это публичный ключ владельца аккаунта). В базовом случае (см. ниже нулевой воркчейн), под этими данными подразумевается баланс пользователя. «Занять» конкретный account_id может кто угодно, но изменять его значение можно только по определённым правилам.
Смарт-контракт (smart-contract). По сути — частный случай аккаунта, дополненный кодом смарт-контракта и хранилищем его переменных. Если в случае «кошелька» можно зачислять и списывать деньги из него по относительно простым и заранее определённым правилам, то в случае смарт-контракта эти правила записаны в виде его кода (на некоем Тьюринг-полном языке программирования).
Состояние блокчейна (state of blockchain). Совокупность состояний всех аккаунтов/смарт-контрактов (в абстрактном смысле — хэш-таблица, где ключами являются идентификаторы аккаунтов, а значениями — хранимые в аккаунтах данные).
Сообщение (message). Выше я употребил выражение «зачислять и списывать деньги» — это частный пример сообщения («передать N грамм с аккаунта account_1 на аккаунт account_2»). Очевидно, отправить такое сообщение может только узел, владеющий закрытым ключом аккаунта account_1 — и способный подтвердить это подписью. Результатом доставки таких сообщений обычному аккаунту является увеличение его баланса, а смарт-контракту — выполнение его кода (который обработает прием сообщения). Разумеется, возможны и другие сообщения (переносящие не денежные суммы, а произвольные данные между смарт-контрактами).
Транзакция (transaction). Факт доставки сообщения называется транзакцией. Транзакции изменяют состояние блокчейна. Именно из транзакций (записей о доставке сообщений) состоят блоки в блокчейне. В этом плане можно представлять себе состояние блокчейна как инкрементальную базу данных — все блоки являются «диффами», которые нужно применить последовательно, чтобы получить текущее состояние БД. О конкретике упаковки этих «диффов» (и восстановлении полного состояния по ним) пойдёт речь в следующей статье.
Блокчейн в TON: что это и зачем?
Как упоминалось в предыдущей статье, блокчейн — это структура данных, элементы (блоки) которой упорядочены в «цепь», и каждый следующий блок цепи содержит в себе хэш предыдущего. В комментариях задали вопрос: а зачем вообще нужна такая структура данных, когда у нас уже есть DHT — распределённая хэш-таблица? Очевидно, что какие-то данные можно хранить и в DHT, но это подходит только для не слишком «чувствительной» информации. Балансы криптовалюты хранить в DHT нельзя — в первую очередь из-за отсутствия проверок на целостность. Собственно, вся сложность структуры блокчейна вырастает ради предотвращения вмешательств в хранимые в нём данные.
Однако блокчейн в TON выглядит ещё сложнее, чем в большинстве других распределённых систем — и на то есть две причины. Первая — стремление минимизировать потребность в форках. В традиционных криптовалютах все параметры заданы на начальном этапе и любая попытка их изменить приводит фактически к появлению «альтернативной вселенной криптовалюты». Вторая причина — поддержка дробления (шардинга, шардирования) блокчейна. Блокчейн — структура, не способная стать меньше с течением времени; и обычно каждый узел, отвечающий за работоспособность сети, вынужден хранить её полностью. В традиционных (централизованных) системах для решения подобных проблем применяется шардирование: часть записей в БД находится на одном сервере, часть — на другом, и т.д. В случае с криптовалютами такая функциональность пока что довольно редка — в частности, из-за того, что сложно добавить шардирование в систему, где оно не было запланировано изначально.
Каким же образом TON планирует решить обе вышеописанных проблемы?
Содержимое блокчейна. Воркчейны.
В первую очередь, поговорим о том, что же планируется хранить в блокчейне. Храниться там будут состояния аккаунтов («кошельков» в базовом случае) и смарт-контрактов (для простоты будем считать, что это то же, что и аккаунты). По сути, это будет обычная хэш-таблица — ключами в ней будут идентификаторы account_id, а значениями — структуры данных, содержащих в себе такие вещи, как:
- баланс;
- код смарт-контракта (только для смарт-контрактов);
- хранилище данных смарт-контракта (только для смарт-контрактов);
- статистика;
- (опционально) публичный ключ для переводов с аккаунта, по умолчанию account_id;
- очередь исходящих сообщений (сюда они заносятся для пересылки получателю);
- список последних доставленных этому аккаунту сообщений.
Как было сказано выше, непосредственно блоки состоят из транзакций — сообщений, доставленных различным аккаунтам account_id. Однако кроме account_id сообщения содержат также 32-битное поле workchain_id — идентификатор т.н. воркчейна (workchain, working blockchain). Это позволяет иметь несколько независимых друг от друга блокчейнов с разными конфигурациями. При этом workchain_id = 0 считается особым случаем, нулевым воркчейном — именно находящиеся в нём балансы будут соответствовать криптовалюте TON (Grams). Вероятнее всего, на первых порах других воркчейнов существовать не будет вовсе.
Шардчейны. Infinite Sharding Paradigm.
Но на этом рост количества блокчейнов не останавливается. Разберёмся с шардингом. Представим, что каждому аккаунту (account_id) выделен свой собственный блокчейн — в нём лежат все приходящие ему сообщения — и состояния всех таких блокчейнов хранятся на отдельных узлах.
Конечно, это весьма расточительно: скорее всего, в каждый из этих шардчейнов (shardchain, shard blockchain) транзакции будут поступать очень редко, а мощных узлов понадобится много (забегая вперёд, отмечу, что речь идёт не просто о клиентах на мобильных телефонах — а о серьёзных серверах).
Поэтому шардчейны объединяют в себе аккаунты по двоичным префиксам их идентификаторов: если шардчейн имеет префикс 0110, то в него попадут транзакции всех account_id, которые начинаются с этих цифр. Этот shard_prefix может иметь длину от 0 до 60 бит — и главное, что он может меняться динамически.
Как только в один из шардчейнов начинает поступать чрезмерно много транзакций, работающие над ним узлы по заранее определённым правилам «расщепляют» его на два дочерних — их префиксы будут на один бит длиннее (и для одного из них этот бит будет равен 0, а для другого — 1). Например, shard_prefix = 0110b расщепится на 01100b и 01101b. В свою очередь, если два «соседних» шардчейна начнут чувствовать себя достаточно вольготно (в течение некоторого времени), они снова сольются воедино.
Таким образом, шардирование делается «снизу вверх» — мы предполагаем, что каждый аккаунт обладает своим шардом, но они — до поры до времени — «склеены» по префиксам. Это и подразумевает под собой Infinite Sharding Paradigm (парадигма бесконечного шардирования).
Отдельно хочется подчеркнуть, что воркчейны существуют только виртуально — на самом деле, workchain_id это часть идентификатора конкретного шардчейна. Говоря формальным языком, каждый шардчейн определяется парой чисел (workchain_id, shard_prefix).
Исправление ошибок. Вертикальные блокчейны.
Традиционно считается, что любая транзакция в блокчейне является «высеченной в камне». Однако, в случае с TON предусмотрена возможность «переписать историю» — в случае, если некто (т.н. узел-«рыбак») докажет, что один из блоков был подписан некорректно. В этом случае в соответствующий шардчейн добавляется специальный корректирующий блок, содержащий хэш самого исправляемого блока (а не последнего блока в шардчейне). Представляя шардчейн как цепочку блоков, выложенную по горизонтали, можно сказать, что корректирующий блок подцепляется к ошибочному блоку не вправо, а сверху — поэтому считается, что он становится частью маленького «вертикального блокчейна». Таким образом, можно сказать, что шардчейны являются двумерными блокчейнами.
В случае если после ошибочного блока на внесённые им изменения ссылались последующие блоки (т.е., были совершены новые транзакции на основе невалидных), к этим блокам так же «сверху» добавляются корректирующие. Если блоки не затрагивали «поражённую» информацию, на них эти «корректирующие волны» не распространяются. Например, в иллюстрации выше некорректной была признана транзакция первого блока, увеличивающая баланс аккаунта C — поэтому транзакция, уменьшающая баланс этого аккаунта в третьем блоке, также должна быть аннулирована, а поверх самого блока закоммичен корректирующий блок.
Надо заметить — хотя корректирующие блоки и изображаются расположенными «над» оригинальными, фактически они будут дописаны в конец соответствующего блокчейна (туда, где должны находиться хронологически). Двумерное расположение лишь показывает, к какой точке в блокчейне они будут «подцеплены» (посредством находящегося в них хэша оригинального блока).
Можно отдельно пофилософствовать о том, насколько хорошим является решение «менять прошлое». Казалось бы, если мы допускаем возможность появления некорректного блока в шардчейне, то нельзя не допустить и возможности появления ошибочного корректирующего блока. Здесь, насколько я могу судить, разница в количестве узлов, которое должно достигнуть консенсуса по поводу новых блоков — над каждым шардчейном будет трудиться относительно небольшая «рабочая группа» узлов (довольно часто меняющая свой состав), а внесение корректирующих блоков потребует согласия вообще всех узлов-валидаторов. Подробнее о валидаторах, рабочих группах и других ролях узлов я расскажу в следующей статье.
Один блокчейн, чтоб править всеми
Выше перечислено много информации о различных видах блокчейнов, которую саму по себе тоже следует где-то хранить. В частности, речь о следующих сведениях:
- о количестве и конфигурациях воркчейнов;
- о количестве шардчейнов и их префиксах;
- о том, какие узлы в данный момент ответственны за какие шардчейны;
- хэши последних добавленных блоков во все шардчейны.
Как вы уже могли догадаться, все эти вещи записываются в ещё одно хранилище-блокчейн — мастерчейн (masterchain, master blockchain). Благодаря наличию в его блоках хэшей от блоков всех шардчейнов, он делает систему сильно связанной. В том числе это означает, что генерация нового блока в мастерчейне будет происходить непосредственно после генерации блоков в шардчейнах — ожидается, что блоки в шардчейнах будут появляться почти одновременно примерно каждые 5 секунд, а очередной блок в мастерчейне — спустя секунду после этого.
Но кто же будет ответственен за реализацию всей этой титанической работы — за пересылку сообщений, выполнение смарт-контрактов, формирование блоков в шардчейнах и мастерчейне, да ещё и проверку блоков на ошибки? Неужели всё это будут втихомолку делать телефоны миллионов пользователей с установленным на них клиентом Телеграма? Или, быть может, команда Дуровых откажется от идей децентрализации и это будут делать их сервера по-старинке?
На самом деле, ни тот, ни другой ответ не является правильным. Но поля этой статьи стремительно заканчиваются, поэтому разговор о различных ролях узлов (вы могли уже заметить упоминания некоторых из них), а также о механиках их работы пойдёт уже в следующей части.