今回のレポートは、先週に引き続きまして、ビットコインのスクリプトにまつわる話をしたいとおもいます。テーマは、トランザクション・マリアビリティです。トランザクション・マリアビリティというのは、ビットコインの実装における最大の欠陥とも指摘されていたものです。

Mt.Gox事件を当時体験しているひとだったら、かならずや聞いたことのある言葉とおもいます。マーク・カルプレス元CEOの「システムに弱いところがあって、ビットコインがなくなってしまいました」という言葉は記憶に新しいとおもいます。この「システムの弱いところ」というのが、トランザクション・マリアビリティの欠陥をついたバグということなのです。コインが盗まれた原因だとされていました(が、事件の顛末としてはCEOによる内部犯罪の線が濃厚です)。

トランザクション・マリアビリティがある限り、取引所などのシステムにもややこしい対応が必要になりますし、ライトニングネットワークやサイドチェーンといったものが開発できないといわれています。

なんとかして、マリアビリティ問題を解決できないか?

実は、Segwitというのが、このマリアビリティ問題の解決方法になっているのです。Segwitが稼働すると、長らくビットコイン最大の欠陥といわれていたマリアビリティが、完全かつ恒久的に解決します。

これは、めっちゃ大きいです。ですので、Segwitは、今後のビットコインが成長するための基礎がためとして、すごく大きな意味をもってくるというわけです。

ということで、今回のレポートは、3つのことを理解していただきます。

  • マリアビリティとはなにか?
  • なぜマリアビリティが問題になるのか?
  • Segwitにより、どのように解決が行われるのか?

注: マリアビリティは、日本語だと「展性」というのですが、日本に訳したところでそちらも全く聞いたことのない言葉なわけですから、英語のままマリアビリティということにします。

トランザクション・ハッシュとは何か?

一言でいうと、ビットコインのトランザクションひとつひとつにつけられている「トランザクション・ハッシュ」が変更できてしまう問題です。しかも、第三者(悪意がある人)によって、変更することができてしまい、それを防ぐ有効な手立てがありませんでした。

まず、トランザクション・ハッシュについて理解しましょう。

トランザクション・ハッシュとは、ビットコインひとつひとつの送金に付与される固有のIDです。たとえば、Blockchain.infoなどで、送金の内容をチェックすると、一番上に記載されている情報です。

これはブロックに含まれる取引の内容を表示したものですが、これにも、トランザクション・ハッシュが書かれています。

トランザクション・ハッシュは、取引の固有のIDで、取引を特定するためには、このIDを使うことに成ります。

Blockchain.infoでは、URLがこのような形をしています。

https://blockchain.info/tx/3602b1a3813e7841a474c67cf343f0795f9963b975e74b5a131cd55c57eeed82

このtx以下が、まさにトランザクション・ハッシュの値で、これをURLに指定することで、任意の取引の内容を表示させることができます。

一般のユーザーとってはそれほど意味のないIDのように思いますが、取引所の入出金の際には、これは重要です。

たとえば、Poloenixの出金画面。

これは出金の履歴です。

ここに、Txidというのが書いてありますよね。これがトランザクション・ハッシュの値です。

つまり、このIDを、Blockchain.infoなどで検索することで、ブロックチェーン上で確かにそのトランザクションが存在しているのか、取引所の出金金額と同じなのか、採掘はされているのか、といった基本情報を確かめることができるわけです。

よくあるトラブルに、取引所から出金したけど、出てこないとか、その反対で、入金したけど反映されないというものがあります。

このとき、トランザクションハッシュがわからないとトラブルになります。

たとえば、ビットコインを送ったけど、入金反映されてない場合。

あなた「入金まだですか?」

取引所「入金した証拠ありますか?トランザクションハッシュはどれですか?」

あなた「えっ。わかりません」

取引所「じゃあ、証拠ないから反映わかりません」

とか、そういう感じでシラをキられたらたいへんです。確実にどこそこに送金して、マイニング済みだということは、ブロックチェーン上で、トランザクション・ハッシュを指定して、これだと証拠をみせつけるほかにありません。

