Production hardening: unwrap safety, 14 new tests, diagnostics

Harden unwrap() calls in handler hot paths (handshake, encrypted,
rekey, session handlers) with proper error propagation.

Add 14 tests: profile rejection (6), MMP forward-compat (4),
discovery min_mtu pruning (3), XX duplicate msg1 dedup (1).

Add NodeProfile Display impl with structured tracing for profile
mismatch logging. Expose bloom compression diagnostics
(total_compressed_bytes, total_raw_bytes) in control socket.

Add module documentation for XX identity timing, profile decision
tree, and bloom codec strategy.
This commit is contained in:
Johnathan Corgan
2026-04-11 13:14:17 +00:00
parent 8b5f1e349f
commit 10122d7d87
12 changed files with 424 additions and 22 deletions
+30 -1
View File
@@ -13,6 +13,25 @@
//! Bytes 10+: TLV entries, each:
//! [field_num:2 LE][length:2 LE][value:N]
//! ```
//!
//! ## Node Profile Decision Tree
//!
//! Profiles are self-declared (bits 0-2 of the feature bitfield):
//!
//! - **Full** (0): Full routing. Combines bloom filters from children,
//! forwards transit traffic, participates in spanning tree.
//! - **NonRouting** (1): Tree participation but no transit forwarding.
//! Receives bloom filters (one-way: F→N) but does not send them.
//! The full peer inserts N's identity via `leaf_dependents`.
//! - **Leaf** (2): Single upstream peer, no tree/bloom/transit.
//! Full peer inserts L's identity via `leaf_dependents`.
//!
//! **Link pairing rule**: at least one side must be Full. Invalid
//! pairings (N↔N, N↔L, L↔L) are rejected during FMP negotiation.
//!
//! **Routing implications**: `forward_lookup_request()` only considers
//! Full peers as transit. `peer_inbound_filters()` excludes non-Full
//! peers from bloom filter merging.
use super::ProtocolError;
@@ -58,6 +77,16 @@ pub enum NodeProfile {
Leaf = 2,
}
impl std::fmt::Display for NodeProfile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Full => write!(f, "full"),
Self::NonRouting => write!(f, "non-routing"),
Self::Leaf => write!(f, "leaf"),
}
}
}
impl TryFrom<u8> for NodeProfile {
type Error = ProtocolError;
@@ -274,7 +303,7 @@ impl NegotiationPayload {
pub fn validate_profiles(ours: NodeProfile, theirs: NodeProfile) -> Result<(), ProtocolError> {
if ours != NodeProfile::Full && theirs != NodeProfile::Full {
return Err(ProtocolError::Malformed(format!(
"invalid profile pairing: {:?} <-> {:?} (at least one must be Full)",
"invalid profile pairing: {} <-> {} (at least one must be full)",
ours, theirs
)));
}