
2010年8月15日18:08 UTC頃、ビットコイン開発者ジェフ・ガージックがブロック74638で異常を発見し、「Strange block 74638」という新しいトピックを立て、ブロックの生データを貼った。
ジェフ・ガージック(BitcoinTalkへの投稿、2010年8月15日18:08 UTC)奇妙なブロック74638 — 92233720368.54277039 BTC? これはUINT64_MAXだろうか?
9分後、最初の返信でエリック・スワンソン(lachesis)がこの見立てを訂正した。数字は2^63/10^8で、UINT64_MAXではなくINT64_MAXのようだという。つまり、関わっていたのは符号のない整数ではなく、符号付きの整数の上限だった。次に述べるバグも、まさにこの符号付きの整数の上限で起きている。
ブロック74638のある一つのトランザクションは、0.5 BTCの入力を使い、92,233,720,368.54277039 BTCの出力を2つ生成した。ブロックのマイナーは50 BTCの報酬に加えて手数料として0.51 BTCを受け取った。この手数料は、入力の0.5 BTCより0.01 BTC多い。このブロックが無から生み出した額は、全部で184,467,440,737.09554078 BTCとなり、ビットコインの総発行予定量2,100万BTCの約9,000倍にあたる。ビットコインWikiは184,467,440,737.09551616 BTCとしている。これはちょうど2⁶⁴ satoshiにあたる値で、ブロックに記録された数字から計算した額より2,462 satoshi少ない。
バグの内容:トランザクション検証コードは個々の出力が非負であることを確認していたが、出力を足し合わせたときの整数オーバーフローは確かめていなかった。64ビット符号付き整数の最大値(INT64_MAX ≈ 9.2 × 10¹⁸)に近い2つの出力を足すと、オーバーフローして負の値になり、検証を通ってしまった。入力の0.5 BTCが、オーバーフローしたあとの出力の合計(-0.00997538 BTC)以上だと判定されたのである。この事件で突かれたのは、各出力の金額をsatoshi単位で入れる8バイトの項目である。トランザクション設計ページにあるとおり、この項目の値は、この事件をきっかけに上限が設けられるまで、プロトコルの決まりの上ではいくらでも大きくできた。
対応:発見から5時間40分後、サトシはBitcoin version 0.3.10を公開した。CheckTransaction()に新しい検査を2つ加えるソフトフォークだった。
- 各出力はMAX_MONEY(21,000,000 BTC)を超えてはならない
- すべての出力の合計はMAX_MONEYを超えてはならない
ギャビン・アンドレセンは並行して独自の緊急パッチを試していた。その作業の出発点にしたのは、フォーラムの参加者knightmbが前もって公開していた、バグの影響を受けていないブロックチェーンのスナップショットである。
サトシは同じ日のうちに、オーバーフローの修正を入れたv0.3.10を出し、BitcoinTalkに「バージョン0.3.10 - ブロック74638オーバーフローパッチ!」と題して告知した。読者には、バグそのものを論じるスレッド(topic 823)へ移るよう促した。
結果:修正したチェーンは、不正なブロックから約15時間後のブロック74691で、無効なチェーンより長くなった。1,844億BTCは、ブロックチェーンの受け入れられた履歴から、事実上消えた。この事件で実際に働いたのは、ビットコインに一般に備わる仕組みである。緊急のソフトフォークで直したチェーンに、ほかのどのチェーンよりも多くの計算が積み上がると、ノードはそちらを受け入れていく、という仕組みである。この仕組みは、コンセンサス設計のエントリーで詳しく説明した。
偶発か悪用か? ここで、誰もが思う疑問がある。これは偶然起きたのか、それとも意図的な悪用だったのか。v0.3のソースコードとトランザクションの構造を分析すると、一つの点については答えがはっきり出るが、もう一つの点は分からないまま残る。
トランザクションの組み立て方を見れば、偶然ではありえない。オーバーフローを発動させるには、合計がINT64_MAX(約9.2 × 10¹⁸ satoshi)を超える2つの出力が必要で、それぞれ約922億BTC近辺の値でなければならない。普通にウォレットを使っていて、こんな値が出てくることは事実上ない。2010年当時、個人が922億BTCを保有することは不可能で、現実的な残高は最大でも数千BTC程度だった。標準のビットコインのウォレットは、出力の額が残高を超えていないか、MAX_MONEYを超えていないかを確かめてから、トランザクションを組み立てる。
92,233,720,368.54277039 BTCという値はタイプミスや丸め誤差で偶然現れる数字ではない。int64の最大値を10⁸ で割った値より0.00498768 BTCだけ小さく、オーバーフローする寸前の値を狙って、わざと指定しなければ出てこない。ブロック74638のトランザクションを作成するには、生のトランザクションのバイト列を手作業またはカスタムツールで構築し、CheckTransaction() のint64の足し算があふれるような出力の値を、わざわざ選んで入れ、署名してブロードキャストする必要があった。いずれの工程も意図的で技術的知識を要する行為である。
実行者と動機は公開記録からは特定できない。ブロックチェーンにはトランザクションと2つの受取アドレスが記録されているが、作成者の身元はオンチェーンデータから復元できない。悪意による攻撃か、概念実証のデモンストレーションか、ストレステストだったかは、コードやトランザクション自体からは判断できない。ブロック74638を採掘したマイナーが悪用に気づいていたかも不明である。バグのある検証ロジックの下では、正直に動作していたノードもそのトランザクションを「有効」と判定するため、ブロックに入れたからといって、悪用に加担したことにはならない。
バグであり、同時に悪用でもある。CheckTransaction() のint64オーバーフローの存在はバグ(CVE-2010-5139)である。オーバーフローをトリガーするトランザクションの作成とブロードキャストは意図的な悪用行為であり、トランザクション構成の要件からそれは確定できる。その行為が悪意を伴ったかは公開記録からは判定できない。セキュリティ用語では、脆弱性をわざと突く行為は、実行した人が自分の意図をどう説明するかに関係なく「攻撃」と呼ぶ。この語そのものは、悪意があったことまでは意味しない。したがって本事件はバグ(根底の欠陥)と攻撃(悪用行為)の両方である。
この事件で、ビットコインが抱える根本の矛盾があらわになった。分散型のシステムが、一人の判断によって危機を脱したのである。緊急の対応の中で、コミュニティは、修正したクライアントを自分たちで確かめることなく使い始め、サトシの判断に任せた。設計は分散型なのに、危機のときには一人の権威に頼る。この食い違いが、このとき初めて誰の目にも見えた。次に二つのチェーンが争ったのは2013年3月のチェーン分岐で、サトシはもういなかった。IRCのチャンネルにいた数人の開発者と二つのプールの運営者が、短いほうのチェーンを選んだ。
このサイトでは、ほかのいくつものページが、この事件を話の土台にしている。この事件の構造と矛盾を論じた分析では、同じ5時間の対応を、分散と集中がぶつかり合う典型として読んだ。knightmbのスナップショットと、その伝説を論じた分析では、事件の直前のブロックチェーンのスナップショットを公開していた人を取り上げた。復旧のパッチは、このスナップショットを頼りにしていた。事件の直前のビットコインの様子は、Slashdotのビットコイン記事 (2010年7月)とknightmbの伝記で読める。マウントゴックスの破綻のページでは、取引所の破綻をめぐる議論で使われる「プロトコルは生き残った」という言い方に対する、初期の反例としてこの事件を挙げた。事件の1週間後、サトシは自分だけが持つ鍵で署名する警告の仕組みを発表した。その鍵で何ができたのか、警告の仕組みが2018年までにどう廃止されたのかを論じたページも、話はこの事件から始まる。




