ですから、自分でトランザクション・ハッシュの情報を取得できない送金は危険です。入金の証拠をブロックチェーン上で見せてよ、といわれても、それを示せないのですから。

そういう事になってしまう危険な送金の例として次のようなものがあります

  • 取引所から取引所に直接送る。出金アドレスに他の取引所の入金アドレスを指定する
  • クラウドマイニングの出金アドレスに、取引所の入金アドレスを指定する
  • Shapeshiftなどを利用して、変換したものを直接取り引所に入れる。もしくはその逆。

これらは、トランザクション・ハッシュが取得できないことが多々あるため、あとから取引のトラッキングが難しいです。マイニングされたかどうか以前に、そのトランザクション自体が発行されたのかどうか確認することができないため、トラブルになった場合、証拠がなく、状況が追えません。コインが何処かにいってしまっても、状況がわかりません。届いてないことになっても、立証できなかったり。最悪泣き寝入りすることになります。

※トランザクションハッシュは、取引ハッシュ、取引ID、トランザクションIDなど、Tx IDなどさまざまな呼び方で言われているようで、統一された言い方はないようです。

本レポートでは、開発者の中で一般的なトランザクション・ハッシュをそのままつかいます。

トランザクション・マリアビリティとは何か?

トランザクション・ハッシュは、取引を一意に特定するためのIDで、非常に重要だということがわかりました。

さて、トランザクション・マリアビリティ(をついた攻撃)とは、何でしょうか?

ずばり言うと、このトランザクション・ハッシュが、悪意のあるひとによって、変更されてしまうことです。えー、それじゃ、取引の特定に使えないじゃん?とお思いになるかもしれませんが、そのとおりです。だから問題なのです。

先程の、取引所とのやり取りを例に取ってみましょう。

あなた「10BTC送ったんですが、入金されてないので、調べてください。トランザクション・ハッシュは、b1a3813e7841a474c67cf343f0795f9963b975e74b5a131cd55c57eeed82で、こちらで10BTC入金しましたよ」

(ここでハッカーのマリアビリティをついた攻撃に合って、IDを変えられてしまう)

取引所「しらべてみましたが、そんなID、ブロックチェーン上に無いですよ」

あなた「ええ??私のウォレットにはそのIDが表示されるのですが・・・・」

取引所「なにいってるの?ブロックチェーンを調べてよ、IDないから」

あなた「(再度調べる)ホントだ・・そんなバカな。私の取引は何処に行った・・・助けてください。他にも証拠は有るんです。送金したアドレスと、入金のアドレス、そしてこのくらいの時刻に送ってます」

取引所「なるほど・・・たしかに、アドレスから、うちのアドレスに確かに10BTC入金はされてますね。トランザクションハッシュは別のものに変わってますけど・・・。マリアビリティですかね。入金はしておきますよ。」

という感じです。あなたは、入金はしてもらえたものの、冷や汗を書きました。

マリアビリティ攻撃があると、トランザクションハッシュだけでは取引を特定できなくなります。送金元アドレス、送金先アドレス、金額など、別の情報と組み合わせないと、取引を特定できないというわけなのです。

トランザクションハッシュ値は変わるのに、マイニングされる?

もう一つの特徴は、ハッカーによってトランザクションハッシュ値が書き換わっても、取引が無効になったりすることはなく、正常な送金として、マイニングされることです。

逆の言い方をすると、ハッカーは、送金先や送金金額などを変えることはできず、唯一トランザクションハッシュの値だけを変える攻撃が可能であるとも言いかえられます。

これを図にまとめます。

Aさんが送金取引をつくってネットワークに流します。それを途中で中継した悪意あるハッカーは、マリアビリティの欠陥をついて、トランザクションハッシュの値を変えてしまいます。そして、値が変更されたトランザクションの方を中継することで、これがマイナーに届き、うまくいけばブロックに含まれます。その場合、最初のトランザクションもハッカーでないノード経由でネットワーク上は同時に存在するのですが、すでに同じ送金がブロックに含まれてしまっているので、マイナーはこれを二重支払いとして却下します。

