mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-22 07:48:26 +00:00
node: correct stale liveness claims in machine, executor, and timer docs
Several module and field docs still described the per-peer machine as unwired shadow scaffolding. It has been live for some time: machines are inserted at dial and inbound msg1, stepped by the handshake handlers and the rekey-cadence and liveness-reap routers, and the executor's SwapSendState/CompleteDrain/InvalidateSendState arms are the authoritative paths (the inline bodies survive only as debug-assert release fallbacks). Rewrite those docs to the current truth while keeping the still-true dormancy facts: PeerEvent::Timeout and PeerEvent::Tick are never dispatched in production, retransmit fires on the machine-armed deadline while the timeout reaper keys on timer presence with the config threshold, and the remaining inert executor stubs are SendRekey, SendLinkMessage, and the connected-UDP arms. Drop the stale allow(dead_code) on the peer_machines field.
This commit is contained in:
@@ -12,14 +12,17 @@
|
||||
//! (`handle_msg1` → `step(InboundMsg1)`), the outbound msg2 promote
|
||||
//! (`handle_msg2` looks up the dial-persisted machine), the connectionless
|
||||
//! outbound msg1 send (`SendHandshake` with `their_index == None` →
|
||||
//! `send_stored_msg1`, driven from `initiate_connection`), and the
|
||||
//! `send_stored_msg1`, driven from `initiate_connection`), the
|
||||
//! connection-oriented dial (`OpenTransport` performs the non-blocking
|
||||
//! `transport.connect`; `TransportConnected` drives the connect-resolution msg1
|
||||
//! send from `poll_pending_connects`).
|
||||
//! send from `poll_pending_connects`), the rekey cadence (`check_rekey` →
|
||||
//! `route_rekey_cadence` → `RekeyConsume`, driving the `SwapSendState` and
|
||||
//! `CompleteDrain` arms), and the liveness reap (`route_link_dead` →
|
||||
//! `LinkDeadSuspected`, driving `InvalidateSendState` → `remove_active_peer`).
|
||||
//!
|
||||
//! Not yet driven, so their arms stay inert stubs: rekey/crypto installs,
|
||||
//! link-control frames, and the connected-UDP plane. `RegisterDecryptSession` is
|
||||
//! a deliberate no-op — see its arm for the note.
|
||||
//! The genuine inert stubs remaining are `SendRekey`, `SendLinkMessage`, and
|
||||
//! the connected-UDP arms. `RegisterDecryptSession` is a deliberate no-op —
|
||||
//! see its arm for the note.
|
||||
//!
|
||||
//! The timer arms (`SetTimer`/`CancelTimer`) populate/clear the per-peer timer
|
||||
//! store (`peer_timers`). The `HandshakeRetransmit` and `HandshakeTimeout`
|
||||
@@ -386,13 +389,15 @@ impl Node {
|
||||
}
|
||||
}
|
||||
PeerAction::SwapSendState { .. } => {
|
||||
// Initiator cutover. Reproduces the `ConnAction::Cutover`
|
||||
// body in `handlers/rekey.rs:53-88` EXACTLY. `addr` is resolved
|
||||
// Initiator cutover: the live authoritative rekey-cadence
|
||||
// path, routed here from `check_rekey` via
|
||||
// `route_rekey_cadence` → `PeerEvent::RekeyConsume`; the
|
||||
// inline body survives only as `cutover_peer_inline`, a
|
||||
// debug-assert release fallback. `addr` is resolved
|
||||
// from the ambient verified identity (as `InvalidateSendState`
|
||||
// does). The decrypt re-register folds HERE, gated on
|
||||
// `did_cutover` — the generic `RegisterDecryptSession` arm stays a
|
||||
// no-op so a promote never double-registers. Shadow-only until the
|
||||
// cadence fold routes here.
|
||||
// no-op so a promote never double-registers.
|
||||
let node_addr = *ambient.verified_identity.node_addr();
|
||||
let did_cutover = if let Some(peer) = self.peers.get_mut(&node_addr) {
|
||||
if let Some(_old_our_index) = peer.cutover_to_new_session() {
|
||||
@@ -436,12 +441,14 @@ impl Node {
|
||||
let _ = did_cutover;
|
||||
}
|
||||
PeerAction::CompleteDrain { peer: node_addr } => {
|
||||
// Initiator drain completion. Reproduces the
|
||||
// `ConnAction::Drain` body in `handlers/rekey.rs:90-111` EXACTLY.
|
||||
// Extract the real previous index + transport_id under the peer
|
||||
// borrow, drop the borrow, then run the cache_key cleanup (which
|
||||
// takes &mut self for unregister_decrypt_worker_session).
|
||||
// Shadow-only until the cadence fold routes here.
|
||||
// Initiator drain completion: the live authoritative
|
||||
// rekey-cadence path, routed here from `check_rekey` via
|
||||
// `route_rekey_cadence` → `PeerEvent::RekeyConsume`; the
|
||||
// inline body survives only as `drain_peer_inline`, a
|
||||
// debug-assert release fallback. Extract the real previous
|
||||
// index + transport_id under the peer borrow, drop the
|
||||
// borrow, then run the cache_key cleanup (which takes
|
||||
// &mut self for unregister_decrypt_worker_session).
|
||||
let drained = self.peers.get_mut(&node_addr).and_then(|peer| {
|
||||
peer.complete_drain().map(|idx| (idx, peer.transport_id()))
|
||||
});
|
||||
|
||||
@@ -1271,9 +1271,10 @@ impl Node {
|
||||
};
|
||||
// The outbound Msg2 Promote step cancels the two dial-armed handshake
|
||||
// timers (the machine survives promotion, so they would otherwise linger
|
||||
// in the driver's shadow store) and then promotes. The cancels are
|
||||
// behavior-neutral at this rung — `peer_timers` is written but not yet
|
||||
// driven — and `PromoteToActive` is still what performs the promotion.
|
||||
// in `peer_timers` until `drive_peer_timers` lazily discards them — the
|
||||
// promoted leg's `connections` entry is gone and the machine has left
|
||||
// `SentMsg1`, so they can no longer fire) and then promotes.
|
||||
// `PromoteToActive` is still what performs the promotion.
|
||||
debug_assert_eq!(
|
||||
promote_actions,
|
||||
vec![
|
||||
|
||||
@@ -355,22 +355,27 @@ pub struct Node {
|
||||
|
||||
// === Per-Peer Control Machines ===
|
||||
/// Per-peer lifecycle control FSMs, keyed by the stable `LinkId` that spans
|
||||
/// the handshake→active lifetime. A NEW parallel structure introduced by the
|
||||
/// the handshake→active lifetime. A parallel structure introduced by the
|
||||
/// node-runtime decomposition: `connections`/`peers` stay byte-unchanged (hot
|
||||
/// path pristine) and are cut over to this machine home path-by-path. Unwired
|
||||
/// initially — the executor (`dataplane/peer_actions.rs`) and advance helper
|
||||
/// exist but the live `handle_msg1`/`handle_msg2` path does not drive them yet;
|
||||
/// the inbound cutover is wired later.
|
||||
#[allow(dead_code)]
|
||||
/// path pristine) and are cut over to this machine home path-by-path.
|
||||
/// Machines are inserted at dial and inbound msg1, and stepped in production
|
||||
/// by the handshake handlers, the rekey-cadence and liveness-reap routers,
|
||||
/// and the lifecycle paths, with the executor (`dataplane/peer_actions.rs`)
|
||||
/// performing the returned actions. Timer FIRING decisions remain
|
||||
/// shell-side: `PeerEvent::Timeout` is never dispatched in production.
|
||||
peer_machines: HashMap<LinkId, PeerMachine>,
|
||||
|
||||
/// Per-peer timer store, keyed by `LinkId` then `TimerKind`, holding each
|
||||
/// armed timer's absolute deadline (ms). The sans-IO time-as-input backing
|
||||
/// for `PeerEvent::Timeout`: populated/cleared by the machine's
|
||||
/// `SetTimer`/`CancelTimer` actions (`dataplane/peer_actions.rs`) and dropped
|
||||
/// alongside the machine through the `remove_peer_machine` choke-point. At
|
||||
/// this rung it is a SHADOW of the legacy tick timers — written but not yet
|
||||
/// read by any driver (the handshake-kind fold wires the reader).
|
||||
/// alongside the machine through the `remove_peer_machine` choke-point. The
|
||||
/// `HandshakeRetransmit`/`HandshakeTimeout` kinds are driven by
|
||||
/// `drive_peer_timers` (the retransmit fires on the stored deadline; the
|
||||
/// timeout reap keys on the timer's presence, with the threshold read from
|
||||
/// config); the rekey/liveness kinds are still SHADOWS of their
|
||||
/// own shell drivers, and the machine's `on_timeout` handlers stay dormant
|
||||
/// (`PeerEvent::Timeout` is never dispatched in production).
|
||||
peer_timers: HashMap<LinkId, HashMap<TimerKind, u64>>,
|
||||
|
||||
// === Peers (Active Phase) ===
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
//! The unified per-peer lifecycle state machine that folds the scattered
|
||||
//! `connections`/`peers`/rekey state carriers into one place. It provides the
|
||||
//! FSM types, the machine struct (control-tier state only), and the pure `step`
|
||||
//! reducer, plus its unit tests. It is **unwired** — nothing in the codebase
|
||||
//! calls it yet; the driver wiring and the send-state boundary land in later
|
||||
//! commits.
|
||||
//! reducer, plus its unit tests. `step` is driven in production by the
|
||||
//! handshake handlers, the rekey-cadence and liveness-reap routers, and the
|
||||
//! dial/lifecycle paths, with the executor in
|
||||
//! `crate::node::dataplane::peer_actions` performing the returned actions.
|
||||
//! Still dormant: `PeerEvent::Timeout` is never dispatched — timer FIRING
|
||||
//! decisions stay with the shell drivers.
|
||||
//!
|
||||
//! ## Shape
|
||||
//!
|
||||
@@ -66,11 +69,14 @@ use crate::{NodeAddr, PeerIdentity};
|
||||
// ============================================================================
|
||||
// Timing placeholders
|
||||
//
|
||||
// This module is unwired; the real intervals come from `NodeConfig` when the
|
||||
// driver is wired. The `poll_*` cores already take the interval/backoff as
|
||||
// arguments, so these are only used to compute `SetTimer{at_ms}` deadlines and
|
||||
// the `Closed{backoff_deadline_ms}` park time. The unit tests assert on timer
|
||||
// *kinds*, not exact deadlines.
|
||||
// The `poll_*` cores already take the interval/backoff as arguments, so these
|
||||
// are only used to compute `SetTimer{at_ms}` deadlines and the
|
||||
// `Closed{backoff_deadline_ms}` park time. The handshake timers are armed
|
||||
// live at dial time from these constants: the retransmit driver fires on the
|
||||
// machine-armed deadline, while the timeout reaper keys on the timer's
|
||||
// presence with its threshold read from `NodeConfig`, which also governs the
|
||||
// reschedule cadence shell-side. The unit tests assert on timer *kinds*, not
|
||||
// exact deadlines.
|
||||
// ============================================================================
|
||||
|
||||
const HANDSHAKE_RETRANSMIT_INTERVAL_MS: u64 = 1_000;
|
||||
@@ -1267,9 +1273,9 @@ impl PeerMachine {
|
||||
}
|
||||
|
||||
fn on_tick(&mut self, now: u64) -> Vec<PeerAction> {
|
||||
// The driver evaluates due machine timers on the quantized tick and
|
||||
// re-enters the Timeout{kind} handlers. This module is unwired; the
|
||||
// deadline bookkeeping is threaded from the driver, so Tick is a no-op here.
|
||||
// Dormant no-op: `PeerEvent::Tick` is not dispatched in production.
|
||||
// The shell drivers evaluate due timer deadlines themselves, so there
|
||||
// is no machine-side bookkeeping to advance here.
|
||||
let _ = now;
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user