公開日
- 17 分で読めます
第5話 「スマートコントラクトの検証」
信頼ゼロ社会を生きるWEB3.0原理主義エンジニアの奇妙すぎる日常に翻弄される僕 ~フルノードを立てなきゃDAppも使えません!
注
この物語は、WEB3.0の世界を楽しく学ぶことを目的に、生成AIを活用して執筆されています。 技術的な情報の正確性には細心の注意を払っていますが、その内容がすべて真実であることを保証するものではありません。 あくまで学習の補助ツールとして、肩の力を抜いてお楽しみください。
登場人物紹介
- 私: DTPオペレータからエンジニアに転職したばかりの新米開発者
- D氏: 40代半ば、極度の警戒心を持つWEB3.0原理主義者
第5話 「スマートコントラクトの検証」
ハードウェアウォレットの設定を終え、D氏の自宅「聖域」を訪れてから数日が経った。「シードフレーズは絶対にデジタルに保存するな」というD氏の言葉に従い、手書きで記録したものの、12個の英単語を正確に書き写すだけでも一苦労だった。DTPオペレータ時代は、印刷物のミスを見つけるのが仕事だったが、今は自分のミスが資産喪失に直結する。この緊張感の違いに、私はWEB3.0の世界の厳しさを実感していた。
私は少しずつWEB3.0の世界に慣れ始めていたが、同時にその深さと危険性に圧倒されてもいた。D氏の極端な警戒心は時に笑いものに思えるが、彼の言葉にはいつも一理ある。そんな中、彼から新たな暗号化メッセージが届いた。
スマートコントラクトの検証を教える。
明日、秋葉原の某カフェで13時。
座標は前回と同じ。
事前にEtherscanで任意のコントラクトを一つ選んでおけ。
「Verified」マークがあるだけで安心するな。
私は早速ノートPCを開き、Etherscanで人気のDeFiプロジェクトのスマートコントラクトを物色した。目に留まったのは、ある分散型取引所(DEX)の流動性プールコントラクト。ユーザー数が多く、Twitterでも評判が良いプロジェクトだ。「Verified」マークも付いている。これならD氏も納得するだろうか?でも、彼の「安心するな」という言葉が頭をよぎり、妙な不安が胸に広がった。
翌日、指定された時間に秋葉原のカフェに到着した。店内はいつものように薄暗く、D氏は奥のテーブルでノートPCと自作デバイスを前に座っていた。彼の隣には、見慣れない黒い箱が置かれている。いつものモバイルルーターかと思ったが、よく見ると小さなアンテナが付いている。怪しすぎる。
「遅刻していないな。良い」
D氏は腕時計を確認し、無言でうなずいた。私は席に着き、選んだコントラクトのアドレスを伝えた。彼は一瞥して、鼻で笑った。
「人気のDEXだと?愚か者の選択だ。『みんなが使っているから安全』という思考は、中央集権的な盲信そのものだ」
彼はコーヒーを一口飲み、真剣な表情で私を見つめた。
「スマートコントラクトはWEB3.0の心臓だ。だが、それが正しく動く保証はどこにもない。Etherscanの『Verified』マークは単なる飾りだ。誰かがソースコードをアップロードしただけに過ぎない。バイトコードと一致しているか、悪意あるコードが埋め込まれていないか、それを自分の目で確かめなければ意味がない」
「でも、オープンソースならコミュニティがチェックしているんじゃ…」と反論しかけたが、D氏は冷たく遮った。
「コミュニティを信頼するだと?歴史を振り返れ。2008年の金融危機、ポンジスキーム、無数の詐欺…大勢が信じていても、それが正しい保証にはならない。コードだけが事実だ」
彼はノートPCを開き、私が選んだコントラクトのEtherscanページを表示した。
「これから、このコントラクトを一行一行検証する。君も参加しろ」
D氏はまず、コントラクトのソースコードをローカルにダウンロードした。そして、仮想環境を立ち上げ、Solidityコンパイラ(solc)のインストールを始めた。
「公式バイナリをダウンロードするなど論外だ。自分でソースからビルドしろ。誰かが改ざんしていない保証はどこにある?」
彼はコンパイラのバージョンをコントラクトの指定(0.8.10)に合わせ、コードをコンパイル。その間、私はカフェのケーキを食べようとした。
「待て」D氏は突然声を上げた。「そのケーキ、食べる前に成分を分析したのか?」
「え?いや、普通に注文しただけですが…」
「冗談だ」D氏は珍しく口元を緩めた。「だが、デジタル資産に関しては、そのケーキより慎重になるべきだ」
彼は画面に戻り、生成されたバイトコードをEtherscan上のバイトコードと比較した。
「一致した。だが、これで安心するな。次にコードの中身を見る」
画面に表示されたSolidityコードをスクロールしながら、D氏は私に質問を投げかけた。
「クイズだ。再入攻撃とは何か?」
私は慌てて記憶を掘り起こした。「えっと…関数が再帰的に呼び出されて、資金が何度も引き出される攻撃ですよね?」
「概ね正しい」D氏は珍しく認めた。一週間前の私なら、この質問に答えられなかっただろう。D氏の厳しい指導のおかげで、少しずつではあるが、この複雑な世界を理解し始めている実感があった。
D氏はコード内の transfer
関数に注目した。「ここに call
を使っている。外部コントラクトを呼び出す際、再入攻撃のリスクがある。対策として nonReentrant
修飾子が使われているか確認しろ」
私がコードを追うと、確かにOpenZeppelinの ReentrancyGuard
がインポートされ、該当関数に nonReentrant
が付いている。D氏は満足げにうなずいたが、すぐに次のポイントに移った。
「次だ。オーバーフロー対策は?アクセス制御は?オーナー権限の関数は?」
彼の目は特に onlyOwner
修飾子付きの関数に注がれた。
「この関数、オーナーが全資金を引き出せるぞ。『緊急時用』と書いてあるが、誰がその緊急時を定義するんだ?オーナーが悪意を持てば終わりだ」
私は少し反発した。「でも、このプロジェクトは評判が良くて、監査も受けているらしいですよ」
「評判?」D氏は呆れたように言った。「そんな曖昧なものを信じるのか?監査だって人間がやることだ。ミスもあるし、賄賂で黙る可能性だってある。コードが事実だ。評判は幻想に過ぎない」
D氏は私に課題を出した。
「このコントラクトに10ETHを預けるとして、リスクを洗い出せ。30分やる」
私は緊張しながらコードを読み始めた。D氏の指導を受けつつ、いくつかの怪しい点に気づいた。
withdraw
関数のガス代が異常に高く、意図的なDoS(サービス拒否)攻撃の可能性がある。- 外部ライブラリのバージョンが古く、既知のオーバーフロー脆弱性が修正されていない。
- オーナーがアップグレード可能なプロキシパターンを使用しており、いつでもロジックを変更可能。
30分後、私は結果を報告した。「ガス代が高すぎる点と、プロキシのリスクが気になります。あと、古いライブラリも…」
D氏は珍しく笑みを浮かべた。「悪くない。だが、まだ甘い。プロキシパターンの危険性を過小評価している。オーナーがロジックを書き換えれば、君の10ETHは一瞬で消える」
その時、D氏のスマートフォンに暗号化された通知が鳴った。彼は一瞥して表情を硬くした。
「このコントラクト…今朝、ハッキングされたらしい。オーナーがプロキシを使って不正なロジックに切り替え、全資金を抜いた」
私は声を上げそうになり、慌てて口を押さえた。隣のテーブルの客が不審そうに振り向く。D氏の黒い箱とアンテナ付きデバイスは、どう見ても怪しい。店員も時折、私たちのテーブルを警戒するように見ている。
「声を抑えろ」D氏は小声で言った。「我々は単なる技術者だ。不審に思われるな」
私は深呼吸して冷静さを取り戻した。「僕が選んだコントラクトが…」
D氏は冷静に言った。「偶然だ。だが、これが現実だ。コードを検証しなければ、君の資産は一瞬で消える。運が良かったな。今回は実害がない」
私は背筋が冷たくなった。D氏の極端な警戒心が、初めて現実的な意味を持つ瞬間だった。
D氏はコーヒーを飲み干し、私に最後の教えを授けた。
「スマートコントラクトは魔法ではない。人間が書いたコードだ。ミスもあれば悪意もある。Etherscanの『Verified』マークやプロジェクトの宣伝文句を信じるな。自分の目で確かめたコードだけが真実だ」
彼はノートPCを閉じ、こう付け加えた。
「君はまだ初心者だ。だが、今日の経験を忘れるな。WEB3.0の世界では、誰も君を救ってくれない。自分で守るしかない」
私は思い切って質問した。「D氏は…過去に何か、被害に遭ったことがあるんですか?」
D氏の表情が一瞬だけ曇った。「それは…また別の機会に話そう」
彼は話題を変えるように、USBメモリを私に手渡した。
「次はDeFiの罠について教える。これに資料が入っている。覚悟しておけ」
私はUSBを受け取り、うなずいた。「実は…最近、年利30%を謳うDeFiプロトコルに興味があって…」
D氏の目が鋭く光った。「30%だと?そんな高利回りがリスクなしで得られると思うのか?USBの中にあるレポートを見れば、君が興味を持っている年利30%のDeFiプロトコルの危険性がわかるだろう。」
彼はそう言い残し、店を出て秋葉原の雑踏に消えていった。
その夜、自宅に戻った私は、選んだコントラクトのコードを再度開いた。D氏から受け取った本『暗号技術の基礎』を手に、Solidityの基本を学び直す決意を新たにした。画面には、Etherscanのコントラクトページが表示されている。さっきまで「安全そう」と思っていたそのコードが、今は得体の知れない怪物のように見えた。
もし今朝、そのコントラクトに資金を預けていたら…。想像するだけで背筋が凍る。D氏の極端な警戒心を笑っていた自分が恥ずかしくなった。彼の言葉が頭に響く。「コードだけが事実だ」
私はキーボードに手を置いた。検証の第一歩を踏み出すために。DTPオペレータ時代、印刷物の誤字脱字を見つけるのが仕事だった。今は、コードの中の「死角」を見つける目を養わなければならない。命を守るための新たな技術だ。
豆知識
再入攻撃(Reentrancy Attack)
外部コントラクトが呼び出し元の関数を再帰的に呼び出し、意図しない動作を引き起こす攻撃です。例えば、資金引き出し関数が外部にETHを送る前に状態を更新しない場合、攻撃者が何度も引き出しを繰り返せます。対策として、OpenZeppelinの nonReentrant
修飾子やガス制限が用いられます。
プロキシパターン
コントラクトのロジックをアップグレード可能にする設計パターン。実装を分離し、プロキシ経由で呼び出すことで柔軟性を持たせますが、オーナーが悪意を持てばロジックを不正なものに変更するリスクがあります。
オーバーフロー
Solidityの古いバージョン(0.8.0未満)では、整数演算でオーバーフローが発生する脆弱性がありました。例えば、255 + 1が0に戻るような挙動です。現在はSafeMathライブラリや新しいコンパイラで自動的に対策されています。
次回予告 「第6話:DeFiの罠」
D氏の次なる指導は、DeFiプロジェクトの隠されたリスクだった。「流動性プールに預けるだけで儲かるだと?そんな甘い話はない」と警告するD氏。彼が暴くDeFiの闇とは…?
第5話 おわり