Skip to content

fix: gate WASM CLI-spec registration on unix; fix all-features test compile; monotonic RPC rate limiter - #28

Merged
secsovereign merged 8 commits into
mainfrom
fix/windows-ipc-gate-storage-test
Oct 22, 2026
Merged

secsovereign merged 8 commits into
mainfrom
fix/windows-ipc-gate-storage-test

Conversation

@secsovereign

@secsovereign secsovereign commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Fixes the blvm-node compile errors in blvm's v0.1.62 Official Release (run 39185005040, job 117845486301). This PR is based on main and is independent of #27.

1. Windows lib build (src/module/manager.rs)

  • Log: error[E0609]: no field 'ipc_server' on type '&mut ModuleManager' at blvm-node-0.1.73/src/module/manager.rs:724, followed by E0282.
    • This came from the Windows blvm-sdk experimental build (--all-features, which brings in blvm-node/wasm-modules), so blvm-sdk experimental failed for Windows.
  • ipc_server (decl :39-40) and the ipc_server() accessor (:315) are both #[cfg(unix)]. The WASM load path read the field ungated.
  • Fix: one #[cfg(unix)] on that if let, the same pattern as the other three uses (:457, :569, :898).
    • On Windows cli_spec is then unused. [lints.rust] unused_variables = "allow" covers that, and cargo check for the Windows target shows no new warning.
  • I swept for other ungated unix-only uses with real Windows checks rather than grep. Nothing else fails (results below), so this is the only change in src/.

2. tests/storage_tests.rs (cargo build --release --all-features --tests in the same run)

  • :1221: store_witness(&[Vec<Witness>]) was given Vec<Vec<Vec<u8>>>. It now builds tx → input → stack: Vec<Vec<Witness>> = vec![vec![vec![vec![..]]]].
  • The same log shows two more errors in this file. :1305 used a u32 value where i64 is required (now i64::from(i)), and :1310 inserted a UTXO where Arc<UTXO> is required (now Arc::new). No API change.

3. tests/payment_rest_integration_tests.rs (not in the original ask; found by --keep-going)

  • Under ctv + bip70-http + rest-api it still did use uuid::Uuid, but c161636 removed uuid in favour of blvm_node::utils::new_request_id. Cargo stopped at storage_tests first, so the release log never showed this error.
  • Switched to new_request_id(), which uses the crate's own helper and adds no dependency.
  • Known failure: with this the file compiles, and one test that hadn't compiled since c161636 fails on its assertion: test_rest_unvault_vault, 400 instead of 200. The request id is opaque and has nothing to do with it. 9 of 10 pass.
    • This PR doesn't fix it. Node's CI cargo test doesn't enable ctv, so CI isn't affected. In blvm's release workflow the test build and test run are if ! cmd | tee, so they never fail the job, and the log reported "Tests compiled successfully" even though the build failed.

Run locally (sibling crates at their origin/main)

  • Windows lib cargo check -j 2 --lib --target x86_64-pc-windows-gnu --no-default-features --features <blvm-sdk's node set>,wasm-modules:
    • main: E0609 + E0282, matching the log.
    • this branch: passes.
  • The same Windows check with default features passes. --all-features can't run on Windows here: secp-gpu needs nvcc.
  • Linux cargo check -j 2 --tests --keep-going with every feature except secp-gpu/rocksdb/erlay/tidesdb (those need CUDA, libclang, or a C toolchain the box lacks) passes.
  • cargo test --test storage_tests compression (sled+compression): 5/5 pass, including both fixed tests.

4. RPC rate limiter refills from a monotonic clock (d5079c8)