さて、この攻撃をうまくつかって、コインが盗まれたと称された事例がMt.Goxです。

Mt.Goxの出金システムでは、トランザクションハッシュの値のみをつかって、出金の管理をしていました。また会話調でわかりやすく説明してみます。実際は会話してるのではなく、コンピュータ同士のやり取りです。

Mt.GOX 「10BTC出金しましたよー。トランザクションをハッシュは736182です。これ私のところのデータベースに記録してますんで」

ハッカー (しめしめ、このトランザクション・ハッシュを変更してしまおう。変更成功。変更後のIDがマイニングされたぞ。成功だ!)「Mt.Goxさん、さっきの出金は拒否されて成功してませんよ。もう一回送り直してください」

Mt.Gox 「(トランザクション・ハッシュだけを確認して)たしかに、その取引はたしかにマイニングされてませんね。途中で拒否されてしまったようです。もう一回10BTC出金します」

ハッカー「ありがとう」

(しめしめ、もういっかいこのハッシュも変更してしまおう・・・)

以下繰り返し。

これが、Mt.Goxに対して行われたと言われているマリアビリティ攻撃です。Mt.Gox側は、トランザクション・ハッシュだけをデータベースにいれて管理していました。一定時間後、このハッシュを検索して、ブロックチェーンに含まれていれば出金成功。そうでなければ、失敗とみなして、もう一回出金を自動でしていたといわれています。

トランザクションハッシュが変わっても、トランザクションはブロックに含まれビットコインの出金自体は成功しています。実際は、お金がとどいているのに、トランザクションハッシュが違うので、Mt.Goxはその出金が失敗したと勘違いし、自動でまた出金してしまった。これがループになって繰り返された結果・・・・すべてのビットコインを失った。

というのがMt.Goxの説明でした。

ひどい話ですが、なかなかややこしい話でもあります。取引所は、出金できたかの確認を行うには、トランザクション・ハッシュだけを管理しただけでは駄目だからです。取引を一意で指定できるものが無いというのは、メッチャ不便で、危険であります。

実際に、Mt.Goxはこのマリアビリティ攻撃でコインを失ったとも言われています。ただし、そのコインの数はそれほど多くなく、CEOのこのバグを言い訳つかっていたようです。結局、コインを失った真相は内部犯行であったといわれています。

ライトニングネットワークなどへの影響

マリアビリティによって、取引所が困ったという例をだしましたが、影響が大きいのは、ペイメントチャネルや、ライトニングネットワーク、サイドチェーンといった次世代の技術への影響です。

たとえば、ライトニングネットワークなどでは、ペイメントチャネルという技術をつかっています。これは、特定のマルチシグのアドレスに予めビットコインをデポジットしておき、精算の期日をきめて精算トランザクションをつくっておきます。期日までのその間は、精算の額だけをやり取りすることで、オフチェーンで取引が無限にできるというコンセプトです。

こういうものをやろうとすると、デポジットのトランザクションがマイングされたのかどうかといったことを確認する必要があります。当然それを管理するのはトランザクションハッシュで管理したいところなのですが、マリアビリティの問題が有ると、それが上手く出来ないというか、簡単にできません。

また、ライトニングでは、チャネルがが有効かどうかを常時見張るというモニタリングという作業が必要になります。これに際しても、フルノードを建てて常時監視ができればいいのですが、一般のユーザーはそれができないため、アウトソースすることになるのですが、マリアビリティの問題が有ると、そのアウトソースが出来ないようなのです。これでは、フルノードでないと、ライトニングを使えないことになってしまいます。

マリアビリティの問題が解決すれば、こういした次世代システムの開発のハードルが一つクリアされると言われています。

マリアビリティはなぜ起こるか?どうやって起こすことができるか?

次に、マリアビリティがどうして起きるのか、どうやって起こすのかということについて、解説します。

これを理解するためには、ビットコインのスクリプトについての基礎理解が必要ですので、先週のレポートを熟読ください。それを理解している前提で、以下を解説します。

  • レポート「050 よくわかるビットコイン・スクリプト入門」

