Also match DropReason::IdentityChanged as a unit variant in the harness,
since the variant-level non_exhaustive is gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fuzz oracle and its proptest twin checked much less than §7 claims
(S8). The harness now:
- records every frame the node attempts to send, before the transport
can refuse it, so the "every frame fits mtu()" check can fail;
- reads the MTU from its own u16 (input bytes 1-2), mapped into
MIN_MTU..=MAX_FRAME, instead of reusing the first step tag;
- reconciles drained drop counts per address: VersionMismatch/Malformed
drops equal the frames that fail decode (the overflow slot makes up
any difference), IdentityChanged drops never exceed frames whose
`from` changed, and nothing is counted beyond the frames received;
- checks no addressed observation is stored, or forwarded in an Obs
frame, past its lifetime;
- checks signatures round-trip from the input (an observation stored from
a whole Obs frame keeps exactly its signature, and a stored signature
never changes), replacing the input-independent sig_round_trip call;
- checks every acked id is stored (F1) and bounds the broadcast ids
offered per received summary by SEND_QUEUE_CAP (F9);
- has a real adversarial Mode (input byte 0): well-formed Obs, Frag,
Summary and Ack frames from 3 x MAX_PEERS addresses, with signed and
max-size observations (u16 payload lengths), chosen observers
(including the node's own id), ages, lifetimes and addressing,
structured fragment streams that conflict, repeat or arrive out of
order, replayed summaries, `from` values that change within a contact,
and Acks from the peer's real id naming ids the node really sent;
- adds the §10 peer-table recovery probe, which the fuzz target runs
after every adversarial input.
fuzz_twin.rs now runs the fuzz entry point on random bytes, generated
adversarial steps, and two rewritten properties that were trivially
true: summary_repeat_does_not_resend seeds broadcasts and checks a
replay re-sends nothing while the next on-time summary re-sends all;
peer_table_recovers_after_flood floods the table, checks it is full,
and probes recovery after PEER_IDLE_MS. The recovery tests are
#[ignore]d until the S1 idle sweep lands, and the fuzz target fails on
the first adversarial input until then. A 180 s nightly run with the
probe disabled found nothing else.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Decision K (spec §5): the local capacities and timings are defaults that
will become configuration, so they are `pub(crate)`. The wire constants
stay public.
- MAX_PEERS, SUMMARY_INTERVAL_MS, REASSEMBLY_TIMEOUT_MS, SEND_QUEUE_CAP,
MIN_MTU, EVENT_QUEUE_CAP, DROP_TABLE_CAP, BCAST_SENT_CAP, ACK_BUFFER_CAP
and PEER_IDLE_MS are now `pub(crate)`; a compile_fail doctest pins it.
- MIN_MTU's doc claimed it was the floor below which a fragment has no
room. It is only the frag sweep's lower bound (BLE ATT's 23). The real
floors are now stated on `Transport::mtu` and pinned by
`consts::tests::mtu_floors`: 85 to carry every valid observation, 66 to
fragment at all, 35 for an Ack, 26 for a summary.
- tines-bt caps live connections with its own MAX_PEERS (8).
- tines-sim's props mirror MAX_PEERS/SEND_QUEUE_CAP, checked against core
through the hidden `conformance` module; the fuzz twin reads it there.
- node.rs: drain_events' rustdoc no longer links the now-private consts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>