rpc::rest::tests::security_tests::test_rate_limiting_time_window flaked on main f82d8c4. Run 39405496449 failed at security_tests.rs:266 (assertion failed: !auth.check_rate_limit(&user_id).await), and the rerun 39408183650 passed. origin/main is merged into this branch (d52c907, a clean merge) before this commit.

  • Cause: RpcRateLimiter (src/rpc/auth_impl.rs) refilled from current_timestamp(), which is whole wall-clock Unix seconds (last_refill: u64, elapsed * rate).

    • A wait that crossed a second boundary was credited as a full second. The test's 2s sleep could count as 3s (the failure at :266), and the immediate post-burst check at :259 could also be refilled within microseconds.
    • Because it was the wall clock, NTP or manual clock steps also added or withheld tokens.
  • Fix: the limiter uses tokio::time::Instant and credits elapsed time exactly in integers.

    • Each check adds elapsed_ns * rate (u128) plus a carried sub-token remainder (units of 1e-9 token, < 1e9). Whole tokens go into the bucket and the remainder is carried.
    • Tokens stay clamped to burst_limit. A full bucket or rate == 0 banks nothing.
    • new(burst, rate), check_and_consume[_n] (including the n > burst reject) and tokens_remaining() are unchanged.
    • The only API change is last_refill(), which now returns the monotonic Instant of the last check instead of Unix seconds. Its only user is cleanup_stale_limiters(), which keeps the same one-hour threshold on Instant::now().
  • Callers get the same limits as before. No construction site changed its (burst, rate) arguments:

    • RpcServer::build_auth_manager → with_rate_limits(required, rate_limit_burst, rate_limit_rate), from RpcAuthConfig (TOML/env; defaults 100 / 10 in config/rpc.rs)
    • RpcAuthManager::new default (100, 10)
    • per-user set_user_rate_limit, per-method set_method_rate_limit
    • the IP path (check_ip_rate_limit_with_endpoint[_n]: the default limit, halved when auth is required)
    • check_rate_limit[_n] / check_rate_limit_with_endpoint[_n] (user limiters)

    Both RPC servers (rpc/server.rs, rpc/rest/server.rs) only call these checks. The peer and module limiters (network/peer_manager.rs, module/api/hub.rs) are separate types and aren't touched.

  • Tests:

    • Limiter tests that slept, or that exhausted a burst and expected the next call to be denied, now run with #[tokio::test(start_paused = true)] and tokio::time::advance. tokio test-util was added as a dev-dependency at the same =1.48.0. Files:
      • src/rpc/rest/tests/security_tests.rs: 3 tests
      • tests/rpc_rate_limiting_tests.rs: all of them. test_rate_limiter_high_rate is no longer #[ignore].
      • tests/rpc_auth_security_tests.rs: 2
      • tests/rpc_auth_comprehensive_tests.rs: 5. The two stub tests now assert refill and the cap.
    • The ignored quic_rpc_rate_limit_rejection (needs quinn) uses rate 0, so its 429 doesn't depend on timing. It's not compiled under default features and I didn't run it.
    • New unit tests in auth_impl.rs:
      • a real-clock reproduction that lines up 50-100 ms before a wall-clock second boundary, exhausts the bucket, waits 200 ms, and expects no token
      • wall-clock time passing under a paused monotonic clock adds no tokens
      • 1000 × advance(1ms) gives exactly floor(rate * t) at every step, and exactly rate after 1s, for rates 1/3/7/10/999/1000/1001
      • 10,000 × 1ms with consumption in between grants exactly 70 tokens at 7/s
      • the burst cap holds after idles up to a year and for rate = u32::MAX
      • u32::MAX burst and rate with ~100 years elapsed: no overflow or panic, and a partial refill is exact
      • stale eviction runs on the monotonic clock (idle limiter evicted after 3601s, active one kept)

Evidence for item 4 (local, sibling patches stripped as CI does, fresh lock: blvm-protocol 0.1.34, blvm-consensus 0.1.64)

  • Red on the old limiter: the old auth_impl.rs, applied with cp, plus the new real-clock and wall-clock tests. The new boundary test, the wall-clock test and test_rate_limiting_time_window (:266) failed on all 50 of 50 runs.
  • Green on the fix: 50 of 50 runs passed. Each run was the 13 lib rate_limit tests plus rpc_rate_limiting_tests (10), rpc_auth_comprehensive_tests (17) and rpc_auth_security_tests (15).
  • Tests:
    • cargo test -j 2 --lib: 1468 passed, 2 ignored
    • cargo test -j 2 --features stratum-v2 --lib: 1469 passed, 2 ignored
    • rpc/rest integration tests (rpc_tests, rpc_routing, rpc_edge_cases, rpc_batch_edge_cases, rest_api_endpoints, plus the 3 above): all pass
  • Lint: cargo fmt --check passes, and both CI clippy invocations are clean.
  • Windows-gnu: cargo check -j 2 --target x86_64-pc-windows-gnu passes for default features and for --tests.
  • The full cargo test didn't finish locally because the box ran out of disk while linking. CI runs it.

