ARPABLE
アープらしいエンジニア、それを称賛する言葉・・・アーパボー!
ブロックチェーン

Segwitとは

SegWitについて

SegWit(Segregated Witness)は、ビットコインが抱えるスケーラビリティ(Scalability)トランザクション展性(Transaction Malleability)と呼ばれる2つの問題を解決するために導入されました。

今回は、BIP-141(Bitcoin Improvement Proposals)の定義に基づきそのロジックを分かりやすく説明したいと思います。

ビットコインのスケーラビリティ問題とは

ビットコインのスケーラビリティ問題とは、銀行決済やクレジットカード決済に比べ、仕様上の制限から処理能力に拡張性がなく、決済するまでに時間がかかってしまう点です。

例えば、ビットコインの取引が発生してから確定するまでに1時間以上かかる、あるいは1秒間に確定する取引はせいぜい6~7件ぐらいであり、クレジットカードの処理性能より3桁程度遅くなってます。

具体的には以下の2点の仕様がかかわってます。

  1. ブロックサイズは1MBと定義されている
    (通常2000個~4000個のトランザクションしか入らない)
  2. ブロックは平均して10分毎に1つしか生成できない
    (2週間に1度、ナンス発見の難易度Difficulty Bitsで調整されている)

❶に関する対策としてはブロックサイズを8MBに拡張したビットコインキャッシュがありますが、結果的にビットコインからハードフォークしてしまいました。

❷に関してはビットコインのコンセンサスアルゴリズムにPoW(Proof of Work)を使用している限り改善するのは難しいとされてます。

このように取引に時間がかかるという問題を抱える中で、マイナーはより高い手数料のトランザクションを優先して処理するため、手数料(ガス代)が高止まりするという副次的な問題も生じてます。

❶の問題をハードフォークすることなく解決する為、最大で4MBまで拡張可能な仕様を提案したのがSegWitであり、同時に次に説明するトランザクション展性(ID書換容易性)の問題も解決してます

トランザクション展性とは

トランザクション展性とはとは、トランザクションIDの書換容易性のことで、この脆弱性により通貨には決してあってはならない「2重支払い」を引き起こす可能性が高まります。
(単に「マリアビリティ(Malleability)」と呼ばれることもあります。)

具体的な事件としてはスロベニア共和国の暗号資産交換業者Bitstampや、日本にあった世界最大級の取引所であるマウントゴックス(Mt.GOX)社などが挙げられます。
マウントゴックス社の事件は日本でもずいぶん騒がれたのでご存じの方も多いと思います。

※)マウントゴックス社は、2014年2月25日にすべての取引を停止しました。
当時CEOだったマルク・カルプレス氏によると「トランザクション展性に起因する問題が生じた」と説明してます。
参考記事は「マウントゴックス事件とは?ビットコインが消失した事件の全貌を知る」等を参照ください。

このロジックを分かりやすく説明するためにまずはトランザクション検証について復習しましょう。

トランザクション検証

以前ブロックチェーントランザクションとは(2)で学習した「2重支払防止のための3ステップ」を説明した時の図を再掲します(スクリプトはP2PKH(Pay-to-Public-Key-Hash)ベース)

ここでは五条さんが以前虎杖君からもらった70ビットコインを使ってパンダに10ビットコインを送金する場合を想定してます。
※)金額がインフレなのが気になる方は単位をドルとか円に変えてご覧になってください。

このようなトランザクションは、ブロックチェーンの自律分散型ネットワークにブロードキャストされて各ノードが検証することで正当性が保証されます。

ブロックチェーントランザクションとは(2)で説明したように、五条さんがINPUTにセットしたUTXOである「TxIDが1234でインデックス値が0(※)」が使えるかどうかを、トランザクションスクリプトであるScriptSigとScriptpubkeyの正当性を評価して判断していました。

※)「インデックス値が0」ということは、虎杖君が作成したトランザクションのOUTPUTの先頭のUTXOのことです。

トランザクションIDの書換え手法とは

トランザクションについて概要を理解した上で、どのようにTxIdを書換えるのか、つまり改ざん手法についてについて見ていきましょう。

上図のトランザクションID(TxId)はトランザクションのデータ全体をハッシュ化したものです。

2重支払を引き起こすためには、トランザクションの内容自体を変えずに、無害なスクリプト等を追加することによりトラザクションIDを書き換えます。

例えば、P2PKH(Pay-to-Public-Key-Hash)の場合のscriptSig(アンロッキングスクリプト)は以下のオペコード、つまり電子署名と公開鍵で構成されます。

<sig> <pubkey>

これに対してその内容を変えず実質的に無害なオペコードを追加すればいいので、例えば、以下のようなコードが想定されます。

  • OP_NOPを追加(何もしない)。
  • OP_DUP OP_DROP(スタック上の値を複製し、それを削除する、つまり変化なし)

トランザクション展性の問題点とは

ところで、ScriptSigに含まれる署名は、トランザクション全体からScriptSigを除いた部分に対してSHA256で2回ハッシュ化したものを秘密鍵で暗号化したものです。

ここで、何故ScriptSigを除くかというと、これからハッシュ値を計算するにはその対象は確定してなければならないからです。
つまりハッシュ値を暗号化したものがScriptSigになるので、ハッシュ値を計算する時点ではその値は未定となっているからです。
考えてみれば当たり前のことですね。

