Develop with pleasure!

福岡でCloudとかBlockchainとか。

Standards Track BIPのレイヤーの分類を定義したBIP-123

Standards Track BIPのヘッダの項目の1つにLayerがあるが、このLayerについて定義したのがBIP-123↓

bips/bip-0123.mediawiki at master · bitcoin/bips · GitHub

動機

Bitcoinはさまざまな標準を含むシステムで、そのいくつかは相互運用性を担保するのに必須の要件だが、それ以外のものはオプションとみなすことができサポートするかどうかは実装者に委ねられる。この相互運用の要件をより厳密に反映するBIPプロセスを実現するためには、要件に応じたBIPの分類が必要になる。低レイヤーのプロポーザルは標準として受け入れられデプロイするまでに多くの課題があるだろう。

仕様

Standards BIPsは以下の4つのレイヤーに分類する。(上から低レベル層 >> 高レベル層)

  • Consensus
  • Peer Services
  • API/RPC
  • Applications

Standards Track BIP以外のBIPはこれらのレイヤー分類をしてもいいし、しなくてもいい。

Consensusレイヤー

Consensusレイヤーでは、暗号コミットメント構造を定義する。このレイヤーの機能の目的は、誰もがローカル環境で特定の状態や履歴が有効か、セトルメントが保証されているか評価できるようにすることである。

Consensusレイヤーはメッセージがネットワーク上でどのように伝搬するかについては関与しない。

Consensusレイヤーに関する意見の不一致は、異なるノードが互換性のない異なる履歴を受け入れることになりネットワークの分断やフォークを招く可能性がある。

さらにConsensusレイヤーの変更をSoftforkとHardforkに細分化する↓

Softfork

Softforkでは、古いルールで有効だったいくつかの構造は新しいルールの下では有効でなくなる。古いルールで無効だった構造は新しい構造でも引き続き無効である。

Hardfork

Hardforkでは古いルールの下で無効だった構造が、新しいルールの下では有効になる。

Peer Servicesレイヤー

Peer Servicesレイヤーでは、ノードがどうやってメッセージを伝播するか定義する。

基本的なノードの相互運用性には、指定された全てのピアサービスのサブセットのみがあればよく、ノードは将来のオプション拡張をサポートする。

既存サービスとの互換性を損なうことなく新しいサービスを追加したり、古いサービスを段階的に廃止することは可能で、サービス中断の重大なリスクなくネットワーク全体をアップグレードすることができる。

API/RPCレイヤー

API/RPCレイヤーは、アプリケーションからアクセス可能な高レベルの呼び出しを定義する。このレイヤーのBIPのサポートは基本的なネットワークの相互運用には必要ないものだが、特定のアプリケーションによっては求められる場合もある。

このレイヤーでは、基本的なネットワークの相互運用性を損なうことなく、競合する標準を設ける余地がある。

Applicationsレイヤー

Applicationsレイヤーは、異なるアプリケーションが同様の機能のサポートやデータの共有をするための高レベルの構造、抽象化及びルールを定義する。

既存のBIPの分類

BIPのREADMEの表のLayer欄でまとめられてる↓

bips/README.mediawiki at master · bitcoin/bips · GitHub

メッセージが拒否された理由を明確にするBIP-061

2014年4月にリリースされたBitcoin Core 0.9.0、ネットワークのプロトコルバージョン70002でサポートされたrejectメッセージのBIP↓

bips/bip-0061.mediawiki at master · bitcoin/bips · GitHub

リモートピアとメッセージを交換する際、意図する動作にならないケースにおいて、その原因が何なのかリモートピアに通知するためにrejectメッセージが追加された。

動機

ピアに対しなぜトランザクションやブロックが受け入れられなかったのか、フィードバックすることは異なる実装間の相互運用性を助けることになる。また、SPVクライアントにとっても、優先度や手数料が不十分でトランザクションがリジェクトされた際に、何が悪かったのか判断する助けになる。

仕様

この仕様のデータタイプは以下のWikiで記載されている通り。

Protocol documentation - Bitcoin Wiki

reject

このBIPで新しいメッセージタイプrejectを導入する。
このメッセージはversiontxblockメッセージのレスポンスとしてピアに送信される。

例えば、2つのピア間の通信で、なんらかの理由でリジェクトされたトランザクションのメッセージフローは以下のようになる。

--> inv
<-- getdata
--> tx
<-- reject

P2Pプロトコルバージョンが70002以降の実装では、このrejectメッセージをサポートする必要がる。

共通のペイロード

rejectメッセージは、以下のフィールドで始まる。
(一部のメッセージは、メッセージ固有のデータが追加される。)

サイズ 名称 データタイプ 内容
可変 response-to-msg var-str リジェクトの原因となったメッセージ
1 reject-code uint8_t 0x01から0x4fまでのリジェクトコード(後述)
可変 reason var_string デバッグのための人が認識できるリジェクトメッセージ

reasonデバッグのためのだけに用意されており、特に実装が異なる場合は異なる文字列が使用されることがある。この文字列は問題を診断するためにだけ使い、決してユーザに表示したりしないこと。

reject-codeには以下のカテゴリに分類される。(以下に記述するserverrejectメッセージを生成するピアで、clientrejectメッセージを受信するピア)

範囲 カテゴリ
0x01-0x0f プロトコルシンタックスエラー
0x10-0x1f プロトコルのセマンティックエラー
0x40-0x4f serverのポリシールール

全メッセージタイプ共通のreject code

code 定義
0x01 メッセージがデコード出来ませんでした

reject version code

versionメッセージへのレスポンスとして、初期コネクションを確立する際に生成されるコード

code 定義
0x11 clientでは廃止されサポートされていないバージョンです。
0x12 重複したversionメッセージを受け取りました。

reject tx payload, codes

トランザクションのリジェクトメッセージには、既存のメッセージに加え以下のTXIDが付与される。

サイズ 名称 データタイプ 内容
32 hash char[32] リジェクトされたトランザクションのハッシュ

以下のコードが使用される。

code 定義
0x10 トランザクションが何らかの理由(無効な署名、出力のコインの量が入力のコインの量を超えているなど)で無効です。
0x12 入力は既に使用されています。
0x40 (serverにとって)非標準なトランザクションなのでマイニング/リレーされません。
0x41 1つ以上の出力の量がdust閾値以下です。
0x42 マイニングやリレーをするのい充分なトランザクション手数料/優先度がありません。

payload, reject block

ブロックのリジェクトメッセージは、既存のメッセージに加え以下のブロックヘッダのハッシュが付与される。

サイズ 名称 データタイプ 内容
32 hash char[32] リジェクトされたブロックヘッダのハッシュ

以下のコードが使用される。

code 定義
0x10 ブロックが何らかの理由(無効なProof-of-Work、無効な署名など)で無効です。
0x11 サポートされなくなったブロックのバージョンです。
0x43 コンパイルされたチェックポイントが異なるブロックチェーンのブロックです。

注意:serverのベストチェーンには含まれていないが、そのブロックが有効なブロックである場合は、rejectメッセージを作成してはいけない。

互換性

rejectメッセージを認識しない古いピアでは、rejectメッセージは無視されるるため、下位互換性がある。

実装上の注意事項

実装者は、攻撃者が有効なトランザクションやブロックに対しrejectメッセージを送信したり、ランダムにrejectメッセージを送信するような攻撃や、DoS攻撃が行われた際に何が起きるか考慮する必要がある。例えば、rejectメッセージを受信した際に全てそれをユーザーに通知するようにしていたら、攻撃者がユーザーへの嫌がらせをする攻撃が可能になる。また、全てのrejectメッセージをログに書き込むだけでも、ユーザーのディスクを溢れさせるような攻撃に繋がる。

リモートピアのメモリプール内のトランザクション情報を取得できるようにしたBIP-035

2012年の8月に提案されたmempoolメッセージのBIP↓

bips/bip-0035.mediawiki at master · bitcoin/bips · GitHub

mempoolメッセージを受け取ったノードはメモリプール内の全トランザクションハッシュを返すようになり、getdataメッセージで取得可能なトランザクションもメモリプール内の全トランザクションに対象が広がる。

動機

以下のようなユースケースではネットワークノードのトランザクションのメモリプールを公開するほうが良い。

  • SPVクライアントが送信もしくは受信された0 confirmationのトランザクションを入手したい
  • マイナーが再起動後に手数料の良い既存のネットワークトランザクションをダウンロードしたい
  • リモートネットワークの診断

仕様

  1. pchCommand == “mempool"のmempoolメッセージは空のメッセージとして定義される。
  2. mempoolメッセージを受信したノードは、ノードのトランザクションメモリプール内の全てのトランザクションのMSG_TXハッシュを含むinvメッセージで応答する。
  3. invのレスポンスに対する典型的なノードの振る舞いはgetdataだが、Satoshiクライアントは直近で中継されたトランザクションハッシュ以外のトランザクションハッシュに対する要求は無視する。mempoolメッセージをサポートするためにはgetdataメッセージで取得可能なトランザクションをメモリプール内の全トランザクションにまで拡張する必要がある。
  4. この機能が有効かどうかは以下のversionメッセージの属性で判断する。
  5. プロトコルバージョンが60002以上
  6. nServicesにNODE_NETWORKビットがセットされている。

既存の実装ではvector sizeが50000を超えるinvメッセージはドロップされることに注意すること。

後方互換

古いクライアントは、この変更後も100%の互換性を持ち、相互運用が可能。

実装

github.com