5. Docs: verification claims narrowed (7ade033, comments and docs only)
This matches blvm-consensus docs/VERIFICATION.md (ed2fac0), where 3 of 182 anchors are mutation-validated. create_new_block has no #[spec_locked].

  • src/rpc/mining.rs:4: "Uses formally verified blvm-consensus mining functions." now says the functions are covered by tests, are not formally verified, and links VERIFICATION.md.
  • src/module/traits.rs:495: "same formally verified consensus function" now reads "same tested blvm-consensus mining function".
  • SECURITY.md:175: "Formal verification of critical paths (property tests ...)" now reads "Property tests for node invariants on critical paths (testing, not formal verification)".
  • docs/NETWORK_VERIFICATION.md:
    • The title and overview now say "spec-lock checks" (annotated and Z3-checked), with the 3/182 link.
    • Dandelion "✅ Verified" now reads "annotated and Z3-checked".
    • The CI line says "Spec-lock checks run".

…t types

ModuleManager::ipc_server only exists on unix, but the WASM load path read
it ungated, so blvm-node failed to compile for x86_64-pc-windows-gnu with
wasm-modules (E0609/E0282; blvm run 39185005040, Windows blvm-sdk
experimental). Gate the registration like the other ipc_server uses.

tests/storage_tests.rs: the witness-compression test passed a witness
stack one level too shallow for store_witness(&[Vec<Witness>]), and the
utxo-compression test used a u32 value and an unwrapped UTXO. Build the
correct shapes (one tx -> one input -> one stack; i64 value; Arc<UTXO>);
no API change.
…crate

c161636 dropped uuid from the manifest in favour of
utils::new_request_id, but payment_rest_integration_tests still imported
uuid::Uuid, so the test failed to compile with ctv+bip70-http+rest-api
(any --all-features --tests build). It was hidden behind the
storage_tests failure, which stops cargo first.
RpcRateLimiter refilled from current_timestamp(), whole wall-clock Unix
seconds. Any wait that straddled a second boundary was credited a full
second of tokens, so 2s could count as 3s and a request right after the
burst could be refilled after a few microseconds. That is the flake in
rpc::rest::tests::security_tests::test_rate_limiting_time_window
(run 39405496449). Wall-clock steps (NTP, manual) also moved the bucket.

The limiter now uses tokio::time::Instant and credits elapsed nanoseconds
exactly: elapsed_ns * rate plus a carried sub-token remainder (units of
1e-9 token, u128 math), whole tokens into the bucket, remainder carried.
Tokens stay clamped to burst_limit; a full bucket or rate 0 banks nothing.
Burst, rate and batch (n > burst) semantics are unchanged, and every
caller still builds the limiter with the same (burst, rate) as before.

last_refill() now returns the monotonic Instant of the last check, and
cleanup_stale_limiters() compares against Instant::now() with the same
one-hour threshold.

Tests: limiter tests that slept now run on a paused tokio clock with
advance() (tokio test-util as a dev-dependency). New unit tests cover a
real-clock second-boundary reproduction, wall-clock independence, exact
carry over 1000 x 1ms and with interleaved consumption, the burst cap,
huge elapsed/rate without overflow, and monotonic stale eviction. The
ignored QUIC 429 smoke test uses rate 0 so it no longer depends on timing.
@secsovereign secsovereign changed the title fix: gate WASM CLI-spec registration on unix (Windows build); fix all-features test compile fix: gate WASM CLI-spec registration on unix; fix all-features test compile; monotonic RPC rate limiter Oct 20, 2026
blvm-consensus docs/VERIFICATION.md (ed2fac0) counts 3 of 182 anchors as mutation-validated; create_new_block has no #[spec_locked]. Say the mining functions are tested, call spec-locked network code annotated and Z3-checked, and say the SECURITY.md item is property tests. Comments and docs only.
@secsovereign
secsovereign merged commit b86f56a into main Oct 22, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant