📝 Отображение состояния блокчейна должно быть абстрактно и не являться частью консенсуса (перевод статьи @dantheman)
В моей предыдущей статье я утверждал, что блокчейны должны проектироваться по подобию многопользовательских игр, где для достижения консенсуса о состоянии игры требуется только синхронизировать входные данные пользователей. Сегодня я хотел бы поговорить о том, что сама форма, которую принимает состояние игры, нерелевантна до тех пор, пока все игроки не придут к согласию относительно действительности новых изменений игры. Иными словами, я считаю, что нет необходимости в достижении консенсуса о каноническом глобальном состоянии, которое может быть представлено в виде хеша.
Я пишу это потому, что многие люди из блокчейн-экосистемы считают это утверждение спорным. Ethereum - первый пример основанного на сообщениях (основанного на входных данных) протокола, который не использует модель UTXO, но требует от заверителей блоков всеобщего согласия относительно точного хеша канонического состояния. Я считаю это требование излишним.
Любой разработчик скажет вам, что чем больше ограничений (требований) вы накладываете на решение, тем труднее становится его оптимизировать. Если вы чересчур узко определяете свою проблему, то сами вынуждаете себя разрабатывать менее гибкие и более вычислительно сложные шаблоны решения. В худшем случае столь ограниченная проблема будет неразрешима. Вот почему так важно убедиться, что каждое требование действительно добавляет ценности конечному продукту.
Почему Ethereum отслеживает хеш глобального состояния?
Первая и основная причина поддержки хеша глобального состояния — это создание доказательств. Если у вас есть хеш глобального состояния, а состояние организовано в виде древовидной структуры вроде Меркла-Дамгарда, то с доказательством в 1КБ вы можете показать, что в определенный момент времени некоторая переменная имела определенное значение. Например, вы можете доказать, что в прошлый понедельник ваш баланс составлял 100 ETH, или что 15 секунд назад ваш баланс составлял 20 ETH.
На первый взгляд это звучит замечательно, но при этом вы не можете доказать, что прямо сейчас ваш баланс больше 10 ETH. Ценность прошлых состояний не имеет значения, если каждая обрабатываемая транзакция способна изменить текущее состояние. Опытные разработчики могут обойти это, поставив на состояние временной замок, чтобы если вы доказали, что 30 секунд назад ваш баланс составлял 20 ETH, и что он был заблокирован в состоянии, которое предотвратит его изменение на 1 минуту, то тогда вы можете доказать, что «прямо сейчас» он больше 10 ETH.
С временным замком есть одна проблема: он полагается на предположение, что код поведет себя таким образом, что это ограничение будет принудительно применено к возможным будущим значениям баланса. Это значит, что в дополнение к доказательству состояния вы также должны доказать поведение кода.
Вторая причина поддержки хеша глобального состояния - обнаружение багов в программном обеспечении. В данном контексте баг означает недетерминистическое поведение. Хеш глобального состояния не сможет обнаружить логические баги, такие как тот, что привел к провалу DAO. В момент обнаружения недетерминистического поведения сеть разделится на два или более фрагмента, в зависимости от характера недетерминированности. В лучшем случае в сети существует явное большинство, и она продолжит движение, тогда как меньшинству нужно будет выяснить, почему их машины не соответствуют большинству. В худшем случае явного большинства нет, только наибольшее из меньшинств. Наличие хеша состояния ни в коем случае не позволит ноде меньшинства автоматически исправить себя и присоединиться к форку большинства.
Что произойдет, если у вас нет хеша глобального состояния?
Блокчейны вроде Steem и BitShares выбирают как не генерировать, так и не требовать от всех нод согласия о хеше глобального состояния. Это значит, что невозможно что-либо доказать о состоянии блокчейна, используя только заголовки блоков и доказательство Меркла. Всё, что способны доказать Steem и BitShares, это что в определенное время определенная транзакция была валидна. Любая недействительная транзакция просто не будет частью блокчейна.
Если два пира пострадали от детерминистического бага, то это не обнаружится, пока один из них не примет транзакцию, которую отвергнет другой. Это может длиться дни, месяцы и годы спустя изначального расхождения в состоянии. Когда же это наконец происходит, результат похож на то, что случилось с Ethereum: пиры меньшинства вынуждены выяснить, почему они не согласны с большинством и исправить баг.
Из этого можно заключить, что преимущество хеша состояния в том, что он раньше перестает работать в случае недетерминирстического поведения.
Источники недетерминистического поведения
Если мы хотим обеспечить соблюдение конструктивного ограничения для обнаружения недетерминистического поведения, то для начала нам важно понять, как всё это работает. В целом всё можно свести к следующим широким категориям:
- Ссылка на внешнее состояние, такое как время, неинициализированная память или генератор случайных чисел
- Различия в оптимизации компилятора
- Различия в архитектуре центрального процессора
- Различия в динамически линкуемых библиотеках
- Космическое излучение и ошибки оборудования
Если предположить, что программное обеспечение всех машин работает в одинаковой среде, и что космическое излучение и ошибки оборудования случаются только на отдельных узлах, то единственным источником недетерминизма остаются структурные ошибки программирования.
Альтернативные способы обнаружения ошибок программирования
Даже если хеш состояния не является частью официального консенсуса, это не значит, что пиры не могут вычислить хеш их состояния с целью раннего обнаружения багов. Теоретически, два пира, работающих на одном и том же коде, должны производить одинаковое состояние; следовательно, если они хешируют свое состояние, то по идее получат одинаковое значение. Если они делают это периодически, то смогут обнаружить детерминистический баг до того, как он приведет к ситуации, где разные пиры приходят к разным заключениям относительно правильности конкретной транзакции.
Код для обнаружения багов не должен находиться в конечном продукте
Разработчики прекрасно знают, что прежде чем отослать код, нужно убрать все свои инструменты отладки и мониторинга производительности. Этот инструментарий вредит производительности и в конечном счете может не соответствовать требованиям к производительности продукта в режиме реального времени. Включение хешей в глобальный консенсус заставляет разработчиков в точности воспроизводить все исторические баги, даже если они не оказывают заметного влияния на поведение платформы. Пример такого бага, который не влияет на поведение, это переключение с массива на связный список. В этом случае значение структуры данных остается тем же, но характеристики производительности сильно различаются.
Математическое обобщение состояния блокчейна
Обычно я стараюсь не использовать математику для доказательства чего-либо, потому как заметил, что подавляющее большинство людей не способно уследить за логикой. Сегодня же я воспользуюсь математикой, дабы вывести доказательство того, что поведение блокчейна может быть полностью абстрагировано с различными представлениями состояния, будучи математически эквивалентным.
Представьте, что для всех возможных входных параметров f(i)
существует функция, которая возвращает значение true
, если входной параметр был правильным, и false
, если входной параметр был неправильным. Пусть I
- это набор всех возможных входных параметров, а i
- конкретный входной параметр из этого набора.
Блокчейны вроде Steem или BitShares могут быть определены так, что f(i)
возвращает f(i)
, если входной параметр неверен, и возвращает f2(i)
, если входной параметр действителен.
f2(i)
- это новая функция, которая ведет себя схоже с функцией f(i)
, и которая должна передавать следующий входной параметр. Можно сказать, что если возвращенная функция является такой же, как и исходная функция, то значение вернется на false
, если нет - то на true
.
Две функции считаются идентичными, если для каждого i
в I
они производят одно и то же выходное значение. С логической точки зрения внутренняя реализация f(i)
является черным ящиком. Поэтому самое абстрактное отображение состояния - это отображение от i
к true
или false
. С каждым входным параметром новая функция возвращается с новым отображением для каждого возможного входного параметра.
Должно быть очевидно, что представлять состояние таким путем не очень практично, несмотря на то, что математически точно. Это непрактично, так как размер набора входных параметров I
фактически бесконечен. Чтобы преодолеть эту проблему, можно использовать метод алгоритмического сжатия. Например, если все НЕЧЕТНЫЕ числа неверны, а все ЧЕТНЫЕ — верны, то можно выразить f(i)
как i % 2 == 0
. Мы также можем выразить это как i&1 == 0
. В обоих случаях реализация f(i)
различна, но обе реализации математически эквивалентны.
Из этого мы можем заключить, что не имеет значения, как реализована функция, пока все стороны используют реализацию, которая производит одинаковый true/false
выходной параметр для всех возможных входных параметров. Мы также можем сказать, что если проверка блокчейнов выполняется путем проверки результата f(i)
для каждой транзакции, то все пиры с эквивалентными функциями останутся в консенсусе.
Невозможность доказательства равенства двух функций
Критики тут же скажут, что математическое доказательство на практике ничего не значит, так как невозможно доказать, что две разных реализации f(i)
произведут одинаковый результат для всех входных i
. Всё, что лежит дальше простых математических функций, становится невозможным доказать. Ethereum пытается доказать, что они равны, генерируя хеш состояния, содержащегося в f(i)
, и затем утверждая, что при одинаковых состояниях и одинаковой логике обе функции равны.
И вот где нюанс: Ethereum все еще требует доказательства того, что логика равна и свободна от детерминированных багов. Это круговое доказательство! Если мы утверждаем, что код свободен от детерминированных багов, тогда мы можем утверждать, что состояния будут равны; тем не менее, если мы не можем доказать, что код свободен от детерминистических багов, то доказательство того, что состояние одинаково, никак не поможет нам доказать, что две функции равны. Вы можете доказать наличие бага, но не его отсутствие. Разница в хеше состояния может не иметь значения, если она сочетается с компенсирующей разницей в логике.
Учитывая, что доказательство одинаковости состояния с помощью сравнения хеша на самом деле не доказывает наличие консенсуса, где консенсус определяется как согласие всех сторон о верности или неверности всех возможных входных параметров, тогда в чем его польза? Это особенно актуально в случае наличия нескольких реализаций на разных языках. Доказать, что вы произвели то же состояние - это неплохое начало, но на самом деле оно ничего не доказывает, в чем мы можем убедиться на примере прошлых форков Ethereum.
Почему это важно?
Это важно, так как в попытках доказать невозможное инженеры блокчейнов излишне ограничивают процесс консенсуса и не дают блокчейнам масштабироваться так, как этого требуют потребности реального мира.
Разработчики вынуждены придерживаться канонических представлений, а заверители вынуждены производить вычисления, являющиеся избыточными для детерминистического кода.
Это важно, потому что доказательство исторических состояний куда менее полезно, чем доказательство текущего состояния. Это имеет значение, так как создание масштабируемых блокчейнов, не являющихся чрезмерно нестабильными, имеет решающее значение для повсеместного внедрения технологии блокчейн в целом.
Заключение
Блокчейны не должны делать какое-либо специальное выражение состояния обязательной частью своего алгоритма консенсуса. Такое решение только повредит производительности и сделает программное обеспечение уязвимым, а исправление багов — более сложным для ретроактивного применения. Если разработчики хотят проверить наличие багов, они просто могут запустить версию кода, которая генерирует и сравнивает хеши. Если кому-либо нужны исторические доказательства, они могут быть сгенерированы и использованы сторонами, работающими на сборке программного обеспечения для разработчиков.
Принимая во внимание преимущества, связанные с тем, что хэш состояния можно получить без включения его в консенсус, и тот факт, что включение хеша состояния в консенсус имеет высокую цену, можно сделать вывод, что у разработчиков блокчейнов нет никаких убедительных оснований делать хэши состояния частью консенсуса.