先日BIPリポジトリにマージされたBIP-434↓
https://github.com/bitcoin/bips/blob/master/bip-0434.md
Bitcoinのような分散環境のノード実装は、他のノードと接続しメッセージを交換することでブロックやトランザクションを伝播しチェーンの同期を行う。これを行うためには、互いが送受信するメッセージを解釈し適切に処理できる必要がある。Bitcoinの実装ではこれまで、初期に実装されていたメッセージ以外にも多数の新たなメッセージを導入してきた。当然すべてのノードが一斉にバージョンアップするということはないので、接続相手がどのメッセージをサポートしているのか確認した上で適切な振る舞いをする必要がある。
P2Pプロトコルバージョン
そのためP2Pプロトコルのバージョンを定義し、ノードに接続した後に最初に送信するversionメッセージ内に自身がサポートするP2Pプロトコルのバージョンを指定することで、相手ノードにバージョンを通知している。Bitcoin Coreではこれまで以下のようにこのバージョンをアップグレードすることで新しいP2P機能を導入してきた。
| バージョン | 時期 | Bitcoin Core | 導入機能 |
|---|---|---|---|
| 106 | 2009/10 | 0.1.6 | versionメッセージにaddress-from、nonce、user-agent、current block heightフィールドを追加 |
| 209 | 2010/5 | 0.2.9 | addrメッセージが複数のネットワークアドレスのリストを受け入れ可能に。メッセージヘッダーにチェックサムフィールドを追加 |
| 311 | 2010/8 | 0.3.11 | alertメッセージを追加 |
| 31402 | 2010/10 | 0.3.15 | addrメッセージにタイムスタンプフィールドを追加 |
| 31800 | 2010/12 | 0.3.18 | getheadersメッセージとheadersメッセージを追加 |
| 60000 | 2012/3 | 0.6.0 | ネットワークプロトコルバージョンをBitcoin Coreのソフトウェアバージョンと分離 |
| 60001 | 2012/5 | 0.6.1 | pongメッセージを追加し、pingメッセージにnonceフィールドを追加 |
| 60002 | 2012/9 | 0.7.0 | mempoolメッセージを追加し、getdataメッセージを拡張しmempool内のトランザクションをダウンロード可能に |
| 70001 | 2013/2 | 0.8.0 | versionメッセージにfRelayフラグを追加。Bloom Filter用のfilterload、filteradd、filterclear、merkleblockメッセージを追加。notfoundメッセージを追加。getdataメッセージにMSG_FILTERED_BLOCKインベントリタイプを追加 |
| 70002 | 2014/3 | 0.9.0 | rejectメッセージを追加。mempoolメッセージへの応答で必要に応じて複数のinvメッセージを送信 |
| 70011 | 2016/2 | 0.12.0 | NODE_BLOOMサービスビットを導入し、このサービスビットの指定がない状態でのfilter*メッセージを無効化 |
| 70012 | 2016/2 | 0.12.0 | sendheadersメッセージを追加。ノードが新しいブロックのアナウンスをinvではなくheadersメッセージで受信することを優先できるように |
| 70013 | 2016/8 | 0.13.0 | feefilterメッセージを追加し、alertメッセージシステムを廃止 |
| 70014 | 2016/8 | 0.13.0 | コンパクトブロックリレーのメッセージsendcmpct、cmpctblock、getblocktxn、blocktxnを追加。 |
| 70015 | 2017/1 | 0.13.2 0.14.2 | コンパクトブロックの不正ブロックに対するBAN動作の更新 |
| 70016 | 2021/1 | 0.21.0 | wtxidrelay、sendaddrv2メッセージを追加 |
ハンドシェイク中のメッセージ送信
versionメッセージのプロトコルバージョンを使わずに導入されたP2Pメッセージもある。BIP-155とBIP-339では、ハンドシェイクにおいて、versionメッセージに対してverackを送信する前に、それぞれsendaddrv2メッセージとwtxidrelayメッセージを送信するというアプローチを採っている。またBIP-330のsendtxrcnclメッセージも同様。
BIP-434が提案された背景
versionメッセージを使ったプロトコルのアップグレードの問題点は、基本的にプロトコルバージョンがインクリメントされていくという点にある。ノードとしてはサポートする必要のない機能であっても、新しい機能をサポートする場合、それ以下のすべての機能をサポートしなくてはならなくなってしまう。こういった機能の展開については、インクリメントされる数値ではなく、LNのfeature bitのような表現方法の方が適している。
また、70001のアップグレードでversionメッセージにfRelayフラグを追加した際は、本来フィールド数が固定されているメッセージにオプショナルフィールドが加わると互換性問題が生じた。
また、ハンドシェイク中に送信されるメッセージについては、未知のメッセージを受け取ったノードがどのような振る舞いをするかに依存した実装になる。未知のメッセージを受け取ったノードはそのメッセージを無視するのであれば問題ないが、未知のメッセージに受け取って際に接続を切断する実装があった場合、将来的にネットワークが分断される可能性がある。Bitcoin Coreでも、version-verack間に未知のメッセージを送るとペナルティが課されるようにハンドシェイクを厳格化し(2017年)、その後wtxidrelayの提案によりversion-verack間の未知のメッセージは無視するよう方針転換(2020年)された経緯がある。
そこで、BIP-434では↑のBIP-339の仕組みを将来のP2Pのアップグレードのために一般化する提案をしている。個別のメッセージではなく、任意の新機能を通知するためのネゴシエーション機能としてfeatureメッセージを導入する。
BIP-434の使用
BIP-434をサポートするノードは、
versionメッセージのプロトコルバージョンとして>= 70017を設定する必要がある。- プロトコルバージョンが70017未満のピアに対しては、
featureメッセージを送信してはならない versionメッセージの後、verackメッセージの前に受信したfeatureメッセージを受け入れなければならない。verackメッセージを送信した後で、featureメッセージを送信してはならない。versionメッセージの後、verackメッセージの前に受信した未知のメッセージは無視すべき。verackメッセージを送信した後で、featureメッセージを受信した場合、無視してもいい。verack後にfeatureメッセージを送信するピアを切断してもいい。featureメッセージにより対象の機能のサポートを示していないピアに、その対象機能が導入するメッセージのverack後の送信を禁止しなければならない。
featureメッセージ
featureメッセージは以下のペイロードを持つ:
| 名称 | 型 | 定義 |
|---|---|---|
featureid |
string | 機能の一意識別子。長さを指定するCompactSizeの後にその長さのデータが続く。文字列はASCII文字。BIPが公開されている場合、BIP番号であるべき。 |
featuredata |
byte-vector | 機能固有の設定データ。長さを指定するCompactSizeの後にデータが続く。これらのデータが解釈されるかはその機能仕様次第。サイズは512 byteを超えてはならない。不要な場合は空のバイト列になる。 |
ノードは、
- サポートしていない
featureidのfeatureメッセージは無視する。 - 認識していない
featureidでもfeatureメッセージのペイロードが正しく解析できない場合、送信ピアを切断してもいい。
featureメッセージは1つの機能に対応しているので、複数の機能をサポートする場合は、その数分featureメッセージを送信することになる。feature bitみたいに機能ビットで機能セットを表現せずに個別に送信するのは、機能によっては、featuredataデータが必要になるからかな。