BIP: 9
Title: Version bits with timeout and delay
Authors: Pieter Wuille <pieter.wuille@gmail.com>
Peter Todd <pete@petertodd.org>
Greg Maxwell <greg@xiph.org>
Rusty Russell <rusty@rustcorp.com.au>
Status: Deployed
Type: Informational
Assigned: 2015-10-04
License: PD
概要
本書は、ビットコインブロック中の “version” フィールドの意味論を変更する提案を規定する。これにより、複数の後方互換な変更(以下「ソフトフォーク」と呼ぶ)を並行して展開できるようになる。この方式は、version フィールドをビットベクトルとして解釈することに依拠しており、各ビットを独立した変更の追跡に用いることができる。これらはリターゲット期間ごとに集計される。コンセンサス変更が成功またはタイムアウトした後には「休閑」期間が設けられ、その後にそのビットを後続の変更に再利用できる。
動機
BIP 34 は、あらかじめ定めたフラグタイムスタンプ(またはフラグブロック高)を用いず、ブロックヘッダー中のより高いバージョン番号で示されるマイナーの支持を測定することに依拠した、ソフトフォーク変更の仕組みを導入した。しかし、バージョン番号を整数として比較することに依拠しているため、一度に展開できる変更は 1 件のみに限られ、提案間の調整が必要となり、また恒久的な却下を扱う手段がない。すなわち、1 件のソフトフォークが完全に展開されていない限り、その後のソフトフォークをスケジューリングすることができない。
さらに BIP 34 は、その 95% しきい値に達した後、整数比較 (nVersion >= 2) をコンセンサスルールとしたため、有効な version 番号の集合から 2^31+2 個の値が失われた(nVersion は符号付き整数として解釈されるため、すべての負の値、および 0 と 1 が該当する)。これは本方式のもう一つの欠点を示している。すなわち、アップグレードのたびに、許容される nVersion フィールド値の集合が恒久的に制限されるのである。この手法はその後 BIP 66 と BIP 65 でも再利用され、nVersion の 2 と 3 という選択肢がさらに失われた。以下で示すとおり、これは不要なことである。
仕様
各ソフトフォーク展開は、以下のチェーンごとのパラメーターによって規定される(詳細は後述)。
- name は、識別子として使用するのに適した、ソフトフォークの簡潔な説明を示す。単一の BIP で記述される展開については、N を該当する BIP 番号として “bipN” という名前を用いることが推奨される。
- bit は、ソフトフォークのロックインおよび起動をシグナリングするために用いる、ブロックの nVersion フィールド中のビット位置を定める。{0,1,2,…,28} の集合から選ばれる。
- starttime は、そのビットが意味を持ち始めるブロックの最小の期間中央値時刻 (median time past) を定める。
- timeout は、その展開が失敗したとみなされる時刻を定める。あるブロックの期間中央値時刻が timeout 以上であり、かつソフトフォークが(そのブロック自身のビット状態を含めて)まだロックインしていなければ、その展開はそのブロックのすべての子孫において失敗したものとみなされる。
選択の指針
以下は、ソフトフォークについてこれらのパラメーターを選択する際の指針である。
- name は、同時に存在するか否かを問わず、いかなる 2 つのソフトフォークも同じ名前を使わないように選ぶべきである。
- bit は、同時に存在する 2 つのソフトフォークが同じビットを使わないように選ぶべきである。
- starttime は将来のある日付、当該ソフトフォークを含むソフトウェアのリリース日からおよそ 1 か月後に設定すべきである。これにより多少のリリース遅延を許容しつつ、プレリリース版ソフトウェアを実行している当事者によるトリガーを防ぐ。
- timeout は starttime から 1 年(31536000 秒)とすべきである。
同じビットを用いる後続の展開は、starttime が前回の timeout または起動よりも後であれば可能だが、必要になるまでは推奨されず、必要になった場合でも、不具合のあるソフトウェアを検出するための間隔を設けることが推奨される。
状態
各ブロックとソフトフォークには、展開状態が関連付けられる。取りうる状態は以下のとおりである。
- DEFINED は、各ソフトフォークが開始時に取る最初の状態である。ジェネシスブロックは、定義上、各展開についてこの状態にある。
- STARTED は、starttime を過ぎたブロックに対する状態である。
- LOCKED_IN は、STARTED ブロックを含む最初のリターゲット期間のうち、対象ビットが nVersion に設定されたブロック数が threshold 以上であった期間の、1 リターゲット期間後に対する状態である。
- ACTIVE は、LOCKED_IN のリターゲット期間の後のすべてのブロックに対する状態である。
- FAILED は、LOCKED_IN に達しなかった場合の、timeout の時刻を過ぎてから 1 リターゲット期間後の状態である。
ビットフラグ
nVersion ブロックヘッダーフィールドは(現状のとおり)32 ビットのリトルエンディアン整数として解釈され、ビットはこの整数中の値 (1 << N)(N はビット番号)として選択される。
STARTED 状態にあるブロックは、対象のビット位置が 1 に設定された nVersion を持つ。そうしたブロックの上位 3 ビットは 001 でなければならず、したがって実際に取りうる nVersion 値の範囲は [0x20000000…0x3FFFFFFF](両端含む)である。
BIP 34、BIP 66、BIP 65 によって課された制約のため、実際に利用可能な nVersion 値は 0x7FFFFFFB 個しかない。これにより最大 30 件の独立した展開に制限される。上位 3 ビットを 001 に制限することで、本提案の目的のためにそのうち 29 件を確保し、(上位ビット 010 と 011 という)異なる仕組みによる将来の 2 件のアップグレードにも対応できる。ブロックの nVersion の上位ビットが 001 でない場合、展開の目的においてはすべてのビットが 0 であるものとして扱われる。
マイナーは、採用状況を可視化するため LOCKED_IN 段階でも引き続きビットを設定すべきであるが、これはコンセンサスルールには影響しない。
新しいコンセンサスルール
各ソフトフォークの新しいコンセンサスルールは、ACTIVE 状態にある各ブロックに対して強制される。
状態遷移

