スマートコントラクトとEVM
なぜスマートコントラクトは全ノードで同じ結果を出せ、無限ループで台帳を止められないのか。EVMのスタックマシン・ガス計量・決定的な状態遷移を原理から辿り、堅牢なコントラクト設計の勘所まで腑に落ちます。
- 1.EVMは256ビット語のスタックマシンで、全ノードが同じバイトコードを同じ順序で実行し同一の状態遷移を得る。決定性がなければ合意(台帳の一致)が成り立たない。
- 2.計算資源はガスで計量する。各オペコードに固定コストがあり、ガスが尽きると out-of-gas で全体が巻き戻る。これが停止性問題を回避し、資源濫用(DoS)を経済的に抑える。
- 3.外部呼び出し・再入・整数演算・ストレージコストがセキュリティの急所。Checks-Effects-Interactions と再入ガードで状態の一貫性を守るのが定石。
スマートコントラクトとは何か
スマートコントラクトは、ブロックチェーン(分散台帳)上に配置され、台帳の状態を条件付きで書き換えるプログラムです。Ethereum ではコントラクトはアカウントの一種で、残高に加えてコードと永続ストレージを持ちます。ユーザーが署名したトランザクションがコントラクトを呼び出すと、各ノードはそのコードを実行し、台帳の状態を次の状態へ遷移させます。
ここで本質的な要請が生じます。分散台帳は多数のノードが同じ状態のコピーを保持し、合意(コンセンサス)で一致を保つ仕組みです。したがって、あるトランザクションを実行した結果はどのノードでも寸分違わず一致しなければなりません。1ノードでも異なる結果を出せば、状態のハッシュがずれ、そのブロックは合意から弾かれます。この「全ノードでビット単位に同じ結果」という制約が、後述する EVM の設計すべてを規定します。ノード間の合意そのものの原理は /devops/ の分散合意の解説に譲り、本稿は単一トランザクションの実行モデルに集中します。
スマートコントラクトの実行は「入力(トランザクション)と現在の状態から次の状態を決める純粋な関数」とみなせます。状態遷移関数を S' = f(S, Tx) と書くと、f は全ノードで同一のバイトコードとして共有され、同じ S と Tx からは常に同じ S' を返す——この決定性が台帳一致の土台です。
EVMの実行モデル:スタックマシン
EVM(Ethereum Virtual Machine)は、コントラクトのバイトコードを解釈実行する仮想マシンです。物理CPUに依存しない抽象機械を挟むことで、どのハードウェア・OS上でも同一の計算結果を保証します。EVM の設計は次の特徴を持ちます。
- スタックマシン:レジスタを持たず、深さ最大 1024 のオペランドスタックで演算する。
PUSHで値を積み、ADDは上位2要素を取り出して和を積み直す。 - 256ビット語:ワードサイズが 256 ビット。Keccak-256 ハッシュや楕円曲線(secp256k1)の演算と桁を合わせ、暗号処理を効率化するための選択です。暗号プリミティブ自体の原理は /security/ を参照。
- 3層のデータ領域:揮発的で安価な stack/memory(実行中のみ、線形バイト配列)と、永続的で高価な storage(
{key: value}の 256→256 ビット写像、ブロックを跨いで残る)。
ソースは Solidity や Vyper で書かれ、コンパイラが EVM バイトコード(オペコード列)へ変換します。例えば PUSH1 0x05 PUSH1 0x03 ADD は「5 と 3 を積んで加算」を表し、実行後スタック頂上には 8 が残ります。オペコードは1バイトで表現される(理論上は最大256種)ため、実際に定義済みのものは約140種で、算術・比較・ストレージ入出力・制御フロー・環境情報取得・他コントラクト呼び出しなどを担います。
バイトコード実行の骨子(1トランザクション)
pc ← 0 # プログラムカウンタ
gas ← Tx.gasLimit
loop:
op ← code[pc] # 現在のオペコードを取得
cost ← gasCost(op, state) # そのオペコードのガス消費量
if gas < cost: revert(out-of-gas) # ガス不足なら全巻き戻し
gas ← gas - cost
execute(op) # stack/memory/storage を更新
pc ← next(pc, op) # JUMP系以外は pc を進める
if op == STOP or RETURN: break
ガス:計算資源の計量と停止性
任意のプログラムには停止性問題(あるプログラムが停止するか一般には判定不能)がつきまといます。もしコントラクトが無限ループを書けてしまえば、それを実行するノードは永久に止まり、台帳全体が前進できません。EVM はこれをガスという計量単位で回避します。
各オペコードには固定のガスコストが割り当てられます。加算のような軽い演算は 3 ガス、ストレージ書き込みのような重い操作は桁違いに高い、といった具合です。トランザクション発行者は gasLimit(消費してよい上限)と gasPrice/手数料(1ガスあたりの支払い)を指定します。実行のたびに残ガスが減り、ゼロになった瞬間に out-of-gas 例外が発生してその呼び出しは打ち切られます。無限ループは「無限のガス」を要し、有限の gasLimit では必ず途中で停止します。停止性の理論的困難を、経済的な有限資源にすり替えて実務的に解いているのが要点です。
ガス切れやその他の例外(REVERT、失敗した assert など)が起きると、そのトランザクションが行った状態変更はすべて巻き戻り、実行前の状態に戻ります(アトミック性)。ただし消費されたガスは返らない——ここが直感に反します。計算資源はすでに全ノードが費やしており、その対価は徴収されるためです。「途中まで書き込んだストレージが半端に残る」ことは起きません。
ガスにはもう一つの役割があります。DoS(サービス妨害)への経済的防壁です。もし計算が無料なら、攻撃者は重い処理を無数に投げてネットワークを麻痺させられます。ガス課金により、資源を使うほど費用がかかるため、濫用は割に合わなくなります。さらにガスコストの相場観は誤ってはならず、実際に過小評価されたオペコードが 2016 年に DoS 攻撃を招き、ハードフォークでコスト再調整(EIP-150 など)が行われた歴史があります。
| 操作 | 相対コスト | 理由 |
|---|---|---|
| 算術・スタック操作(ADD 等) | 極小(数ガス) | CPU 内で完結し副作用がない |
| memory 拡張 | 使用量の二次で増加 | 確保バイト数が増えるほど急騰させ濫用を抑止 |
| storage 新規書き込み(SSTORE 0→非0) | 非常に高い(2万ガス級) | 全ノードの永続状態を恒久的に肥大化させるため |
| storage 解放(非0→0) | 一部が返金 | 状態を縮小するインセンティブを与える |
| 外部コントラクト呼び出し(CALL) | 高い+転送ガス | 別コードの実行を巻き込み再入の起点にもなる |
状態遷移の決定性を保証する仕組み
決定性は「祈って得られる」ものではなく、EVM が非決定的になりうる入力源を徹底的に排除することで作られます。通常のプログラムが当然に使う機能の多くが、コントラクトでは意図的に禁止されています。
| 通常のプログラム | EVMでの扱い | なぜ |
|---|---|---|
| 現在時刻(壁時計) | block.timestamp のみ(ブロック確定値) | 実時間はノードごとに異なる。合意された1つの値だけ参照可 |
| 乱数生成器 | オンチェーンには真の乱数源がない | ノードごとに違う乱数は状態を分岐させる |
| ファイル・ネットワークI/O | 一切不可 | 外部世界の応答は非決定的で再現できない |
| 浮動小数点演算 | 存在しない(整数のみ) | 丸め・実装差で結果がぶれるため排除 |
| 並行・マルチスレッド | 単一スレッドで逐次実行 | 実行順序の非決定性を消すため |
参照できる「外部情報」は、block.number/block.timestamp/msg.sender/msg.value のように、そのブロックとトランザクションで合意済みの確定値に限られます。これらは全ノードで同一なので決定性を壊しません。逆に、乱数やタイムスタンプに依存してお金を動かす設計は、採掘者・提案者による操作(timestamp のわずかな調整など)に晒される脆弱性となります。ブロックチェーン外のデータ(為替レート、天気)が必要なら、それを署名付きで持ち込むオラクルという別レイヤに頼るしかなく、EVM 内部からは外界を直接観測できません。
オンチェーンのゲームや抽選で乱数が必要な場合、block 由来の値をそのまま乱数に使うのは危険です。検証可能な乱数関数(VRF)など、チェーン外で生成し証明付きでオンチェーン検証する方式を使うのが定石です。EVM の決定性という制約は、乱数を「内部で作れない」という形で設計に跳ね返ってきます。
コントラクトの制約とセキュリティ
決定性・ガス・永続ストレージという性質は、そのままセキュリティ上の急所になります。とりわけ外部呼び出しが絡む攻撃が代表的です。
再入攻撃(reentrancy) が最も有名です。コントラクトAが送金のため外部アドレスBを CALL すると、制御がBのコードへ移る。もしAが「送金してから残高を減らす」順序だと、Bは受け取り処理の中でAを再び呼び出すことができ、まだ減っていない残高を根拠に二重・多重に引き出せてしまいます。2016 年の The DAO 事件はこの型で、Ethereum のハードフォーク(Ethereum Classic 分岐)に至りました。
防御の基本は処理順序の規律です。(1) 事前条件を検査(Checks)、(2) 自分の状態を先に更新(Effects=残高を減らす)、(3) 最後に外部呼び出し(Interactions)——この順を守れば、外部へ制御が渡る時点で状態は既に確定済みで、再入しても不正が成立しません。加えて nonReentrant の再入ガード(実行中フラグで再入自体を拒否)を併用するのが堅牢です。
他の主要な急所も、EVM の性質から論理的に導けます。
| 脅威 | 原因(EVMの性質) | 対策 |
|---|---|---|
| 整数オーバーフロー/アンダーフロー | 256ビット固定幅の剰余演算で 0-1 が最大値に化ける | checked 算術(Solidity 0.8+ は既定で revert) |
| 再入 | CALL 中に制御が外部へ渡り再入可能 | Checks-Effects-Interactions+再入ガード |
| 未検査の外部呼び出し結果 | CALL は失敗しても例外を投げず false を返す | 戻り値を必ず検査、または revert する呼び出しを使う |
| アクセス制御の欠落 | コントラクトのコードと状態は公開・誰でも呼べる | 呼び出し元(msg.sender)を明示的に権限検査 |
| フロントランニング(MEV) | 保留トランザクションが公開され順序を操作可能 | コミットメント方式や順序に依存しない設計 |
もう一つ根本的な制約がコードの不変性です。デプロイされたバイトコードは原則書き換えられません。バグがあっても後から直接パッチできないため、実務ではプロキシパターン(呼び出しを受ける薄い契約が、差し替え可能な実装契約へ DELEGATECALL で処理を委譲する)で更新可能性を確保します。ただし DELEGATECALL は呼び出し先のコードを自分のストレージ文脈で実行するため、ストレージ配置のずれや悪意ある実装への差し替えが新たな攻撃面になります。加えて、コードもストレージも公開が原則で、「秘密の値をコントラクトに埋め込めば隠せる」という発想は成り立ちません(ネットワーク上のデータ機密性の考え方は /network/ の暗号化通信とは別問題で、オンチェーンの値はそもそも全公開です)。
「なぜEVMは256ビット整数のみで浮動小数点を持たないか」「なぜガスが必要か」「再入攻撃の原理と対策」は頻出です。核は一貫して決定性——全ノードで同一の状態遷移を得るために、非決定的要素(浮動小数・実時間・乱数・並行実行・無限計算)を排除し、資源をガスで有限化した、と一本の筋で説明できると強いです。
まとめ
- スマートコントラクトは分散台帳の状態遷移関数であり、全ノードがビット単位に同一の結果を出す決定性が合意(台帳一致)の前提になる。
- EVM は 256 ビット語のスタックマシン。stack/memory(揮発・安価)と storage(永続・高価)を分け、バイトコードを逐次実行して状態を更新する。
- ガスは各オペコードに固定コストを課し、gasLimit の枯渇で out-of-gas 巻き戻しを起こす。これが停止性問題の回避と DoS の経済的抑止を同時に担う。
- 決定性のため、実時間・乱数・I/O・浮動小数・並行実行は排除され、参照できるのは合意済みの確定値のみ。乱数はオラクル/VRF で外から持ち込む。
- セキュリティの急所は外部呼び出し(再入)・整数演算・ストレージコスト・不変性で、Checks-Effects-Interactions と再入ガード、checked 算術、明示的アクセス制御が定石。
ブロックチェーン Article
スマートコントラクトとEVMを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
ブロックチェーン
比較で見る軸
難易度: advanced / カテゴリ: ブロックチェーン / タグ数: 6
導入後に効く点
計算資源はガスで計量する。各オペコードに固定コストがあり、ガスが尽きると out-of-gas で全体が巻き戻る。これが停止性問題を回避し、資源濫用(DoS)を経済的に抑える。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- ブロックチェーン
- タグ数
- 6
判断チェックリスト
- 自社の用途が「ブロックチェーン / スマートコントラクト」に近いか確認する。
- 強みである「EVMは256ビット語のスタックマシンで、全ノードが同じバイトコードを同じ順序で実行し同一の状態遷移を得る。決定性がなければ合意(台帳の一致)が成り立たない。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。