Skip to content

fix(timing): take slot duration from the beacon node (#642) - #685

Merged
rootwarp merged 1 commit into
developfrom
cursor/bn-slot-duration-4a37
Oct 16, 2026
Merged

rootwarp merged 1 commit into
developfrom
cursor/bn-slot-duration-4a37

Conversation

@rootwarp

@rootwarp rootwarp commented Oct 16, 2026 •

Copy link
Copy Markdown
Owner

Fixes #642 (parent #638). Rebased onto develop d47e2302.

Slot duration for the doppelganger epoch, liveness sleep, index-resolver tick, import hold, keymanager and CLI default exit epoch, slashing-monitor interval, and the Gloas payload deadline comes from GET /eth/v1/config/spec. The unused LivenessChecker / DoppelgangerStatus / DoppelgangerResult / BeaconLivenessAdapter surface is removed. ValidatorLivenessData stays.

Security fixes:

  • Slot start times, deadlines, and the next-slot wait use millisecond boundaries. A 1500 ms slot starts slot 2 at genesis+3000 ms.
  • Responding beacon nodes must agree on SLOT_DURATION_MS or SECONDS_PER_SLOT, on SLOTS_PER_EPOCH matching the network preset, and on genesis time matching the configured genesis. A missing, zero, conflicting, or disagreeing value fails closed.
  • Duration is bounded to 1 s..=60 s. A named network must report that network's preset (Network::slot_duration_ms). Custom may accept a bounded beacon-node value. There is no silent 12 s fallback.
  • On a named network, a beacon node that does not respond does not block boot. At least one must respond, and responders that disagree fail closed. Custom still requires every configured beacon node.
  • A beacon node skipped at boot stays out of duties, indices, and liveness until its slot duration, SLOTS_PER_EPOCH, genesis time, and genesis validators root match that boot agreement. Recovering /eth/v1/node/syncing does not admit it.
  • On a named network, a spec that arrives before a genesis timeout is still compared. A conflicting spec fails startup. That node stays unadmitted because genesis did not complete.
  • HTTP 502, 503, 504, and 429 from a proxy count as unreachable at boot. HTTP 500 stays boot-fatal. Those statuses are still returned as ApiError.
  • A named network rejects a genesis_time that differs from the preset. Beacon genesis must match the configured genesis.
  • Epoch 0 marks normally registered keys Safe only on a named network, after responding beacon nodes match that preset genesis. Custom does not set the flag: those nodes also choose the slot duration, and a long duration can hold the clock at epoch 0.
  • The keymanager voluntary exit takes the startup genesis time as a required argument of VoluntaryExitManagerAdapter::new. There is no zero default and no with_genesis_time. It does not re-fetch spec or genesis from whichever pool node answers first.
  • The CLI exit command has a single beacon URL, so it cannot cross-check a pool. It still rejects a missing, zero, conflicting, out-of-range, or non-preset duration, a SLOTS_PER_EPOCH that is not the preset, and a genesis time that is not the named-network preset.
  • Deadline basis points use checked multiplication. The Gloas aggregate deadline cannot land after the payload-attestation deadline.
  • The unused wall_clock_epoch helper, which assumed a 12 s slot, is removed.
  • A zero or out-of-range duration no longer makes the index-resolver tick Duration::MAX. The slashing-monitor interval uses the startup duration. BlockService::new and KeystoreManagerAdapter::new still start from 12 s; production replaces those, and docs/releases/UNRELEASED.md says so.
  • Beacon-node agreement is seeded with the configured genesis validators root. A responder whose root differs is rejected: it is not admitted, it is not the chain identity, and it is not a source of doppelganger liveness. Startup checks every responder, not the first genesis success. Recovery admission compares against that configured root, so two wrong-chain nodes that match each other on spec and genesis time stay unadmitted after the good node drops.
  • The live event stream does not connect to an unadmitted beacon node. Head and reorg events from that node are not delivered until it is admitted. The admission flag is read per chunk, so a later chunk from a node that loses admission is dropped.
  • SSE failover searches forward from the current index for the next admitted node. With a failing admitted node, an unadmitted node, and a later healthy node, the stream uses the healthy node.
  • A genesis validators root mismatch is BeaconError::GenesisValidatorsRootMismatch. Startup matches that variant.

fork_hazard_inventory and .gitleaksignore are re-pinned where lines moved.

Q-188 and B-026 are tracked in the external review notes.

Verification on 7643540439916ba51c393e037d9be3c99a7a6c49: cargo nextest run --workspace --locked (6061 passed, 9 skipped), cargo clippy --workspace --all-targets --locked -- -D warnings, and cargo llvm-cov nextest --workspace --locked (6061 passed, 9 skipped).

Open in Web Open in Cursor 

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Coverage Report

Total: 94.40% (98253 / 104080 lines)

File Coverage Lines
src/bls_to_execution.rs 92.2% 237/257
src/deposit.rs 100.0% 297/297
src/existing_mnemonic.rs 85.7% 126/147
src/exit.rs 94.0% 218/232
src/fs_util.rs 98.5% 65/66
src/main.rs 0.0% 0/79
src/network.rs 100.0% 139/139
src/new_mnemonic.rs 94.3% 484/513
src/password.rs 93.1% 189/203
src/verify.rs 97.7% 84/86
src/main.rs 95.1% 869/914
src/cli.rs 87.4% 480/549
commands/prepare_exit.rs 79.7% 169/212
commands/signed_exit.rs 72.0% 232/322
commands/slashing.rs 91.8% 146/159
commands/submit_exit.rs 80.2% 73/91
commands/voluntary_exit.rs 54.7% 41/75
src/logging.rs 89.9% 659/733
src/main.rs 100.0% 45/45
bin/generate_architecture_md.rs 0.0% 0/15
src/lib.rs 85.1% 211/248
src/client.rs 89.6% 1563/1744
src/error.rs 100.0% 35/35
src/http_caps.rs 88.0% 44/50
src/retry.rs 100.0% 56/56
src/ssz_deser.rs 95.9% 649/677
src/types.rs 96.1% 1461/1521
src/v4_wire.rs 100.0% 86/86
src/error.rs 90.5% 19/21
src/metrics.rs 100.0% 52/52
service/mod.rs 92.3% 799/866
src/traits.rs 81.0% 68/84
src/validation.rs 99.3% 139/140
src/broadcast.rs 98.1% 155/158
src/error.rs 100.0% 13/13
src/fan_out.rs 95.2% 1719/1805
src/health.rs 89.3% 309/346
src/manager.rs 95.3% 4566/4793
src/metrics.rs 98.1% 153/156
src/mock.rs 92.9% 964/1038
src/sse.rs 88.8% 1032/1162
sse/frame.rs 98.4% 376/382
src/submit.rs 97.1% 574/591
src/sync_status.rs 99.0% 892/901
src/traits.rs 95.9% 232/242
src/types.rs 99.2% 242/244
src/service.rs 100.0% 1454/1454
src/traits.rs 85.7% 12/14
src/bls.rs 90.7% 224/247
src/composite_signer.rs 92.5% 443/479
src/eip2333.rs 96.9% 219/226
src/error.rs 100.0% 63/63
src/insecure.rs 100.0% 130/130
src/key_manager.rs 96.0% 811/845
src/keystore.rs 98.5% 1251/1270
src/mnemonic.rs 100.0% 90/90
src/signer_trait.rs 97.8% 90/92
src/signing.rs 98.5% 128/130
src/signing_root.rs 98.5% 475/482
src/test_utils.rs 100.0% 40/40
src/typed_signer.rs 94.4% 654/693
src/voluntary_exit_signing.rs 100.0% 135/135
src/enablement.rs 100.0% 3/3
src/epoch_clock.rs 100.0% 37/37
src/forward_window.rs 96.6% 144/149
src/restart_skip.rs 100.0% 34/34
src/duty.rs 100.0% 47/47
src/metrics.rs 100.0% 24/24
src/tracker.rs 98.4% 2032/2064
src/aggregation.rs 100.0% 307/307
src/aggregator.rs 96.4% 81/84
src/block.rs 99.1% 750/757
src/block_body.rs 94.0% 1449/1542
src/block_body_json.rs 95.8% 114/119
src/builder.rs 99.0% 96/97
src/builder_request_auth.rs 90.6% 106/117
canonical/gvr_hex.rs 100.0% 25/25
canonical/pubkey_hex.rs 98.5% 64/65
canonical/signing_root_hex.rs 100.0% 13/13
src/deposit.rs 98.2% 161/164
src/domains.rs 100.0% 64/64
src/duties.rs 98.5% 65/66
src/fork.rs 99.3% 446/449
src/hex_fixed.rs 100.0% 108/108
src/lib.rs 96.5% 137/142
src/networks.rs 100.0% 77/77
src/payload_attestation.rs 98.5% 64/65
src/proposer_preferences.rs 86.4% 19/22
src/serde_signature.rs 97.1% 68/70
src/signed_block_contents_json.rs 94.1% 48/51
src/ssz_helpers.rs 96.2% 400/416
src/sync_committee.rs 99.0% 302/305
src/tree_hash_utils.rs 93.1% 244/262
src/client.rs 92.8% 1011/1089
src/lib.rs 100.0% 212/212
src/auth.rs 95.8% 341/356
src/error.rs 92.9% 260/280
src/handlers.rs 84.4% 655/776
src/lifecycle.rs 90.5% 637/704
src/server.rs 80.3% 343/427
src/traits.rs 97.0% 32/33
src/types.rs 96.4% 80/83
src/url_validator.rs 90.4% 197/218
src/definitions.rs 99.5% 189/190
src/lib.rs 95.5% 252/264
src/server.rs 94.8% 311/328
src/hex.rs 100.0% 39/39
src/logging.rs 100.0% 288/288
src/pubkey.rs 100.0% 62/62
src/span_names.rs 100.0% 67/67
src/client.rs 83.2% 129/155
src/wire.rs 95.3% 662/695
src/error.rs 83.3% 5/6
src/lib.rs 100.0% 10/10
src/network.rs 100.0% 37/37
sections/beacon.rs 0.0% 0/12
sections/builder.rs 98.2% 109/111
sections/builder_limits.rs 54.5% 12/22
sections/duties.rs 100.0% 55/55
sections/fork_schedule.rs 78.6% 44/56
sections/grpc_signer.rs 0.0% 0/8
sections/keymanager.rs 95.7% 111/116
sections/keys.rs 0.0% 0/9
sections/logfile.rs 62.5% 15/24
sections/mod.rs 99.2% 121/122
sections/monitoring.rs 46.2% 6/13
sections/network.rs 0.0% 0/8
sections/proposer_config.rs 52.2% 12/23
sections/safety.rs 61.9% 13/21
sections/secret_provider.rs 22.2% 6/27
sections/server.rs 0.0% 0/3
sections/slashing.rs 0.0% 0/6
sections/timing.rs 100.0% 92/92
sections/tracing.rs 61.9% 26/42
containers/attestation.rs 92.9% 105/113
containers/block_body.rs 97.9% 94/96
containers/body_leaves.rs 94.5% 188/199
containers/builder_requests.rs 95.3% 61/64
containers/envelope.rs 95.4% 83/87
containers/execution_payload.rs 94.2% 49/52
containers/leaves.rs 95.4% 145/152
containers/payload.rs 92.9% 91/98
containers/requests.rs 97.6% 41/42
src/lib.rs 100.0% 67/67
src/merkle.rs 94.7% 89/94
src/roots.rs 98.4% 252/256
bin/gen_spec_kat.rs 81.8% 1094/1337
src/loader.rs 89.4% 202/226
src/lib.rs 91.9% 147/160
background_tasks/config_url.rs 97.0% 639/659
background_tasks/monitoring.rs 95.2% 256/269
src/beacon_adapter.rs 98.2% 381/388
bootstrap/beacon.rs 96.8% 508/525
bootstrap/enablement.rs 96.9% 401/414
bootstrap/executor.rs 95.6% 651/681
bootstrap/keys.rs 78.9% 300/380
bootstrap/mod.rs 100.0% 59/59
bootstrap/run.rs 93.7% 667/712
bootstrap/services.rs 96.9% 474/489
bootstrap/signer_probe.rs 92.4% 595/644
bootstrap/slashing.rs 92.0% 173/188
bootstrap/tasks.rs 96.9% 743/767
config/builder.rs 94.2% 2193/2329
config/error.rs 100.0% 5/5
config/network.rs 100.0% 102/102
config/types.rs 95.0% 3007/3165
src/deletion_denylist.rs 90.5% 248/274
src/index_resolver.rs 97.1% 601/619
src/key_admission.rs 93.6% 524/560
keymanager_adapters/config.rs 100.0% 82/82
keymanager_adapters/doppelganger.rs 66.7% 72/108
keymanager_adapters/import_window.rs 96.2% 938/975
keymanager_adapters/kdf_budget.rs 100.0% 173/173
keymanager_adapters/keystore.rs 82.6% 673/815
keymanager_adapters/notifier.rs 83.3% 20/24
keymanager_adapters/remote_keys.rs 98.6% 69/70
keymanager_adapters/slashing.rs 78.8% 93/118
keymanager_adapters/spawn.rs 92.4% 159/172
keymanager_adapters/validator.rs 67.1% 47/70
keymanager_adapters/voluntary_exit.rs 99.0% 388/392
src/liveness_loop.rs 95.6% 630/659
src/metrics.rs 99.5% 219/220
orchestrator/aggregation.rs 92.3% 1220/1322
orchestrator/attestation.rs 90.7% 1219/1344
block_proposal/mod.rs 97.7% 43/44
coordinator/mod.rs 90.2% 874/969
orchestrator/dispatch.rs 100.0% 33/33
orchestrator/duty_management.rs 99.0% 576/582
orchestrator/error.rs 100.0% 27/27
orchestrator/head_events.rs 99.4% 335/337
orchestrator/payload_attestation.rs 97.7% 2299/2353
orchestrator/slot_anchor.rs 95.0% 114/120
orchestrator/slot_context.rs 94.6% 596/630
orchestrator/sync_committee.rs 96.1% 933/971
orchestrator/utils.rs 99.2% 491/495
validation/attestation_data.rs 100.0% 149/149
src/pubkey_index.rs 91.1% 133/146
src/quiesce.rs 98.1% 259/264
src/slashing_monitor.rs 96.6% 284/294
src/startup.rs 98.5% 661/671
src/format.rs 83.7% 87/104
src/key_source_manager.rs 94.1% 925/983
src/lib.rs 97.6% 83/85
src/metrics.rs 100.0% 72/72
src/refresh.rs 97.6% 530/543
src/lib.rs 78.9% 310/393
src/lib.rs 100.0% 20/20
audit/cn.rs 86.8% 125/144
audit/log.rs 100.0% 31/31
audit/mod.rs 100.0% 41/41
backend/basic.rs 93.7% 312/333
backend/signer_adapter.rs 53.8% 7/13
src/config.rs 99.9% 797/798
src/error.rs 56.2% 27/48
src/gate_shared_across_transports.rs 100.0% 91/91
src/grpc_common.rs 98.9% 373/377
grpc_tls/mod.rs 98.4% 120/122
grpc_tls/server_builder.rs 100.0% 12/12
accept_loop/mod.rs 93.8% 120/128
http_api/dispatch.rs 98.9% 376/380
http_api/mod.rs 97.9% 137/140
http_api/pubkey.rs 98.7% 76/77
http_api/request.rs 95.2% 79/83
http_api/response.rs 98.4% 248/252
routes/mod.rs 96.7% 202/209
tls_config/mod.rs 97.0% 64/66
src/insecure_startup.rs 100.0% 66/66
src/metrics.rs 96.6% 288/298
src/reload.rs 81.7% 215/263
server/backend.rs 99.0% 95/96
server/grpc.rs 88.2% 149/169
server/http.rs 91.5% 236/258
server/mod.rs 93.4% 1141/1222
server/slashing.rs 87.6% 220/251
src/service.rs 97.4% 1819/1868
src/sign_plan.rs 98.7% 811/822
slashing/config.rs 100.0% 73/73
src/tls_io.rs 100.0% 11/11
src/trace_ctx.rs 100.0% 31/31
src/core.rs 92.8% 1063/1146
src/error.rs 96.7% 148/153
src/fail_closed.rs 100.0% 3/3
src/gate.rs 94.0% 219/233
src/lib.rs 96.9% 3435/3546
src/locks.rs 83.0% 39/47
src/metrics.rs 97.0% 230/237
service_util/circuit_breaker.rs 100.0% 135/135
src/test_utils.rs 98.2% 111/113
src/traits.rs 75.0% 12/16
src/audit.rs 95.7% 22/23
db/interchange.rs 96.9% 1204/1242
db/migrations.rs 85.2% 445/522
db/mod.rs 98.8% 794/804
db/open.rs 89.9% 339/377
db/records.rs 98.8% 490/496
db/watermarks.rs 96.8% 583/602
src/error.rs 100.0% 7/7
src/group_commit.rs 89.1% 416/467
src/history.rs 98.3% 405/412
src/metrics.rs 94.6% 70/74
src/reader.rs 88.0% 44/50
src/rules.rs 96.0% 386/402
src/scoped.rs 98.6% 215/218
src/stage.rs 88.4% 281/318
src/types.rs 92.5% 393/425
src/config.rs 100.0% 188/188
src/file_appender.rs 92.7% 127/137
src/format.rs 94.4% 403/427
src/init.rs 95.9% 355/370
src/propagation.rs 93.9% 263/280
src/shutdown.rs 90.3% 28/31
src/trace_id.rs 97.0% 161/166
src/clock.rs 96.7% 559/578
src/error.rs 100.0% 12/12
src/lib.rs 100.0% 19/19
src/block_selection.rs 100.0% 75/75
src/config.rs 100.0% 15/15
src/store.rs 96.8% 3327/3438
src/hex_serde.rs 92.2% 47/51
src/lib.rs 97.9% 570/582

@cursor
cursor Bot force-pushed the cursor/bn-slot-duration-4a37 branch 5 times, most recently from 6a24bd3 to 31c1d5a Compare October 16, 2026 12:25
Doppelganger epoch, liveness sleep, the index-resolver tick, the import
hold, the default exit epoch, the slashing-monitor interval, and the Gloas
payload deadline use the slot duration from the beacon node spec. Slot
boundaries stay in milliseconds, so a 1500 ms slot starts slot 2 at
genesis+3000 ms.

Responding beacon nodes must agree on the duration, SLOTS_PER_EPOCH, and
genesis time. A missing, zero, conflicting, out-of-range, or disagreeing
value fails closed. A named network skips a beacon node that does not
respond, requires at least one response, and must match the preset duration
and genesis. Custom still requires every configured beacon node. An explicit
genesis_time cannot override a named-network preset.

Epoch 0 marks keys Safe only on a named network after that preset match.
The keymanager exit uses the startup duration and genesis time. The CLI
exit rejects a genesis time that is not the named-network preset.

Fixes #642

Co-authored-by: Joonkyo Kim <rootwarp@gmail.com>
@cursor
cursor Bot force-pushed the cursor/bn-slot-duration-4a37 branch from 31c1d5a to 7643540 Compare October 16, 2026 13:13

@rootwarp rootwarp left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formal review — PR #685 R6 (head 7643540, base develop d47e230, 1 ahead / 0 behind)

Delta reviewed: R5 31c1d5a → 7643540 (7 files, +236/−27).

CI

All 9 required checks pass: Check, Coverage, Scripts, Secret Scan, Architecture Gates, Spec Vectors, Trace continuity, GitGuardian, Test (all features).

Local (cargo 1.99.0, 8 affected crates)

  • fmt: clean. clippy -D warnings --all-targets --all-features: clean.
  • nextest: 2994/2997 pass. The 3 failures are known box flakes (refresh_clean_shutdown, async_heartbeat, cli start_help timeout).
  • gitleaks (sse.rs, error.rs): no leaks.
  • Fork-hazard inventory, checked with my own parser: 60/60 needles sit on their pinned lines and match the audit table in both directions. The re-pins are client.rs 548→547, 1247→1246 and 2124→2123.
  • Spawn census: 17/17 rows hold. Rows that name the task through a constant were checked by hand; R6 does not touch those files.

R5 items

  • B1 — resolved. The SSE admission fallback (sse.rs:311-318) now scans (1..len).map(|o| (current_idx + o) % len), so it searches forward and wraps. The new three-node test sse_failover_reaches_a_later_admitted_node uses [failing, unadmitted, healthy]. Mutation M5, which restores the scan from index 0, is caught.
  • S1 — resolved. sse_drops_chunks_after_admission_is_revoked covers the per-chunk check. M4, which makes that check (if !endpoint_admitted(current_idx) { break; }) a no-op, is now caught. Both new tests passed 20/20 under --stress-count 20.
  • Doc fix — done. The subscribe_events_admitted doc now says "per chunk", which matches the code.
  • S3 — resolved. New BeaconError::GenesisValidatorsRootMismatch { root }. Both producers use it: ensure_genesis_validators_root (client.rs:213) and confirm_genesis_validators_root (manager.rs:2834). startup::validate_genesis_root matches on the variant, and the string match is gone.
    • M6 (startup matches a different variant) is caught by test_validate_genesis_root_mismatch and test_connect_beacon_rejects_genesis_root_mismatch.
    • M7 (manager returns HttpError) is caught by test_connect_beacon_rejects_genesis_root_mismatch.
  • S2, S4: Ash has deferred both to a follow-up. Both fail closed.
  • R4 and R5 checks still hold. The R5 mutations M1–M3 are unaffected by this delta.

Soft

  • S5 (Codex P3, kept as P3 soft): sse_drops_chunks_after_admission_is_revoked (sse.rs:~1726) relies on a fixed 400 ms sleep. It does not confirm that chunk 3 was sent before asserting. On a heavily loaded runner it could pass without exercising the guard. Locally it does catch M4, and it passed 20/20 under stress. Suggest syncing on a server-side "chunk 3 written" signal.
  • Hygiene: the commit subject "fix(timing): take slot duration from the beacon node" does not describe this delta. Consider rewording it at squash time.

Blocking

None.

Codex gpt-6.1-sol (high, read-only) session 01a144da-2f26-7192-b4e2-5dcb6d9c2caf: one P3 (S5), no P1 or P2.

Formal verdict: APPROVE

@rootwarp rootwarp left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security re-audit (R6, scoped, Codex only) of #685 at head 7643540 (vs 31c1d5a, base develop @ d47e230)

Verdict: CLEAR-WITH-P3 (my rating). Stricter verdict: BLOCK (Codex).

  • Codex gives BLOCK because two of the three requested fixes are incomplete, not because it found a new risk.
  • Both gaps predate R6 and fail safe, so I rate them P3.
  • No doppelganger or slashing risk, so I did not escalate to Claude.

Interdiff scope: sse.rs and its tests, plus the typed root-mismatch change: the beacon/src/error.rs variant, client.rs ensure_genesis_validators_root, a 4-line change in manager.rs confirm_genesis_validators_root, and the startup.rs match. The rest is fork-hazard line pins (client.rs 547/1246/2123), which resolve correctly. No dependency changes.

Items

  1. SSE failover: FIXED. The scan is forward-circular over admitted endpoints (bn-manager/src/sse.rs:315), so there's no more starvation. With none admitted it sleeps 50 ms and selects on shutdown (sse.rs:324-331), so there's no busy-spin.
  2. Chunks dropped after revocation: PARTIAL.
    • Admission is checked before awaiting resp.chunk() (sse.rs:454). There's no re-check after the await, before framing and forwarding (:465). Queued callbacks don't check admission either (sse.rs:283).
    • The new test only rejects the second post-revocation frame (slot "3") and lets the first through (slot "2") (sse.rs:1724, :1734).
    • The doc at sse.rs:259 ("read before each connection and per chunk") is accurate, but it isn't a delivery guarantee.
    • Nothing marks a node unadmitted after boot today, so this can't currently trigger. P3.
    • Fix: re-check after the await, and carry endpoint identity through the callback queue.
  3. Typed root mismatch: PARTIAL.
    • The typed variant BeaconError::GenesisValidatorsRootMismatch replaces the string match. The startup match is now exact (startup.rs:123), and other errors still go to err.into() and fail closed.
    • No classification regression. is_unreachable and failover classification never saw this error, because the raw client path doesn't produce it (manager.rs:70, :2663, client.rs:484). confirm_genesis_validators_root still skips wrong-root responders and returns the typed error only when none match (manager.rs:2815). Admission rejects before setting the flag (manager.rs:2750).
    • Not fixed (R5 P3): in agreed_spec_and_genesis, a node's genesis_time is folded into agreement before its root is checked (manager.rs:2605 then :2606). Under Responding, a wrong-root node with a different genesis_time still stops boot instead of being skipped. This fails safe; it's an availability problem. P3.
    • Fix: check the root first, then fold in that node's values.

Checks

  • fmt OK. clippy -D warnings OK.
  • Scoped nextest (rvc-bn-manager, rvc, rvc-architecture-tests): 1977 run, 1975 passed, 2 failed.
    • config_url::refresh_clean_shutdown: known flake.
    • import_budget::async_heartbeat_has_no_gap_over_50ms_while_100_keystores_import: a timing test that passed on rerun.
  • No dependency changes, so no cargo audit.
  • CI: all green (Check, Test all-features, Architecture Gates, Spec Vectors, Coverage, Scripts, Trace continuity, Secret Scan, GitGuardian).

@rootwarp
rootwarp merged commit 55da4f5 into develop Oct 16, 2026
9 checks passed
@rootwarp
rootwarp deleted the cursor/bn-slot-duration-4a37 branch October 16, 2026 13:43
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