ジェネシスブロックは、定義上、各展開について DEFINED 状態にある。
State GetStateForBlock(block) {
if (block.height == 0) {
return DEFINED;
}
リターゲット期間内のすべてのブロックは同一の状態を持つ。すなわち、floor(block1.height / 2016) = floor(block2.height / 2016) であれば、両者はすべての展開について同一の状態を持つことが保証される。
if ((block.height % 2016) != 0) {
return GetStateForBlock(block.parent);
}
それ以外の場合、次の状態は直前の状態に依存する。
switch (GetStateForBlock(GetAncestorAtHeight(block, block.height - 2016))) {
開始時刻またはタイムアウトのいずれかを過ぎるまで、初期状態にとどまる。以下のコード中の GetMedianTimePast は、あるブロックとその直前 10 個のブロックの nTime の中央値を指す。式 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 ブロック以上(2016 の 95%)、テストネットでは 1512 ブロック以上(2016 の 75%)である。 FAILED への遷移が優先される。そうしなければ曖昧さが生じうるためである。同じビット上に重ならない 2 件の展開が存在し、一方が LOCKED_IN へ遷移すると同時に、もう一方が STARTED へ遷移する場合がありうる。これは両方が同じビットの設定を要求することを意味してしまう。
ブロックの状態は自身の nVersion には依存せず、その祖先の 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 の倍数となる各ブロックについて、その親をインデックスとして結果の状態をキャッシュすることで、上記の仕組みを効率的かつ安全に実装できる。
警告機構
アップグレードの警告をサポートするため、「未知のアップグレード」を追加で追跡する。これには「暗黙のビット」マスク = (block.nVersion & ~expectedVersion) != 0 を用いる。nVersion に予期しないビットが設定されている限り、このマスクは非ゼロになる。未知のアップグレードについて LOCKED_IN が検出された場合、ソフトウェアは今後のソフトフォークについて大きく警告すべきである。次のリターゲット期間の後(未知のアップグレードが ACTIVE 状態にある時点)には、さらに強く警告すべきである。
getblocktemplate の変更
テンプレートリクエストオブジェクトは、以下の新しい項目を含むよう拡張される。
template request
| キー | 必須 | 型 | 説明 |
|---|---|---|---|
| rules | いいえ | 文字列の配列 | サポートするソフトフォーク展開の一覧(名前による) |
テンプレートオブジェクトも同様に拡張される。
template
| キー | 必須 | 型 | 説明 |
|---|---|---|---|
| rules | はい | 文字列の配列 | active 状態にあるソフトフォーク展開の一覧(名前による) |
| vbavailable | はい | オブジェクト | 保留中でサポートされるソフトフォーク展開の集合。それぞれソフトフォーク名をキー、ソフトフォークビットを値として用いる |
| vbrequired | いいえ | 数値 | サーバーが提出物に対して有効化を要求するソフトフォーク展開バージョンビットのビットマスク |
テンプレートの “version” キーは維持され、サーバーの展開についての選好を示すために用いられる。 versionbits が使用されている場合、“version” は versionbits の範囲 [0x20000000…0x3FFFFFFF] 内でなければならない。 マイナーは、特別な “mutable” キーを伴わずにブロックバージョンのビットをクリアまたは設定してよい。ただし、テンプレートの “vbavailable” に列挙されており、かつ(クリアを望む場合は)“vbrequired” にビットとして含まれていないことが条件である。
“rules” または “vbavailable” のキーとして列挙されるソフトフォーク展開名には、’!’ 文字が前置される場合がある。 この接頭辞がない場合、GBT クライアントは、そのルールがそのままのテンプレートの使用に影響を与えないと仮定してよい。典型的な例は、これまで有効だったトランザクションが無効になる場合であり、BIP 16、65、66、68、112、113 がこれに該当する。 クライアントが接頭辞なしのルールを理解しない場合、そのまま変更せずマイニングに用いてよい。 一方、この接頭辞が用いられている場合は、ブロック構造またはコインベーストランザクションへのより微妙な変更を示す。例としては BIP 34(コインベースの構成を変更するため)や 141(txid のハッシュ方式を変更し、コインベーストランザクションへのコミットメントを追加するため)が挙げられる。 ’!’ が前置されたルールを理解しないクライアントは、そのテンプレートを処理しようとしてはならず、変更せずともマイニングに用いようとしてはならない。
将来の変更への対応
上記の仕組みは非常に汎用的であり、将来のソフトフォークに向けたさまざまな変種が可能である。以下、考慮に値するいくつかの案を挙げる。
しきい値の変更 1916 というしきい値(BIP 34 の 95% に基づく)を永遠に維持する必要はないが、変更を行う際には警告システムへの影響を考慮すべきである。特に、警告システムで用いているものと整合しないロックインしきい値を持つことは長期的な影響を及ぼしうる。警告システムはもはや恒久的に検出可能な条件に依拠できなくなるためである。
競合するソフトフォーク いずれ、互いに排他的な 2 件のソフトフォークが提案される可能性がある。これに対処する素朴な方法は、両方を実装するソフトウェアを決して作らないことだが、これは少なくとも一方が敗れることを前提とした賭けになる。より良い方法は、「ソフトフォーク X はロックインできない」ということを、競合するソフトフォーク側のコンセンサスルールとして符号化することである。これにより、両方をサポートしつつ、競合する変更を同時に発火させることは決してないソフトウェアが実現できる。
多段階のソフトフォーク 現在のところ、ソフトフォークは典型的には真偽値として扱われる。すなわち、ブロック内で非有効状態から有効状態へと遷移する。おそらくいずれは、追加の検証ルールを 1 つずつ有効化していく、より多くの段階を持つ変更への需要が生じるだろう。上記の仕組みは、孤立したビットとしてではなく、ビットの組み合わせを整数として解釈することで、これに対応できるよう適応させることができる。警告システムはこれと両立する。増加していく整数に対しては、(nVersion & ~nExpectedVersion) が常に非ゼロとなるためである。
論拠
失敗タイムアウトにより、ソフトフォークが一度も起動しなかった場合でもビットの再利用が最終的に可能になり、そのビットの新しい用途が新しい BIP を指すことが明確になる。これは意図的にかなり粗い粒度に設定されている。妥当な開発・展開の遅延を考慮に入れるためである。失敗する提案の数がビット不足を引き起こすほど多くなることは考えにくい。
ソフトフォーク試行の終了時に設けられる休閑期間は、不具合のあるクライアントをある程度検出できるようにするとともに、成功したソフトフォークについて警告とソフトウェアのアップグレードのための時間を確保する。
展開
展開提案の最新の一覧はこちらで確認できる。
著作権
本文書はパブリックドメインに置かれる。


