BIP: 9 Title: Version bits with timeout and delay Author: Pieter Wuille <pieter.wuille@gmail.com> Peter Todd <pete@petertodd.org> Greg Maxwell <greg@xiph.org> Rusty Russell <rusty@rustcorp.com.au> Translate: Karpov Aleksey <admin@bitaps.com> Comments-Summary: No comments yet. Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0009 Status: Final Type: Informational Created: 2015-10-04 License: PD
В этом документе указано предлагаемое изменение семантики поля 'version' в блоках Bitcoin, позволяющее параллельно развертывать несколько обратных-совместимых изменений (далее называемых "софтфорками"). Оно полагается на интерпретацию поля version как битового вектора, где каждый бит может использоваться для отслеживания независимого изменения. Они подсчитываются за каждый период перерасчета сложности майнинга. После того, как изменение консенсуса достигается или отклоняется по таймауту, возникает пауза, после которой бит может быть повторно использован для последующих изменений.
В BIP 34 был введен механизм для софтфорка без предопределенной флага метки времени (или флага высоты блока), вместо этого полагаясь на измерение поддержки майнерами, указанную более большим номером версии в заголовках блока. Однако, поскольку он полагается на сравнение номеров версий как целых чисел, он поддерживает только одно одно изменение, которое необходимо выполнить, требуя координации между предложениями, и не допускает постоянного отказа: пока один софтфорк не будет полностью развернут, никакой другой нельзя будет запланировать.
Кроме того, BIP 34 делает целочисленное сравнение (nVersion >= 2) по правилу консенсуса, после достижения 95%-го порога, удалив 231+2 значений из набора допустимых номеров версий (все отрицательные числа, поскольку nVersion интерпретируется как целое число со знаком, а также 0 и 1). Это указывает на другой недостаток такого подхода: каждое обновление постоянно ограничивает набор допустимых значений поля nVersion. Позднее этот подход был использован в BIP 66 и BIP 65, что привело к дополнительному удалению версии 2 и 3 в качестве допустимых вариантов, как уже использованных. Как будет показано далее, это не обязательно.
Каждое развертывание софтфорка определяется следующими параметрами каждой цепочки (подробно описано ниже):
- name указывает очень краткое описание софтфорка, подходящее для использования в качестве идентификатора. Для развертываний, описанных в одном BIP, рекомендуется использовать имя «bipN», где N -соответствующий номер BIP.
- bit определяет, какой бит в поле nVersion блока должен использоваться для сигнализации блокировки и активации софтфорка. Он выбирается из множества {0,1,2, ..., 28}.
- starttime задает минимальное среднее время последнего блока, в котором бит получает свое значение.
- timeout указывает время, в течение которого развертывание считается неудачным. Если среднее время блока >= timeout и софтфорк еще не перешел в стадию блокировки (включая состояние бит этого блока), развертывание считается неудачным для всех преемников блока.
Предлагаются следующие рекомендации для выбора параметров софтфорка:
- name должно быть выбрано таким образом, чтобы два софтфорка, одновременно или как-то иначе, никогда не использовали одно и то же имя.
- bit должен быть выбран таким образом, чтобы два параллельных софтфорка не использовали один и тот же бит.
- starttime должно быть установлено на определенную дату в будущем, примерно через месяц после даты выпуска программного
- timeout должен быть равен 1 год (31536000 секунд) после starttime.
С каждым блоком и софтфорком мы связываем состояние развертывания. Возможные состояния:
- DEFINED это первое состояние, в котором начинется каждый софтфорк. Генезис блок находиться в этом состоянии по определению для каждого развертывания.
- STARTED для блоков, появивишихчя после starttime.
- LOCKED_IN для одного периода между перерасчетом сложности после первого периода с блоками STARTED, у которых достигнут порог с соответствующим битом, установленным в nVersion.
- ACTIVE для всех блоков после периода LOCKED_IN.
- FAILED для одного периода после наступления таймаута, если LOCKED_IN не был достигнут.
Поле заголовка блока, поле nVersion должно интерпретироваться как 32 битовое целое число с little-endian порядком байтов, биты выбираются внутри этого целого в качестве значений (1 << N) , где N - это число бит.
Блоки в состоянии STARTED устанавливают в nVersion, бит в соответвующей битовой позиции равным 1. Первые 3 бита таких блоков должны быть 001, поэтому диапазон реально возможных значений nVersion равен [0x20000000...0x3FFFFFFF] включительно.
Из-за ограничений, установленных BIP 34, BIP 66 и BIP 65, мы имеем только 0x7FFFFFFB возможных значений nVersion. Это ограничивает нас не более чем 30 независимыми развертываниями. Ограничивая первые 3 бита до 001, мы получаем 29 из них для целей этого предложения и поддерживаем два будущих обновления для разных механизмов (первые биты 010 и 011). Когда в блок знчение nVersion не имеет первых бит равных 001, он обрабатывается так, как будто все биты для целей развертывания равны 0.
Майнерам следует продолжить сигнализировать биты в фазе LOCKED_IN, хотя это и не влияет на правила консенсуса.
Новые правила консенсуса для каждого софтфорка действуют для каждого блока, имеющего статус ACTIVE.
Генезис блок имеет состояние DEFINED для каждого развертывания, по определению.
State GetStateForBlock(block) { if (block.height == 0) { return DEFINED; }
Все блоки в периоде за котрый делается перерасчет сложности имеют одинаковое состояние. Это означает, что если floor (block 1.height / 2016) = floor (block 2.height / 2016), они гарантированно имеют одинаковое состояние для каждого развертывания.
if ((block.height % 2016) != 0) { return GetStateForBlock(block.parent); }
В противном случае следующее состояние зависит от предыдущего состояния:
Мы остаемся в исходном состоянии до тех пор, пока мы не пройдем starttime или не неаступит timeout. GetMedianTimePast в коде ниже относится к медианному nTime блока и его 10 предшественников. Выражение GetMedianTimePast (block.parent) упоминается как MTP в приведенной выше диаграмме и рассматривается как монотонная синхронизация, определяемая цепочкой.
case DEFINED: if (GetMedianTimePast(block.parent) >= timeout) { return FAILED; } if (GetMedianTimePast(block.parent) >= starttime) { return STARTED; } return DEFINED;
После периода в состоянии STARTED, если наступает таймаут то мы переходим к FAILED. Если нет, подсчитываются усатновелнные биты в блоках за данный перод и происходит переключение в LOCKED_IN, если достаточное количество блоков за прошедший период установили бит развертывания в их номерах версий. Порог ≥1916 блоков (95% от 2016) или ≥1512 для тестовой сети (75% от 2016 года). В одном бите могут быть два непересекающихся развертывания, где первый переходит в LOCKED_IN, а другой одновременно переходит к STARTED, что означает, что оба потребуют установки бит.
Обратите внимание, что состояние блока никогда не зависит от его собственной nVersion; только от его предшественников.
case STARTED: if (GetMedianTimePast(block.parent) >= timeout) { return FAILED; } int count = 0; walk = block; for (i = 0; i < 2016; i++) { walk = walk.parent; if (walk.nVersion & 0xE0000000 == 0x20000000 && (walk.nVersion >> bit) & 1 == 1) { count++; } } if (count >= threshold) { return LOCKED_IN; } return STARTED;
После периода LOCKED_IN мы автоматически переходим к ACTIVE.
case LOCKED_IN: return ACTIVE;
И ACTIVE и FAILED являются терминальными состояниями, в которых остается развертывание при их достижении.
case ACTIVE: return ACTIVE;
case FAILED: return FAILED; } }
Реализация Следует отметить, что состояния поддерживаются вдоль ветвей цепочки блоков, но может потребоваться перерасчет при реорганизации цепочки блоков.
Учитывая, что состояние конкретной комбинации блоков/развертываний полностью определяется его происхождением до текущего периода перерасчета (т.е. до и включая его предшественника с высотой block.height - 1 - (block.height % 2016)), является возможным рализовать механизм описанный выше эффективным и безопасным, используя кеширование результатов состояния каждых 2016 блоков, проиндексированных по их родительскому блоку.
Для предупреждений об обновлении отслеживается дополнительное «неизвестное обновление» с использованием маски «неявного бита» mask = (block.nVersion & ~expectedVersion) != 0. Маска будет не равна нулю, когда в nVersion устанавливается неизвестный бит. В случае если наступает состояние LOCKED_IN для неизвестного обновления, программное обеспечение должно явно предупреждать о предстоящем софтфорке. При переходе в состояние ACTIVE для неизвестног обновления, предупреждение должны быть еще более явными. Оно должно предупреждать еще более явно громче после следующего периода перенастройки (когда неизвестное обновление находится в состоянии ACTIVE).
Объект запроса шаблона дополнен для включения нового элемента:
template request | |||
---|---|---|---|
Key | Required | Type | Description |
rules | No | Array of Strings | list of supported softfork deployments, by name |
Объект шаблона также дополнен:
template | |||
---|---|---|---|
Key | Required | Type | Description |
rules | Yes | Array of Strings | list of softfork deployments, by name, that are active state |
vbavailable | Yes | Object | set of pending, supported softfork deployments; each uses the softfork name as the key, and the softfork bit as its value |
vbrequired | No | Number | bit mask of softfork deployment version bits the server requires enabled in submissions |
"Version" шаблона сохраняется и используется для указания софтфорка предпочитаемого сервером. Если используются versionbits, "version" ДОЛЖНА находиться в пределах диапазона версий [0x20000000...0x3FFFFFFF]. Майнеры МОГУТ очищать или устанавливать биты в версии блока БЕЗ любого специального «изменяемого» ключа, если они перечислены среди значений «vbavailable» шаблона и (когда требуется очистка) НЕ включается как бит в «vbrequired».
Названия развертывания софтфорка, перечисленные в «правилах» или как ключи в «vbavailable», могут иметь префикс «!». Без этого префикса клиенты GBT могут предположить, что это правило не повлияет на использование шаблона в таком виде; типичными примерами этого могут быть случаи, когда ранее действующие транзакции перестают быть действительными, такие как BIP 16, 65, 66, 68, 112 и 113. Если клиент не понимает правило без префикса, он может использовать его без изменений для майнинга. С другой стороны, когда этот префикс используется, он указывает на более тонкое изменение структуры блока или транзакции генерации; примерами этого может быть BIP 34 (поскольку он модифицирует построение coinbase) и 141 (поскольку он изменяет хеширование txid и добавляет comitment транзакции генерации). Клиент, который не понимает правило с префиксом «!»; не должен пытаться обрабатывать шаблон и не должен пытаться использовать его для майнингеа даже в немодифицированном виде.
Механизм, описанный выше, очень общий, и возможны варианты для будущих софтфорков. Вот некоторые идеи, которые можно принять во внимание.
Измененные пороговые значения Порог 1916 (основанный на 95% BIP 34) не должен поддерживаться вечность, но изменения должны принимать во внимание систему предупреждения. В частности, наличие порога lock-in, несовместимого с тем, который используется для системы предупреждения, может иметь долгосрочные последствия, поскольку система предупреждения больше не может полагаться на постоянно обнаруживаемое состояние.
Конфликтующие софтфорки В какой-то момент могут быть предложены два взаимоисключающих софтфорка. Наивный способ справиться с этим - никогда не создавать программное обеспечение, которое реализует оба софтфорка, но это означает ставку на то, что, по крайней мере, один вариант не будет принят. Лучше было бы кодировать «софтфорк X не может перейти в состояние locked-in» как правило консенсуса для конфликтующего софтфорка - позволяя создание программного обеспечения которое поддерживающее оба варианта, но никогда не может вызывать противоречивые изменения.
Многоступенчатые софтфорки. Софтфорки сейчас обрабатываются как логические значения: они переходят из неактивного в активное состояние в блоках. Возможно, в какой-то момент появится потребность в изменении, которое имеет большее количество этапов, с дополнительными правилами проверки, которые включаются один за другим. Вышеупомянутый механизм может быть адаптирован для поддержки этого варианта, интерпретируя комбинацию битов как целых, а не как изолированных. Система предупреждения совместима с этим, так как (nVersion & ~nExpectedVersion) всегда будет ненулевым при увеличения целых чисел.
Тайм-аут допускает возможное повторное использование бит, даже если софтфорк никогда не активировался, поэтому ясно, что новое использование бит относится к новому BIP. Он преднамеренно весьма длительный, с учетом разумных задержек разработки и развертывания.
Малоактивный период в конце софтфорка позволяет обнаруживать баги в клиентах и предоставляет время для предупреждений и обновлений программного обеспечения для успешных софтфорков.
Актуальный список предложений по развертыванию можно найти здесь.
Этот документ размещен как общественное достояние.