Skip to content

Latest commit

 

History

History
199 lines (131 loc) · 9.62 KB

File metadata and controls

199 lines (131 loc) · 9.62 KB

Rust Development

Guidance for AI agents working with Rust code in the Optimism monorepo. See dev-workflow.md for tool versions, PR workflow, and other cross-language guidance.

Workspace Layout

All Rust code lives under rust/. This is a unified Cargo workspace — always run Rust commands from this directory. The workspace contains three main component groups:

  • Kona — Proof system and rollup node (rust/kona/)
  • Op-Reth — OP Stack execution client built on reth (rust/op-reth/)
  • Op-Alloy / Alloy extensions — OP Stack types and providers

Check rust/Cargo.toml for the full workspace member list, dependency versions, and lint configuration. The Rust toolchain version is pinned in rust/rust-toolchain.toml.

Workspace tool config lives at rust/.config/nextest.toml (test settings, JUnit output) and zepter.yaml (feature-propagation lint). nextest, zepter, and cargo-release discover config only from the workspace root, so per-component rust/<crate>/.config/*.toml files are not read and have no effect — put new test/lint config at rust/.config/. (rust/op-rbuilder and rust/rollup-boost are separate vendored Cargo workspaces with their own root configs.)

Migrated, not vendored

Most of the OP Stack Rust code here was officially migrated into the monorepo in coordination with the upstream repository owners — it is not a vendored copy. The upstream crates have been deleted or deprecated, so the entire Rust OP Stack is now developed here. This applies to:

  • op-reth (rust/op-reth/)
  • kona-* (rust/kona/)
  • op-alloy-* (rust/op-alloy/)
  • alloy-op-* (rust/alloy-op-evm/, rust/alloy-op-hardforks/)
  • op-revm (rust/op-revm/)

These crates are owned and edited directly here — do not look upstream for their source. They still depend on generic upstream crates (e.g. reth's engine/provider crates, alloy, revm), which remain external and pinned in rust/Cargo.toml; a change to one of those generic APIs has to go upstream first and then be consumed via a version bump. When changing upstream behavior or API leads to a better overall solution than working around it locally, it is acceptable — and often preferable — to propose that change upstream (a PR to the respective repository); suggest this when it applies.

Known exception: op-alloy-flz has not been migrated yet and is still an external dependency, tracked by #21087.

Still vendored: rust/op-rbuilder/ and rust/rollup-boost/ are vendored copies, slated for deprecation.

Build System

Run just --list in rust/ to see all available targets. The key ones:

cd rust

# Build the workspace
just build

# Build the workspace excluding example crates with the fast-build profile
just build-no-examples

# Build in release mode
just build-release

# Build specific binaries
just build-node      # kona-node
just build-op-reth   # op-reth

superchain-registry submodule (op-reth)

The reth-optimism-chainspec crate's build.rs materializes its chain-config archive (res/superchain-configs.tar, gitignored) from the superchain-registry submodule at the repo root. Any op-reth build that enables the superchain-configs feature (the op-reth binary, clippy --all-features, the chainspec tests) needs that submodule checked out, or the build fails. Initialize it with just update-superchain-registry-submodule (or just sync-superchain, which also does it). Once res/ holds the archive, later builds reuse it without touching the submodule (default mode mirrors kona's KONA_SYNC_SUPERCHAIN); set OP_RETH_SYNC_SUPERCHAIN=1 to force a regeneration.

Running Tests

Tests use cargo-nextest (not cargo test) for unit tests:

cd rust

# Run all tests (unit + doc tests)
just test

# Unit tests only (excludes online tests)
just test-unit

# Doc tests only
just test-docs

Running op-reth E2E Tests

The op-reth E2E tests (rust/op-reth/tests/proofs/) run a full devnet with op-geth (sequencer) and op-reth (validator). They require two build prerequisites:

  1. Forge artifacts — the devnet deploys contracts from compiled artifacts:

    cd packages/contracts-bedrock
    mise exec -- just build-no-tests
  2. op-reth release binary — the test harness (op-devstack/sysgo/rust_binary.go) only searches target/release/, not target/debug/. Options:

    # Option A: let the test build it (slow first run, cached after)
    RUST_JIT_BUILD=1 go test -v -run TestName ./rust/op-reth/tests/proofs/core/
    
    # Option B: pre-build the binary
    cd rust && just build-op-reth

Run from the monorepo root:

mise exec -- go test -v -run TestExecutePayloadSuccess -count=1 ./rust/op-reth/tests/proofs/core/

Generating Prestates

Kona prestates are built via Docker:

cd rust
just build-kona-prestates

Linting

cd rust

# Run all lints (format check + clippy + doc lints)
just lint

# Individual lint steps
just fmt-check      # formatting (requires nightly)
just lint-clippy    # clippy with all features, -D warnings
just lint-docs      # rustdoc warnings

Lint configuration lives in rust/Cargo.toml (workspace lints section), rust/clippy.toml, and rust/rustfmt.toml.

Formatting Requires Nightly

Formatting uses a pinned nightly toolchain (defined as NIGHTLY in rust/justfile). It is installed via mise.

Then use just fmt-fix to auto-format, or just fmt-check to verify.

no_std Compatibility

Many kona and alloy crates must compile without the standard library (for the fault proof VM). If you modify these crates, verify no_std builds:

cd rust
just check-no-std

This builds affected crates for the riscv32imac-unknown-none-elf target.

Dependency Auditing

The workspace uses cargo-deny for license, advisory, and dependency checks. Configuration is in rust/deny.toml.

cd rust
just deny

Before Every Commit

Run these checks from rust/. Fix all issues — CI enforces zero warnings.

  1. Format — only after the final edit, never between edits:

    just fmt-fix

    The nightly formatter has opinions (e.g., collapsing multi-line let bindings onto one line) that the Edit tool doesn't replicate — running it mid-session and then editing again leaves unformatted code behind and fails the rust-fmt CI check. After formatting, run git diff --stat to confirm the working tree matches what you're about to commit.

  2. Lint — this checks formatting, clippy, and doc lints:

    just lint
  3. Test — run tests for changed packages:

    just test-unit
  4. no_std — if you changed any proof, protocol, or alloy crate:

    just check-no-std

CI

Op-reth requires clang / libclang-dev for reth-mdbx-sys bindgen. CI installs this automatically — if you see bindgen errors locally, install clang.

Hardforks

The OP fork → implied L1 (Ethereum) fork mapping is defined once, in OpHardfork::activates_l1_fork in rust/alloy-op-hardforks/src/lib.rs. When a new OP hardfork rides an L1 fork (e.g. Isthmus → Prague, Karst → Osaka), add the single match arm there; the cumulative (implied_l1_fork) and inverse (activating_op_fork) views and all downstream consumers (op-revm, op-reth chainspec, kona) derive from it.

Updating the reth dependency

The full guide lives at rust/UPDATING-RETH.md. Read it before bumping the reth pin in rust/Cargo.toml — or run the /update-reth skill (.claude/skills/update-reth/), which wraps the guide in an end-to-end agent workflow.

When reviewing a bump (rather than performing one), use reth-update-review.md and the reth-update-reviewer agent — they surface upstream reth/revm/alloy changes that should have forced a change in our in-tree op- forks but produced no diff in our tree.

Agent-specific tips beyond what's in the guide:

  • The bump is iterative — run cargo check --workspace --tests, fix the first batch of errors, re-run, repeat. Don't try to enumerate every API change up front and don't ask the user to confirm every line of adaptation; just iterate to a green compile and report the diff at the end.
  • If you have a local checkout of paradigmxyz/reth, use it to look up upstream trait signatures and run git log <old-rev>..<new-rev> to find the commit that changed any given symbol — much faster and more reliable than hand-fetching raw GitHub URLs. If you don't know whether one is available, ask the user. Don't assume a path.
  • For trait methods that gained an ignored parameter, prefix the new param with _ (e.g. _block_access_list_hash: Option<B256>) so it doesn't generate an unused-variable warning. Don't invent a meaningful name unless you're actually plumbing the value through.
  • If upstream removed a trait or re-export that op-reth still uses, vendor it locally with a comment pointing at the upstream removal PR — don't try to refactor op-reth to do without it without first confirming the consumer is actually unused. The "stale" label upstream doesn't mean unused downstream.
  • When the new rev pulls in new transitive deps (visible as Adding <crate> lines from cargo update), check whether they're from upstream reth's own deps or from a misconfiguration on our side. cargo tree -i <crate> traces the path.

Skills

  • Fix Rust Formatting (.claude/skills/fix-rust-fmt/SKILL.md): Fixes rust-fmt CI failures by installing the pinned nightly toolchain and running just fmt-fix. Invoke with /fix-rust-fmt.