Skip to content
Open
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
15 changes: 0 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ on:

env:
CARGO_TERM_COLOR: always
TRANSPORT_REF: 7868bea593dec5748ad7475d1909fc3a2de0d4ad

permissions:
contents: read
Expand All @@ -27,13 +26,6 @@ jobs:
path: evm-fork-cache
fetch-depth: 0

- name: Check out exact transport candidate
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
repository: KaiCode2/alloy-transport-balancer
ref: ${{ env.TRANSPORT_REF }}
path: alloy-transport-balancer

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
with:
Expand Down Expand Up @@ -110,13 +102,6 @@ jobs:
with:
path: evm-fork-cache

- name: Check out exact transport candidate
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
repository: KaiCode2/alloy-transport-balancer
ref: ${{ env.TRANSPORT_REF }}
path: alloy-transport-balancer

- name: Install Rust 1.90 (declared MSRV)
uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
with:
Expand Down
17 changes: 16 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ versions (`0.x.0`); patch versions (`0.x.y`) are non-breaking. The roadmap in
[`docs/ROADMAP.md`](docs/ROADMAP.md) deliberately reshapes the API before the
surface freezes at 1.0.

## [Unreleased]

## [0.4.0] - 2026-09-05

### Changed

- Promotes the current alpha cache and subscriber surface, including canonical
subscription log coverage, gap recovery, provider provenance, isolated
preconfirmation snapshots, bounded raw Flashblocks normalization, and
cooperative simulation cancellation. Uses `alloy-transport-balancer 0.3.0`.

- Correct test-helper feature gates for isolated reactive and WebSocket builds.

## [0.4.0-alpha.5] - 2026-08-22

### Migration checklist
Expand Down Expand Up @@ -1356,7 +1369,7 @@ 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/compare/v0.4.0-alpha.4...HEAD
[Unreleased]: https://github.com/KaiCode2/evm-fork-cache/compare/v0.4.0...HEAD
[0.4.0-alpha.4]: https://github.com/KaiCode2/evm-fork-cache/compare/v0.4.0-alpha.3...v0.4.0-alpha.4
[0.4.0-alpha.3]: https://github.com/KaiCode2/evm-fork-cache/compare/v0.4.0-alpha.2...v0.4.0-alpha.3
[0.4.0-alpha.2]: https://github.com/KaiCode2/evm-fork-cache/compare/v0.4.0-alpha.1...v0.4.0-alpha.2
Expand All @@ -1365,3 +1378,5 @@ pre-release development phases (see [`docs/ROADMAP.md`](docs/ROADMAP.md)).
[0.2.1]: https://github.com/KaiCode2/evm-fork-cache/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/KaiCode2/evm-fork-cache/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/KaiCode2/evm-fork-cache/releases/tag/v0.1.0

[0.4.0]: https://github.com/KaiCode2/evm-fork-cache/releases/tag/v0.4.0
16 changes: 9 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "evm-fork-cache"
version = "0.4.0-alpha.5"
version = "0.4.0"
edition = "2024"
rust-version = "1.90"
license = "MIT OR Apache-2.0"
Expand Down Expand Up @@ -68,7 +68,7 @@ alloy-rlp = "0.3"
alloy-rpc-client = ">=1.0.38, <1.7"
alloy-rpc-types-eth = ">=1.0.38, <1.7"
alloy-sol-types = ">=1.4, <1.7"
alloy-transport-balancer = { version = "0.3.0-alpha.2", path = "../alloy-transport-balancer", default-features = false }
alloy-transport-balancer = { version = "0.3.0", default-features = false }

futures = "0.3"
bincode = "1.3"
Expand Down
19 changes: 9 additions & 10 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Releasing

`evm-fork-cache` 0.4.0-alpha.4 adds bounded, caller-timed single-index reorder
`evm-fork-cache` 0.4.0 adds bounded, caller-timed single-index reorder
tolerance to the default-off raw JSON Flashblocks normalization layer, immutable
snapshot lineage lookups, and provider-free cooperative cancellation scopes for
related overlay calls. Publish `alloy-transport-balancer 0.3.0-alpha.2` first,
related overlay calls. Publish `alloy-transport-balancer 0.3.0` first,
then publish this crate before any extension crate that declares
`evm-fork-cache = "0.4.0-alpha.4"`.
`evm-fork-cache = "0.4.0"`.
No release step is automatic: use clean, reviewed commits and never publish
from a credential-bearing working tree.

Expand Down Expand Up @@ -53,9 +53,8 @@ Confirm every third-party `uses:` entry remains pinned to the officially
verified full commit recorded in `SECURITY.md`, not a mutable tag or branch.
The stable and MSRV jobs must use the same pinned `dtolnay/rust-toolchain`
action with explicit `toolchain: stable` and `toolchain: 1.90.0` inputs.
Confirm the workflow's transport checkout still names the reviewed exact
candidate commit recorded in `SECURITY.md`; a moving branch is not an acceptable
substitute.
Library CI resolves the checked-in registry-only dependency graph. It must not
introduce sibling source overrides.

