FABRKNT
Reth Fundamentals — Alloy で動かす最初の一歩
EVM の中身を覗く
レッスン 6 / 11·CONTENT12 分25 XP
コース
Reth Fundamentals — Alloy で動かす最初の一歩
レッスンの役割
CONTENT
順序
6 / 11

レッスン6 — EVMはスタックマシンだ

問い

これまで Alloy で「外側から」Ethereum と話してきた。ここから EVM の中身を覗く — Ethereum Virtual Machine は何で、どう動くか。EVM は スタックマシン、レジスタもメモリアドレスもなく、ただ「上に積む / 下から取り出す」だけで計算する。

原理(最小モデル)

  • スタックマシン vs レジスタマシン. スタック = 値の置き場が 1 つ(top)/ レジスタ = 名前付き場所が複数(R0, R1, ...)。EVM は 1024 段のスタックのみ。
  • 5 つの記憶領域. Stack(1024 段、現行計算)/ Memory(揮発、tx 内のみ)/ Calldata(読み専用、tx の入力)/ Storage(永続化、ブロックチェーン状態)/ Code(読み専用、コントラクトコード)。
  • Opcode = 1 バイト命令. 0x01 ADD / 0x60 PUSH1 / 0x52 MSTORE / 0x55 SSTORE、ADD はスタックトップ 2 値を pop して和を push。
  • ガス. 全 opcode が gas コスト持ち、tx に gas limit、消費でランタイム停止、Storage 書き込みが最も高い(永続化)。
  • スタックマシン選択の理由. 命令セット小 + オペランドエンコード単純 = コンセンサスバグ少 + 検証 / ZK 回路化容易、トレードオフはネイティブレジスタコードに対するランタイム効率。

具体例 + ステップで組み立てる

EVMはスタックマシンだ

EVM(Ethereum Virtual Machine)は スタックマシン と呼ばれる仮想機械の一種である。レジスタや関数呼び出し規約を持たず、ほぼすべての計算を スタック 上で行う。

主要な「場所」

EVMは命令を実行する際、以下の3つを使います:

場所性質用途
StackLIFOの最大1024段計算の入出力
Memory1トランザクション内で揮発大きなデータの一時置き場
Storage永続化される(高コスト)コントラクトの状態

ADD命令の動き

例えば「2つの数を足す」というOpcode ADD は次のように動きます:

事前: スタック [..., 7, 5]
ADD実行
事後: スタック [..., 12]

つまり:

  1. スタックの一番上を2つ pop
  2. 加算
  3. 結果を push

本物のRevm Stack

これは理論ではなく、crates/interpreter/src/interpreter/stack.rs にある実構造体:

pub const STACK_LIMIT: usize = 1024;

#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Stack {
    /// スタックの内部データ
    data: Vec<U256>,
}

これがスタック構造の全て:U256 のVec、上限1024。インタープリター内で呼ばれるメソッド群:

pub fn new() -> Self
pub fn push(&mut self, value: U256) -> bool
pub fn pop(&mut self) -> Result<U256, InstructionResult>
pub fn peek(&self, no_from_top: usize) -> Result<U256, InstructionResult>
pub fn popn<const N: usize>(&mut self) -> Option<[U256; N]>
pub fn dup(&mut self, n: usize) -> bool
pub fn swap(&mut self, n: usize) -> bool

注意深く読む:

  • push(...) -> bool — pushできたら true、1024を超えたら false。インタープリターのマクロがこれをチェックして StackOverflow で抜ける。
  • pop(...) -> Result<...> — 明示的なアンダーフロー検出、InstructionResult::StackUnderflow として返す。
  • popn<const N: usize>()N個を一度にpop し、固定サイズ配列で返す。const ジェネリクスによりコンパイラがpopループを展開。これが popn_top! を速くしている正体

ADD の実際の動作

2つpop、加算、push。擬似コード:

事前: スタック [..., 7, 5]
ADD
事後: スタック [..., 12]

Revm の 本物の add ソース(中級ティアで一行ずつ分解する)は、両方popしてpushすらしない。1つpopし、もう1つへ可変参照経由で書き戻す。Revmのインタープリターは、EVM概念モデルをRustへ写しつつ、サイクル単位の最適化を重ねた設計である。

なぜEVMはスタックマシンか

  • シンプルさ — 命令セットが小さく、コンセンサスバグを減らせる
  • 再現性 — 再実行と検証がしやすい
  • ZK化との相性 — スタックセマンティクスは制約系にきれいに写る(後のzkEVMで見ます)

練習

リポジトリで crates/interpreter/src/interpreter/stack.rs を開く。次を探す:

  1. push 内の STACK_LIMIT チェック — オーバーフロー時に何をする?
  2. popn の実装 — const N でコンパイラがループを完全に省略する仕組み
  3. dupswap メソッド — 確保せずインデックスを操作するだけ

これで次のレッスンで自分のミニスタックマシンを書く準備ができました。

まとめ(3行)

  • EVM = スタックマシン、1024 段スタック + 5 記憶領域(Stack / Memory / Calldata / Storage / Code)、Storage のみ永続化。
  • 1 バイト Opcode、ADD は pop 2 + push 1、wrapping arithmetic(mod 2²⁵⁶)、全 opcode が gas コスト持ち、Storage 書き込み最も高い。
  • スタックマシン選択 = 命令セット小 + 検証容易 + ZK 回路化容易、トレードオフはレジスタコードに対するランタイム効率、次はミニ EVM スタッククイズ。