Develop with pleasure!

福岡でCloudとかBlockchainとか。

トランザクションのmalleabilityを利用した攻撃を受けた話

最近testnetでブロードキャストしたトランザクションが、消えて無くなる現象が続いたので調べてみた。

消えたといっても実際に消えてるわけではなく、ブロードキャストした際のTXIDとは別のTXIDでブロックに格納されている。

実際にブロードキャストした際のTXIDは

16693caa1fa4a9eb5792063138d36c4c12a02287426b8c1fd759efb35199b5ef

で、その内容は↓

{
  "hex": "010000000201e4a0f1fa83c642b91feafae36a0f8fded4158dfa6fd650e046b4364b805684000000006a473044022045c65646abc12c71352335dbec2824b2dbdef9253366b4b83439b2190ce098d20220112f48fc8e2c76d079a4bc1d448ec134d22737d9765421b7a9b050dd4dc9368f01210259f6658325c4e3ca6fb38f657ffcbf4b1c45ef4f0c1dd86d5f6c0cebb0e09520ffffffff31137db564a7fad07c9db5b6b862786589977c68d1270819030a9079941ca6c9010000006a47304402204354565632eedd30fb9ca5c22bb70ef848afd74f7bed354d267705a6e71ea885022019159daf2d623ef634a65399ce70e34c4422c8eba19bf877e6b822ffc6a4607601210259f6658325c4e3ca6fb38f657ffcbf4b1c45ef4f0c1dd86d5f6c0cebb0e09520ffffffff02801d2c04000000001976a914322653c91d6038e08b6d971e4560842c155c8a8888ac80248706000000001976a9143b9722f91a2e50d913dadc3a6a8a88a58a7b859788ac00000000",
  "txid": "16693caa1fa4a9eb5792063138d36c4c12a02287426b8c1fd759efb35199b5ef",
  "hash": "16693caa1fa4a9eb5792063138d36c4c12a02287426b8c1fd759efb35199b5ef",
  "size": 372,
  "vsize": 372,
  "version": 1,
  "locktime": 0,
  "vin": [
    {
      "txid": "8456804b36b446e050d66ffa8d15d4de8f0f6ae3faea1fb942c683faf1a0e401",
      "vout": 0,
      "scriptSig": {
        "asm": "3044022045c65646abc12c71352335dbec2824b2dbdef9253366b4b83439b2190ce098d20220112f48fc8e2c76d079a4bc1d448ec134d22737d9765421b7a9b050dd4dc9368f[ALL] 0259f6658325c4e3ca6fb38f657ffcbf4b1c45ef4f0c1dd86d5f6c0cebb0e09520",
        "hex": "473044022045c65646abc12c71352335dbec2824b2dbdef9253366b4b83439b2190ce098d20220112f48fc8e2c76d079a4bc1d448ec134d22737d9765421b7a9b050dd4dc9368f01210259f6658325c4e3ca6fb38f657ffcbf4b1c45ef4f0c1dd86d5f6c0cebb0e09520"
      },
      "sequence": 4294967295
    }, 
    {
      "txid": "c9a61c9479900a03190827d1687c9789657862b8b6b59d7cd0faa764b57d1331",
      "vout": 1,
      "scriptSig": {
        "asm": "304402204354565632eedd30fb9ca5c22bb70ef848afd74f7bed354d267705a6e71ea885022019159daf2d623ef634a65399ce70e34c4422c8eba19bf877e6b822ffc6a46076[ALL] 0259f6658325c4e3ca6fb38f657ffcbf4b1c45ef4f0c1dd86d5f6c0cebb0e09520",
        "hex": "47304402204354565632eedd30fb9ca5c22bb70ef848afd74f7bed354d267705a6e71ea885022019159daf2d623ef634a65399ce70e34c4422c8eba19bf877e6b822ffc6a4607601210259f6658325c4e3ca6fb38f657ffcbf4b1c45ef4f0c1dd86d5f6c0cebb0e09520"
      },
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 0.70000000,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 322653c91d6038e08b6d971e4560842c155c8a88 OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a914322653c91d6038e08b6d971e4560842c155c8a8888ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "mk67zfwR4f5UZg2Lfa7nuvbdsm7mdmUpcV"
        ]
      }
    }, 
    {
      "value": 1.09520000,
      "n": 1,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 3b9722f91a2e50d913dadc3a6a8a88a58a7b8597 OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a9143b9722f91a2e50d913dadc3a6a8a88a58a7b859788ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "mkx3DXaWZt1GGaXLFh7cGPvW3NC4ZoHnMR"
        ]
      }
    }
  ]
}

ブロードキャストして、外部のブロックチェーンエクスプローラにもそのトランザクションが伝播したことは確認した。しばらくして、このTXIDのトランザクションが承認されたか確認しようとしたら、

No such mempool or blockchain transaction. Use gettransaction for wallet transactions.

となり、ブロックチェーンエクスプローラ側でもそんなトランザクションは無いと言われる。(さっきまであったのに)

ただ実際にトランザクションが消えてなくなった訳ではなく、

59af5c2f932b23dc19e06dd92e1ead1fd2a2546df7cbb834f47e2c1355272cc6

という別のTXIDでブロックに格納されていた。このトランザクションの内容は↓

{
  "hex": "010000000201e4a0f1fa83c642b91feafae36a0f8fded4158dfa6fd650e046b4364b805684000000006b483045022045c65646abc12c71352335dbec2824b2dbdef9253366b4b83439b2190ce098d2022100eed0b70371d3892f865b43e2bb713ec9e887a50d38f47e8416220daf826d0ab201210259f6658325c4e3ca6fb38f657ffcbf4b1c45ef4f0c1dd86d5f6c0cebb0e09520ffffffff31137db564a7fad07c9db5b6b862786589977c68d1270819030a9079941ca6c9010000006b48304502204354565632eedd30fb9ca5c22bb70ef848afd74f7bed354d267705a6e71ea885022100e6ea6250d29dc109cb59ac66318f1cb2768c13fb0daca7c3d91a3b8d0991e0cb01210259f6658325c4e3ca6fb38f657ffcbf4b1c45ef4f0c1dd86d5f6c0cebb0e09520ffffffff02801d2c04000000001976a914322653c91d6038e08b6d971e4560842c155c8a8888ac80248706000000001976a9143b9722f91a2e50d913dadc3a6a8a88a58a7b859788ac00000000",
  "txid": "59af5c2f932b23dc19e06dd92e1ead1fd2a2546df7cbb834f47e2c1355272cc6",
  "hash": "59af5c2f932b23dc19e06dd92e1ead1fd2a2546df7cbb834f47e2c1355272cc6",
  "size": 374,
  "vsize": 374,
  "version": 1,
  "locktime": 0,
  "vin": [
    {
      "txid": "8456804b36b446e050d66ffa8d15d4de8f0f6ae3faea1fb942c683faf1a0e401",
      "vout": 0,
      "scriptSig": {
        "asm": "3045022045c65646abc12c71352335dbec2824b2dbdef9253366b4b83439b2190ce098d2022100eed0b70371d3892f865b43e2bb713ec9e887a50d38f47e8416220daf826d0ab2[ALL] 0259f6658325c4e3ca6fb38f657ffcbf4b1c45ef4f0c1dd86d5f6c0cebb0e09520",
        "hex": "483045022045c65646abc12c71352335dbec2824b2dbdef9253366b4b83439b2190ce098d2022100eed0b70371d3892f865b43e2bb713ec9e887a50d38f47e8416220daf826d0ab201210259f6658325c4e3ca6fb38f657ffcbf4b1c45ef4f0c1dd86d5f6c0cebb0e09520"
      },
      "sequence": 4294967295
    }, 
    {
      "txid": "c9a61c9479900a03190827d1687c9789657862b8b6b59d7cd0faa764b57d1331",
      "vout": 1,
      "scriptSig": {
        "asm": "304502204354565632eedd30fb9ca5c22bb70ef848afd74f7bed354d267705a6e71ea885022100e6ea6250d29dc109cb59ac66318f1cb2768c13fb0daca7c3d91a3b8d0991e0cb[ALL] 0259f6658325c4e3ca6fb38f657ffcbf4b1c45ef4f0c1dd86d5f6c0cebb0e09520",
        "hex": "48304502204354565632eedd30fb9ca5c22bb70ef848afd74f7bed354d267705a6e71ea885022100e6ea6250d29dc109cb59ac66318f1cb2768c13fb0daca7c3d91a3b8d0991e0cb01210259f6658325c4e3ca6fb38f657ffcbf4b1c45ef4f0c1dd86d5f6c0cebb0e09520"
      },
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 0.70000000,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 322653c91d6038e08b6d971e4560842c155c8a88 OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a914322653c91d6038e08b6d971e4560842c155c8a8888ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "mk67zfwR4f5UZg2Lfa7nuvbdsm7mdmUpcV"
        ]
      }
    }, 
    {
      "value": 1.09520000,
      "n": 1,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 3b9722f91a2e50d913dadc3a6a8a88a58a7b8597 OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a9143b9722f91a2e50d913dadc3a6a8a88a58a7b859788ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "mkx3DXaWZt1GGaXLFh7cGPvW3NC4ZoHnMR"
        ]
      }
    }
  ],
  "blockhash": "00000000000022e3612bb0d79705417ff2cd77212d778a87156cc29769fbc83c",
  "confirmations": 5,
  "time": 1491551887,
  "blocktime": 1491551887
}

ブロードキャストしたトランザクションの各入力はSIGHASH_ALLで署名しているので、出力等のデータは全く変わっておらず、Bitcoinが盗まれた訳ではない。入力も当然OutPoint(参照先のTXIDと出力インデックス)は変わってないが、scriptSigが変わっていることが分かる。2つある入力の全てのscriptSigがブロードキャスト時のトランザクションと変わっている。

入力の参照先のscriptPubkeyはP2PKHなので、入力のscriptSig

<署名>[SIGHASH_TYPE] 公開鍵

という構成になる。これがどう改竄されたのか最初の入力データで確認してみる。

ブロードキャスト時のデータ
473044022045c65646abc12c71352335dbec2824b2dbdef9253366b4b83439b2190ce098d20220112f48fc8e2c76d079a4bc1d448ec134d22737d9765421b7a9b050dd4dc9368f01210259f6658325c4e3ca6fb38f657ffcbf4b1c45ef4f0c1dd86d5f6c0cebb0e09520

署名データは

3044022045c65646abc12c71352335dbec2824b2dbdef9253366b4b83439b2190ce098d20220112f48fc8e2c76d079a4bc1d448ec134d22737d9765421b7a9b050dd4dc9368f01
ブロックに取り込まれたデータ
483045022045c65646abc12c71352335dbec2824b2dbdef9253366b4b83439b2190ce098d2022100eed0b70371d3892f865b43e2bb713ec9e887a50d38f47e8416220daf826d0ab201210259f6658325c4e3ca6fb38f657ffcbf4b1c45ef4f0c1dd86d5f6c0cebb0e09520

署名データは

3045022045c65646abc12c71352335dbec2824b2dbdef9253366b4b83439b2190ce098d2022100eed0b70371d3892f865b43e2bb713ec9e887a50d38f47e8416220daf826d0ab201

攻撃方法

上記のデータから改竄されているのは署名部分であることが分かる。

各署名は、DER形式のECDSA署名で、以下の形式でシリアライズされている。

0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash-type]
  • total-length
    この後に続くデータの長さ( sighash-typeを除く)
  • R-length
    Rの値の長さを指す1バイトデータ。
  • R
    任意のデータ長で、署名作成時に生成した乱数値。ビッグエンディアンエンコードされている。
  • S-length
    Sの値の長さを指す1バイトデータ。
  • S
    Rと署名対象のメッセージのハッシュ値秘密鍵から計算した値。任意のデータ長で、ビッグエンディアンエンコードされている。
  • sighash-type
    署名時に使用したハッシュタイプのフラグ(0x01, 0x02, 0x03, 0x81, 0x82, 0x83を指定可能)

↑のブロードキャスト時とブロックに入れれた際の署名を見ると、Rの値(45c65646abc12c71352335dbec2824b2dbdef9253366b4b83439b2190ce098d2)は同じであることが分かる。変わっているのはSの値。

これは署名値(r,s)のsの値をマイナスにした、(r, -s (mod N))の場合もその署名は有効と判断するECDSAの署名のmalleabilityを利用したものだ。

ブロードキャスト時のS値は

112f48fc8e2c76d079a4bc1d448ec134d22737d9765421b7a9b050dd4dc9368f

だが、この-s (mod N)の値を計算すると↓

require 'bitcoin'

origin_s = '112f48fc8e2c76d079a4bc1d448ec134d22737d9765421b7a9b050dd4dc9368f'
s = OpenSSL::BN.from_hex(origin_s).to_i

n = Bitcoin.bitcoin_elliptic_curve.group.order.to_i

modified_s = OpenSSL::BN.new((s * -1) % n).to_hex
> 'eed0b70371d3892f865b43e2bb713ec9e887a50d38f47e8416220daf826d0ab2'

となり、改竄後の↓の値に合致する。

eed0b70371d3892f865b43e2bb713ec9e887a50d38f47e8416220daf826d0ab2

改竄されたデータにはさらに先頭に00が付与されている。

影響

今回の署名の改竄は出力先や量が変更できるものではないので、Bitcoinを盗まれるという影響は無い。ただ双方向のペイメントチャネル等のまだブロードキャストされていないトランザクションのTXIDを元にトランザクションを作るようなケースでは、確定したTXIDが必要になる。これはSegwitを導入により回避できる。

そういう用途で使用する際は事前にP2WPKH宛にコインを送って送る必要がありが、その際のトランザクションは今回のようなmalleabilityを利用した攻撃によりTXIDが書き換わる可能性があることを想定しておく必要がある。

また一般に公開されているtestnetのFaucetやウォレットアプリではまだP2WPKHやP2WSHを使った送金ができる機能は見かけないので、そういったアプリ側が早くWitnessなトランザクションを作れるようになる必要がある。そのためにもP2WPKHやP2WSHのアドレスフォーマットを定義したBech32↓のBIP登録が待たれる。

github.com

あと、mainnetでのSegwitのアクティベートも。

他にもtestnetでmalleabilityを利用した改竄被害にあった人いるみたい↓

www.reddit.com

誰が改竄してる?

ブロードキャストしたトランザクションはブロックに入るまでは、外部のブロックチェーンエクスプローラでも確認できたので中間者による改竄ではなくブロックをマイニングしたマイナーの可能性が高い。

通常ブロックをマイニングしたのがどのマイニングプールかは、コインベーストランザクションのcoinbase scriptに記載されていることが多い。 ↑トランザクションはブロックheightが1116497のブロックに入っていて、そのcoinbaseは

0351091100048f46e75804a17031160cdfffe558d87db95884fb08001d2f4542f09f92a92f4144f09f98b12f434b506f6f6c5072696d6172792f

BIP-034によりこの先頭はblock heightで、その後が任意のスクリプトになっており、マイニングプールの情報が記載されていることが多い。block heightを除いたデータをデコードすると↓のようになる。

 �F�X�p1���X�}�X� /EB💩/AD😱/CKPoolPrimary/

CKPoolPrimaryがそうなのか?著名なマイニングプールではなさそう。

それにしても、malleabilityの問題自体は認識していたものの、今まで実際にやられたことは無かったので良い経験になった。

Confidential Assetsの概要

Blockstreamが新しくConfidential Assetsなる機能をリリースしていたので、どういったものなのか見てみる。

blockstream.com

Open Assets ProtocolやCounterpartyやColuなどBitcoinブロックチェーン上で、Bitcoin以外のアセットを発行・送付するプロトコルが登場しているように、同じチェーン上で複数のアセットタイプをサポートするのはブロックチェーン技術の自然な進化と考えられる。複数のアセットタイプが同一のチェーンで扱えることで、それぞれのアセットタイプで同じチェーンのセキュリティ特性を共有可能になり、マルチアセットのアトミック・スワップのような新しいユースケースが可能になる。
複数のアセットタイプをサポートするには、各トランザクションの出力にasset tagをラベル付けするだけで実現できるが、この場合ユーザがそのトランザクションでどれだけの量を取引しているのか誰もが確認することができてしまう。Confidential Assetsは、Confidential Transactionの秘匿技術を利用してマルチアセットタイプをサポートする際にasset tagをブラインディングする技術になる。

Confidential Transaction*1と同様、Confidential Assetsではそのトランザクションが正しいトランザクションであることを誰もが暗号的に検証することができ、予期せずアセットが作成、破棄、変更されることが無いことを保証する。一方、そのトランザクションで取引されているアセットタイプやその量は、取引の当事者のみしか知ることができず、アセットタイプや量を他のユーザーから秘匿できる。

マルチアセットを扱うチェーンでは、通常特定のタイプのアセットの作成と破棄が行われる。Confidential Assetsにおけるアセットの発行というのは、新しいasset tagの作成とアセットの量を指定する発行用のトランザクションを作成することを指す。その後のアセットの再発行は、アセットに関連付けられた再発行トークンに関する権限を証明することで実行できる。この発行トークンはアセット作成時に一緒に作成できるが、アセットが再発行をサポートしない場合この発行トークンは作成しない。

発行トランザクションは、入力と出力のバランスを取らなくていいという点で独特なトランザクションになる。この時、発行量が公開されていれば、そのアセットの発行量は周知になる。

では、具体的にConfidential Assetsの仕組みをみていく。

Confidential Assetsの技術

Asset TagとRangeproofs

Confidential Assetsの技術を知るためにはまずConfidential TransactionのベースになっているPedersen commitmentsについて知る必要がある。

commitment = xG + aH

ここでG楕円曲線の標準のベースポイントで、Hは2つめのベースポイントで、Commitment TransactionではH = to_point(SHA256(ENCODE(G)))といったロジックで算出される。このようなベースポイントをnothing-up-my-sleeve = NUMSと呼ぶ。

Commitment Transactionで↑はxを使ってコインの量aをブラインディングするcommitmentとして説明されている。Confidential Assetsでは、Haコインとして扱う(= Hをアセットタイプとして扱う)ことができるのではないかという点に注目している。
別のNUMSベースポイントIを追加し、以下の2つのcommitmentを考えたとき、

commitment_1 = xG + aH
commitment_2 = xG + aI

ここでHIは異なるアセットを表すものとして考えることができ、この2つのcommitmentは、量は同じaを持つ異なるアセット(HI)へのcommitmentであると言える。

以下のようなアセットタイプの異なる2つの入力と2つの出力を持つトランザクションを考えてみる。

