From 472ea4176a20eab094bf17082ada37639f9ed44b Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 18 May 2026 13:42:47 +0800 Subject: [PATCH] fix: docs update --- current_vs_alternative_approach.md | 12 ++++++------ docs/fuzzing.md | 31 ++++++++++++++++++++---------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/current_vs_alternative_approach.md b/current_vs_alternative_approach.md index 5b24b1b..f829315 100644 --- a/current_vs_alternative_approach.md +++ b/current_vs_alternative_approach.md @@ -11,7 +11,7 @@ The `lez-fuzzing` repository is a **coverage-guided, structured mutation fuzzing | Rich generators | [`fuzz_props::generators`](fuzz_props/src/generators.rs) adds `proptest` strategies for pathological sequences, phantom-account attacks, overflow amounts, replay sequences | | Protocol invariants | [`fuzz_props::invariants`](fuzz_props/src/invariants.rs) expresses zero-mutation-on-rejection and replay-rejection as reusable `ProtocolInvariant` objects | | ZK-awareness | `RISC0_DEV_MODE=1` stubs out `risc0-zkvm` proofs, enabling ~5 000–200 000 exec/sec depending on target | -| 9 dedicated targets | Covers encoding, signature verification, stateless checks, state transitions, state diffs, replay prevention, validate/execute consistency, block verification | +| 14 dedicated targets | Covers encoding, signature verification, stateless checks, state transitions, state diffs, replay prevention, validate/execute consistency, block verification, state serialization, witness-set verification, program deployment lifecycle, split-path equivalence, multi-block sequences | | CI integration | GitHub Actions smoke, regression, and performance-baseline jobs run on every PR | | Pre-seeded corpus | Hundreds of minimised seed files in [`fuzz/corpus/`](fuzz/corpus/) ensure regressions are caught instantly | @@ -32,7 +32,7 @@ The `lez-fuzzing` repository is a **coverage-guided, structured mutation fuzzing | CI ergonomics | Requires AFL++ binary in CI image | `cargo install cargo-fuzz` only | | Rust integration | `cargo-afl` | `cargo-fuzz` | -**Decision-maker view**: AFL++ and libFuzzer find *different* bugs because they use different mutation heuristics. Running both on the same corpus is the industry-standard "belt and suspenders" approach. [`docs/fuzzing.md`](docs/fuzzing.md:334) already lists `just fuzz-afl` as planned future work. **Incremental cost is low** — the same [`fuzz_props`](fuzz_props/src/lib.rs) crate and seed corpus work unchanged. +**Decision-maker view**: AFL++ and libFuzzer find *different* bugs because they use different mutation heuristics. Running both on the same corpus is the industry-standard "belt and suspenders" approach. [`docs/fuzzing.md`](docs/fuzzing.md:355) already lists `just fuzz-afl` as planned future work. **Incremental cost is low** — the same [`fuzz_props`](fuzz_props/src/lib.rs) crate and seed corpus work unchanged. --- @@ -71,7 +71,7 @@ The `lez-fuzzing` repository is a **coverage-guided, structured mutation fuzzing **What it is**: Feed identical inputs to two independent implementations of the same interface and assert identical outputs. Already **partially implemented** in [`fuzz_validate_execute_consistency.rs`](fuzz/fuzz_targets/fuzz_validate_execute_consistency.rs) — it compares [`validate_on_state`](fuzz/fuzz_targets/fuzz_validate_execute_consistency.rs:61) vs. [`execute_check_on_state`](fuzz/fuzz_targets/fuzz_validate_execute_consistency.rs:65), and also asserts balance conservation. -The extension noted in [`docs/fuzzing.md`](docs/fuzzing.md:335) is: +The extension noted in [`docs/fuzzing.md`](docs/fuzzing.md:356) is: > Feed the same block to `SequencerCore` and `indexer_core` and assert identical state roots. @@ -111,7 +111,7 @@ The extension noted in [`docs/fuzzing.md`](docs/fuzzing.md:335) is: | Execution time | Slow (recompile per mutation) | Continuous | | Output | Surviving mutants = assertion gaps | Crash artifacts | -**Decision-maker view**: `cargo-mutants` would **audit the invariant assertions themselves** — revealing if [`assert_invariants()`](fuzz_props/src/invariants.rs:90) has gaps. [`StateIsolationOnFailure`](fuzz_props/src/invariants.rs:38) is fully implemented; [`ReplayRejection`](fuzz_props/src/invariants.rs:65) is intentionally a no-op in `InvariantCtx` (enforced directly in `fuzz_state_transition` and `fuzz_replay_prevention`). This is a **complementary quality gate**, not a fuzzing replacement. Low cost (~1 day), highly useful before an external security audit. +**Decision-maker view**: `cargo-mutants` would **audit the invariant assertions themselves** — revealing if [`assert_invariants()`](fuzz_props/src/invariants.rs:325) has gaps. Three invariants are fully implemented: [`StateIsolationOnFailure`](fuzz_props/src/invariants.rs:60), [`BalanceConservation`](fuzz_props/src/invariants.rs:94), and [`FailedTxNonceStability`](fuzz_props/src/invariants.rs:130). Two are registry stubs: [`ReplayRejection`](fuzz_props/src/invariants.rs:169) and [`NonceIncrementCorrectness`](fuzz_props/src/invariants.rs:196) — each enforced via dedicated standalone helpers (`assert_replay_rejection`, `assert_nonce_increment_correctness`). This is a **complementary quality gate**, not a fuzzing replacement. Low cost (~1 day), highly useful before an external security audit. --- @@ -135,9 +135,9 @@ The current implementation is **well-architected and production-ready** for a pr **Highest-ROI next steps, in priority order:** -1. **Verify and extend the inline invariants** — [`StateIsolationOnFailure`](fuzz_props/src/invariants.rs:38) in [`fuzz_props/src/invariants.rs`](fuzz_props/src/invariants.rs:40) is fully implemented. [`ReplayRejection`](fuzz_props/src/invariants.rs:65) is intentionally a no-op in `InvariantCtx` but enforced directly in `fuzz_state_transition` and `fuzz_replay_prevention`. Add `BalanceConservation` as a registered `ProtocolInvariant` to make it reusable across all targets (currently checked inline only). +1. **The invariant framework is complete for the current target set** — three invariants are fully implemented and auto-run by [`assert_invariants()`](fuzz_props/src/invariants.rs:325): [`StateIsolationOnFailure`](fuzz_props/src/invariants.rs:60), [`BalanceConservation`](fuzz_props/src/invariants.rs:94), and [`FailedTxNonceStability`](fuzz_props/src/invariants.rs:130). Two further invariants ([`ReplayRejection`](fuzz_props/src/invariants.rs:169) and [`NonceIncrementCorrectness`](fuzz_props/src/invariants.rs:196)) are registered stubs; callers use the dedicated `assert_replay_rejection` and `assert_nonce_increment_correctness` helpers directly. The next step is to audit all 14 targets to confirm every applicable invariant is wired up, then add mutation tests via `cargo-mutants`. -2. **Add the sequencer-vs-replayer differential target** — highest new bug-finding value, unique to this protocol's architecture, already identified in [`docs/fuzzing.md`](docs/fuzzing.md:335). +2. **Add the sequencer-vs-replayer differential target** — highest new bug-finding value, unique to this protocol's architecture, already identified in [`docs/fuzzing.md`](docs/fuzzing.md:356). 3. **Add AFL++ as a parallel fuzzing lane** (`just fuzz-afl`) — zero corpus migration cost, discovers different mutation paths through the same targets as libFuzzer. diff --git a/docs/fuzzing.md b/docs/fuzzing.md index 0bc46cc..afb2b1c 100644 --- a/docs/fuzzing.md +++ b/docs/fuzzing.md @@ -219,22 +219,33 @@ Concrete invariants currently registered in `assert_invariants()`: | Invariant | Description | Implementation status | |-----------|-------------|----------------------| -| `StateIsolationOnFailure` | Per-account balance must not change for any tracked account when a transaction is rejected | ✅ Fully implemented in `fuzz_props/src/invariants.rs` | -| `ReplayRejection` | An accepted transaction must be rejected when replayed | ⚠️ Returns `None` from `InvariantCtx` (see note below); enforced directly in `fuzz_state_transition` and `fuzz_replay_prevention` | +| `StateIsolationOnFailure` | Per-account balance must not change for any tracked account when a transaction is rejected | ✅ Fully implemented | +| `BalanceConservation` | Total balance of all known accounts must be conserved when a transaction succeeds | ✅ Fully implemented | +| `FailedTxNonceStability` | Every account's nonce must remain unchanged when a transaction is rejected | ✅ Fully implemented | +| `ReplayRejection` | An accepted transaction must be rejected when replayed | ⚠️ Registry stub — always returns `None` from `InvariantCtx`; use `assert_replay_rejection()` directly (see note below) | +| `NonceIncrementCorrectness` | Every signer account's nonce must be incremented by exactly one after a successful transaction | ⚠️ Registry stub — always returns `None` from `InvariantCtx`; use `assert_nonce_increment_correctness()` directly (see note below) | -> **Note on `ReplayRejection`:** The check cannot be fully exercised through `InvariantCtx` -> because it requires re-applying the `ValidatedTransaction` returned on `Ok` by -> `execute_check_on_state` — which consumes `self` — to the post-execution state. The -> invariant is enforced in two complementary ways: (1) `fuzz_state_transition.rs` replays -> every accepted transaction in the next block and asserts rejection; (2) the proptest suite -> `replay_rejection_proptest` in `fuzz_props/src/invariants.rs` exercises the same property -> with reproducible structured inputs. +> **Note on stub invariants:** `ReplayRejection` and `NonceIncrementCorrectness` cannot be +> fully exercised through `InvariantCtx` alone. Each requires information that is consumed +> before `InvariantCtx` is built: +> +> - **`ReplayRejection`**: `execute_check_on_state` returns the `NSSATransaction` on `Ok`, +> consuming `self`. Replaying it requires re-applying the returned transaction to the +> post-execution state — not possible via a shared `&InvariantCtx`. Use the standalone +> `assert_replay_rejection(applied_tx, state, next_block_id, next_timestamp)` helper +> immediately after each successful execution. The proptest suite `replay_rejection_proptest` +> in `fuzz_props/src/invariants.rs` provides reproducible structured coverage of this +> invariant. +> +> - **`NonceIncrementCorrectness`**: `apply_state_diff` consumes the `ValidatedStateDiff` +> whose signer-account list is private to the `nssa` crate. The caller must derive signer +> IDs from the transaction's witness set before consuming the diff, then call the standalone +> `assert_nonce_increment_correctness(signer_ids, nonces_before, state_after)` helper. Additional invariants enforced **inline** in specific targets (not via `ProtocolInvariant`): | Invariant | Targets | |-----------|---------| -| `BalanceConservation` | `fuzz_state_transition`, `fuzz_validate_execute_consistency` | | `HashRoundTrip` / `HashPreimage` / `TxOrderCommitment` | `fuzz_block_verification` | | Diff forward containment / reverse completeness | `fuzz_state_diff_computation` |