Bitcoin用Protocol Buffers
別の場所で、bitcoinにprotocol buffersを使うことについて議論が続いてきた。利点をまとめると以下の通りだ:
-> エンコードが小さい -> 非常に高速 -> 多数の言語で実装が存在する (新しいクライアントを書くのが格段に楽になる) -> 前方互換性がある (これこそがprotocol buffersの眼目だ) -> コード上で極めてシンプルに使える
最初の段階としては、ウォレットファイルをprotocol buffersで保存することを提案したい。これは破壊的変更ではなく、しかも他のプログラムからウォレットファイルを解析しやすくなる。最終的には、ネットワーク通信にもbitcoinがprotocol buffersを採用することを期待したい。
protocol buffersは手書きのパケット形式より大きくなるかもしれない、と指摘する人もいる。私は逆で、protocol buffersが用いる巧妙なエンコードのおかげで実際にはより小さくなると見ている。これを決着させるには検証が必要だ。protocol buffersでウォレットファイル/ネットワークパケットをエンコードし、現行方式でのパケットサイズと比較してみる。ただ、パケットの中に何が入っていて、どんなデータがどんな形式で格納されているのか、さっぱり分からない。
参考までに、パケットを60バイト未満にするのは無意味だ——Ethernetパケットの最小サイズが60バイトだからだ。パケットがそれより小さい場合、60バイトまでパディングされる。
確かに、だがversionパケットは送られるすべてのパケットの中でおそらく一番小さいものなので、ほかで得られる利点の方が大きい。それと、本題を見失わないでほしい。Protocol Buffersのサイズが小さいという事実は、前方互換性があり、bitcoinを言語間で移植可能にするという事実に添えられた、うれしいおまけだ。
[削除済み] Quote from: martin on July 30, 2010, 11:37:59 AM
Mmartinの投稿(2010年7月29日23:29 UTC)エンコードされたprotocol bufferはわずか55バイトだが、bitcoinバージョンは85個の0x00セット(それぞれ2バイトを表すと仮定)だ。つまり、私の設計の悪いprotocol bufferでも手作りのレイアウトの半分以上のサイズだ!
“0x00”グループはそれぞれ1バイトを表す。標準バージョンパケットの長さはヘッダー20バイトに加えて87バイトだ。ヘッダーも大幅に最適化できるだろう:
message start "magic bytes" - 0xF9 0xBE 0xB4 0xD9
command - name of command, 0 padded to 12 bytes "version\0\0\0\0\0"
size - 4 byte int
checksum (absent for messages without data and version messages) - 4 bytes
ここでproto buffersを使うのは明らかに破壊的変更だが、かなりの容量を節約できるだろう。特に、「トランザクションを作成した」パケットの名前が”tx”であるため、そうしたパケットのそれぞれに少なくとも10バイトのオーバーヘッドがあることになる。
なぜ破壊的変更だと思うのかな? 最初に新しいプロトコルで試して、次に古いbitcoinシリアライゼーション方式でリトライすることは十分可能だろう。また、BitCoinコミュニティがまだ小さいうちに、早めに行うべき変更だと思う。これは新しいクライアント作成の大きな障壁であり、遅らせればbitcoinの普及を妨げることになる。
Protocol Buffersやboostシリアライゼーションを使わなかった理由は、一分の隙もなく安全にするには複雑すぎるように見えたからだ。コードが大きすぎて、予期しないことを行うような入力を形成する方法がないと確認できるほど読み通せない。
車輪の再発明は嫌いだし、自分でシリアライゼーションルーチンを書くことにしたのは不本意だった。現在のシリアライゼーション形式は可能な限りシンプルでフラットだ。入力ストリームの形成方法に余分な自由度はない。各ポイントで、データ構造の次のフィールドが期待される。与えられる選択肢は受信者が期待しているもののみだ。アップグレードが可能なようにバージョニングがある。
CAddressは、かなりの予約スペースを持つほぼ唯一のオブジェクトだ。(フラグ用に約7バイト、将来のIPv6拡張の可能性のために12バイト)
ブロックやトランザクションのような大きなものは、サイズに関してこれ以上あまり最適化できない。データの大部分はハッシュ、鍵、署名であり、これらは圧縮不可能だ。シリアライゼーションのオーバーヘッドは非常に小さく、通常サイズフィールドに1バイトだ。
ギャビンのP2Pブロードキャストインフラの既存のものを使うというアイデアについてだが、存在しないと思う。ブロードキャストのみを必要とするP2Pシステムはほとんどない。分散ハッシュテーブルインフラを提供しようとするChordのようなライブラリがあるが、それは私たちが必要としない、また望まない非常に大きくて困難な問題だ。それらのライブラリは、私たち自身のものよりもインストールがはるかに難しい。
Protocol Buffersやboostシリアライゼーションを使わなかった理由は、一分の隙もなく安全にするには複雑すぎるように見えたからだ。コードが大きすぎて、予期しないことを行うような入力を形成する方法がないと確認できるほど読み通せない。
失礼な言い方になるかもしれないが、それはトランザクションのSCRIPTフィールドの危険性と同じことに聞こえる。ブロックがクライアントに操作を示すような評価言語まるごとを書くことには抵抗がないのに、protocol buffersのようなライブラリを使うことには抵抗がある、というのは?
ウォレットファイルをカスタム形式の代わりにprotocol buffer形式で出力するオプションを含めることを検討してもらえないか? そうすればデフォルトはあなたがより信頼するカスタム形式のままで、ユーザーは新しいクライアントへ移行したい場合に自分のウォレットをprotobuf形式へエクスポートできる。
— martin
そのケースならなぜXMLを使わない? エクスポートに関して言えば、ディスク上のウォレットファイルのサイズはそれほど大きな問題ではないし、XMLはかなり良く圧縮できる。おまけに完全に人間が読める形式だ ― 実際に何が保存されているのかを理解する助けになるだろう。
確かに、だがversionパケットは送られるすべてのパケットの中でおそらく一番小さいものなので、ほかで得られる利点の方が大きい。それと、本題を見失わないでほしい。Protocol Buffersのサイズが小さいという事実は、前方互換性があり、bitcoinを言語間で移植可能にするという事実に添えられた、うれしいおまけだ。
カスタムでないフォーマットはデバッグもしやすい。自分一人が使っているわけではなく、別のプロジェクトにいる大勢の人間がバグを探し、修正してくれる。パケットをデコード・表示するツールも手に入ることが多く、何か問題があったときに気づきやすくなる。個人的にはパケットサイズは最も重要度の低い理由だと思う。