in1 = xG + aH, H --\   /-- uG + cH, H = out1
                   |---|
in2 = yG + bI, I --/   \-- vG + dI, I = out2

Commitment Transactionの場合ベースポイントは1つだったので、トランザクションの入力と出力で量が合っているかどうかは

out1 + out2 - in1 - in2 = 0

を検証すればよかった。複数アセットの場合も同様の式が成立する↓

0 = out1 + out2 - in1 - in2  = (uG + cH) + (vG + dI) - (xG + aH) - (yG + bI)  = (u + v - x - y)G + (c - a)H + (d - b)I

HIは両方とも NUMSポイントなので、この式が成り立つのは個々の項が0の場合か、c = a かつ b = dの場合。言い換えると、この式はアセットHの総量が入力と出力で同じで、アセットIの総量が入力と出力で同じ場合に成立する。

これは2つ以上のasset tagでも成立し、実際にそれぞれ一意のNUMSポイントを割り当てることができる限り、無制限の種類のアセットをサポートすることができる。

Commitment Transactionと同様、この式だけでは値のオーバーフローやマイナス値への考慮が不十分なため、Commitment Transactionと同様、各出力にrangeproofをアタッチすることでこれを解決する。唯一違うのは、検証者が予め決まっていた固定ベースポイントHを使う代わりに、asset tagを使って検証しなければならない点だけ。

Blinded Asset TagとAsset Surjection Proofs

↑の例では、各トランザクション出力にNUMSポイント、もしくはasset tagが関連付けられており、同じアセットタイプの出力では同じtagが使用される前提だった。これだと出力毎にどんな種類のアセットが取引されているのか分かってしまうというプライバシー上の問題が残る。

これは各asset tagを以下の形式のblinded asset tagに置き換えることで解決できる。

A = H + rG

Hは↑のasset tagで、rはランダムなシークレット値。rを知る人は誰でもこのタグが表すアセットが分かるが、rを知らない人にとってはランダムな楕円曲線上のポイントにしかみえない。Aを持つ値へのコミットメントはHを持つ値へのコミットメントなので、"出力の総量 - 入力の総量 = 0"というルールはトランザクションの検証時に引き続き機能する↓

commitment_A = xG + aA = xG + a(H + rG) = (x + ra)G + aH = commitment_H

このブラインディングファクターの導入は、ユーザーがrangeproofを生成するのに影響を与えないが、入出力のバランスの計算が少し複雑になる。

というのも各blinded asset tagは検証者から見るとランダムな値に見えるので、そのトランザクションasset tagが正しいか検証する際に(以下のようなblinded asset tagの悪用を考えると)"出力の総量 - 入力の総量 = 0"という検証だけでは充分でないという問題がある。

A′ = -H + rG

この場合ブラインドされたアセットA′ の量は、アセットHの負の値をとり、攻撃者が違法に資金供給量を増やすのに使われることになってしまう。

この問題を解決するためasset surjection proofという仕組みが導入される。これはトランザクション内のどの出力がその入力に対応しているのかをブラインディングした状態で、各トランザクション出力のアセットタイプがある入力のアセットタイプと同じであることを暗号的に証明する仕組みになる。

この仕組みは簡単で、同じasset tagアセットHにコミットするblinded asset tagABがあるとして、

A - B = (H + aG) - (H + bG) = (a - b)G

秘密鍵a-bに対応する署名鍵となる。
トランザクション出力out1が与えられると、トランザクションの全ての入力に対し、out1 - in1out1 - in2..の鍵で(いくつかの秘密鍵の内のどれかで署名されたことは分かるがどの秘密鍵が使われたのかは分からない)リング署名を使う。out1がある入力と同じasset tagを持つ場合、トランザクションの署名者は署名鍵の中から対応する秘密鍵を知り、リング署名をすることができる。

asset surjection proof はこのリング署名で構成されている。

デモ

DG LabがOSSで公開してる↓

github.com

まとめ&所感

  • どうやってアセットを表現するのかと思ったけど、Commitment Transactionのcommitment作成する際の2つめのベースポイントをアセットタイプとしてみなすことで実現してる。
  • そのためアセットの量の秘匿もCommitment Transactionのロジックそのままで秘匿可能。
  • 量は秘匿できても、各出力に割り当てられたasset tagから出力でどのアセットが取引されているかわかるので、アセットタイプも秘匿したい場合はblinded asset tagを使う。
  • blinded asset tagは、ランダムなシークレット値rを使用してH + rGを計算した値で、rを知るものしか本当にアセットタイプが何か知ることができない。
  • ただ、 blinded asset tagを悪用してマイナスの量を組みこめてしまうので、単純な入出力のバランス以外にasset surjection proof という仕組みで不正がないか検証できるようにしてる。
  • ベースポイントを拡張アイテムとして使うアプローチは楕円曲線の特性を上手く活かしてて面白い。
  • 時間のある時にホワイトペーパーの方も読んでおきたい。

*1:Blockstreamのサイドチェーンで実装されているコインの取引量を秘匿する技術。詳細は以前書いた↓参照。techmedia-think.hatenablog.com

Bitcoin Core 0.14.0のリリース

Bitcoin Core 0.14.0がリリースされたのでどんな変更があったのか見ておく↓

Bitcoin Core :: Bitcoin Core 0.14.0 Released with Performance Improvements

主な新機能は↓

IBDのパフォーマンス改善

ブロックチェーンのデータサイズは絶えず増加していて、現在mainnetのデータ量は125GBと巨大。新しくノードを立ち上げる場合、最初に大量のブロックデータをダウンロードして処理する必要がありウォレットが利用可能になるまで結構な時間がかかる。この初期ブロックのダウンロード(IBD)時間を改善するため今までいろんな改良が加えられてきた↓

リリース IBDに関する改善
0.5.0 チェックポイントより前の署名のスキップ
0.8.0 LevelDBへの切り替えと署名検証の並列化
0.10.0 ヘッダを先に同期し、その後ブロックを並列ダウンロード
0.11.0 (オプション)ディスクスペースを節約するためのブロックファイルの剪定
0.12.0 楕円曲線のライブラリを独自実装(libsecp256k1)し署名検証を高速化
0.13.1 将来古い署名のダウンロードをスキップできるようにするSegwitの導入

今回の0.14.0のリリースではこのIBDの速度を大幅に向上させる2つの改善点が含まれている↓

  1. Assumed valid blocks
  2. mempoolとUTXOのDBキャッシュ間のメモリ共有

この結果、EC2のt2.xlarge(4コア、メモリ16GB)で、Bitcoin Core 0.13.2とBitcoin Core 0.14.0で同じ設定で初期ブロックのダウンロード行うと

  • Bitcoin Core 0.13.2だと、全ブロックダウンロードが終わるのに1日と12時間40分
  • Bitcoin Core 0.14.0だと、全ブロックダウンロードが終わるのに6時間25分

と5.7倍高速化してる。

ちなみに↑はデフォルト設定の場合で、起動オプションの1つである-dbcacheオプション(デフォルト300(MB))を-dbcache=8000(8GB)を指定して実行すると約3時間で同期ができる。

で2つの改善点がどういうものかというと

Assumed valid blocks

0.3.2でチェックポイントと呼ばれる仕組みが導入された。これはフルノードに、ある時点でベストチェーンとは別のチェーンを検証させ無駄な負荷をかけるようなDoS攻撃を防ぐためのもの。

0.5.0では最新のチェックポイントより前のブロックの署名をスキップすることでIBDを高速化した。

しばらくしてBitcoinのセキュリティの改善やその他の改善(ヘッダの初期同期やminimum chainworkなど)によりチェックポイントの必要性は減り、チェックポイントは既に有効だと判断したチェーンのみを受け入れるものではあるが、どのチェーンが有効か判定しているように見えセキュリティモデルについて開発者の混乱を招くおそれがあったので、多数の開発者からチェックポイントを削除したいという要望が出た。

Assumed valid blockは、署名スキップの最適化処理をDoSを防ぐためのチェックポイントから分離し、それぞれ独立して実行できるようにした新機能になる。

どういうものかというと、フルノードを新規に起動する際、そのユーザが有効なブロックを知っている場合、その有効な最新ブロックのブロックハッシュをBitcoin Core 0.14.0に渡すことで、そのブロックの前までの署名をスキップする機能になる。署名の検証処理はIBDの処理の中でもCPU消費が大きいためAssumed valid blockを使うとIBDを大幅に高速化できる。指定したAssumed valid block以降のブロックについては、全てちゃんと署名が検証される。

チェックポイントとAssumed valid blockの大きな違いは、チェックポイントに指定されているブロックはブロックチェーンの一部でないといけないけど、Assumed valid blockは別にチェーンの一部でなくても良い。ユーザーから渡されたAssumed valid blockがチェーンの一部でない場合、Bitcoin Coreは単純に全ての履歴ブロックの署名を検証するだけ。また、フォークが発生し最新のPoWの結果有効なチェーンにAssumed valid blockで指定したブロックが無い場合、Bitcoin CoreはAssumed valid blockを破棄して新しい有効なチェーンに切り替える。