Inspect `cargo package --list --locked` and confirm that secrets, local databases,
planning/spec documents, and build output are excluded while consumer
Expand Down Expand Up @@ -88,11 +87,11 @@ the core's retained canonical history exactly.

```bash
cargo publish --locked
git tag -s v0.4.0-alpha.4 -m "Release evm-fork-cache v0.4.0-alpha.4"
git push origin v0.4.0-alpha.4
git tag -s v0.4.0 -m "Release evm-fork-cache v0.4.0"
git push origin v0.4.0
```

Wait for 0.4.0-alpha.4 to appear in the crates.io index before removing sibling path
dependencies and verifying downstream extension packages. Publish only after
Wait for 0.4.0 to appear in the crates.io index before verifying downstream
extension packages against their final registry-only lockfiles. Publish only after
explicit authorization; preparing or running this checklist is not permission
to publish, tag, or push.
21 changes: 16 additions & 5 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,19 @@ The stable and MSRV jobs use the same reviewed toolchain-action commit and pass
their requested toolchain explicitly. Updating any action requires verifying
the new upstream ref and full commit before changing the pin.

Sibling development dependencies are also immutable in CI. The alpha.4 cache
workflow checks out `alloy-transport-balancer` at exact commit
`7868bea593dec5748ad7475d1909fc3a2de0d4ad`, matching the first candidate in
the documented publish order. Changing that revision requires rerunning the
cache's complete locked release matrix.
CI resolves the registry-only graph recorded in `Cargo.lock`. Changing a
released dependency requires rerunning the applicable locked release matrix.

## Alloy LRU advisory scope

`RUSTSEC-2026-0253` concerns `lru::LruCache::pop` when a stored key's destructor
panics and execution subsequently continues after catching that panic. The
remaining `lru 0.16.4` copy comes only from `alloy-provider 1.6.3`. Its block
stream uses `BlockNumber` (`u64`) keys and its cache layer uses `B256` keys;
neither has a custom destructor. This dependency remains affected as a library,
but the key-destructor trigger is absent from these selected Alloy uses.
Reassess on an Alloy or key-type change. The fix is in lru 0.18.2, outside
Alloy's 0.16 dependency range; no registry patch is hidden in this release.

The TUI dependency set uses the patched `lru 0.18.2` where that version range
is supported. Do not generalize the Alloy assessment to other lru consumers.
24 changes: 24 additions & 0 deletions docs/raw-json-flashblocks-acceptance.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,30 @@ authentication behavior, retry policy, an application AMM integration, or
permission to execute against speculative state. Those remain consumer-owned
acceptance boundaries.

## September release revalidation

On 2026-09-05, the same optimized subscriber-output probe compared the official
public Optimism raw feed with a paid QuickNode canonical WebSocket. It paired
100 of 100 raw swaps with identical canonical content in 112.312 seconds, with
no duplicate identities or unmatched mature raw records. Canonical delivery
was first in 81 pairs; raw delivery was first in 19. Absolute pairing delay
was 9,172.015 ms p95. Three canonical records remained unpaired at shutdown.

**The live latency gate failed** (`raw delivery was not usually first`). This
window does not qualify that source pair for a latency advantage or establish
that the three unmatched canonical records were source omissions. The probe
stops at its target pair count and does not drain a lagging source afterward.
The historical August result remains a separate observation. The stable crate
provides the opt-in JSON conversion and bounded handoff API; applications must
qualify their own source, delivery policy, and continuity before using it.

The optimized resource probes on the release candidate measured a 4,108,968-byte
frame (4,500 transactions, 9,000 logs) at 8.8615–10.835 ms and 361.65–442.21
MiB/s, and a 15,521,468-byte frame (17,000 transactions, 34,000 logs) at
39.429–45.773 ms and 323.39–375.42 MiB/s. These are Criterion confidence
intervals from 20 samples per large-frame case on the local M1 Pro; they measure
conversion, not source delivery or trading readiness.

## Resource-limit policy

The library defaults are deliberately broad compatibility ceilings: 16 MiB per
Expand Down
2 changes: 1 addition & 1 deletion scripts/check-security-exceptions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ bincode_graph="$({
} | sed -E 's# \(/[^)]*\)$##; s# \(\*\)$##')"
expected_bincode_graph="$(printf '%s\n' \
'0bincode v1.3.3' \
'1evm-fork-cache v0.4.0-alpha.5')"
'1evm-fork-cache v0.4.0')"
if [[ "$bincode_graph" != "$expected_bincode_graph" ]]; then
echo "The accepted bincode 1 compatibility scope changed." >&2
echo "Expected:" >&2
Expand Down
10 changes: 0 additions & 10 deletions src/reactive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22066,11 +22066,6 @@ mod subscriber_helper_tests {
}
}

