Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 74 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,51 @@ surface freezes at 1.0.

## [Unreleased]

### Added

- **Bundle simulation + coinbase accounting (Phase 6 Track A+B).** New
`EvmOverlay::simulate_bundle` (and the cache-side convenience
`EvmCache::simulate_bundle`, which snapshots internally and never mutates the
cache) apply an ordered sequence of `Call`-kind transactions over **cumulative**
block state on a single overlay — transaction `i` observes the committed writes
of `0..i`. A `RevertPolicy` chooses between `Atomic` (any revert rolls the whole
bundle back) and `AllowReverts(indices)` (whitelisted reverts roll back only
their own transaction and execution continues). `BundleResult.coinbase_payment`
reports the miner payment as the block beneficiary's balance delta — the honest
priority fee plus any direct coinbase tips; revm already burns the base-fee
portion in-EVM (EIP-1559), so no base-fee correction is applied. New public types
live in the `bundle` module and are re-exported at the crate root: `BundleTx`,
`BundleOptions`, `RevertPolicy`, `TxOutcome`, `BundleResult`.
- **`EvmCache::set_basefee(U256)`** installs a block base fee (the `BASEFEE`
opcode) that propagates into the next `create_snapshot()`, so offline caches
with no fetched header can exercise base-fee-aware (`Mainnet`) bundle accounting.
- Call-frame tracing (`tracing` module): `CallTracer`, a `revm::Inspector` that
reconstructs the call-frame tree (`CallTrace`) of a simulation — top-level call
plus nested `CALL`/`STATICCALL`/`DELEGATECALL`/`CALLCODE` and `CREATE`/`CREATE2`
frames, each with from/to/value/input/gas/output/`CallStatus`/depth/subcalls.
Implemented via the `call`/`call_end`/`create`/`create_end` hooks (no
opcode/step or `SLOAD`/`SSTORE` tracing). Re-exported at the crate root as
`CallTracer`, `CallTrace`, `CallKind`, `CallStatus`.
- `InspectorStack<A, B>`, a composing `revm::Inspector` that fans out every hook
to two inner inspectors so, e.g., a `CallTracer` and a `TransferInspector`
capture independently in one pass. Re-exported at the crate root.
- `EvmOverlay::call_raw_with_inspector`: a public, inspector-generic single-call
seam that attaches any `revm::Inspector` (honoring `TxConfig` and `commit`) and
returns the raw `ExecutionResult` (a revert/halt is `Ok`, not `Err`) alongside
the inspector for the caller to read.

## [0.1.0] - 2026-06-25

This is the first release line. It captures the work done across the
pre-release development phases (see [`docs/ROADMAP.md`](docs/ROADMAP.md)).

### Changed

- **Breaking:** the in-memory chain ID is no longer hard-defaulted to Arbitrum
(`42161`). When no chain ID is set explicitly and no disk `CacheConfig` is
supplied, the cache now infers it from the provider (`eth_chainId`) and falls
back to `1` (Ethereum mainnet) only if that query fails. Set it explicitly with
the new `EvmCacheBuilder::chain_id` / `EvmCache::set_chain_id`.
- **Breaking:** extracted the old in-crate AMM adapter surface before public
release. Protocol-specific storage layouts, protocol metadata, injector
helpers, tick snapshots, and protocol log decoders belong in `evm-amm-state`;
Expand All @@ -33,12 +73,27 @@ pre-release development phases (see [`docs/ROADMAP.md`](docs/ROADMAP.md)).

### Added