リモートピアの生存確認とレイテンシーの計測を可能にするBIP-031

2012年5月にリリースされたBitcoin Core 0.6.1から導入されたPeer Serviceレイヤーの拡張で、リモートピアの生存確認と、レイテンシーの測定を可能にするBIP-031↓

bips/bip-0031.mediawiki at master · bitcoin/bips · GitHub

簡単に言うとよくあるping送ってその反応が来るまでの速度を計測する機能をBitcoinP2Pメッセージにも実装しようというもの。

動機

Bitcoinのユーザーエクスペリエンスを低下するネットワーク関連の問題がいくつかある↓

  1. 一部のBitcoinクライアントは、スリープ状態になり警告なしにいつでも実行が停止されるプラットフォームで動作している。これは特に携帯やノートPCでよくある(ふたを閉めるとか)。システムが復帰するとスリープ前のTCP接続は残っているが、IPアドレスが変わっていたり、リモートピアがオフラインになっていたり、他のシステムによりタイムアウトしているなりして正しく機能する状態にないことは充分考えられる。こういう状況が発生した場合、それに気づくまでにしばらく時間がかかることがある。
  2. Satoshiクライアントは大部分がシングルスレッドで、(ブロックチェーンデータのダウンロードなどで)負荷がかかると、ネットワークメッセージに応答するのが非常に遅くなる。これを検出する簡単な方法はなく、リモートピアからのブロードキャストを受動的に待っている場合は特に顕著になる。オーバーロードされたリモートピアを検出し回避する方法は、負荷のバランスとり、より応答性の優れたシステムを提供するのに役立つ。
  3. ブロックチェーンのような大規模なデータ構造をダウンロードする際は、ネットワーク的に近いピアを選択する方が効率的である。現状リモートピアへのレイテンシーを測定するのは難しいので、クライアントは気にせずランダムにピアを選択している。

これら全て、下位互換性のあるプロトコルの変更によって解決することができる。

仕様

verメッセージのプロトコルバージョンが60000より大きい場合、pingメッセージにはnonceと呼ばれるuint64フィールドが含まれている必要がある。pingメッセージを送信しているピアはnonceにランダムな値を設定し、受信者は(受け取ったnonceをセットした)単一のuint64フィールドを含む新しいpongメッセージをエコーバックする。

こうすることでクライアントはpingメッセージを送りpongメッセージが返ってくるまでの時間を計測することができる。クライアントが最初のpongメッセージを受け取る前に、pingメッセージを2つ送っている場合、その応答がどちらのpingに対するものかはnonceで判断することができる。クライアントが重複してpingを送らない場合は、nonceの値は0で良い。

後方互換

クライアントは自身のプロトコルバージョンが6000より大きいと宣言する場合は新機能にオプトインする必要がある。古いプロトコルバージョンのクライアントは、pingメッセージにnonceを提供するとは思われず、pongメッセージを返さない。

実装

github.com

これ書いたのマイクハーンだったのか。

ProtocolのバージョンとUser Agentを分離したBIP-014

作られたのは2011年とだいぶ前のBIPだけど、あまりPeer ServiceレイヤーのBIPを見てなかったので見てみる。
昔はプロトコルのバージョンとクライアントのバージョンが同じだったのね。

bips/bip-0014.mediawiki at master · bitcoin/bips · GitHub

動機

BitcoinプロトコルはSatoshiクライアントから始まったが、現在ではコミュニティも多様化し、様々な言語(Java, Python, Javascript, C++)で書かれた独自のコードベースを持つクライアントが、独自の機能セットを急速に開発 している。

プロトコルにはバージョンが埋め込まれている。このバージョン番号は主にversionメッセージとgetblocksメッセージ、ブロックを作成したソフトウェアのバージョンを示すblockメッセージに含まれている。現在このバージョン番号はクライアントのバージョン番号と同じである。このBIPでは、プロトコルのバージョン番号とクライアントのバージョン番号を分離するための提案である。

論拠

バージョン番号が分離されていないと、Satoshiクライアントのリリースの度に内部のバージョン番号がインクリメントされることになり、こうなると他の全てのクライアントもSatoshiのバージョン番号に追いつく必要が出てくる。Bitcoin開発者のあるグループのリリーススケジュールに全てのソフトウェアのリリースを合わせるといったことになり、これはBitcoinが本来持つ分散型の特性とは異なる。

バージョンのバンプは非互換性を生み、ネットワークを破壊する可能性もある。ネットワークの健全性が維持されるためには、プロトコルの開発とプロトコルの実装は分けて考える必要がある。全てのグループの代表者と共にプロトコルの舵をきるニュートラルな第3のエンティティの存在が、Bitcoinが最小のリスクで積極的に成長する機会を提供することになる。

