BitcoinのP2Pネットワークにおいて、トランザクションパッケージのリレーをサポートするBIP-331が先日登録された↓
https://github.com/bitcoin/bips/blob/master/bip-0331.mediawiki
トランザクションリレーとmempool
現状のBitcoinのトランザクションリレーは、基本的に1つのトランザクションをリレーするようになっている。
新しいトランザクションを受信したノードは、そのトランザクションを自身のmempoolに追加するかどうかを判断する。mempooolのサイズには上限があり、無限にデータを格納することはできないので、mempoolがいっぱいになった場合は、手数料率の低いトランザクションがmempoolから削除されようになっている。
手数料不足でmempoolから排除されたトランザクションについては、そのタイミングのmempoolの手数料率を考慮して高い手数料率を持つトランザクションを新たに作成して送信することになる。このようなトランザクションが自分1人が関連するトランザクションであれば特に問題はないけど、2人以上が参加するマルチパーティコントラクトにおいては、問題が生じる。
ライトニングネットワークのチャネル更新時には2人の参加者が協力してトランザクションを作成し、そのトランザクションをオフチェーンで管理し、チャネルを一方的に閉じる必要がある場合、そのトランザクションをネットワークにブロードキャストする。このような場合、トランザクションを作成した際のネットワークの手数料率とトランザクションをブロードキャストする際の手数料率は異なる可能性がある。
比較的手数料率が低いタイミングでトランザクションを作成し、その後手数料率が上がると、作成したトランザクションはブロードキャスト時には手数料率が低すぎてネットワークの各ノードのmempoolに入らないレベルになってしまう可能性がある。相手がオンラインで協力できる状態であれば協調クローズのために新たな手数料を設定することも可能だけど、1人ではトランザクションの手数料を引き上げる術がなくなってしまう。CPFPにより子トランザクションで手数料を引き上げようとしても、親子の各トランザクションはそれぞれ別々にブロードキャストされ、個別にmempoolに対して評価されるため、その親トランザクションがmempoolに受け入れもらえないような手数料率では手数料が引き上げられないといった状況が発生する。
また、BIP-133のfeefilter
メッセージを使用するとピアに指定した手数料率以下のトランザクションを通知しないよう要求することができるが、親トランザクションがこの設定値を下回り、子トランザクションは高手数料率である場合も同様の状況が発生する。
このような状況を回避するためには、トランザクションをパッケージ(親子トランザクションのセット)として扱えるようにし、個別のトランザクションがmempoolの手数料要件を満たさない場合に、パッケージ対応したmempoolポリシーで評価できるようにする必要がある。
そのために必要なトランザクションパッケージの要求やリレーを可能にするP2Pメッセージを新たに追加しようという提案がBIP-331の内容。
Ancestor Package Relay
↑トランザクションパッケージをリレーする仕組みがAncestor Package Relay。親子のセットだけでなく、さらにその親、その親の親と祖先を最大100個を1つのパッケージで送信できる。
このパッケージリレーをサポートするために以下の4つ新しいP2Pメッセージが追加される。
sendpackages
sendpackages
は、パッケージリレーをサポートしていることを相手に通知するためのメッセージで、ピアとの接続を開始する際のハンドシェイク中(version
メッセージとverack
メッセージの間)に送信される。
パッケージリレーを有効にする場合、両方のピアがwtxidベースのリレーをサポートしている必要がある(BIP-339)。
ペイロード
sendpackages
メッセージのペイロードは↓
フィールド | 型 | サイズ | 目的 |
---|---|---|---|
versions | uint64_t | 4バイト | 送信者がサポートするパッケージのバージョン |
versions
は、64 bitの値で、サポートするパッケージの機能を各bit値で指定するようになっている。現在定義されているのは以下の2つのbit↓
- PKG_RELAY_PKGTXNS(1 << 0):パッケージリレーのデータのダウンロードラウンドをサポートするシグナル
- PKG_RELAY_ANC(1 << 1):パッケージリレーのパッケージ情報ラウンドをサポートするシグナル
↑2つのシグナルがあるが、パッケージリレーでは、パッケージの情報を通知する(ancpkginfo
)パッケージ情報ラウンドと、その後実際に不足しているパッケージ内のトランザクションをダウンロードするラウンド(getpkgtxns
、pkgtxns
)で構成される。
パッケージリレーのメッセージフローの例が↓。例では、feefilter
メッセージで3 sat/B
未満のTxをリレーしない設定で、A, Cを受け取っている状態で、親Bをパッケージリレーしている。
ancpkginfo
ノードが未承認の親トランザクションを持つ子トランザクションを受信し、その親トランザクションを知らない場合、その子トランザクションのwtxidと識別子MSG_ANCPKGINFO
を指定してピアにgetdata
メッセージを送信することで、未承認パッケージの情報を要求することができる(パッケージ情報ラウンド)。この要求を受け取ったピアがパッケージ情報を送信するのに使用するのがancpkginfo
メッセージ。要求されたwtxidの祖先パッケージがない場合は、notfound
メッセージを返す。
MSG_ANCPKGINFO
のgetdata
メッセージを受信したピアは、その祖先トランザクションのwtxid
のリストをancpkginfo
メッセージで送信する。
※ これに伴いgetdata
のinv
タイプにMSG_ANCPKGINFO
タイプが追加されている。
ペイロード
ancpkginfo
メッセージのペイロードは↓
フィールド | 型 | サイズ | 目的 |
---|---|---|---|
txns_length | CompactSize | 1 or 3バイト | パッケージ内のトランザクション数 |
txns | wtxidのリスト | txns_length*32バイト | パッケージ内の各トランザクションのwtxidのリスト |
この時、txnsの最後のwtxidはgetdata
で指定された子トランザクションのwtxidで、リストのそれより前はその祖先のトランザクションのwtxidになる(祖先のwtxidのリストはトポロジー的にソートされている=祖先順にソートされることが推奨される)。
getpkgtxns
ancpkginfo
メッセージで未承認の祖先パッケージをのwtxidのリストを受信したピアは、getpkgtxns
メッセージを使って、自分が持っていない祖先のトランザクションを要求する(トランザクションデータのダウンロードラウンド)。
ペイロード
getpkgtxns
メッセージのペイロードは↓
フィールド | 型 | サイズ | 目的 |
---|---|---|---|
txns_length | CompactSize | 1 or 3バイト | 要求するパッケージ内のトランザクション数 |
txns | wtxidのリスト | txns_length*32バイト | 要求するパッケージ内の各トランザクションのwtxidのリスト |
txns_length
の最大数は100個。
pkgtxns
getpkgtxns
メッセージを受信したピアは、指定されたトランザクションのリストをpkgtxns
メッセージを使って送信する。既存のtx
メッセージと比べて、不足しているトランザクションを一括で送信できる。
要求されたトランザクションについて、1つでも自分が持っていないものがある場合は、getpkgtxns
のwtxidのリストをSHA-256ハッシュした値と識別子MSG_PKGTXNS
を使ってnotfound
メッセージを返す(これに伴いnotfound
のinv
タイプにMSG_PKGTXNS
タイプが追加されている)。
ペイロード
pkgtxns
メッセージのペイロードは↓
フィールド | 型 | サイズ | 目的 |
---|---|---|---|
txns_length | CompactSize | 1 or 3バイト | 送信するトランザクション数 |
txns | トランザクションのリスト | 可変長 | トランザクションのリスト |
現状の使用例と今後の拡張
↑の現状の使用例は、子トランザクションを受信したノードが自信がもっていない未承認の祖先を要求し、それをリレーしてもらうものになっている。つまり、未承認のパッケージをinv
で通知する機能はない。以下のように送信者がinv
メッセージを使って未承認のパッケージをトリガーするような機能については、今後の拡張になるっぽい。