一方、トランザクションIDはトランザクション全体を対象としてハッシュ値を求めますので、INPUTのScriptSigも含まれることになります。

その結果、トランザクションの内容(アドレスや取引量)が全く同じで、トランザクションIDだけが異なる2つのトランザクションを作成することができてしまいます。

その結果正しいトランザクションが一旦却下され再送要求が出るなどすると結果的に2重支払い問題が生じてしまいます。
更に、悪意のある外部の人間もこの操作を行えますので大きな問題に発展してしまうことがあります。

では、具体例を挙げてみましょう。

例えば、上図のトランザクションの場合、五条さんがパンダに10ビットコイン送金するという情報は、五条さんの秘密鍵による署名で保護されているため、トランザクションIDを変更したとしてもビットコインの送金先、送金額の変更は出来ません。

従って直接ビットコインを操作することは不可能です。

問題となるのは、ウォレットや取引所がこのトランザクションID「だけ」を参照して取引していた場合です。

こういった取引所やウォレットが管理しているトランザクションIDを外部から変更することで、トランザクションID不一致が多く出てしまいます。

その結果、ビットコインの2重送信が多発してしまったり、データの整合性が取れなくなり、改ざんされた取引がネットワークに承認されてないかのように見せかけることにより、再送が出来てしまうこともあるようです。

2014年にビットコインを消失したマウントゴックス社の主張が正しいとすると、このような攻撃により取引所を閉じることになったということが言えるでしょう。

SegWitとは

SegWitとは、以下の2点を実現する仕様です。

  1. ScriptSigを同一ブロック内の別領域である「Witness Data」に移動した。
  2. 1ブロックを最大4MBまで拡張可能とした。

❶により、トランザクションIDを求めるためのハッシュ値の対象から、改ざん可能なScriptSigを外すことによりID書換問題を解決しました。

❷の仕様は従来のトランザクション形式(ブロックサイズ1MB)もサポートしているため、ハードフォークすることなくスケーラビリティの問題も改善しました。

スケーラビリティ問題の対策

この対策ですぐに思いつくのは1MBに制限されているブロックサイズの拡張ですが、これには以下のような副作用が心配されます。

  • これはハードフォークになってしまい後方互換性が失われる
  • マイニングが難しくなるため、マイナーの中央集権化が進む恐れがある

そこで、SegWitではトランザクションの多くの部分を占めるscriptSigを、同一ブロック内のwitness dataと称する領域へ移動させました。

そしてSegWit 未実装のノードはwitness dataを無視します。
一方、SegWit対応ノードはwitness dataを含めたデータをブロックに取り込みます。SegWit対応ノードのブロックは後述するように「ブロックウエイト」という手法を導入することにより最大4MBまで拡張可能となります。

このように、従来のノードでもSegWitノードでも同じ形式のトランザクションを扱うことができることから、SegWit はソフトフォークで済むのです。

ブロックサイズとブロックウェイト

従来のノードでは「ブロックサイズを1MB以下」というように制限していましたが、SegWit からは新しい「ブロックウェイト」と呼ばれる概念を用いてサイズを制御します。

ブロックウェイトは、以下の式で定義されます。

Block Weight = Base_Size X 3 + Total_Size
        = Base_Size X 4 + Witness data

  • Base_Size:Witness Dataを除いたブロックサイズ
  • Total_Size:Witnessを含んだ全体のブロックサイズ
  • Block weight:上限は4M weight unit(4MB)

ブロックウェイトとは、ブロックからそれぞれのトランザクションのwitness dataを除いたものに3を掛け、それにブロックサイズを足したものです。

従って、SegWitが普及してくるとBase_Sizeが次第に小さくなり1つのブロックが最大4MBまで拡張可能となり、Segwitトランザクションがない場合には従来の仕様通りブロックサイズは1MBのままになるわけです。

またwitnessの割合に対するブロックサイズの関係を式で表してみましょう。

T≦4,000,000÷(4 – 3r) ・・・❶

  • witnessの割合:r
  • ブロックサイズ:T

❶は定義通りに4(1-r)T+rT≦4,000,000から導いたのものです。

この式からR=0の時は1MBとなりr=100%の時は4MBとなることが分かります。

一般的にScriptSigのサイズはトランザクション全体の60%と言われますので、この場合のブロックサイズは1.82MBとなるようです。
T=4,000,000÷(4 – 3 x 0.6)
≒1.82MB

ScriptSig≒60%の妥当性について

このような問題に対して答えてくれるサイトがBitcoin Optechです。
このサイトにはトランザクションサイズを算出してくれるサービスがあります。

このサービスによるとINPUTが2個、OUTPUTが2個の標準的なトランザクションの場合、

  • ScriptSig:214バイト(スクリプトタイプはP2PKH)
  • トランザクション全体:374バイト

となりScriptSigの比率は57.2%となります。

  • スクリプトタイプがP2SH 2-of-3の場合は508バイトに増加する
  • INPUT、OUTPUTの数が増えるとScriptSigの比率が増加する傾向がある

ことから60%というのは比較的妥当な数値ではないでしょうか。

(アーパボー)