プロトコルバージョンを利用することで、ネットワーク上の全ての実装に共通の標準を設定することができ、何がプロトコルで何が実装依存なのかの境界について誰もが同意できるようになる。User Agentの文字列はクライアントがネットワーク内で自分自身を識別するためのvanity-plateとして提供される。

ネットワークプロトコルを実装から分離し、参加者間の相互合意によりプロトコルを開発するということは、議論によって合意が困難な場合にはこの民主的な意思決定は不利益をもたらす。この問題を軽減するためには強力なコミュニケーションチャネルと迅速なリリーススケジュールが必要となるが、それについてはこのBIPの範囲外。

User Agentは、使っているクライアントの実装や共通のアーキテクチャ/OSなどのネットワークデータを把握するのに便利な余分なトラッキング情報を提供する。稀に、ネットワークの健全性を脅かすクライアントを排除するための緊急の方法として利用されることも考えられるが、これは基本的に推奨される使い方ではない。User Agentは異なるクライアントの実装をワークアラウンドに動作させるための方法ではない(そういう使い方をするとプロトコルの破壊に繋がる)。

まとめると

  • Protocol version
    異なる動作をするノードを区別する方法
  • User Agent
    シンプルな情報ツール(User Agentに応じてプロトコルを変更しないこと)

ブラウザのUser-Agents

RFC 1945ではUser Agentを製品の文字列にオプションのコメントを付けて曖昧に指定している。

Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.6) Gecko/20100127 Gentoo Shiretoko/3.5.6

User Agentは人間よりコンピュータによって解析されることの方が多い。スペースで区切るフォーマットでは簡単、高速にまた効率的に解析することができない。またデータにはその階層を示す構造は含まれていない。

情報の最も直接的な部分には、ブラウザの製品名、レンダリングエンジンおよびビルド(Gentoo Shiretoko)とバージョン番号がある。デスクトップ環境やプラットフォーム、言語、ビルドのリビジョン番号などさまざまな情報はコメントに含まれる。

仕様

versiongetblocksパケットのversionフィールドは、プロトコルのバージョン番号になる。blocksのバージョン番号は、そのブロックが作成されたときのプロトコルのバージョンを反映する。

versionパケットの現在使われていないsub_version_numフィールドが新しいUser Agentの文字列となる。

BitcoinのUser Agentは、パースしやすく一貫性のあるデータを構成するためブラウザのUser Agentに変更を加えたものになる。Bitcoinでは、ソフトウェアは通常、コアのcode-baseからGUIまでのスタックで構成されている。そのためUser Agentの文字列でこの関連をコード化する。

基本フォーマットが↓

/Name:Version/Name:Version/.../

サンプル

/Satoshi:5.64/bitcoin-qt:0.4/
/Satoshi:5.12/Spesmilo:0.8/

ここでbitcoin-qtとSpesmiloはプロトコルバージョン5.0を使うが、両者が使っている内部のコードベースは同じソフトウェアの異なるバージョンになっている。バージョン番号には厳密なフォーマットは定義されてないが、このガイドでは以下を推奨する。

  • バージョン番号は、メジャー.マイナー.リビジョン(例:2.6.41)というフォーマットを使う
  • リポジトリビルドにはYYYYMMDD(例:20110128)というフォーマットのデータを使う

gitリポジトリのビルドの場合、リビジョンビルドにgitのcommitを使用することも可能だが、先行するリポジトリがなければすぐには明らかにならないという問題もある。そのため上記の日付のフォーマットを推奨するが、これは決して要件ではない。

オプションで-r1-r2をUser Agentのバージョン番号に付与できる。これは推奨事項だが必須要件ではない。User Agentの構文エラーとなる():/を含めない限り、バージョン番号は任意のフォーマットで指定できる。

バージョン番号の後にオプションのコメントフィールドを設けることも可能。その際コメントは()で区切ること。コメント内の区切り文字としてはセミコロンの使用を推奨している。

サンプル

/BitcoinJ:0.2(iPad; U; CPU OS 3_2_1)/AndroidBuild:0.8/

従って予約シンボルは/:()

  • /
    code-stackのデリミタ
  • :
    特定のスタックの実装バージョンを指定するのに使用
  • ()
    コメントのデリミタ(オプションでコメント内のデータのデリミタとして;

タイムライン

このBIPが公開された時点でBitcoinプロトコルとSatoshiクライアントのバージョンは0.5で、開発が行われている。この変更の対応における影響を最小限にするため、次のプロトコルバージョン0.6はクライアントバージョンから除外された。2012年1月からプロトコルと実装(クライアント)のバージョン番号はそれぞれ異なる値になっている。

プロトコルのバージョン遷移は↓に記載されていて、0.6.0からこのBIPのルールがて適用されている。

https://bitcoin.org/en/developer-reference#protocol-versions

現在のバージョンはBIP-152をサポートにより70014が最新みたい。

311→31402にガッツリ上がってるのは何があったのか?