SegWit とは?
SegWit とは、BIP141 において提案された Bitcoin の規格である。BIP141 では witness というトランザクションマークルツリーから別々にブロックをコミットする構造を定義しており、署名スクリプト(<ScriptSig>
)を別の構造に移すことで以下のメリットを得ることができる。
- トランザクション展性(Transaction Malleability)を排除
- 署名検証の効率化
- ブロックあたりの処理可能トランザクション量の向上
トランザクション展性とは?
トランザクション展性とは、署名スクリプトの一部を変えることで、取引内容はそのままで txid を変更できる性質のことであり、電子署名の正当性を保ったままトランザクションの改ざんを行うことができる。
例えば以下の図のようにトランザクションが伝搬していくときに、悪意のあるノード(赤い人)が途中でそのトランザクションを改ざんしたとする。すると、取引内容は同じ(例:A -> B 10BTC)であるが txid の異なるトランザクションが複数存在することになる。これらのトランザクションは同じ UTXO を入力で参照しているため、どちらか一方のトランザクションのみがブロックチェーンに入ることができる。
txid を変える方法
txid を変える方法はいくつかある。まずは、署名自体を改ざんする方法である。これは暗号学的なテクニックを駆使するものであり、ここで詳細に触れることは控える。2つ目は、署名検証に影響ないように余計なバイトコードを <ScriptSig>
に付与することでスクリプトを改ざんする方法である。スクリプトは電子署名の対象ではないため、スクリプトを改ざんしても電子署名自体の正当性は保つことができる。また、txid の計算にはスクリプトも含んでいるため、スクリプトを改ざんするともちろん txid も変わる。
スクリプトの改ざん例を以下に示す。<ScriptSig>
は通常、以下のように電子署名とそれを検証するための公開鍵で構成されている。
<ScriptSig>
= <signature>
<Pubkey>
そこに <OP_DUP>
と <OP_DROP>
を付け加える。<OP_DUP>
はスタックに入っている一番上の要素を複製する意味を持つ。そして、<OP_DROP>
はスタックの一番上の要素を捨てる。つまり、この2つのオペコードはあってもなくても全体を通して操作は同じ結果になる。
<ScriptSig>
= <signature>
<Pubkey>
<OP_DUP>
<OP_DROP>
2 つの ID
SegWit を導入すると各トランザクションは txid の他に wtxid という ID を持つようになる。それぞれの定義は以下のようになっている。(txid は SegWit 導入前と同じ定義)
- txid -> SHA256 ([nVersion][txins][txouts][nLockTime])
- wtxid -> SHA256 ([nVersion][marker][flag][txins][txouts][witness][nLockTime])
ブロックへの組み込み
SegWit に対応したトランザクションをブロックチェーンに取り入れるために新たなコミットメント構造が設けられている。SegWit トランザクションが含まれている場合、そのブロックのコインベーストランザクションには witness commitment への出力が含まれる。witness commitment とは、witness ルートハッッシュと witness 予約値を連結したハッシュ値である。witness ルートハッシュとは、そのブロック内のトランザクションの wtxid のマークルルートである。
Witness 検証ロジック
witness には従来の <ScriptSig>
にあたる内容が格納されている。また、<ScriptPubkey>
は以下のような内容が格納されている。
<ScriptPubkey> = <version byte> <witness program>
現在、SegWit を対応したスクリプトは P2PWPKH と P2WSH の2種類がある。
- P2WPKH (Pay-to-Witness-Pubkey-Hash)
--<version byte>
: 0
--<witness program>
: 20-byte (HASH160(Pubkey))
--<ScriptSig>
は空(署名と公開鍵を witness に入れる) - P2WSH (Pay-to-Witness-Script-Hash)
--<version byte>
: 0
--<witness program>
: 32-byte (SHA256(<redeemScript>
))
-- witness には<redeemScript>
とそれを満たすために必要なアイテムを入れる
署名検証の効率化
従来では、トランザクションの入力数が 2 倍になると検証する署名数が 2 倍になり、トランザクションのサイズも 2 倍になる。つまり、ハッシュ計算にかかる時間が 2 乗倍になる。これは入力数が増えることに対する署名検証が非効率である。そこで SegWit では各入力ごとに個別のハッシュ値を計算するのではなく、共通のハッシュ値を用いる手法を取っている。これによって、ハッシュにかかる時間が入力数に比例するようになり、効率的な署名検証を可能にしている。
ブロックあたりの処理可能トランザクション数
従来の Bitcoin では、ブロックごとにおいてブロックサイズが 1MB 以下かつ署名演算数が 20,000 以下という制限が設けられている。SegWit は Bitcoin をソフトフォークさせるためにこの制限を変更していない。その代わりに block weight という新たな尺度を導入した。ブロックあたり最大 4,000,000WU(weight unit)という上限を設けており、各ブロックの重さは以下のように計算される。
Block_Weight = Base_Size * 3 + Total_Weigth
Base_Size とはブロック全体から witness を除いたブロックサイズであり、Total_Size は witness も含んだブロック全体のサイズである。つまり、Segwit が普及すると Base_Size が小さくなるため、ブロックに入れることができるトランザクション数が多くなる。
ブロックの重さ計算の例を以下に示す。ブロックの Base_Size が 3,000-byte で、witness が 5,000-byte とする。すると、全体の重さは 3,000 * 4 + 5,000 = 17,000WU になる。また、ブロックに含まれる witness の割合を r とし、ブロックサイズを T とする。r を変化させるとブロックサイズは以下の表のように変化する。witness 使用率が 100% になると、理論的にはブロックサイズは従来の 4 倍になることがわかる。