Исправлены ошибки Segwit2x
вилка Segwit2x была отменена чуть более двух недель назад в сообщении электронной почты в список рассылки 2x. Несколько сторон угрожали разбить сеть в любом случае, и мы с нетерпением ждали блока 494784, чтобы узнать, сможет ли кто-нибудь добыть 2x жесткую вилку или нет.Как оказалось, в программном обеспечении Segwit2x произошла ошибка, из-за которой клиент остановился на этапе 494782. В этой статье я рассмотрю детали того, что заставило программное обеспечение остановиться, почему он остановил блок перед этим должен был и что бы произошло, если бы Belshe и другие не отменили жесткую вилку на неделю раньше.
Настройка
2-я часть жесткой вилки была запланирована на протяжении последних 6 месяцев. Нью-Йоркское соглашение было согласовано в конце мая, код был написан в основном в июне, а программное обеспечение btc1 / Segwit2x было выпущено в июле.Специфика NYA требовала, чтобы:
- Segwit активируется при 80% вместо 95%
- 2x жесткая вилка будет активирована в течение 6 месяцев (от 23 мая)
Программное обеспечение было создано в репозитории btc1, с ведущим разработчиком Джеффом Гарзиком. Чтобы сделать первое условие, они включили предложение BIP91 Джеймса Хиллиарда, которое действительно успешно активировало Segwit в сети 24 августа на блоке 481824.Чтобы сделать второе условие, программное обеспечение btc1 включило предложение, которое активировало жесткую вилку, чтобы удвоить размер блока ровно 144 * 90 блоков после активации Segwit. Это число было выбрано потому, что 10 минут на блок означает около 144 блоков в день, поэтому 144 * 90 блоков занимают около 90 дней. Это положило высоту блока форкирования на 494784 и фактическую вилку вокруг 15 ноября или около того, что действительно удовлетворяло бы вторую часть NYA.
Баг
В базе данных btc1 было ограниченное количество различий по сравнению с Bitcoin Core. В общей сложности было около 500 строк изменений, большинство из которых не были критичными по консенсусу. Тем не менее, в 100 или около того изменившихся строках было по крайней мере две ошибки для поддержки жесткой вилки в блоке 494784.Чтобы понять эту ошибку, было бы проще всего взглянуть на набор изменений, которые впервые представили идею разветвления после 144 * 90 блоков:
...
Использование командной строки для ясности
Вы можете видеть, что существует параметр, как долго потребуется после активации Segwit удвоить размер блока. В частности, 144 * 90 блоков. В коде это концепция под названием «Segwit seasoning». В принципе, это позволяет Segwit существовать самостоятельно без удвоения размера блока для 144 * 90 блоков.Чтобы выяснить, пора ли использовать более крупные блоки, логическая переменная fSegwitSeasoned имеет значение True, если 144 * 90 блоков прошли, False, если нет. Следующий оператор if специально использует это логическое значение для определения того, какой максимальный размер базового блока (размер блока минус данные свидетеля) должен быть (2mb, если True, 1mb, если False). Обычно базовые блоки будут отклонены, если блок больше 1 Мб, но здесь мы видим, что блоки отклоняются, если блок больше, чем 2 МБ, если fSegwitSeasoned является True. Это критическая часть консенсусного кода, который отклоняет слишком большие блоки и, следовательно, требует жесткой вилки.Чтобы на самом деле выяснить, должно ли fSegwitSeasoned быть установлено значение True или False, здесь здесь используется функция VersionBitsState. В частности, код должен смотреть на блок 144 * 90 блоков предыдущего и проверять, был ли Segwit активным в сети. Если Segwit был активным 144 * 90 блоков назад, это означает, что> 1MB базовые блоки являются законными для этого блока. Это то, что нужно проверить.
VersionBitsState
Здесь присутствует тонкая ошибка, и она связана с тем, как вызывается VersionBitsState. Чтобы понять, посмотрите на фактическую функцию, определенную в файле versionbits.cpp:
Это будет выглядеть как gobbledygook, если вы не знаете что-то о кодовой базе, но позвольте мне объяснить. Первый аргумент функции VersionBitsState должен быть указателем на блок. Имя переменной pindexPrev указывает, что это не указатель на сам блок, а на родительский блок. Фактически, каждый другой вызов VersionBitsState в файле validation.cpp специально использует указатель на родительский блок, а не сам блок по этой причине.Вот проблема: pindexForkBuffer выше - 144 * 90 блоков перед текущимблоком, а не родителем текущего блока. Таким образом, по сути, мы смотрим, активирован ли блок 144 * 90-1 до текущего, когда Segwit активирован или нет. Мы отключены на 1 блок, и, таким образом, более крупные блоки активируются на 1 блок раньше.
Как это не попалось?
Этот конкретный набор изменений был частью гораздо большего запроса на растяжение, найденного здесь . Запрос на pull имеет 221 комментарий, большинство из которых аргументируют определение блоков 2MB. Вы можете видеть, что этот конкретный фиксатор фактически не попадает в этот запрос на растяжение до тех пор, пока он не станет ниже на странице. Только один человек, похоже, одобрил изменения (opetruzel), и есть жалобы в конце от deadalnix (он из биткойнской наличности) об этом запросе на тягу, не имеющем достаточного количества тестов.
Ошибки при ошибках
Этот фрагмент кода вычисления 144 * 90 блоков прошлого был принят как правильный способ делать вещи, и никто не поймал тот факт, что это вызовет проблемы позже.Чтобы убедиться, что 2x жесткая вилка не будет настигнута цепью 1x и реорганизована (в основном, полностью уничтожена), они установили правило для защиты от вытеснения. Для этого требуется, чтобы блок блокировки имел размер базового блока размером более 1 МБ. Использовалась та же логика, что и выше, и по существу принудительный блок 494783 имел размер базового блока> 1 МБ, а не блок 494784.Вот почему btc1 застрял на 494782, потому что программное обеспечение btc1 ожидает базовый блок размером более 1 МБ на 494783.
Но подождите, там больше
Как будто этой ошибки « один за другим» было недостаточно, в коде BlockAssembler есть еще одна ошибка . BlockAssembler является частью файла miner.cpp, который является кодом, ответственным за создание новых блоков. Как правило, это только код полезен для шахтеров, поскольку они единственные, которые фактически создают новые блоки.В частности, переменная fWitnessSeasoned не инициализируется, а используется. Это неопределенное поведение, которое показал Питер Уил .
...
Примечание. FWitnessSeasoned используется 4 раза без инициализации в любом месте
Почему это важно? Ну, оказывается, что эта конкретная переменная определяет максимальный размер и вес блока, которые сделает программное обеспечение! Если эта переменная является ложной, то программное обеспечение никогда не сделает достаточно большой блок для разветвления цепочки, так как максимальный вес блока будет составлять 4 000 000, а не 8 000 000, как того требуют спецификации 2x жесткой вилки. И наоборот, если эта переменная истинна, то программное обеспечение будет генерировать недопустимые блоки перед развилкой цепочки. Таким образом, было возможно, что даже если шахтер захотел разминировать на 2x, это программное обеспечение не позволит им!Это изменение кода было введено в этом запросе на растяжение, и Джефф Гарзик снова был автором, и он был объединен с, возможно, одним рецензентом (FaysalM), который не поймал ошибку.
Что случилось бы, если бы 2x не было отменено?
Шахтеры , которые планировали раскошелиться с 2x, естественно , думали 494784 был блок , так как Джефф Гарзик и segwit2x команда заявили многочисленные раз , что это разветвление блок.Даже если майнеры не использовали вышеприведенный код, который, возможно, помешал бы им создавать большие блоки, они бы настроили свое программное обеспечение, чтобы найти более крупные блоки для блока 494784, а не 494783. Это вызвало бы тот же ларек в блоке 494782, и каждый начали пытаться отлаживать то, что вызывало проблему.Скорее всего, какой-то шахтер бы разобрал вещи и просто добыл большой блок для разблокировки 2x в любом случае. Как долго это было бы сделано, так это догадываться, но это довольно ясно, что это было бы катастрофой для пиаров.Более того, как указывает Грег Максвелл, обмены будут замороженными учетными записями на этапе 494784, а не 494783, поэтому все остатки для 2x монет были бы отключены в зависимости от того, кто попал на блок 494783. Это снова вызвало бы некоторые серьезный ущерб.
Вывод
Пересмотр и тестирование консенсусных изменений действительно очень тяжело. Похоже, у btc1 было ровно 1 кодер и 1 рецензент для этих критических консенсусных изменений, и этого просто недостаточно, чтобы обнаруживать тонкие ошибки, такие как первые или очевидные ошибки, такие как второй. Более того, поскольку изменение «один за другим» было принято довольно рано (~ 15 июня), позже, когда код использовался для защиты от вытеснения, этот код считался хорошим из-за предыдущего «обзора».По сути, даже один или два слабых отзыва в цепочке обзоров могут разрушить всю систему консенсуса с катастрофической ошибкой.Надеюсь, это может быть объектным уроком для того, чтобы критические изменения были тщательно рассмотрены. Будьте в безопасности и благодарите разработчиков, которые делают тяжелую работу не только для кодирования, но и для просмотра.