話を単純化するため、実際の単純なアウトプットスクリプトを考えます。レポート050の中でもでてきた、このスクリプトです。

<Output Script>

OP_ADD 3 OP_EQUAL

このスクリプトを使ってコインをロックした場合、これを使うためには、どういうインプット・スクリプトが必要でしょうか?

上記スクリプトは、翻訳すると、こういうものです

  • OP_ADD: インプットの値を2つ拾ってきて、それを足しなさい
  • 3 OP_EQUAL: 足した結果と「3」がイコールかどうかを調べなさい。
  • イコールだったらTRUEを返しなさい

というものです。

これを満たすためのインプットは、何になるでしょうか。

ふたつ値を入力して、足したら3になるようにすればいいのですから、

答えは、

1 2 

です。1と2です。

これをさきほどのアウトプットスクリプトに入力してあげれば、辻褄があうので、そのアウトプットスクリプトのコインが使えるようになります。

ここまでは、前回の復習になります。

さて、ここで問題があります。足して3になるふたつの数というのは、ほかにもあるわけです。

1 2

のほかに、

2 1

という組み合わせがあります。2 1 と入力しても、足したら3なので、このコインは使えるようになります。

つまり、1 2 、2 1 の2つの入力どちらでも、このアウトプットスクリプトは解除されます。これがマリアビリティの原因になります。

トランザクションハッシュ生成の仕組と、マリアビリティ攻撃の核心部

トランザクションハッシュというのは、その名の通り、トランザクションに含まれるデータをハッシュ関数に入れて結果を、IDとして採用しています。ビットコインでは取引の一覧を管理する中央の主体がありませんから、一意のIDをつけるには、このような方法をつかっています。だれも管理しなくても、各自がトランザクションのデータ全体をハッシュすれば、IDが得られるのですから賢い方法です。

さて、このような方法でIDがつけられているとすると、どうでしょうか。ハッシュ関数と言うのは入力が一文字でも違っていれば、別の値を出力します。

つまり、トランザクションのデータを一文字でも改変できれば、トランザクション・ハッシュも別の値になるとうことなのです。

ハッカーは、トランザクションを中継するときに、トランザクションの中味を1文字でいいので変えてしまいます。1文字でも変えれば、トランザクションハッシュの値は、まったく別の値になるのですから。

しかし、普通、トランザクションの内容を変えてしまっては、マイナーの検証にひっかかって、偽造とみなされしまいます。金額を訂正したり、アドレスを変えたり、電子署名を変えたり、そういうところは、変えられません。偽造として見抜かれてしまいます。

しかしながら、ある部分だけは、変更したとしても、そのままスルーされて、マイニングされてしまう場所があるのです。

そうです、それがインプットスクリプトのところです。

先程の単純化した例では

1 2

2 1

どちらのインプットスクリプトも、正しいと判断されました。

本来は1 2 であったものを、攻撃者が途中で2 1 に変更してしまって、これをマイナーに中継します。しかし、マイナーとしては、1 2も2 1も、アウトプットスクリプトの条件をみたすので、正当なものとして、マイニングするわけです。

これがマリアビリティ攻撃です。

下記に、模式を示します。

<オリジナルのトランザクション内容>

INPUT 1 2

OUTPUT OP_ADD 3 OP_EQUAL

AMOUNT 10 BTC

これを実際にハッシュすると、トランザクションハッシュとして、

0f98371f0ac3109ae2c40cd3b17e4ec6ebc5097d69b6ad6da824f0b8e55c5bb1

という値が得られます。

<攻撃後のトランザクション内容>

INPUT 2 1

OUTPUT OP_ADD 3 OP_EQUAL

AMOUNT 10 BTC

これをハッシュすると、トランザクションハッシュとして

3b996b036a4161f2f9cbc52a548cd5e2538d0179f863b6d9035c4ac03763ecaf

という全く別の似ても似つかない値になります。

これが、マリアビリティ攻撃の正体です。

トランザクションの内容を変更しても、マイニングが通る部分を変えてしまうことによって、トランザクションハッシュを変更する、ということです。

ビットコインでの実際の事例(スクリプトを追う)

