name: Fuzzing on: push: branches: [main, develop, feat-add-afl-fuzzing] pull_request: schedule: # Nightly full run - cron: "0 2 * * *" env: RISC0_DEV_MODE: "1" CARGO_TERM_COLOR: always jobs: # ── Smoke fuzz: 60 s per target ───────────────────────────────────────────── smoke-fuzz: name: Smoke fuzz (${{ matrix.target }}) runs-on: ubuntu-latest strategy: fail-fast: false matrix: target: - fuzz_transaction_decoding - fuzz_stateless_verification - fuzz_state_transition - fuzz_block_verification - fuzz_encoding_roundtrip - fuzz_signature_verification - fuzz_replay_prevention - fuzz_state_diff_computation - fuzz_validate_execute_consistency - fuzz_state_serialization - fuzz_witness_set_verification - fuzz_program_deployment_lifecycle - fuzz_apply_state_diff_split_path - fuzz_multi_block_state_sequence - fuzz_sequencer_vs_replayer steps: - uses: actions/checkout@v4 - name: Checkout logos-execution-zone alongside lez-fuzzing uses: actions/checkout@v4 with: repository: logos-blockchain/logos-execution-zone path: logos-execution-zone - name: Symlink logos-execution-zone to sibling directory run: ln -s "$GITHUB_WORKSPACE/logos-execution-zone" "$GITHUB_WORKSPACE/../logos-execution-zone" - name: Install Rust nightly (required by cargo-fuzz) uses: dtolnay/rust-toolchain@nightly with: components: llvm-tools-preview - name: Cache cargo registry uses: actions/cache@v4 with: path: | ~/.cargo/registry ~/.cargo/git target key: fuzz-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} - name: Install logos-blockchain-circuits uses: ./logos-execution-zone/.github/actions/install-logos-blockchain-circuits with: github-token: ${{ secrets.GITHUB_TOKEN }} - name: Install cargo-fuzz run: cargo install cargo-fuzz - name: Build fuzz target run: cargo fuzz build ${{ matrix.target }} - name: Run smoke fuzz (60 s) run: | mkdir -p corpus/libfuzz/${{ matrix.target }} cargo fuzz run ${{ matrix.target }} \ corpus/libfuzz/${{ matrix.target }} \ -- -max_total_time=60 -jobs=2 -workers=2 - name: Calculate and show edge bitmap coverage if: always() run: | TARGET="${{ matrix.target }}" CORPUS="corpus/libfuzz/${TARGET}" mkdir -p "$CORPUS" # ── Build and replay the corpus with LLVM coverage instrumentation ── # cargo fuzz coverage builds into fuzz/target//coverage/ # and writes the merged profdata to fuzz/coverage//coverage.profdata cargo fuzz coverage "$TARGET" "$CORPUS" || true # ── Locate llvm-cov from the installed nightly toolchain ── SYSROOT="$(rustc --print sysroot)" HOST_TRIPLE="$(rustc -vV | awk '/^host:/{print $2}')" LLVM_COV="${SYSROOT}/lib/rustlib/${HOST_TRIPLE}/bin/llvm-cov" # The coverage-instrumented binary lives under a "coverage" profile directory, # NOT "release" — search specifically for that path. PROFDATA=$(find fuzz/coverage/"$TARGET" -name "coverage.profdata" 2>/dev/null | head -1) BINARY=$(find fuzz/target -path "*coverage/${TARGET}" -type f 2>/dev/null | head -1) branches_covered="n/a" branches_total="n/a" pct="n/a" if [ -n "$PROFDATA" ] && [ -f "$PROFDATA" ] && \ [ -n "$BINARY" ] && [ -f "$BINARY" ]; then JSON=$("$LLVM_COV" export "$BINARY" \ --instr-profile="$PROFDATA" \ --summary-only \ --ignore-filename-regex='\.cargo|rustc' 2>/dev/null || echo "{}") branches_covered=$(echo "$JSON" | python3 -c " import sys, json data = json.load(sys.stdin) try: br = data['data'][0]['totals']['branches'] print(br['covered']) except Exception: print('n/a') ") branches_total=$(echo "$JSON" | python3 -c " import sys, json data = json.load(sys.stdin) try: br = data['data'][0]['totals']['branches'] print(br['count']) except Exception: print('n/a') ") pct=$(echo "$JSON" | python3 -c " import sys, json data = json.load(sys.stdin) try: br = data['data'][0]['totals']['branches'] print(f\"{br['percent']:.2f}\") except Exception: print('n/a') ") fi echo "Branch coverage: ${branches_covered}/${branches_total} (${pct}%)" # ── GitHub Step Summary ── { echo "## Edge Bitmap Coverage — \`${TARGET}\`" echo "" echo "| Method | Covered branches | Total branches | Coverage % |" echo "|---|---|---|---|" echo "| \`llvm-cov\` (libFuzzer corpus) | ${branches_covered} | ${branches_total} | **${pct}%** |" echo "" echo "> Coverage computed over \`${CORPUS}\` using LLVM source-based branch instrumentation." } >> "$GITHUB_STEP_SUMMARY" - name: Upload crash artifacts if: failure() uses: actions/upload-artifact@v4 with: name: crash-${{ matrix.target }} path: fuzz/artifacts/${{ matrix.target }}/ # ── Corpus regression ──────────────────────────────────────────────────────── regression: name: Corpus regression (${{ matrix.target }}) runs-on: ubuntu-latest strategy: fail-fast: false matrix: target: - fuzz_transaction_decoding - fuzz_stateless_verification - fuzz_state_transition - fuzz_block_verification - fuzz_encoding_roundtrip - fuzz_signature_verification - fuzz_replay_prevention - fuzz_state_diff_computation - fuzz_validate_execute_consistency - fuzz_state_serialization - fuzz_witness_set_verification - fuzz_program_deployment_lifecycle - fuzz_apply_state_diff_split_path - fuzz_multi_block_state_sequence - fuzz_sequencer_vs_replayer steps: - uses: actions/checkout@v4 - name: Checkout logos-execution-zone alongside lez-fuzzing uses: actions/checkout@v4 with: repository: logos-blockchain/logos-execution-zone path: logos-execution-zone - name: Symlink logos-execution-zone to sibling directory run: ln -s "$GITHUB_WORKSPACE/logos-execution-zone" "$GITHUB_WORKSPACE/../logos-execution-zone" - uses: dtolnay/rust-toolchain@nightly with: components: llvm-tools-preview - name: Install logos-blockchain-circuits uses: ./logos-execution-zone/.github/actions/install-logos-blockchain-circuits with: github-token: ${{ secrets.GITHUB_TOKEN }} - run: cargo install cargo-fuzz - name: Reproduce corpus run: | mkdir -p corpus/libfuzz/${{ matrix.target }} cargo fuzz run ${{ matrix.target }} \ corpus/libfuzz/${{ matrix.target }} -- -runs=0 # ── proptest property tests ────────────────────────────────────────────────── proptest: name: Property tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Checkout logos-execution-zone alongside lez-fuzzing uses: actions/checkout@v4 with: repository: logos-blockchain/logos-execution-zone path: logos-execution-zone - name: Symlink logos-execution-zone to sibling directory run: ln -s "$GITHUB_WORKSPACE/logos-execution-zone" "$GITHUB_WORKSPACE/../logos-execution-zone" - uses: dtolnay/rust-toolchain@stable - name: Install logos-blockchain-circuits uses: ./logos-execution-zone/.github/actions/install-logos-blockchain-circuits with: github-token: ${{ secrets.GITHUB_TOKEN }} - run: cargo test -p fuzz_props --release # ── Performance baseline (nightly only) ───────────────────────────────────── perf-baseline: name: Performance baseline runs-on: ubuntu-latest if: github.event_name == 'schedule' steps: - uses: actions/checkout@v4 - name: Checkout logos-execution-zone alongside lez-fuzzing uses: actions/checkout@v4 with: repository: logos-blockchain/logos-execution-zone path: logos-execution-zone - name: Symlink logos-execution-zone to sibling directory run: ln -s "$GITHUB_WORKSPACE/logos-execution-zone" "$GITHUB_WORKSPACE/../logos-execution-zone" - uses: dtolnay/rust-toolchain@nightly with: components: llvm-tools-preview - name: Install logos-blockchain-circuits uses: ./logos-execution-zone/.github/actions/install-logos-blockchain-circuits with: github-token: ${{ secrets.GITHUB_TOKEN }} - run: cargo install cargo-fuzz - name: Measure throughput (30 s per target) run: | for target in \ fuzz_transaction_decoding \ fuzz_stateless_verification \ fuzz_state_transition \ fuzz_block_verification \ fuzz_encoding_roundtrip \ fuzz_signature_verification \ fuzz_replay_prevention \ fuzz_state_diff_computation \ fuzz_validate_execute_consistency \ fuzz_state_serialization \ fuzz_witness_set_verification \ fuzz_program_deployment_lifecycle \ fuzz_apply_state_diff_split_path \ fuzz_multi_block_state_sequence \ fuzz_sequencer_vs_replayer; do echo "=== $target ===" | tee -a perf_baseline.txt cargo fuzz run "$target" -- -max_total_time=30 2>&1 \ | grep -E "exec/s|execs_per_sec" | tail -1 | tee -a perf_baseline.txt done - uses: actions/upload-artifact@v4 with: name: perf-baseline path: perf_baseline.txt