クイズ — ミニEVMスタック
問い
EVM のスタック操作を Rust の Vec で再現 する。pop + push + wrapping arithmetic + unwrap_unchecked の安全パターン — Revm のホットパスでも同じ仕組みが使われている。
原理(最小モデル)
Vec::popの戻り値はOption<T>. 空 →None、Some(v)→ 値取り出し、null チェックを型システムで強制。- EVM スタックリミット 1024. Revm に
pub const STACK_LIMIT: usize = 1024;、超えるとStackOverflow。 - EVM ADD は
wrapping_add. mod 2²⁵⁶ wrap、saturating_addやchecked_addはコンセンサス外、+は debug/release で挙動分岐。 unwrap_unchecked()+unsafe. 直前で長さチェック済 → パニックパスがデッドコード →unwrap_uncheckedでホットパスから消去、手動チェック後unsafeで不変条件を符号化する最適化。- スタックマシン vs レジスタマシン. 命令セット小 + オペランドエンコード単純 = ZK 回路化 / 形式検証容易、レジスタコード並みの速度は出ない。
具体例 + ステップで組み立てる
クイズ:ミニEVMスタック
Rust で小さな EVM 風スタックを 3 操作だけで作ります:
push(n): スタックに数値を積むadd(): 上から2つ pop し、合計を pushpeek(): 上の値を読むだけ(pop しない)
前レッスンで読んだ本物の Revm Stack と同じ形を、シンプル化のため U256 の代わりに i64 で作るだけである。
必要な要素
Vec<i64>をラップするstructimplブロックにnew()、push(&mut self, n)、add(&mut self)、peek(&self)Vec::popとVec::lastの戻り値の型 — 空の Vec のとき Rust は何を返してくる?- underflow 処理:要素が 2 つ未満のとき
add()はどうする?
EVM 仕様に忠実に、加算はオーバーフローで ラップアラウンド する(飽和もパニックもしない)。整数のメソッドで正しいものを探す。
自分で書いてみる
Rust Playground でスケルトンから始める:
struct MiniEvmStack {
data: Vec<i64>,
}
impl MiniEvmStack {
fn new() -> Self {
Self { data: Vec::new() }
}
// TODO: push, add, peek
}
fn main() {
let mut s = MiniEvmStack::new();
s.push(100);
s.push(200);
s.add().unwrap();
println!("{:?}", s.peek()); // 出力は Some(300) になるはず
}
ヒント:
Vec::popはOption<T>を返す — 空のケースが戻り値の型に符号化されているVec::lastはOption<&T>を返す — コピーではなく借用(安い)addの戻り値はResult<(), &'static str>にすると、pop 呼び出しに.ok_or("stack underflow")?が付けられる- EVM 互換の加算は、名前に「wrap」を含む整数メソッド
動いたら、前のレッスンの本物の Revm Stack と頭の中で設計を比較してみてください — 同じ形のはず。
クイズ
まとめ(3行)
Vec::pop -> Option<T>で空チェック型強制、EVM スタックリミット 1024 = RevmSTACK_LIMIT、ADD はwrapping_add(mod 2²⁵⁶)。unwrap_unchecked+unsafe= 事前長さチェック後の最適化、ホットパスからパニックパス消去、手動チェック後 unsafe で不変条件符号化。- スタックマシンは命令セット小 + ZK / 検証容易 = レジスタコードに対するランタイム速度とのトレードオフ、次は async / トレイト / ジェネリクス。