ここまで概念が理解できるように、単純化したスクリプトで説明しました。

下記は、実際のビットコインの生のデータで、実例をしめします[1]。

<改変前のINPUT>

OP_PUSHDATA 48 30450220539901ea7d6840eea8826c1f3d0d1fca7827e491deabcf17889e7a2e5a39f5a1022100fe745667e444978c51fdba6981505f0a68619f0289e5ff2352acbd31b3d23d8701(※電子署名データ)

OP_PUSHDATA 41

046c4ea0005563c20336d170e35ae2f168e890da34e63da7fff1cc8f2a54f60dc402b47574d6ce5c6c5d66db0845c7dabcb5d90d0d6ca9b703dc4d02f4501b6e44(※公開鍵データ)

これを説明すると、前半部分は電子署名のデータ。後半部分は、公開鍵のデータです。

このスクリプトがなにをやっているかというと、非常に単純で次のような動作をします。

  • OP_PUSHDATA 48  後に続く48バイトの文字列をスタック(メモリのようなもの)に格納せよという命令です。
  • 48バイト分の電子署名のデータがつづきます。これがスタックに格納されます
  • OP_PUSHDATA 41  後に続く41バイトの文字列をスタックに格納せよという命令です。
  • 41バイト分の公開鍵のデータがつづきます。これがスタックに格納されます

以上です。つまり、データを読み込んで、ロードするという読み込み命令です。この命令には、データの長さが指定されています。そうでないと、どこまで読み込んだらいいかわかりませんからね。

次にこれをマリアビリティをつかって、次のように改変することができます。

<改変後のINPUT>

OP_PUSHDATA2 0048 30450220539901ea7d6840eea8826c1f3d0d1fca7827e491deabcf17889e7a2e5a39f5a1022100fe745667e444978c51fdba6981505f0a68619f0289e5ff2352acbd31b3d23d8701(※電子署名データ)

OP_PUSHDATA2 0041

046c4ea0005563c20336d170e35ae2f168e890da34e63da7fff1cc8f2a54f60dc402b47574d6ce5c6c5d66db0845c7dabcb5d90d0d6ca9b703dc4d02f4501b6e44(※公開鍵データ)

赤い文字列のところが改変されています。

たとえば、OP_PUSHDATA 48が、OP_PUSHDATA2 0048になっています。

しかしながら、これは全く同じ動作をするのです。つまり、あとに続く48バイトの文字列を読み込みなさいというもので、実行したときの結果は全くいっしょです。

詳しくいうと、OP_PUSHDATA2 0048 はこのように動作します

  • OP_PUSHDATA2 あとに続く2バイトの数を読んで、そのぶんのデータを、スタックに格納せよ
  • 0048  48という数字を読む。これで何バイト読めばいいかわかった。
  • 結果、48バイトの文字列(電子署名)を、スタックに格納

という感じです。

要するに、48バイトのデータをロードする動作はいっしょなのですが、オリジナルは直接「48バイトロード」と書いてあるのにたいして、改変後は「次の数字の分だけロードせよ。48」と、回りくどく書いているということです。動作は全くいっしょ。

ですので、どちらのインプットスクリプトを入れてあげても、取引は検証されて、マイニングを通るということになります。電子署名とかの部分を改変するわけではないので、問題ないのです。

ですから、送金自体に問題は生じないのですが、マリアビリティが生じます。トランザクションのデータの文字列が一部変更されているわけですから、これをハッシュした場合、得られるトランザクション・ハッシュの値というのは、全く違った値になってしまいます。

ほかにもいろいろ出来ます。

たとえば、OP_PUSHDATA4というのは、後に続く4バイトの数の分だけデータを読めというものです。ですから、OP_PUSHDATA4 00000048 としても動作は同じになるわけです。

また、現在使えるのかどうかわかりませんが、OP_NOPというコードがあります。これはまさに、何もしないというコードなのですが、このコードを入れても入れなくても、なにもしないというコードなのですから、動作結果はいっしょです。しかしながら、コードを入れた分だけ、データは変わってしまいます。