これからBitcoinを始めようというユーザーは、おそらく有効なブロックとか知らないと思うけど、全てのコンセンサスルールを知っているとも思えないので、そういったユーザーは単純にフルノードをダウンロードして使えば良い。Bitcoin Core 0.14.0には複数の著名な開発者がそれぞれ有効であると確認したデフォルトのAssumed valid blockが組み込まれている↓

  • mainnet(#453354)
    00000000000000000013176bf8d7dfeab4e1db31dc93bc311b436e82ab226b90
  • testnet(#1079274)
    00000000000128796ee387cf110ccb9d2f36cffaf7f73079c995377c65ac0dcc

Assumed valid blockを使わずに全ての署名を検証したい場合は、-assumevalid=0をオプションにつけて起動すればいい。任意のAssumed valid blockを指定する際は以下のようにassumevalidで対象ブロックのブロックハッシュを指定する。

-assumevalid=00000000000000000013176bf8d7dfeab4e1db31dc93bc311b436e82ab226b90

mempoolとUTXOのDBキャッシュ間のメモリ共有

IBDの処理中は、Bitcoin Coreはmempoolを使用しない。これは最新のブロックが無いと最新のトランザクションが有効化どうか確認する方法が無いためで、その間通常よりもメモリ消費は少ない。

0.14.0では、このIBD中に使われていないmempoolのメモリをUTXOデータベースキャッシュと共有するようになる。これによりより多くのUTXOを高速なメモリ上にキャッシュできるようになり、ディスクアクセスを減らすことができる。

新しいブロックの検証とリレーの高速化

0.14.0の↓の4つの改善点は、新しいブロックをできるだけ速く受信したいマイナーやその他のユーザーにとって特に関心があるものになる。

署名キャッシュの改善

最初の機能はcuckoo hashingを使うための署名キャッシュの更新。署名キャッシュはBitcoin Coreが未確認のトランザクションの署名の検証結果をキャッシュできるようにするもので、同じトランザクションが新しいブロックに含まれていた場合に再度署名を検証しなくてすむ。署名の検証は新しいブロックを処理する際に計算コストが最も高い処理なので、署名キャッシュを使うと新しいブロックを処理する速度が大幅に向上する。

既存の0.13.2の署名キャッシュは、CPUが8コア未満であれば上手く機能するけど、8コア以上のCPUを持つ場合、ロックが発生し使用可能なコアを有効活用できない。“cuckoo cache”を利用できるようcuckoo hashingを使用するようアップグレードすると、この問題は解決され、より多くのコアを効果的に利用できるようになる。

16コアの環境でテストすると、0.14.0を使うと0.13.2より40%速く新しいブロックの処理が可能になった。8コア未満の環境では大幅なパフォーマンスの向上は無いけど、同じメモリ量で以前より多くの署名をキャッシュすることができる。

以前のBIP152のCompact Blockリレー

2つめの改善点は、 BIP152のCompact Blockの実装。以前の実装ではBIP-152の2つのオプトインモードがサポートされていた↓

  • 帯域幅モード
    新しいブロックをリレーするのに必要最小限のデータ送信するモードで、受信ノードが特定の新しいブロックの要求を待つ
  • 帯域幅モード
    受信ノードによる特定の新しいブロックの要求を待つこと無く、新しいブロックデータを送信するモード。この場合、受信ノードが既に別ノードから受信したデータと重複するデータを送信するリスクはあるが、ブロックは迅速に転送される。

0.14.2で更新された実装では、ブロックが完全に検証される前に新しいブロックのリレーを始めることで、高帯域幅モードのノードを強化する。最良の場合、検証遅延が無くなることで新しいブロックがP2Pネットワーク上で数ホップに渡って以前より数倍速くブロックを伝播させることができる。最悪の無効なブロックの伝播で余計な帯域幅を消費する。いずれの場合も無効なブロックは受信側のノードで拒否されるのでセキュリティモデルは今までのまま。

※ BIP-152の仕様については↓

techmedia-think.hatenablog.com

並行性とスループットにフォーカスしたP2Pコードのリファクタリング

3つめの改善点は並行性とスループット向上のためのP2Pネットワークコード全体のリファクタリング。並行性の改善では、新しく受信したブロックを優先度の低いトラフィックよりも先に処理できるようになり、ブロックができるだけ速くリレーされ検証できるようになる。

またメッセージの処理中にバックグラウンドでネットワーク処理も継続して行えるようになり、特にIBDの速度の改善にも繋がる。

mempoolをディスクに保存

4つめの改善点は、各ノードが受信した未確認のトランザクションのmempoolが通常のシャットダウン時にディスクに保存され、ノードが再び起動した際にディスクからメモリ上にロードされるようになった。

Compact Blockと組み合わせることで、未確認のトランザクションが新しく生成されたブロックに含まれている際、再ダウンロードする必要がなくなる。署名キャッシュと組み合わせることで、未確認トランザクションの署名の検証結果のキャッシュで、新しいブロックをより迅速に検証することができる。

こういった機能はよくRDBがシャットダウン時にキャッシュをディスクに保存して、次回起動時にそこからキャッシュを復元しウォームアップ期間を短縮するのに使われるテクニックと似てる。

(オプション)手数料の置換

0.14.0のオプション機能(デフォルトは無効)として、ウォレットで前に作成したトランザクションを新しく生成したトランザクションで置換する機能が含まれ、これによりBIP-125のオプトインReplace By Fee が利用可能になる。

Bitcoin Core起動時に-walletrbfオプションを付与してこの機能を有効にすると、作成したけどまだ承認されていないトランザクションについて、より高い手数料を設定して再送信することができるbumpfee RPCが利用できるようになる。オプトインRBFもしくはフルRBFをサポートするマイナーは、手数料の低いトランザクションから高くなったトランザクションにキューのデータを置換する。手数料が高くなった分マイナーにはより速くブロックに入れるインセンティブが働く。

UTXOのサイズ削減のアプローチTXO Commitments

BitcoinのUTXOのデータサイズは増加する一方で、フルノードを運用する際のボトルネックにもなっているが、そのUTXOの成長の問題を解決する方法としてBitcoinのコア開発者の1人であるPerter Toddが昨年 TXO Commitmentsという仕組みについてブログに投稿してた↓ので仕様部分だけまとめてみた。

Making UTXO Set Growth Irrelevant With Low-Latency Delayed TXO Commitments

現状の課題

現状Bitcoinにはブロックサイズの制限以外に、各種データのサイズを制限するようなコンセンサスルールは存在せず、UTXOのサイズも増えている。UTXOのセットを保持するのがフルノードの最小要件なので、UTXOのサイズが大きくなるほどフルノードの実行のハードルが上がる。

またUTXOのディスク上のサイズは圧縮しシリアライズされた状態であり、UTXOをモリ上に展開するとそのサイズは大幅に増える。複数の入力をマージして1つのUTXOを作るなどすればUTXOのサイズを減らせるが、現状そういったことをするインセンティブも無いので、サイズ削減も期待できない。それ以外に、小額過ぎて実際に使うことのないUTXOや秘密鍵の紛失により使用できないUTXO、コインの送付を目的としていないタイムスタンプサービスなどの使い方など、多くの要因によってUTXOサイズは増加している。特にタイムスタンプサービスなどはデータを記録するのが目的なので、通常のBitcoin送付の手数料よりもずっと高額な手数料を支払うことも許容される。

今のところUTXOの成長を防ぐための良いツールはなく、Segregated Witnessには、UTXOのサイズの削減する効果があるが、UTXOの成長を無視できるほどの効果は無い。

UTXOセットのサイズをハードリミットするというアイディアもあるが、これはBitcoinノードを効率的に動かすための最小要件であって、UTXOのスパム問題の根本解決にならない。

TXO Commitments

このUTXOのサイズの問題を解消しようと提案されているのがTXO Commitments。

(使用済み・未使用両方の出力を含む)全てのトランザクション出力の状態にコミットするマークルツリーを構築する仕組みで、出力の現在の状態をコンパクトに証明する方法を提供できる。これによりUTXOセットのアクセスの低い部分をアーカイブし、フルノードで関連データを破棄しても、それらの出力が実際に使用されていないことをノードに証明することで、アーカイブされた出力が使用する仕組みを提供する。

具体的には、TXO Commitmentsでは決定性のあるインデックス可能な挿入型マークルツリーの一種であるMerkle Mountain Range (MMR)を提案しており、ストレージ要件を最小限に抑え、新しいアイテムを安価にツリーに追加することができる。出力がTXO MMRに追加されると、その出力は決して削除されることはない。出力が使用された場合は、その場でステータスが更新される。MMRの特定のアイテムの状態と、MMRのアイテムの変更の有効性は両方ともツリーの先端までのマークルパスからなる { \displaystyle\log{2} n}サイズのプルーフで証明できる。

TXO commitment proofsは署名者の関与無しに生成及びトランザクションへの追加ができる。TXO commitment proofsは自体は署名される必要はなく、トランザクションハッシュの一部でもないので、TXO MMRのデータにアクセスできる人であれば誰でも、不足しているproofを再生成することができ、TXO commitmentsを使用するためにウォレットのソフトウェアに最小限の変更が必要となる。

TXO Commitmentsの実装

コミットメントフルノード実装では次のデータを格納する必要がある。

  1. UTXOセット
    未使用であることが明らかなtxoutsのキー:バリューのMap。既存のUTXOの実装と同様だが、古い出力のUTXOはこのセットから除外されるのが大きく違う。
  2. STXOセット
    直近のTXO commitmentの後に使用されたトランザクション出力のセット。ただしTXO commitmentより前に作成されたもの
  3. TXOジャーナル
    TXO MMRに使用済みとマークする必要がある出力。追加は低レイテンシーで、削除は高レイテンシーで行われる必要がある。
  4. TXO MMRリスト
    pruning可能な順序付けられたTXO MMRのリストで、主にペンディングされてるコミットメント。
Fast-Path: ブロック内で使用されているTxOutの検証

トランザクション出力がブロック内のトランザクションで使われた場合、以下の2ケースが考えられる。

  1. 最近作られた出力の場合
    最新のTXO commitmentの後に作られた出力なので、出力はUTXOセット内に存在する。そのためその出力を使用するトランザクションにはTXO commitment proofは不要。出力をUTXOセットから削除し、TXOジャーナルに追加する。
  2. アーカイブされた出力の場合
    最新のTXO commitmentより前に作られた出力なので、出力がUTXOセット内に存在するとは限らない。トランザクションはその出力が未使用であることを示す最新のTXO commitmentに対するTXO commitment proofを持つ。出力がSTXOに存在していないことを確認し、STXO内に無ければSTXOに追加する。出力と TXO commitment proofをTXOジャーナルに追加する。

どちらのケースでも、出力を使用済みとして記録する際は2つのキー・バリューの更新と1つのジャーナルの追加が必要。既存のUTXOセットは使用の都度1つのキー:バリューの更新が必要で、最悪ブロック内のトランザクション出力の使用が全てアーカイブされた出力であったとしてもブロックの検証レイテンシーは現状の2倍以内になると期待される。

Slow-Path:保留中のTXO Commitmentsの計算

優先順位の低いバックグラウンドタスクでTXOジャーナルをフラッシュし、TXO MMRに各ブロックが使用した出力を記録し、MMRデータをハッシュしTXO commitmentsダイジェストを取得する。さらにこのバックグラウンドタスクはTXO commitmentsに記録された出力をSTXOから削除し、不要になったTXO commitment を剪定する。

TXO commitmentの計算のスループットは既存のUTXOのみのスキームより悪くなる。

TXO MMRの詳細な実装

各TXO MMRの状態は、ほとんどの情報が共有されている前のものの修正で、多数の TXO commitmentsの状態を効率的に保存している。マークル化されたデータ構造では循環参照は不可能なので、ガベージコレクションは単純な参照カウンタで充分。不要になったデータはデータベースから削除することで剪定できる。

実際に構成も見たほうが理解しやすいと思うkので具体例で考えてみる。

2つのtxoutsを持つ以下のTXO MMRを考える。(これを状態#0と呼ぶ)

f:id:techmedia-think:20170306151818p:plain

別のエントリーを追加すると状態#1になる↓

f:id:techmedia-think:20170306152037p:plain

commitment #1で状態#0のデータがどのように再利用されたかに注目してほしい。状態#2を得るためさらに2つのエントリーを追加する。

f:id:techmedia-think:20170306152802p:plain

このケースでは状態#1のデータは再利用されていない。

ここで状態#2が最新のブロックによってブロックチェーンにコミットされたとする。状態#2で作られた出力を使おうとする将来のトランザクションは、それが未使用であることを証明する責務がある。基本的には状態#2のMMRのデータを提供しなければならない。これによりTXO MMRに新しいtxoutを追加するために必要最低限のデータを残し、データを剪定することができる。

f:id:techmedia-think:20170306154439p:plain

実装の詳細に応じて、ノード"2"と"e"に必要なデータはそのハッシュダイジェストだけである。

さらに3つのtxoutを追加すると状態#3になる。

f:id:techmedia-think:20170306155818p:plain

ここで最近作られたtxout fが使用されたとする。MMRを更新するための全てのデータがあり、状態#4になる。ここでは2つの内部ノードと1つのリーフノードを変更する。

f:id:techmedia-think:20170306160150p:plain

もしアーカイブされたtxoutが使われると、トランザクションは最近コミットされたTXO(このケースでは状態#2)にマークルパスを提供するよう要求する。txout bが使用された場合であれば、トランザクションは状態#2から以下のデータを提供する必要がある。

f:id:techmedia-think:20170306160644p:plain

ローカルのTXO MMRをベースに上記データを復元すると↓

f:id:techmedia-think:20170306173933p:plain

この時点でまだ状態#4は変更していない。txout bが使用されたことを示すようデータを更新し状態#5にする。

f:id:techmedia-think:20170306174242p:plain

続いて、現在状態#3がチェーンにコミットされている状態で、状態#3で作られたtxoutを使用するトランザクションを作りたい場合、状態#3のデータからなるTXO proofを提供する必要がある。出力gとhのリーフノードとその内部ノードが状態#3の一部であるため、それらを剪定する↓

f:id:techmedia-think:20170307103616p:plain

最後にa、c、gの3つのtxoutを使って、新しい3つのtxout i、j、kを作る。状態#3が最新のコミットされた状態だったので、aとgを使用するトランザクションはそこまでのマークルパスを提供する必要がある。これには状態#2のデータの一部が含まれる↓

f:id:techmedia-think:20170307105530p:plain

unpruningすると、状態#5のデータは以下のようになる。

f:id:techmedia-think:20170307110846p:plain

a、c、gの3つの出力を使用し、新しい出力i、j、kを追加した結果以下のの状態#6になる。

f:id:techmedia-think:20170307111424p:plain

再度、状態#4の関連データを剪定することができる。さらにSTXOセットの実装方法によっては、この状態以降の使用済みtxtoutに関連するデータも剪定することができる(配下のノードが使用済みとなった内部ノードを含む)。

まとめ&所感

  • 様々な要因によってBitcoinのUTXOのサイズは増加する一方で、フルノードを運用する際のボトルネックにもなっている。
  • 現状の実装ではUTXOセット1つのみの構成で、そこに全UTXOのデータが保持されているけど、アクセス頻度の低いUTXOをアーカイブして、高速にアクセスする必要のあるUTXOのサイズを削減しようというアプローチ。
  • アーカイブされたUTXOは、それがまだ未使用である証拠=TXO commitment proofsを提供することでUTXOとしての正当性を評価して使えるようにする。
  • TXO commitment では、全てのトランザクション出力(未使用・使用済み両方含む)からなるマークルツリー = Merkle Mountain Range (MMR)を構成する。
  • MMRは決定性があるインデックス可能な挿入型のマークルツリー。
  • 未使用・使用済み両方含んでるからUTXOじゃなくTXO Commitmentsなんだろう。
  • トランザクションでは、そのトランザクションで使用しようとしているUTXOが未使用であることをTXO commitment proofsを添付することで証明する。
  • TXO commitment proofsの提供が必要になるのはアーカイブ済みのtxoutsを使用するトランザクションの場合のみ。
  • アーカイブ済みのtxoutを使用するトランザクションは、最新のTXO commitmentへのマークルパスを提供する。
  • UTXOの削減以外の用途でもMMR使ったコミットメントの仕組み利用できるんじゃないかなー。

BIPについて定義したBIP-002

BIPについて定義していたBIP-001が、2016年11月にBIP-002にリプレースされてたので見とく↓

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

概要

BIPはBitcoin Improvement Proposalの略で、Bitcoinコミュニティに情報提供したり、Bitcoinの新しい機能やプロセス、環境について記述した設計ドキュメント。BIPは機能の技術仕様やその機能の根拠について完結に記述する必要がある。

BIPは新しい機能の提案や、コミュニティにおける課題の収集、Bitcoinに取り入れられる設計上の意思決定を文書化するための主要な仕組みであると考えている。BIPの著者にはコミュニティ内でコンセンサスをとり、反対意見を文書化する責任がある。

BIPはバージョン管理されたリポジトリでテキストファイルとして管理されているので、そのリビジョン履歴は機能提案の履歴になる。

BIPのワークフロー

BIPのプロセスはBitcoinの新しいアイディアから始まる。BIP候補には、後述するスタイルとフォーマットを使用してBIPを書き、適切なフォーラムでディスカッションを開催し、アイディアに関するコンセンサスを構築する著者が必要となる。BIPの著者は、そのアイディアがBIP化可能かまず確かめる必要がある。ちょっとした拡張やパッチは、多くの場合複数のプロジェクト間で標準化を行う必要は無い。そういったものにBIPは必要なく適切なイシュートラッカーにパッチを送ると共に、関連するプロジェクトの開発ワークフローに従えばいい。またさまざまな理由でリジェクトされたBitcoinの変更に関するアイディアが今まで多数提出されているので、BIPを作成する際は、まず最初に過去のディスカッションを検索し、アイディアが以前に検討されていないかどうかを確認し、もしあればどのような問題がその時生じたか調べること。過去のディスカッションを調査した後、新しいアイディアを提案する良い方法は、Bitcoinの開発メーリングリストにポストすることである。

BIPを書く前にアイディアを公開するこで、著者とコミュニティの時間の節約になる。オリジナルのアイディアが以前の議論に基づいて明らかにリジェクトされる場合、Bitcoinコミュニティに最初に相談することで無駄な時間を使うことがなくなる。また、アイディアが著者だけでなくコミュニティ全体にとってメリットになるか確認することにもなる。アイディが著者にとってだけ有益なものでBitcoinを使っているほとんどの人々にとっては有益ではないかもしれない。

著者がBitcoinコミュニティにアイディが受け入れられるかどうかを尋ねた後は、bitcoin-devのメーリングリストにBIPのドラフトを提出する必要がある。これは著者にとって、BIPのドラフトを適切なフォーマットで、高品質にし、提案に関する懸念事項に対処するためBIPのドラフトを完成させるチャンスになる。ディスカッションの後、プロポーザルはBIPのgitリポジトリにプルリクエストで提出する。このドラフトは後述するBIPスタイルで書かなければならない。編集者によりBIPに番号が割り当てられるまでは、”bip-johndoe-infinitebitcoins”のように著者の名前/ニックネームと件名を含むエイリアスを使う必要がある。

著者はレビューのためにBIPを提出する前に、初期のアイディアとBIPについてコミュニティからのフィードバックを集める責任がある。しかし可能であれば公開メーリングリストで長い議論は避けるべきである。ディスカッションを効率的にするためには、トピックのための個別のメーリングリストをセットアップし、著者に初期の設計段階のプライベートコメントを受けさせたり、wikiページやgitリポジトリのセットアップなどをするのが効果的である。どれを利用するかはBIPの著者が決めればいい。

1つのBIPに含めるのは、1つのプロポーザルにすることを推奨する。BIPの焦点を絞るほど採用される可能性があがる。必要であればBIPを複数に分割すると良い。

BIPのドラフトが完成すると、編集者はBIPに番号を割り当て、Standards TrackかInformationalかProcessかのラベルを付け、gitリポジトリにプルリクエストをマージする。編集者はBIPを不当に拒否することはない。BIPのステータスを拒否する理由としては、何度も努力したけど、フォーマットルールの無視や、あまりにも範囲が広いケース、技術的に不適切、適切な動機ではない、下位互換性に対処しない、Bitcoinの考え方に従わない、などが挙げられる。BIPが受け入れられるためには、一定の最小基準を満たさなければならない。提案の拡張内容について明確で完全に記述されている必要がある。適用された場合、その提案の実装は堅牢でプロトコルを過度に複雑にしてはならない。

BIPの著者は必要に応じてgitリポジトリ内のドラフトを更新することができる。ドラフトの更新は著者からのプルリクエストで提出される。

BIPの所有権の移転

BIPの所有権を新しい著者に移す必要がある場合がある。一般的に、移転する場合、元の著者を共同著者として残したいと思うが、それはオリジナルの著者次第である。所有権を移転する正当な理由には、元の著者がBIPを更新したりフォローしたりするのに時間や関心を持たなくなったり、(メールが届かなくなったりレスが無いなど)ネット上でコミュニケーションが取れないといった場合である。所有権を移転する悪い理由は、BIPの方向性に同意しない場合で、BIPについてコンセンサスを構築しようとするが、不可能な場合にはいつでも競合するBIPを提出することができる。

BIPの所有権を引き継ぐのに興味があれば、元の著者とBIPの編集者の両方に引き継ぐ意思があることを示すメッセージを送信する。元の著者がタイムリーに返信しない場合、BIPの編集者が一方的な決定を下す(このような決定が行われると元には戻らない)。

BIPの編集者

現在のBIPの編集者はLuke Dashjrで、luke_bipeditor@dashjr.orgでコンタクト可能。

BIPの編集者の責任とワークフロー

BIPの編集者はBitcoinの開発メーリングリストを購読している。メーリングリスト外でのBIP関連の対応はluke_bipeditor@dashjr.org宛(もしくはCc)に送る必要がある。

新しいBIPが届くと編集者は以下のチェックを行う

  • BIPを読み、準備が整っているか(アイディアは受け入れられそうになくても、技術的意味を持っているか)
  • タイトルが内容を正確に記述しているか
  • BIPのドラフトがBitcoinの開発メーリングリストに投稿されているか
  • 動機と後方互換性について言及されているか
  • 定義されたLayerヘッダが仕様通りに正しく割り当てられているか
  • BIPとして受け入れ可能なライセンス条項になっているか

BIPの準備ができていない場合は、編集者が特定の手順で著者に送り返す。

BIPの準備ができたら、BIPのgitリポジトリにプルリクエストを送り、さらなるフィードバックを得る。

そうすると編集者は、

  • プルリクエストに対してBIPの番号を割り当てる
  • プルリクエストをマージする
  • README.mediawikiのリストに加える

BIPの編集者は、管理上及び編集上の責務を果たすこと。また、BIPの編集者はBIPの変更を監視し、必要に応じてBIPのヘッダを更新する。

BIPのフォーマットと構造

仕様

BIPはmediawiki形式で記述する。

各BIPには以下のパートが必要

  • Preamble
    BIPに関するメタデータを含むヘッダ(後述)
  • Abstract
    このBIPで対処する技術的な問題についての記述(200ワードくらい)。
  • Copyright
    許容される著作権下(後述)の明示的な使用許諾
  • Specification
    技術仕様で、新機能の構文と意味について記述する。仕様は現行のBitcoinの各プラットフォームで相互運用可能な実装について詳細に記述する必要がある。
  • Motivation
    動機はBitcoinプロトコルを変更したいBIPにとって重要なもの。既存のBitcoinプロトコルがBIPが解決しようとしている問題に対して不十分である理由を明確に記述すること。充分な動機のないBIPの提出は拒否される場合がある。
  • Rationale
    論理的根拠は、設計上の動機となぜそのような設計上の決定をしたのかを記述することで、仕様を完成させる。ここには考慮した別の設計や関連する作業についても(機能が他の言語でどのようにサポートされているかなど)記述する必要がある。論理的根拠では、ディスカッションの中で提起された重要な異論や議論についてコミュニティ内でのコンセンサスの証拠を提供する必要がある。
  • Backwards Compatibility
    下位非互換性を導入する全てのBIPは、非互換性とその重要度の記述が含まれるセクションが必要。BIPでは著者が非互換性をどのように扱うべきか説明しなければならない。下位互換性について充分な説明のないBIPは完全にリジェクトされる可能性がある。
  • Reference Implementation
    BIPのステータスが"Final"になる前に参照実装は完成させる必要があるが、BIPが受け入れられる前に完成させる必要は無い。コードを書く前にSpecificationとRationaleを最初に完成させ合意する方が良い。最終的な実装には、Bitcoinプロトコルに適したテストコードとドキュメントが含まれていなければならない。
BIP Header Preamble

各BIPはRFC 822 スタイルのヘッダPreambleで始まる。ヘッダは以下の順序で記述する。* の付いたヘッダはオプションで、付いてない項目は必須。

  BIP: <BIP number, or "?" before being assigned>
* Layer: <Consensus (soft fork) | Consensus (hard fork) | Peer Services | API/RPC | Applications>
  Title: <BIP title; maximum 44 characters>
  Author: <list of authors' real names and email addrs>
* Discussions-To: <email address>
* Comments-Summary: <summary tone>
  Comments-URI: <links to wiki page for comments>
  Status: <Draft | Active | Proposed | Deferred | Rejected |
           Withdrawn | Final | Replaced | Obsolete>
  Type: <Standards Track | Informational | Process>
  Created: <date created on, in ISO 8601 (yyyy-mm-dd) format>
  License: <abbreviation for approved license(s)>
* License-Code: <abbreviation for code under different approved license(s)>
* Post-History: <dates of postings to bitcoin mailing list, or link to thread in mailing list archive>
* Requires: <BIP number(s)>
* Replaces: <BIP number>
* Superseded-By: <BIP number>

(Standards Track BIPのみに含まれる)Layerヘッダは、このBIPがBitcoinのどのレイヤーに適用されるか文書化したもの。各BIPレイヤーについてはBIP-123を参照。

AuthorヘッダにはBIPの全ての著者/所有者の名前とメールアドレスが記載されている。Authorヘッダは以下の形式で書く。

  Random J. User <address@dom.ain>

著者が複数いる場合は、RFC 2822の継続行の規則に従って別々の行に記述すること。

通常初期のドラフトフェーズではBIPはプライベートで議論が行われているため、Discussions-ToヘッダにはそのBIPが議論されているメーリングリストもしくはURLを記述する。BIPが著者とプライベートで議論されている場合やBitcoinメーリングリスト上で行われている場合はDiscussions-Toヘッダは不要。

TypeヘッダはBIPの種類を指定する(Standards Track、Informational、Processのいずれか)。

CreatedヘッダにはBIPに番号が割り当てられた日付が記録され、Post-HistoryヘッダにはBIPの新しいバージョンがBitcoinメーリングリストに投稿された日付を記録する。2つともyyyy-mm-ddの形式で記述すること。Post-Historにはメーリングリストアーカイブ内の特定のスレッドのリンクを設定することが許可されている。

BIPにはこのBIPが依存するBIPの番号を示すRequiresヘッダもある。

BIPには後のドキュメントによってBIPが廃止されたことを示すSuperseded-Byヘッダを記述することも可能。設定する値は現在のドキュメントを置換するBIPの番号。置換した側のBIPには、廃止したBIPの番号をReplacesヘッダに記載する必要がある。

補助ファイル

BIPには図などの補助ファイルが含まれれる場合がある。補助ファイルはそのBIPのサブディレクトリに含めるか、BIP-XXXX-Y.extというファイル名にすること(XXXXはBIP番号で、Yは1から始まるシリアル番号、extは実際の拡張子)。

BIPの種類

BIPは以下の3種類に分けられる

  • Standards Track BIPは、ネットワークプロトコルの変更やブロック、トランザクションの検証ルールの変更、Bitcoinを使用するアプリケーションの相互運用性影響する追加・変更など、Bitcoinの実装のほとんどもしくは全てに影響を与える記述をする。Standards Track BIPは設計ドキュメントと参照実装の2つのパートで構成される。
  • Informational BIPは、Bitcoinの設計上の問題や、一般的なガイドラインや情報をBitcoinコミュニティに提供するために記述されるもので、新しい機能を提供するものではない。Informational BIPは、必ずしもBitcoinコミュニティのコンセンサスや勧告を示すものではないので、Informational BIPに従うか、無視するかはユーザー及び実装者の自由である。
  • Process BIPは、Bitconをとりまくプロセスや、プロセスの変更について記述する。Process BIPはStandards Track BIPに似ているが、Bitcoinプロトコル以外の領域も対象とする。Bitcoinのコードベースではなく実装を提案するかもしれない。これらはコミュニティのコンセンサスが必要で、Informational BIPと異なり、ユーザーは通常この提案を無視することはできない。例としては、手順やガイドライン、意思決定プロセスの変更やBitcoinの開発に使われるツールや環境の変更などが含まれる。どのmeta-BIPもProcess BIPとみなされる。

BIPのステータスフィールド

仕様

BIPのステータスの典型的なパスは以下の通り

https://github.com/bitcoin/bips/raw/master/bip-0002/process.png

BIPの著者は、DraftとDeferred、Withdrawn間のステータスの変更は自身で決定することができる。BIPの進捗がない場合、編集者はステータスをDeferredに変更することもできる。

著者が作業を完了し、有効な実装があり、Finalステータスに進めるためのコミュニティ計画があるのみ、ステータスをDraft(もしくはRejected)からProposedに変更される。

BIPに3年間なんの進捗もない場合、誰かから要求があれば、DraftもしくはProposedからRejectedにステータスが変更される。Rejectedに変更されたBIPは、著者が提案の批判に対して意味のある修正を加えた場合はDraftに、また前の段落で説明したように要求された基準を満たす修正である場合はProposedにステータスを変更することができる。

ProposedステータスのBIPは、実世界で特定の基準を満たす採用の動きがあった場合のみFinalに進む。これは提案された変更の性質に応じてBIP毎に異なる(後述)。このステータス変更の評価は、客観的に検証可能で、また開発メーリングリストで議論されるべきである。

FinalのBIPが適切でない場合、そのステータスはReplacedもしくはObsoleteに変更されることがある。この変更は客観的に検証可能で、また議論される必要がある。

Process BIPはメーリングリストで大まかな合意に達したらDraftからActiveにステータスが変わる。大まかな合意というのは、開発メーリングリストでディスカッションを開始して、最低でも1ヶ月、誰も新たに対応が必要な異論が無い場合を言う。充分な対処がされているにも関らず異議があがるような状況では、反対する明確な理由がなければならない。

Finalステータスへの移行

ソフトフォークが必要なBIPは(BIP-9を利用した)ブロックチェーン上の投票によりマイナーの大部分の賛成(95%)が必要になる。さらに(PoWのアルゴリズムを変更するような)ハードフォークを行う意思があると思われる場合には、そのようなハードフォークが多数によってサポートされる限りソフトフォークはFinalにならない。ソフトフォークのBIPはその採用のため追加の要件を設定することがある。マイニングプールなどで行われる代理投票などによって、マイナーのパワーバランスが変わる可能性を考慮すると、BIPの適用には95%という大多数の投票を必要とすることを強く推奨する。(低い閾値でも大丈夫な理論的な根拠がない限り)

ハードフォークが必要なBIPは、Bitcoinエコノミー全体からの合意を必要とする。特にBitcoinの支払いと引き換えに商品やサービスを販売する事業者やコインの所有者など。合意は実際にハードフォークしたバージョンを使用するということである。この経済的な合意は、圧倒的多数を取ればいいという訳ではなく、少数派にもハードフォークの受け入れを強制させない限り(そういったことが可能かどうかはこのドキュメントの範囲外)実現できない。

Peer services BIPは、1ヶ月間少なくとも公開ノードの1%が採用するか監視する必要がある。

API/RPC及びapplicationレイヤーのBIPは、少なくとも2つ以上の独立した互換性のあるソフトウェアアプリケーションで実装される必要がある。

ソフトウェアの作成者は、ステータス変更の検証を支援するため、ソフトウェアがサポートするBIPの概要を公開することが推奨される。良いサンプルはBitcoin Coreのdoc/bips.mdファイルや、Bitcoin Wallet for Androidのwallet/README.specsである。

これらの基準はBIPの事実上の採用を観察する客観的な方法として、BIPをリジェクトする理由として使用されることはない。ここに記載した基準を満たしていないにも関らず、BIPが明確に採用された場合ははFinalステータスに更新される。

論理的根拠

このBIPは何故必要なのか?

  • BIP-1ではBIPのステータスフィールドの基準の定義が曖昧で、よく混乱の原因となった。その結果、実用性のある多くのBIPが長い間DraftやProposedステータスのまま残っている。この提案ではBIPの進行を判断する適切な基準を与えることで、ステータスを正確かつ最新に保つことを目的としている。

Bitcoinのエコノミー全体というのは、モノやサービスを販売する事業者やBitcoinの所有者によって定義されている?

  • Bitcoinが通貨として機能するためには、決済方法として受け入れられる必要がある。Bitcoinと引き換えに何かを手に得ることができないのであれば、そこに価値は無い。そのような決済を受け入れる全員が特定のコンセンサスルールを必要とする場合、"bitcoins"は事実上そのルールによって定義されている。このコンセンサスルールが(ハードフォークのように)拡大すると予想される場合、新しいルールセットの元ので行われた決済を受け入れる必要がある。Bitcoin保有者は、彼らがBitcoinを使った決済ができる事業者を選択するという点で関係がある。

xがエコノミーに含まれないのはなぜ?

  • Bitcoinと引き換えに商品やサービスを提供するエンティティもあり、それらはエコノミーに関与している。
  • マイナーはエコノミーには含まれない。なぜなら彼らは、他の人にとっては価値の無いマイニング成果物を売ったり買ったりしてるだけだから。従って合意のルールを決定する際に、他の人の指示を受け入れる必要がある。
  • 交換所は単にコインを売りたい人と買いたい人をマッチングするサービスを提供してるだけなので、エコノミーには含まれない。例え全ての交換所がBitcoinから撤退しても、ユーザー同士はいつでも直接取引したり独自取引を確立できる。
  • 開発者はコードを書くだけなのでエコノミーには含まれない。そのコードを使うかどうかは他の人にまかせている。

しかし、彼らは何か重要なことをしていて、Bitcoinに多くの投資をしている。重要な決定に彼らを含めるべきでは?

  • このBIPは何を意思決定のベースにすべきかについて書かれたものではない。そのような声明はどんなに完璧に正当化されていても、他者にそれに従うよう強制させる方法が無ければ無駄になるだろう。BIPのプロセスはBitcoinの強力なガバナンスを目指すものではなく、人々が自発的に採用するかどうかについて情報を提供するための共同のリポジトリを提供するだけのもの。”彼らはどのようにすべき”ではなく、”ものごとが実際どのような”状況にあるのか正確に把握することのみを目的としている。

単一の事業者のみがハードフォークしたい場合はどうなる?

  • このBIPはハードフォーク自体のデプロイについてではなく、BIPのステータスフィールドの進捗にのみ対応する。
  • いずれにしても1店舗だけ孤立してオペレーションすることはできない。本当に1事業者のみであれば、彼らの店舗でBitcoinの決済が行われることはなくなるだろう。誰もそんなチェーンでの決済をしたいとは思わない。そしてBitcoinでの決済がなくなるということは、ここでいうエコノミーでも無くなるということになる。

お互いに商品を販売しあう少数の(多分2人)の売り手ではどう?

  • このシナリオでは、以前のBitcoinは動作していてハードフォークは失敗しているようだ。そのような分割を解決する方法は本BIPの対象外である。

経済的な合意はどうやってソフトフォークを拒否できる?

  • マイナーのグループを動的メンバーシップマルチパーティ署名(BitcoinではPoW)のコンセンサスルールによって決めるよう、ハードフォークすることができる。このグループを変更するのに必要な同じ条件がソフトフォークとは反対に満たされる場合、エコノミーがソフトフォークをサポートしないマイナーグループを選択すると、ソフトフォークをサポートしている大部分のマイナーは無効になる。

エコノミーが議論の余地のあるソフトフォークから3ヶ月後にハードフォークをすることを決めたらどうなる?

  • この状況では、議論の余地のあるソフトフォークのステータスは、決まったソフトフォークを置き換えるハードフォークの性質から、FinalからReplacedに変わる。

ピアサービスのプロポーザルを採択するのに必要な理想的なリスニングノードの割合は?

  • これについては未知で、現時点ではむしろ任意に設定される。ピアをランダムに選択し、その拡張を持つ他のピア1つ以上とつながるには13%以上必要だが、ノードはそのようなピアをスキャンし続けることができる。さらにそういったピアを識別するためのservice bitが存在する。

API/RPCとapplicationレイヤーのBIPの実装がすくなくとも2つのプロジェクトでリリースする必要があるのはなぜ?

  • 仕様の実装が1つしかない場合、標準インターフェースを必要としないから。
  • 2つだけのプロジェクトでも、それらの間で標準に関する調整が必要になる。

1つの特定のプロジェクトのみを対象にしたBIPが提案された場合どうなる?

  • BIPのプロセスは独立したプロジェクト間の標準化のために存在する。何かが1つのプロジェクトにしか影響を与えないのであれば、そのプロジェクトの内部プロセスで実行し、BIPとして提案することはできない。

BIPのコメント

仕様

各BIPはそのpreambleに、コメントの要約を含む公開Wikiページのリンクを記載する必要があり、自身がそのBIPの審査員だと思う人はそのWikiページに自分のコメントを投稿する必要がある。コメントページは通常完了したBIPの最終コメントを投稿するためのみに使う。BIPがまだ完成してない場合、レビューアはBIPの著者がレビューによって指摘された問題に対処できるよう、適切な開発メーリングリストのスレッドにポストすべき。

一部のBIPは完了する前に開発コミュニティ外に公開され、他のBIPは全く完了していない場合がある。期間中に批判的なBIPのレビューが気付かれないといったことがないよう、レビューアはコメントページにレビューを投稿してもいいし、最初にメーリングリストにポストし、完成したバージョンを元に後で必要に応じて削除・修正してもいい。このような改訂の場合、前回のレビューを編集しタイムスタンプを更新する必要がある。完成版より前に行われたレビューは、タイムリー(1ヶ月以内)に更新されない場合、削除される可能性がある。

ページはネームスペースとして"Comments"の後に(”BIP 0001”のような)完全なBIP番号を付与した名前にする必要がある。例えばBIP 1ののコメントページのリンクは以下のようになる。

https://github.com/bitcoin/bips/wiki/Comments:BIP-0001

このWikiにコメントを投稿する際は以下のフォーマットで投稿すること。

    <Your opinion> --<Your name>, <Date of posting, as YYYY-MM-DD>

BIPはWikiに加えてコメントのための第二のフォーラムを設置することも可能で、その場合第二のフォーラムのURIはpreambleのプライマリWikiURIの下に掲載される。

しばらくすると、BIPの自体のコメントのサマリの傾向が更新されることがある。サマリの傾向は以下から選択できるが、これでBIPの全てのニュアンスをカバーするつもりはなく、必要に応じて他のサマリを使用することもできる。

  • No comments yet.
  • Unanimously Recommended for implementation
  • Unanimously Discourage for implementation
  • Mostly Recommended for implementation, with some Discouragement
  • Mostly Discouraged for implementation, with some Recommendation

例えばBIP 1のpreambleは以下の行を含むよう更新される。

    Comments-Summary: No comments yet.
    Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0001
                                https://some-other-wiki.org/BIP_1_Comments

これらのフィールドは、BIP-1で定義されているDiscussions-Toヘッダに従う必要がある。(Discussions-Toヘッダが存在しない場合は本来存在する位置に配置する。一般的にStatusヘッダの上)

疑義が生じないよう補足しておくと、BIPを判断するためにコメントとステータスには何の関連もなく、どちらも一方に直接影響を与えてはいけない。

論理的根拠

BIPのコメントの目的は?

  • 一般的に不適切と思われながらも様々なBIPが採用されている。現在BIPは単純にBIP番号が割り当てられているだけで”良いアイディア”とみなされている。新しいBIPを提出するエントリーの敷居が低いため、レビューアの意見を表現する方法がある方が望ましいため。

BIPにコメントをするのは特定の参加者/専門家に制限されてる?

  • 参加者は自分の知識の専門外であっても自由にコメントすべき。コメントは検閲されるべきではないし、参加は一般に公開されるべき。

BIPのライセンス

仕様

新しいBIPは後述するライセンスで受け入れられる。新しい各BIPはpreambleですくなくとも1つ許容するライセンスを分かるようにすること。preambleのLicenseヘッダはCreatedヘッダの後に配置する。各ライセンスは次のいずれかの略語で参照される。

例えばpreambleに以下のライセンスが含まれる場合

    License: BSD-2-Clause
                   GNU-All-Permissive

この場合、BIPのテキストはOSI-approved BSD 2-clauseライセンスとGNU All-Permissiveライセンスの両方のライセンスの下に認可されており、いずれかのライセンス条件に従う限り、誰でもテキストを修正し再配布することができる。言い換えると、リストアップされているライセンスには、AND条件ではなくOR条件が適用される。

BIPのライセンスとは異なるソースコードのライセンスを交付することも可能である。オプションのLicense-CodeヘッダはLicenseヘッダの後に配置する。ここでも後述する各ライセンスの略語を記載する。

例えばオプションのLicense-Codeヘッダを指定するpreambleは以下のようになる。

    License: BSD-2-Clause
                   GNU-All-Permissive
    License-Code: GPL-2.0+

この場合、BIPのコードはBSDもしくはAll-Permissiveライセンスではなく、 GNU General Public License (GPL)のバージョン2以降のライセンスでのみ利用可能になる。コードをバージョン2のみで利用可能とする場合は+のシンボルをライセンスから削除する。以降のバージョン(GPL 3.0など)については、バージョンをインクリメントする(必要に応じて+を付与・削除する)。

    License-Code: GPL-2.0    # This refers to GPL v2.0 *only*, no later license versions are acceptable.
    License-Code: GPL-2.0+  # This refers to GPL v2.0 *or later*.
    License-Code: GPL-3.0    # This refers to GPL v3.0 *only*, no later license versions are acceptable.
    License-Code: GPL-3.0+  # This refers to GPL v3.0 *or later*.

テキストやコードのライセンスが複雑過ぎて簡単に表現できない場合、リストアップせず"Complex"という単一の用語に置き換える必要がある。全ての場合において、ライセンス条項の詳細はBIPのCopyrightのセクションに記載されなければならない。

BIPは承認された条件のもとで独占的に認可される必要はなく、少なくとも1つの許容ライセンスに加えて容認できないライセンスの下で認可される場合もある。この場合、受け入れ可能なライセンスのみがLicense及びLicense-Codeヘッダに記載される。

推奨ライセンス

さらにBIP上に含まれるリテラルコードは、プロジェクトと同じライセンス条項とする二重ライセンスとすることを推奨する。例えば、Bitcoin Coreを対象にしたリテラルコードは、BIPのテキストのライセンスと同様、MIT ライセンス条項の2重ライセンスを取得することになる。

非推奨だが受け入れ可能なライセンス
受け入れ不可能なライセンス

上記のライセンスリストに含まれていないライセンスはBitcoin Improvement Proposal の条件として受け入れられない(後にこのBIPが更新されライセンスが追加された場合は別)。ただし、このBIPの承認以前のBIPは他の条件で許可されており、他のライセンスが明記されていない場合、以下の略語を使用する必要がある。

論理的根拠

BIP 1ではOpen Publication Licenseもしくは public domainを許可していたがこれでは不十分だったのか?

  • OPLは廃止されたものであり、新しい出版物に適したライセンスではない
  • 多くの人はOPLの用語に馴染みがなく、そういった不確実な条件よりpublic domainを好むかもしれない
  • OPLのライセンス条項により著者は出版や派生物の作成を防止できるが、これはBitcoinの標準としては不適切だと考えられてきた。
  • public domainは法的なアクションが普遍的に認められていないため、推奨できない。

ソフトウェアライセンスが含まれているのはなぜ?

  • 一部のBIP(特にコンセンサスレイヤー)は、BIP内にリテラルコードが含まれていることがあり、それはBIPの正確なライセンス条件では利用できない場合があるため。
  • ただ、BIP内の全てのコンテンツに対してソフトウェアライセンスが適用される訳ではない

新しいBIPでPublic Domainが受け入れられないのはなぜ?

  • いくつかの国では、Public Domainは合法的な法的措置であると認識されず、BIPは著作権で保護され、改変や再配布が一切許可されないため。

BIP 1からの変更点

  • BIPとして受け入れ可能なライセンスについて再選択し、多種多様なオープンライセンスを許容すると共に、問題のある古いライセンスを禁止
  • AcceptedステータスをProposedにリネーム
  • ステータスがProposedに進む前に、実装が必要
  • BIPのコメントを新しく導入
  • ライセンスのpreambleヘッダを追加
  • BIP-123からLayerヘッダを追加
  • bip-XXXXサブフォルダに画像ではない補助ファイルを配置可能に
  • 著者のメールアドレスが必須に
  • Post-Historyヘッダは単純な日付に代わって、リンクが記載される場合がある
  • BIPを書くフォーマットとしてmarkdownは不許可に
  • Resolutionヘッダは、最終決定を下す権限がない分散システムには不適切なので削除

所感

  • BIP-001ではProposed(旧Accepted)からFinalになる基準が曖昧だったので、変更の性質に合わせて客観的に移行可能かどうか判断する基準が設けられたのね。
  • 選択できるライセンスがガッツリ変わってる。
  • BIPのリポジトリWikiページみると、数はそんなにないけどたしかにコメントページが作られてる。
  • このBIPとは直接関係ないけど、ピアサービスに関する拡張の場合その拡張をサポートするピアを識別するためのservice bitが存在するのね。2ndレイヤーのピア作る場合とかに仕組みが参考になるかも。
  • BIPはBitcoinのガバナンスを定義したものではなく、自発的な提案とコミュニケーションを促す場であるって認識大事。