Segwitの導入にあたってブロック作成時にwitness トランザクションの署名が確かにブロックに含まれていることを保証するために新しいCommitment構造が導入されている。
techmedia-think.hatenablog.com
コインベーストランザクションに追加されたCommitmentのデータ構造
ブロック作成時に作られるコインベーストランザクションにそのCommitmentデータが新しく追加されているので、実際にどう変わったのか見てみる。
↓がtestnetで確認できたwitnessなトランザクションを含むブロックのコインベーストランザクション
{ "hex": "010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff3603cbd20e00048681fc5704bacfd5370cc69bfb57b25c0000000000000a636b706f6f6c122f4e696e6a61506f6f6c2f5345475749542fffffffff02c0eba012000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9ed670436c55de638c8100326d72998157a61aab2af1a8d4c5785f9093134b78e330120000000000000000000000000000000000000000000000000000000000000000000000000", "txid": "f5b1b47c99ab40fa765582dae0f4346eecbe9b77ce99c66bc765378d315441f3", "hash": "944c9305710c116ddccec64762ed642315e48a7c279c36e11756b74c8c5984e7", "size": 222, "vsize": 195, "version": 1, "locktime": 0, "vin": [ { "coinbase": "03cbd20e00048681fc5704bacfd5370cc69bfb57b25c0000000000000a636b706f6f6c122f4e696e6a61506f6f6c2f5345475749542f", "txinwitness": [ "0000000000000000000000000000000000000000000000000000000000000000" ], "sequence": 4294967295 } ], "vout": [ { "value": 3.12536000, "n": 0, "scriptPubKey": { "asm": "OP_DUP OP_HASH160 876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d6 OP_EQUALVERIFY OP_CHECKSIG", "hex": "76a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac", "reqSigs": 1, "type": "pubkeyhash", "addresses": [ "mss5NFyX96ix4erFMamR1gK3SsvUSMWcjE" ] } }, { "value": 0.00000000, "n": 1, "scriptPubKey": { "asm": "OP_RETURN aa21a9ed670436c55de638c8100326d72998157a61aab2af1a8d4c5785f9093134b78e33", "hex": "6a24aa21a9ed670436c55de638c8100326d72998157a61aab2af1a8d4c5785f9093134b78e33", "type": "nulldata" } } ], "blockhash": "000000000000002b2fcf202c54a21cf792a66592a4f8b655b026f5e3d419d656", "confirmations": 15, "time": 1476170550, "blocktime": 1476170550 }
Commitmentの追加
出力を見ると今までのコインベーストランザクションには無かったOP_RETURNを含む出力が追加されているのが分かる。Segwitでは新たにcommitment用のデータフィールドを設けるのではなく、OP_RETURNを使うことでcommitmentのデータ領域を確保している。
"asm": "OP_RETURN aa21a9ed670436c55de638c8100326d72998157a61aab2af1a8d4c5785f9093134b78e33", "hex": "6a24aa21a9ed670436c55de638c8100326d72998157a61aab2af1a8d4c5785f9093134b78e33",
このcommitmentデータは以下のような内容で構成されている。
バイト数 | 値 | 内容 |
---|---|---|
1 | 0x6a | OP_RETURN |
1 | 0x24 | 36バイト(0x24)のデータを続いてプッシュする |
4 | 0xaa21a9ed | commitmentヘッダ |
32 | 67043...78e33 | commitment hash=Double-SHA256(witness root hash|witness reserved value) |
39バイト目以降 | コンセンサスとは関係ない任意のデータ(オプション) |
witnessなトランザクションの署名がブロックチェーンに記録されていることを確認するためブロック内の全てのwitnessを含むデータからマークルツリーが作られていて、そのマークルルートがwitness root
になり、commitment hash
の計算に使われている。
(従来のトランザクションのマークルツリーはtxidを元に計算されているのでwitnessデータは含まれない)
※ ブロック内のトランザクションが全てwitnessなトランザクションではない場合、commitmentはオプションとなる。
では、実際witness root
とコミットメントハッシュはどのように計算されているのか?
witness rootとcommitment hashの計算
witness root
はwtxid*1のデータから計算されている
Bitcoin Coreで実際にwitness root
を計算しているのは、merkle.cppの↓の部分
uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated) { std::vector<uint256> leaves; leaves.resize(block.vtx.size()); leaves[0].SetNull(); // The witness hash of the coinbase is 0. for (size_t s = 1; s < block.vtx.size(); s++) { leaves[s] = block.vtx[s].GetWitnessHash(); } return ComputeMerkleRoot(leaves, mutated); }
コインベースの場合はそれ自身が有効なwitnessデータを持たないので、コインベースのwtxidはwitness reserved value
の値で構成され、現在は0x00...00
となる。
こうして算出したwitness root
にwitness reserved value
を付加してSHA256ダブルハッシュしたのがcommitment hash
になる。
bitcoin-rubyで↑のコインベーストランザクションを含むブロックのwitness root
とcommitment hash
を計算したのが↓
※ witness root
を計算する際にwtxidのエンディアンを変換しておく必要がある。
実行すると
witness_root = 50187168cf7fb4fa39db8fadefb032fb28e109bf22732a589144f108276945f4 commitment = 670436c55de638c8100326d72998157a61aab2af1a8d4c5785f9093134b78e33
となり、コインベーストランザクションのcommitment hash
と同じ値になっているのが分かる。