diff --git a/solutions/LP-0017.md b/solutions/LP-0017.md new file mode 100644 index 0000000..b8d95e6 --- /dev/null +++ b/solutions/LP-0017.md @@ -0,0 +1,91 @@ +# Solution: LP-0017 — Whistleblower + +**Submitted by:** Tranquil-Flow + +## Summary + +Whistleblower is a Logos Basecamp app that lets a user pick a document, upload it to Logos Storage, broadcast the `(CID, metadata)` envelope over Logos Delivery, and (optionally) anchor the CID on a LEZ registry. A separate permissionless CLI tool batch-anchors accumulated CIDs from the Delivery topic without coordination with the publisher. The on-chain registry uses a **PDA-per-CID** layout — every anchored CID lives in its own program-derived account, so anchor cost is O(1), capacity is unbounded, and idempotency falls out of the default-state check. + +## Repository + +- **Repo:** + +## Approach + +The system has four components built from a shared trait surface: + +1. **Logos Basecamp app** (`ui/`) — Qt6/QML plugin packaged as a portable `.lgx`. Drives upload → Storage, broadcast → Delivery, optional anchor → LEZ via FFI. +2. **Reusable indexing module** (`document-indexing`, Qt-free Rust) — exposes `Publisher`, `StorageClient`, `DeliveryClient`, `RegistryClient` traits. Mock adapter for tests, LEZ adapter for production. +3. **Permissionless batch CLI** (`batch/whistleblower-batch`) — subscribes to the Delivery topic, dedupe via sled-backed ledger, anchors in bounded batches. +4. **LEZ registry program** (`methods/guest/`) — PDA-per-CID storage, idempotent anchors, queryable without a transaction. + +### Key design decisions + +1. **PDA-per-CID registry storage** — not a single root PDA with a `HashMap`. We considered the single-PDA approach and rejected it: a single account's data field is capped at ~100 KiB, which bounds registry capacity by design. PDA-per-CID gives O(1) anchor cost regardless of registry size, unbounded capacity, and idempotency-by-default-state-check (no read-modify-write race). Tradeoff: no on-chain enumeration — acceptable because the spec only requires queryability by CID. +2. **Raw `nssa_core` guest** (not `spel-framework`). We tried `spel-framework` first; its `nssa_core/host` feature pulls `bonsai-sdk` into the `riscv32im` build, which fails to cross-compile on macOS arm64 (`ring` Apple Metal). IDL is hand-written; `spel inspect` still consumes it. Filed upstream as a portability bug — see `BUGS_FILED.md`. +3. **LEZ program path** (not zone SDK). The zone SDK currently requires a single designated consensus inscriber — decentralised sequencers haven't shipped. LEZ program is permissionless from day one, which the censorship-resistance brief demands. +4. **Adapter-based indexing module.** The Qt-free Rust core is `dyn`-safe at three trait boundaries (`StorageClient`, `DeliveryClient`, `RegistryClient`). The Basecamp UI plugin and the batch CLI consume it identically, so the batch path can be re-targeted (e.g. a headless Rust adapter against `logos_host`) without touching publishing logic. Tradeoff: real Storage / Delivery integration lives in the UI plugin (uses in-process `LogosAPIClient`); a future headless Rust adapter is sketched in `adapters/logos/README.md`. +5. **Wallet-free upload + broadcast.** Only on-chain anchoring needs a wallet. The publish path satisfies the spec's "without identifying the uploader" requirement. + +### Why the Logos stack + +The brief is censorship-resistant document publication. Storage gives content-addressed retrieval; Delivery gives broadcast without a central operator; LEZ gives a permissionless on-chain registry anyone can append to. Built on a centralized stack, the publisher's identity leaks through whichever single operator hosts upload + broadcast + indexing — the very property we're avoiding. Logos lets each component live in a different trust domain. + +## Success Criteria Checklist + +Mirrored from the LP-0017 spec. + +- [x] **Functionality 1 — Upload to Logos Storage.** `ui/src/WhistleblowerBackend.cpp::uploadToStorage` via `LogosAPIClient::invokeRemoteMethodAsync("storage_module", "uploadUrl", ...)`. +- [x] **Functionality 2 — Broadcast envelope to Logos Delivery topic.** `ui/src/WhistleblowerBackend.cpp::broadcastEnvelope` to `/lp0017-whistleblower/1/cids/json`. +- [x] **Functionality 3 — Optional on-chain anchor.** `Publisher::anchor_published` (Rust) and `whistleblower_anchor_one` (C FFI) — wired to QML's "Anchor" button. +- [x] **Functionality 4 — Permissionless batch anchor CLI.** `batch/` produces `whistleblower-batch`. Sled-backed dedupe ledger, batch window, idempotent re-anchoring. +- [x] **Functionality 4 idempotency — Re-submit registered CID = no-op.** `process_entry` in `methods/guest/src/bin/whistleblower_registry.rs` uses `AccountPostState::new_claimed_if_default`. +- [x] **Functionality 5 — Registry stores `(CID, metadata_hash, anchor_timestamp)` per doc.** `AnchorEntry` borsh-encoded into one PDA per CID. +- [x] **Functionality 5 — Registry queryable by CID.** `whistleblower_query_by_cid` FFI + `LezRegistryClient::query_by_cid_hash`. +- [x] **Functionality 5 — ≥10 CIDs per batch tx.** 50-CID batch confirmed in `lez_adapter_anchor_50_cids_in_one_tx` (5× headroom). +- [x] **Functionality 6 — Reusable indexing module.** `document-indexing` crate, no Qt deps, three `dyn`-safe trait boundaries. +- [x] **Usability — Basecamp app GUI.** `ui/` Qt6/QML plugin packaged as `dist/whistleblower-plugin.lgx`. +- [x] **Usability — LEZ program IDL via SPEL.** `whistleblower-registry.idl.json` consumed by `spel inspect`. +- [x] **Reliability — Storage retry with backoff.** `Publisher` wraps every adapter call in `with_retry` (5 retries, exponential). +- [x] **Reliability — Delivery dedup.** `DurableDedupeStore` (sled) in `batch::run_batch_loop`. +- [x] **Reliability — Batch resumes from last anchored.** Sled ledger + registry idempotency = safe re-runs from any point. +- [x] **Performance — CU benchmarks.** `BENCHMARKS.md` under `risc0_dev_mode = false`: 50-CID batch ~120ms zkVM executor (~2.5ms/CID amortized), single-CID ~6–12ms. +- [x] **Supportability — Deployed on LEZ devnet.** Per Logos Discord (2026-05-11), local sequencer = LEZ devnet; program exercised under `risc0_dev_mode = false`. +- [x] **Supportability — E2E tests in CI.** `.github/workflows/ci.yml` runs the full workspace + ignored live-LEZ tests against a localnet sequencer. +- [x] **Supportability — README + reproducible E2E demo + narrated video.** `README.md`; `scripts/demo.sh` against `lgs localnet start`; narrated demo at . +- [x] **Submission — GitHub issues filed for upstream problems.** `BUGS_FILED.md` lists every issue raised + link. + +## FURPS Self-Assessment + +### Functionality + +The submission delivers the full publish flow (upload → broadcast → optional anchor) inside a real Basecamp instance, plus the permissionless batch CLI for third-party anchoring. The on-chain registry is queryable by CID hash without submitting a transaction. The 50-CID batch confirms a single transaction handles 5× the spec's ≥10-per-batch requirement. Out of scope: tag-based search across the registry (no on-chain enumeration by design — single-CID lookup only), and a headless real-Delivery adapter for the batch CLI (only the in-process Basecamp path uses real Delivery; the batch CLI uses a mock Delivery client because real Delivery currently requires Qt remote objects against `logos_host`). + +### Usability + +End users install the `.lgx` via `lgs basecamp install` or via Basecamp's "Install plugin" → file picker. Onboarding is two steps: pick a document, hit "Publish". Anchoring is an opt-in second click. The reusable indexing module (`document-indexing`) is documented via rustdoc on `Publisher` + the three adapter traits; integrators implement one adapter trait per Logos service they want to back the publisher with. The batch CLI is a single binary with three flags (`--topic`, `--batch-size`, `--dedupe-store-path`). + +### Reliability + +`Publisher` wraps every adapter call in `with_retry` (5 retries, exponential backoff, surfaces the final error). The batch CLI's dedupe ledger is sled-backed so it survives process restarts; combined with the registry's default-state idempotency, the batch is safe to re-run from any point — re-submitting an already-anchored CID is a no-op at the program level. Failed anchors leave the registry unchanged because the LEZ program rejects transactions atomically before any PDA write. The known limitation is that real-Delivery in the headless batch CLI is mock-only; the UI plugin owns the real Storage / Delivery integration (in-process `LogosAPIClient`). + +### Performance + +Captured under `risc0_dev_mode = false` and `RISC0_DEV_MODE=0` on the local LEZ devnet (per Logos Discord 2026-05-11, the local sequencer is the LEZ devnet — no separate remote endpoint exists). 50-CID batch: ~120 ms total zkVM executor time, ~2.5 ms/CID amortized. Single-CID anchor: 6–12 ms. Anchor cost is constant per CID regardless of registry size — the PDA-per-CID layout means anchoring CID N+1 is independent of CIDs 1..N. Wall-clock latency is dominated by the 15-second block production interval, not by program compute cost. Full numbers in `BENCHMARKS.md`. Note: anchor transactions use the LEZ Public path which bypasses host-side proof generation; the prover does fire under `RISC0_DEV_MODE=0` for the faucet / wallet-bootstrap path shown in Scene 1 of the demo video. + +### Supportability + +CI is green on `main`: 24 fast tests + 4 FFI unit tests + 3 ignored live-LEZ adapter tests + 2 ignored live-LEZ FFI smokes. The CI workflow exercises the full workspace including the risc0 zkVM guest, the FFI cdylib, and the LEZ adapter against a localnet sequencer — not just the pure-Rust shared types. Deployment is documented in `DEPLOYMENT.md` with the exact `lgs basecamp install` + `lgs basecamp launch` commands used to verify the plugin in a real Basecamp instance on 2026-05-09. The codebase is split into eight crates plus the UI plugin so future maintainers can swap individual layers (registry program, indexing traits, batch CLI, UI) independently. + +## Supporting Materials + +- **Narrated demo video:** +- **CU benchmarks:** +- **Deployment instructions:** +- **Demo script (terminal walkthrough):** +- **Upstream bug reports filed during this build:** +- **Hand-written SPEL-compatible IDL:** + +## Terms & Conditions + +By submitting this solution, I confirm that I have read and agree to the [Terms & Conditions](../TERMS.md).