[1] http://www.righto.com/2014/02/bitcoin-transaction-malleability.html

マリアビリティの回避方法はあるか?

さて、マリアビリティを回避するには一体どうすればいいのでしょうか?

  • スクリプトの内容を制限する

というのがまず考えられます。しかしこれをやってしまうと、せっかくの柔軟に表現できるビットコインスクリプトという武器がなくなってしまうように思います。

また、いくらやったとしても、同じ結果を別のコードで示す方法はいくらでもあり、結局のところ、スクリプトシステムという仕組を採用している以上、マリアビリティの問題は解決できないといわれていました。

  • 取引IDの付け方を変える

というのも難しいです。取引のIDを中央集権的に付与してしまったら、中央の管理期間を要することになってしまいます。あくまで、各自が取引のデータをハッシュすることによって、それぞれ独立して値を取得できるという仕組がどうしても必要です。

ということだったのですが・・・ここで、Segwitが登場します。

SegwitはInputScriptを切り離す

ここで、最終兵器として登場したのが、Segwit(Segregated Witness)です。

Segwitとは、ご存知の方も多いと思うのですが、どういう内容かはいろいろなところに書かれているとおもいます。多くの記事では、

  • 電子署名のデータを別に切り離して、ブロック本体とは別に管理する。

と書かれているとおもいます。Segwitは、Witness(署名)を、隔離する(Segregate)、という意味です。

電子署名などは、これは一旦マイナーによって確認されてブロックに含まれてしまえば、その後は確認する必要は殆どありません。つまり一度見れば良いデータなのでして、このような余計なデータをブロックに含めるのは無駄ということです。

実際には、署名の部分だけではなく、マリアビリティを起こす原因である、インプットスクリプト全体を隔離します。というよりは、インプットスクリプト自体を無くしてしまい(空になる)、そのかわりに、Witness Dataという新しいスクリプト領域をつくり、そちらを従来のインプットスクリプトの代わりに参照することになりました。

Segwitではインプットスクリプトには何も書かなくて良いので(空が正しい)、つまりSegwit以降、トランザクション・ハッシュの計算において、ハッカーが改変できる部分が取り除かれた形になります。トランザクションハッシュを途中で変更しようにも、取引データは変更できる部分がなくなってしまいましたので、不可能で、もし他の部分(数量やアドレス、電子署名などの部分のデータ)を変更しようとしたら無効なトランザクションとして却下されることになります。

こうして、Segwitにより、マリアビリティの問題は、最終的、完全かつ恒久的に解決されたというわけです。(拍手)

ビットコインのインプットスクリプトなくすというのは言うは簡単ですが、ビットコインの土台に関わる大改修です。ですから、Segwitは難易度高く、コードの変更も膨大で、開発とレビュー、テストに時間がかかりました。ハードフォークならもっと簡単だったのでしょうが、これをソフトフォークで、従来の動作の原理の上に後方互換性をもって実現できたというのは、とても素晴らしいことです。

Segwitには、ほかにも、様々な面でビットコインの拡張性を担保する良い面があります。Segwitはスケーラビリティの話と捉えられがちで、それでどれだけブロック容量が上がるのかどうかといったことばかりが注目されていますが、開発者は目先のブロック容量をあげるためにSegwitを開発しているのではないことはお分かりいただけるかと思います。

Segwitは、長期的なビットコインの将来のための土台として、重要な改修であるからこれを行ったという方が正しいかもしれません。結果として、さまざまな問題を解決し(ブロック容量の拡張はその効果の一つです)、ビットコインの基礎を強固なものにすると考えられています。

<著作権表示>

(c) 2016大石哲之本レポートの著作権はすべて大石哲之に帰属します。本レポートはビットコイン&ブロックチェーン研究所の会員のみが閲覧できます。本レポートの研究所外への転載および無断引用は固くお断り致します。また、本レポートは、会員本人のみが閲覧いただけます。会員以外へのシェアや法人内での回覧・シェアは固くお断りしております。(回覧人数分の料金を申し受けます)。無断の転載などを見つけた場合、著作権者までご連絡ください。