#[cfg(any(
feature = "raw-flashblocks-json",
feature = "reactive-polling",
feature = "reactive-ws"
))]
fn rpc_block(number: u64, hash: B256) -> alloy_rpc_types_eth::Block {
alloy_rpc_types_eth::Block::empty(alloy_rpc_types_eth::Header {
hash,
Expand Down Expand Up @@ -24332,11 +24327,6 @@ mod subscriber_helper_tests {
}

// A log interest matching `rpc_log` (address 0x42, topic0 0x01).
#[cfg(any(
feature = "raw-flashblocks-json",
feature = "reactive-polling",
feature = "reactive-ws"
))]
fn log_interest_matching_rpc_log() -> ReactiveInterest<Ethereum> {
ReactiveInterest::Logs(LogInterest {
provider_filter: Filter::new()
Expand Down
37 changes: 14 additions & 23 deletions tests/reactive_alloy_subscriber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,28 @@
use std::time::Duration;

use alloy_network::Ethereum;
#[cfg(any(
feature = "raw-flashblocks-json",
feature = "reactive-polling",
feature = "reactive-ws"
))]
#[cfg(any(feature = "reactive-polling", feature = "reactive-ws"))]
use alloy_primitives::B256;
use alloy_primitives::U256;
#[cfg(any(feature = "reactive-polling", feature = "reactive-ws"))]
use alloy_primitives::{Address, keccak256};
#[cfg(any(feature = "reactive-polling", feature = "reactive-ws"))]
use alloy_primitives::{B256, Bytes, Log as PrimitiveLog};
#[cfg(feature = "reactive-polling")]
use alloy_primitives::{Bytes, Log as PrimitiveLog};
use alloy_provider::ProviderBuilder;
#[cfg(any(feature = "reactive-polling", feature = "reactive-ws"))]
use alloy_rpc_types_eth::Filter;
#[cfg(any(feature = "reactive-polling", feature = "reactive-ws"))]
#[cfg(feature = "reactive-polling")]
use alloy_rpc_types_eth::Log;
#[cfg(any(feature = "reactive-polling", feature = "reactive-ws"))]
#[cfg(feature = "reactive-polling")]
use alloy_rpc_types_eth::{Block, Header};
use alloy_transport::mock::Asserter;
use anyhow::Result;
#[cfg(any(feature = "reactive-polling", feature = "reactive-ws"))]
#[cfg(feature = "reactive-polling")]
use anyhow::bail;

#[cfg(feature = "reactive-ws")]
use evm_fork_cache::reactive::BlockInterestMode;
#[cfg(any(feature = "reactive-polling", feature = "reactive-ws"))]
#[cfg(feature = "reactive-polling")]
use evm_fork_cache::reactive::SubscriberBackfill;
#[cfg(feature = "reactive-ws")]
use evm_fork_cache::reactive::SubscriberCapability;
Expand All @@ -44,16 +41,15 @@ use evm_fork_cache::reactive::{
#[cfg(any(feature = "reactive-polling", feature = "reactive-ws"))]
use evm_fork_cache::reactive::{BlockInterest, LogInterest};
#[cfg(any(feature = "reactive-polling", feature = "reactive-ws"))]
use evm_fork_cache::reactive::{BlockRef, HandlerId, SubscriberOwnerStart, SubscriberOwnerState};
#[cfg(feature = "reactive-polling")]
use evm_fork_cache::reactive::{
BlockRef, HandlerId, SubscriberDriverPoll, SubscriberInputScope, SubscriberOwnerStart,
SubscriberOwnerState,
ChainStatus, InputSource, ReactiveInput, SubscriberDriverPoll, SubscriberInputScope,
};
#[cfg(any(feature = "reactive-polling", feature = "reactive-ws"))]
use evm_fork_cache::reactive::{ChainStatus, InputSource, ReactiveInput};
#[cfg(any(feature = "reactive-polling", feature = "reactive-ws"))]
#[cfg(feature = "reactive-polling")]
use evm_fork_cache::reactive::{DeliveryAudience, DeliveryScope, InterestOwnerSubscriber};

#[cfg(any(feature = "reactive-polling", feature = "reactive-ws"))]
#[cfg(feature = "reactive-polling")]
fn rpc_log(address: Address, topic0: B256, block_number: u64, log_index: u64) -> Log {
Log {
inner: PrimitiveLog::new_unchecked(address, vec![topic0], Bytes::new()),
Expand All @@ -74,7 +70,7 @@ fn removed_rpc_log(address: Address, topic0: B256, block_number: u64, log_index:
log
}

#[cfg(any(feature = "reactive-polling", feature = "reactive-ws"))]
#[cfg(feature = "reactive-polling")]
fn rpc_block(point: &BlockRef) -> Block {
Block::empty(Header {
hash: point.hash,
Expand Down Expand Up @@ -113,11 +109,6 @@ fn polling_subscriber(
)
}

#[cfg(any(
feature = "raw-flashblocks-json",
feature = "reactive-polling",
feature = "reactive-ws"
))]
fn asserter_with_chain_id() -> Asserter {
let asserter = Asserter::new();
asserter.push_success(&U256::from(1));
Expand Down
Loading