- **Honest performance section & benchmarks.** The README "Performance" section
is framed against a *competent* baseline (a shared `foundry-fork-db`
`SharedBackend` + `checkpoint`/`revert` isolation), not a naive
fork-per-candidate strawman. It states plainly that within-block fetch count,
single-threaded CPU, and time-to-result are ~1× against that baseline, and
leads with the genuinely-unique wins: cross-block freshness (0 RPC fetches/block,
pinned in `tests/event_pipeline.rs` — `foundry-fork-db`'s cache is not
block-keyed), parallel `Send` fan-out (`benches/fanout.rs`, a modest measured
~1.2× on micro-sims), point-in-time consistency, and the act-then-validate
control plane (`benches/freshness.rs`). Adds `examples/fetch_minimization_counted.rs`
and `tests/fetch_minimization.rs` (the fetch-once mechanic, with the
shared-backend caveat stated). The internal copy-on-write snapshot cost model
moved to [`docs/INTERNALS.md`](docs/INTERNALS.md).
- **Forked EVM cache** (`cache::EvmCache`) backed by `foundry-fork-db` with lazy
RPC loading and on-disk persistence for accounts, storage, bytecode, and
immutable metadata.
- **`EvmCacheBuilder`** — a fluent constructor (`EvmCache::builder(provider)`)
subsuming the positional `with_cache` / `from_backend` constructors, with
block pin, EVM spec, cache-config, and shared-memory-capacity configuration.
block pin, EVM spec, cache-config, chain-ID, and shared-memory-capacity
configuration. `EvmCacheBuilder::chain_id` sets the `CHAINID` opcode value
explicitly (recommended); `EvmCache::set_chain_id` sets it post-construction.
- **Snapshots and overlays** — `create_snapshot()` produces an immutable,
`Send + Sync` `EvmSnapshot`; `EvmOverlay` is a cheap per-simulation clone for
isolated parallel evaluation.
Expand Down Expand Up @@ -164,7 +219,21 @@ pre-release development phases (see [`docs/ROADMAP.md`](docs/ROADMAP.md)).
resync requests. Reversible storage-slot changes are rolled back in reverse
apply order; account/code changes and prior purge effects conservatively fall
back to targeted purge updates because `StateDiff` does not carry enough data
to reconstruct those cache entries exactly. Generic core.
to reconstruct those cache entries exactly. Recovery is bounded to the
configured `ReactiveConfig::journal_depth` (default 64): a reorg deeper than
that — or any reorg when `journal_depth = 0` — recovers only the blocks still
resident in the journal and emits a `tracing::warn!` for the under-recovered
span (the freshness loop is the backstop; see `docs/KNOWN_ISSUES.md`).
Demonstrated offline in `examples/reactive_runtime.rs`. Generic core.
- **Cold-start** (`cold_start` module, default-enabled / reactive-gated) —
declarative warming of a working set of accounts and storage slots into the
cache in one batched pass via `EvmCache::run_cold_start` /
`execute_cold_start_round` and a `ColdStartPlanner` (discover slots through a
view-call, then verify them through the `StorageBatchFetchFn`), returning a
structured `ColdStartRunReport` (`ColdStartConfig`/`Plan`/`Results`/
`RoundSummary`/`Step`/`RoundOutcome`/`Error`, with per-slot `SlotOutcome`s). The
account-warming phase is serial (one fetch per declared account) and aborts the
round on the first account failure; storage verify/probe are batched. Generic core.
- **`StateUpdate::SlotMasked`** (`state_update`, Phase 4) — a cold-aware
read-modify-write *masked* slot write (`new = (old & !mask) | (value & mask)`)
with the `StateUpdate::slot_masked` constructor, so a pure decoder can update
Expand Down Expand Up @@ -277,7 +346,7 @@ pre-release development phases (see [`docs/ROADMAP.md`](docs/ROADMAP.md)).
layouts.
- Simulation entry points that distinguish failure modes return
`SimulationResult<T>` (`Result<T, SimError>`), separating decoded reverts,
EVM halts, and host errors. `SimulationErrorKind` remains as a deprecated alias.
EVM halts, and host errors.

### Fixed

Expand Down Expand Up @@ -369,4 +438,5 @@ pre-release development phases (see [`docs/ROADMAP.md`](docs/ROADMAP.md)).
- `EvmCache` requires a multi-thread tokio runtime for any RPC-touching path.
- See [`docs/KNOWN_ISSUES.md`](docs/KNOWN_ISSUES.md) for current limitations.

[Unreleased]: https://github.com/KaiCode2/evm-fork-cache/commits/main
[Unreleased]: https://github.com/KaiCode2/evm-fork-cache/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/KaiCode2/evm-fork-cache/releases/tag/v0.1.0
24 changes: 24 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ readme = "README.md"
repository = "https://github.com/KaiCode2/evm-fork-cache"
documentation = "https://docs.rs/evm-fork-cache"

# Keep the published crate lean: the per-phase development specs are internal
# planning artifacts (they reference an adapter surface extracted before release),
# and the CI workflow has no consumer value. The consumer-facing ROADMAP.md and
# KNOWN_ISSUES.md under docs/ are still shipped.
exclude = ["docs/phase-*-spec.md", ".github/"]

# Standalone workspace root: keeps this crate from being absorbed by any
# ancestor-directory workspace and gives it its own Cargo.lock.
[workspace]
Expand Down Expand Up @@ -38,6 +44,12 @@ anyhow = "1.0.98"
bincode = "1.3"
foundry-fork-db = "0.22"
revm = { version = "34.0", features = ["std", "serde", "optional_eip3607", "optional_no_base_fee", "optional_balance_check"] }
# Pulled by `reactive-ws` only to select rustls' `ring` crypto provider for the
# alloy WebSocket (wss://) TLS path. If a transitive dep also enabled aws-lc-rs,
# rustls 0.23 would have no unambiguous process default and a wss:// connection
# would panic ("no process-level CryptoProvider available"); the AlloySubscriber
# installs the ring provider best-effort at WS init. Do not remove without
# re-checking the TLS provider story.
rustls = { version = "0.23", default-features = false, optional = true }
serde = { version = "1.0.228", features = ["derive"] }
thiserror = "2.0"
Expand Down Expand Up @@ -71,6 +83,10 @@ harness = false
name = "simulation"
harness = false

[[bench]]
name = "fanout"
harness = false

[[bench]]
name = "freshness"
harness = false
Expand All @@ -87,6 +103,14 @@ harness = false
name = "reactive_alloy_amm_live_probe"
required-features = ["reactive"]

[[example]]
name = "reactive_runtime"
required-features = ["reactive"]

[[example]]
name = "cold_start"
required-features = ["reactive"]

# RPC-gated real-contract benchmarks. Skipped (not failed) when RPC_URL is unset,
# so `cargo bench` stays offline by default.
[[bench]]
Expand Down
Loading
Loading