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>
fuzz/Cargo.toml still required tines-core 0.3.0, so since the 1.0.0
version bump `cargo fuzz run node` failed version selection (B6). The
requirement is now "1" and the lock is regenerated.
scripts/ci.sh now runs rustfmt and clippy (all targets, -D warnings) on
the out-of-workspace fuzz crate on the pinned stable toolchain, so it
can't silently stop building again. fuzz/.gitignore ignores target,
artifacts and coverage.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec §3 (decision D-b) lists `Frame` and `WireError` as private; lib.rs
re-exported both. Only `frame_sender` stays public.
- The pinned vectors in tests/vectors.rs now compile in-crate as a
`#[cfg(test)] #[path]` module (autotests = false keeps Cargo from also
building them as an integration test). Every golden vector now checks
`encode() == GOLDEN` as well as the decode, and new literal vectors pin a
trailing-byte rejection and an addressed Obs with a nonzero age and an
`observed_at`.
- A `#[doc(hidden)]` `conformance` module (behind the `conformance`
feature) gives the fuzz harness byte-level frame encoders/decoders and
core's local limits without exposing `Frame`. The fuzz_twin test target
now requires `conformance`, which scripts/ci.sh enables.
- compile_fail doctests pin that `Frame`/`WireError` are unreachable.
- Remove the THROWAWAY gen_missing_golden_vectors test and fix stale
wire.rs comments about the version/tag byte offsets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>