From ab08a5702982eba87d8d590120ef1076de8a3342 Mon Sep 17 00:00:00 2001 From: Kai Aldag Date: Sat, 5 Sep 2026 16:30:21 +0100 Subject: [PATCH] Release evm-fork-cache 0.4.0 --- .github/workflows/ci.yml | 15 ---------- CHANGELOG.md | 17 +++++++++++- Cargo.lock | 16 ++++++----- Cargo.toml | 4 +-- RELEASING.md | 19 ++++++------- SECURITY.md | 21 ++++++++++---- docs/raw-json-flashblocks-acceptance.md | 24 ++++++++++++++++ scripts/check-security-exceptions.sh | 2 +- src/reactive/mod.rs | 10 ------- tests/reactive_alloy_subscriber.rs | 37 ++++++++++--------------- 10 files changed, 91 insertions(+), 74 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b595451..6412706 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,6 @@ on: env: CARGO_TERM_COLOR: always - TRANSPORT_REF: 7868bea593dec5748ad7475d1909fc3a2de0d4ad permissions: contents: read @@ -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: @@ -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: diff --git a/CHANGELOG.md b/CHANGELOG.md index 5558a62..1b8089f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 @@ -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 @@ -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 diff --git a/Cargo.lock b/Cargo.lock index b9adc06..ca8dc57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -668,7 +668,9 @@ dependencies = [ [[package]] name = "alloy-transport-balancer" -version = "0.3.0-alpha.2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345f93cdb838111028dc97922832699c200a80533675ed272c7809037861b532" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -2056,12 +2058,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "evm-fork-cache" -version = "0.4.0-alpha.5" +version = "0.4.0" dependencies = [ "alloy-consensus", "alloy-contract", @@ -2809,7 +2811,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4116,7 +4118,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4684,7 +4686,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5314,7 +5316,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ba2232a..49ca943 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" @@ -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" diff --git a/RELEASING.md b/RELEASING.md index 927c0d4..e77e65c 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -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. @@ -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 @@ -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. diff --git a/SECURITY.md b/SECURITY.md index ba47ace..80593b2 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -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. diff --git a/docs/raw-json-flashblocks-acceptance.md b/docs/raw-json-flashblocks-acceptance.md index 14a9610..d7b44b5 100644 --- a/docs/raw-json-flashblocks-acceptance.md +++ b/docs/raw-json-flashblocks-acceptance.md @@ -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 diff --git a/scripts/check-security-exceptions.sh b/scripts/check-security-exceptions.sh index f2ecf0e..089f1d6 100755 --- a/scripts/check-security-exceptions.sh +++ b/scripts/check-security-exceptions.sh @@ -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 diff --git a/src/reactive/mod.rs b/src/reactive/mod.rs index e9617d2..9733990 100644 --- a/src/reactive/mod.rs +++ b/src/reactive/mod.rs @@ -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, @@ -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 { ReactiveInterest::Logs(LogInterest { provider_filter: Filter::new() diff --git a/tests/reactive_alloy_subscriber.rs b/tests/reactive_alloy_subscriber.rs index b4043b7..49286f1 100644 --- a/tests/reactive_alloy_subscriber.rs +++ b/tests/reactive_alloy_subscriber.rs @@ -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; @@ -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()), @@ -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, @@ -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));