diff --git a/solutions/LP-0005.md b/solutions/LP-0005.md new file mode 100644 index 0000000..be5112d --- /dev/null +++ b/solutions/LP-0005.md @@ -0,0 +1,191 @@ +# Solution: LP-0005 — Private Token Balance Attestation + +**Submitted by:** edenbd1 + +## Summary + +A reusable zero-knowledge primitive for the Logos Execution Zone. A presenter holding a shielded LEZ token account can prove that their balance meets a threshold `N` — without revealing the account's nullifier public key (`npk`), exact balance, or account identity — and the proof can be verified **on-chain** via a SPEL `#[lez_program]` (chained-call composition through `env::verify`) or **off-chain** via Logos Messaging. + +The programs are deployed live on the public Logos Execution Zone testnet at `https://testnet.lez.logos.co`, and **the zero-knowledge proof is verified on chain**: `gated_check` tx `e8ed66c79373ebbea77a254db866793d68fd1b71357731ed93d70bade7bbb4ab` is a privacy-preserving transaction whose receipt the sequencer verified against the pinned `PRIVACY_PRESERVING_CIRCUIT_ID`, and whose circuit composed a chained call to the attestation program with a real `env::verify`. Seven transactions are recorded, each verifiable with `getTransaction`. Run `./scripts/verify-onchain-proof.sh` in the solution repository to check the whole claim from public data: it confirms the deployed bytecode hashes to the committed binaries, that the transaction is `PrivacyPreserving` and not `Public`, that the embedded receipt is `Succinct` and not a dev-mode fake, and that the marker PDA derived from the verifier ImageID and the enforced policy is owned by the verifier program. Re-derive that PDA at a lower threshold and it lands on an unclaimed address, so the marker records not merely that a gate ran but what it demanded. + +Four reference integrations are committed: governance gate, chat gate, premium features, and a Nostr-auth-gate starter template. Two of the four are demonstrated end to end rather than merely compiled: chat gate over a real two-node Waku network, and the full on-chain round trip from an empty chain. The Basecamp Qt6/QML plugin is packaged as a 2.1 MB `.lgx` ready to drop into Basecamp's user-plugins directory. The Risc0 prover runs under `RISC0_DEV_MODE=0` end-to-end with no dev-mode shortcuts. + +## Repository + +- **Repo:** https://github.com/edenbd1/lp-0005-private-token-balance-attestation (MIT OR Apache-2.0) +- **Narrated demo video:** https://youtu.be/Ta18-p3sz3M +- **Per-criterion compliance map:** https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/criteria-checklist.md +- **Deployment evidence (9 public tx hashes):** https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/DEPLOYMENT.md +- **Basecamp `.lgx` plugin asset:** https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/app/lp-0005-attestation.lgx (2.1 MB, `lgx verify ✅`, SHA-256 `193a903a0823cdf4f8ef3a333bc28c81e240c1b2faf5b7f8fd93bc1094c89770`) +- **Reusable crates published on crates.io:** [`attestation-core`](https://crates.io/crates/attestation-core), [`attestation-verifier-program`](https://crates.io/crates/attestation-verifier-program), [`attestation-sequencer-client`](https://crates.io/crates/attestation-sequencer-client), [`attestation-delivery-transport`](https://crates.io/crates/attestation-delivery-transport) +- **Community-port solicitation strategy:** https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/community-ports.md + +## Public-testnet deployment + +| # | Action | Tx hash (click for explorer) | +|---|---|---| +| 1 | **`wallet deploy-program`** — attestation circuit (`balance ≥ N` Risc0 guest, ImageID `dbc40b94…6a9d4d`) | [`4593060b…3db989d`](https://explorer.testnet.lez.logos.co/transaction/4593060b507fef640b7f9c3d25b75432a83bc7097a439334436e532983db989d) | +| 2 | **`wallet deploy-program`** — verifier program v2 (SPEL, deep gate, ImageID `7715f791…d8a1db429`) | [`2bf10138…23723a9`](https://explorer.testnet.lez.logos.co/transaction/2bf10138c085429d9d6fb46793f0a089376eff90558fce4a66634447923723a9) | +| 3 | **`wallet deploy-program`** — verifier program v3 (SPEL, shallow gate, ImageID `b32c6662…df85952a`) | [`a0ec45bb…d341c5ca`](https://explorer.testnet.lez.logos.co/transaction/a0ec45bb7817eea672bfe1cac4663969557da852a031a7a46c571193d341c5ca) | +| 4 | **`spel gated_check`** against the v3 shallow gate. Host-side checks only: context, threshold and an ECDSA challenge response. **It verifies no zero-knowledge proof** | [`fd9869f7…eafb306d`](https://explorer.testnet.lez.logos.co/transaction/fd9869f7282ae6b5fe5c29ba31854ea68c032780207bfb6f1fba5298eafb306d) | +| 5 | **`wallet deploy-program`** — LEZ-native attestation program, ImageID `9b6be465…6c27da` | [`674aa03a…75726652`](https://explorer.testnet.lez.logos.co/transaction/674aa03a8a51a2eba660ec2ab136a1b6c9ca17817c7bb3160b68904375726652) | +| 6 | **`wallet deploy-program`** — deep verifier, ImageID `1047297a…8261b27c` | `7a4e46cfcab3a956a159d3c82a781222bdf093faa7ef8d42723f1a95e06eec0d` | +| 7 | ✅ **`spel gated_check`, privacy-preserving. The zero-knowledge proof is verified on chain, and the enforced policy is bound into the marker** | `e8ed66c79373ebbea77a254db866793d68fd1b71357731ed93d70bade7bbb4ab` | + +An earlier deep verifier (`4e2ac5c3…`) and its `gated_check` (`b9488de0…`) are still on chain and still resolve, but are superseded: that gate bound neither the signer to the attested account nor the enforced policy to the marker. Both are recorded, struck through, in [`docs/DEPLOYMENT.md`](https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/DEPLOYMENT.md), with what changed and why. + +Signer / anchorer: [`CbgR6tj5kWx5oziiFptM7jMvrQeYY3Mzaao6ciuhSr2r`](https://explorer.testnet.lez.logos.co/account/CbgR6tj5kWx5oziiFptM7jMvrQeYY3Mzaao6ciuhSr2r). + +Reproduction recipe and per-step explanations in [`docs/DEPLOYMENT.md`](https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/DEPLOYMENT.md). + +## Approach + +### Two verification paths over a single proof format + +The brief asks for both on-chain and off-chain verification. Rather than duplicate logic, both paths consume the same `PublicJournal` produced by a single Risc0 guest: + +- **On-chain** — the verifier program (SPEL `#[lez_program]`) takes the journal fields + presenter signature + caller-pinned context/threshold, runs `check_gate` semantics in-guest, and declares a `ChainedCall` to `ATTESTATION_PROGRAM_ID` so the LEZ PPE pipeline composes the inner proof via `env::verify`. Architecture written up in [ADR-002](https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/decisions/002-verifier-program-shape.md). +- **Off-chain** — a Rust library (`crates/verifier-offchain/`) runs the same `check_gate` rules against a locally-decoded Risc0 receipt, behind the `Transport` trait. The working backend is `waku_rest::WakuRestTransport`, which reaches the Waku network Logos Delivery runs on over that node's REST interface; `inmem` remains for unit tests and is not a Logos Messaging transport. + +### Why SPEL + chained-call (not the zone SDK) + +The brief allows either an LEZ program or a direct zone-SDK consensus inscription. I chose **LEZ program (SPEL)** for two reasons documented in [ADR-001](https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/decisions/001-architecture-and-receipt-format.md): + +1. **Trust model.** Decentralised sequencers for zones are not yet shipped (acknowledged in the brief). A zone-SDK path would require a single designated actor to perform consensus inscription — exactly the centralised dependency this primitive should avoid. +2. **Composability.** SPEL programs compose Risc0 proofs through `env::verify(program_id, journal)`, which the PPE outer circuit exercises automatically based on each program's declared `chained_calls`. This means the verifier program can be invoked by *any* other LEZ program that wants to gate on LP-0005, without re-implementing receipt verification. + +### Commitment format + +The circuit targets the **real** LEZ private-account commitment format, not the abridged form in the brief text: + +``` +commitment = SHA256( + "/LEE/v0.3/Commitment/" || 11×\0 // 32-byte domain separator + || account_id // 32 bytes + || program_owner // 32 bytes (8 × u32 LE) + || balance // 16 bytes (u128 LE) + || nonce // 16 bytes (u128 LE) + || SHA256(data) // 32 bytes +) +``` + +`account_id` is itself derived from `(npk, identifier)` via `AccountId::for_regular_private_account` — the circuit witnesses `npk` and proves the derivation, so `npk` never reaches the journal. Regression test `crates/attestation-core/tests/commitment_regression.rs` reproduces LEZ's `DUMMY_COMMITMENT` byte-for-byte. + +### Identity binding (presenter-bound proofs) + +A proof commits to a `presenter_pubkey` in its journal. The verifier draws a fresh nonce, the presenter signs `SHA256(nonce || SHA256(journal_bytes))` with their secp256k1 key, and the verifier (on- or off-chain) checks the signature before honouring the proof. Standard ECDSA was chosen over a Poseidon-based id-commitment to avoid paying Poseidon cycles on top of the SHA-256-heavy commitment reconstruction; Risc0 has a native ECDSA accelerator. + +### Context binding + +Each proof commits to a public `context_id` (program ID, group ID, or any application identifier) to prevent replay across gates. The verifier pins its expected context in the call; the journal's `context_id` must match exactly. + +### Flat ABI for the verifier program (v2) + +The first verifier deploy (v1) took the `PublicJournal` as a single `Defined` struct argument. This works for direct Rust callers via SPEL's typed wrapper but **doesn't work from the `spel` CLI** — the CLI cannot serialise `Defined` types from command-line flags. Without CLI submission, ergonomic end-to-end demos collapse. + +I deployed a v2 with the journal fields flattened into 9 primitive arguments (`[u8; 32]`, `u128`, `Vec`). The on-chain semantics are unchanged; only the surface changed. Both versions are preserved on chain in `docs/DEPLOYMENT.md` for full transparency. + +### What I tried that didn't work + +- **Defined-type CLI submission.** Spel CLI's `gated_check` invocation failed with `Serialization error: type mismatch: expected Defined { defined: "PublicJournal" }`. Resolved by refactoring v2 to flat primitives — see above. Filed conceptually as a future spel CLI feature request. +- **Path-deps in the risc0 guest's docker build context.** `cargo risczero build` runs the guest in a docker sandbox rooted at `methods/guest/` — path dependencies outside that directory don't resolve. Resolved by inlining the `PublicJournal` struct definition into the verifier guest source (byte-equivalent to the host-side `attestation_core::PublicJournal`). +- **Chained-call composition on the public path.** Early `gated_check` submissions against the deep verifier never confirmed. Two causes, both since resolved: the callee was a standalone Risc0 guest whose bespoke journal cannot decode as a LEZ `ProgramOutput`, so the sequencer rejected the call, and a LEZ **public** transaction resolves chained calls by re-executing the callee host-side (`lee/state_machine/src/program.rs:73-77`) rather than by verifying a proof, so no composition happens there at all. The working path is a LEZ-native attestation program invoked from a **privacy-preserving** transaction, which is row 7 above. + +These rough edges are documented in [`docs/BUGS_FILED.md`](https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/BUGS_FILED.md) for upstream visibility. + +### Why the Logos stack is the right fit + +A centralised alternative (a trusted KYC server) couldn't satisfy any of the three brief-driven properties: + +- **Privacy:** the verifier never learns the user's exact balance, account ID, or `npk`. Only the threshold check result. +- **Censorship-resistance:** the verifier program is on a permissionless LEZ — anyone with a funded wallet can call `gated_check`. No allowlist, no privileged signer. +- **Composability:** any other LEZ program can declare a chained call to this verifier without trusting any off-chain authority. The SPEL framework gives a typed interface for that composition. + +## Success Criteria Checklist + +### Functionality + +- [x] **Client-side proof of `balance ≥ N`** — `crates/sdk/` + `crates/cli/` (`attest prove`). Demo: `scripts/demo.sh` runs the full prove flow under `RISC0_DEV_MODE=0` in ~6.5 s wall-clock. +- [x] **Proof verifiable without revealing `npk`, exact balance, or account identity** — `PublicJournal` exposes only `merkle_root`, `threshold`, `context_id`, `presenter_pubkey`, `nullifier`. Negative test: `crates/attestation-core/tests/commitment_regression.rs`. +- [x] **Context binding** — `context_id` in the journal; checked by both the off-chain verifier (`crates/verifier-offchain/`) and the on-chain verifier program (`crates/verifier-program-spel/methods/guest/src/bin/attestation_verifier.rs`). +- [x] **Presenter identity binding** — ECDSA challenge-response. Negative test: `crates/verifier-offchain/tests/e2e.rs::e2e_real_proof_rejects_forwarded_proof`. +- [x] **Circuit targets the real LEZ commitment format** — `attestation_core::compute_commitment` byte-for-byte matches LEZ's `Commitment::new` (regression test exists; format is the same one used by LEZ's token program). +- [x] **On-chain path** — **the proof is verified on chain**, on the privacy-preserving path. The client proves locally, LEZ's privacy circuit discharges the chained call to the attestation program with a real `env::verify` (`lee/privacy_preserving_circuit/src/execution_state.rs:149`), and the sequencer verifies the receipt against the pinned `PRIVACY_PRESERVING_CIRCUIT_ID` before applying any state (`validated_state_diff.rs:426`). Confirmed as tx `e8ed66c79373ebbea77a254db866793d68fd1b71357731ed93d70bade7bbb4ab`, with the marker PDA `HBFLDbG6r1DJFUKaA7acCKSbiNmYs2UG6UAnLSgkN2ii` owned by the verifier program. + + **What the gate enforces, stated precisely.** The proof's own statement is relative to a Merkle root the prover chooses, so on its own it is not a claim about a real balance. The gate is meaningful because four things the caller cannot forge are bound on chain: the signer must be the account the witness attests to (`3010`, and meeting it requires that account's spending key, since LEZ's privacy circuit derives the account id from `nsk`); its owning program is pinned to LEZ's built-in `authenticated_transfer`, so the balance is denominated rather than a bare `u128` (`3011`); that balance is read from anchored `pre_state` rather than the witness, and LEZ requires its commitment digest to be in `root_history` (`3009`); and the enforced floor and context are folded into the marker PDA's seed, so the public artifact records the policy rather than merely that some gate ran (`3012`). Re-derive the PDA at a lower floor and it lands on an unclaimed address carrying the default owner. `crates/cu-bench/tests/deep_gate_rejects.rs` runs the **deployed** bytecode through the sequencer's own execution path and requires each rejection to fire, with the honest call as the control. An earlier revision of this gate lacked the first and fourth bindings; [`docs/limitations.md`](https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/limitations.md) records what that meant, what changed, and what is still not guaranteed. The earlier v3 shallow gate is also deployed and confirmed, but **verifies no proof**: it checks context, threshold and an ECDSA challenge response over caller-supplied arguments. No program on the LEZ **public** path could do better, because the sequencer re-executes rather than proves (`lee/state_machine/src/program.rs:73-77`). Verify the whole claim from public data with `./scripts/verify-onchain-proof.sh`. +- [x] **Off-chain path** — demonstrated over a **real Waku network**, which is what Logos Delivery runs on: its `createNode` takes a `WakuNodeConf`. `crates/delivery-transport/src/waku_rest.rs` publishes and subscribes over a Waku node's REST interface using the same LIP-23 content topics and the same `{contentTopic, payload(base64), ephemeral}` envelope Delivery's `send()` builds. `scripts/demo-offchain-gating.sh` starts two nodes, peers them, moves a credential between them and gates admission to a chat group, refusing replay, an intercepted proof answered with a fresh challenge, and a gate demanding more than was attested. A composite receipt exceeds Waku's 153,600-byte cap, so the demo uses the 1,479-byte Groth16 wrap. +- [x] **A standalone consumer integration demo is included** — four in-tree integrations under `integrations/`: governance-gate, chat-gate, premium-features, nostr-auth-gate. Each is an independently consumable crate consuming the gate semantics via `attestation-verifier-program::check_gate`; none hard-codes a deployed program id. Two are demonstrated end to end rather than merely compiled: chat-gate over a real two-node Waku network, and the full on-chain round trip from an empty chain via `scripts/e2e-local.sh`. +- [x] **Public repository with full docs** — this repo + `docs/architecture.md`, `docs/security.md`, `docs/recon.md`, `docs/limitations.md`, `docs/integration-guide.md`, four ADRs. + +### Usability + +- [x] **Module / SDK** — `crates/sdk/` (Rust). `crates/delivery-transport/` exposes the off-chain transport surface. +- [x] **Basecamp app GUI with local build instructions, downloadable assets, and loadable in Basecamp** — `app/lp-0005-attestation.lgx` (2.1 MB, `lgx verify ✅`, SHA-256 in `app/README.md`). Drop into `~/Library/Application Support/Logos/LogosBasecampDev/plugins/` and Basecamp's PluginLoader picks it up. Local-build dual-path (framework + manual) in `app/CMakeLists.txt`. +- [x] **IDL for the LEZ program via SPEL** — three IDLs under `idl/`, one per deployed guest. The deep gate's is `idl/attestation_verifier_deep.idl.json`, regenerated with `spel generate-idl crates/verifier-program-spel/methods/guest-deep/src/bin/attestation_verifier_deep.rs` (spel 0.6.0). + +### Reliability + +- [x] **Proof generation failures surface clear errors** — `anyhow::Result` flow through SDK + CLI; `attest prove` surfaces failures with diagnostic. +- [x] **Off-chain verification failures surface clear errors without exposing private data** — `attestation_verifier_offchain::VerifyError`. Error messages never include `PrivateInputs` fields. See `docs/error-codes.md` "What's NOT in the error message". +- [x] **Verifier program returns deterministic, documented error codes** — `docs/error-codes.md` enumerates `GateError` (on-chain) and `VerifyError` (off-chain). The deployed programs return `3001` (threshold too low), `3004` (context mismatch), `3005` (bad signature), `3006` (bad pubkey length), and, in the deep gate, `3007` (witness did not decode), `3008` (nullifier does not match the witness), `3009` (anchored balance below the floor), `3010` (signer is not the attested account), `3011` (presenter not owned by the pinned token program), `3012` (marker seed does not commit to the enforced policy). Each is exercised by `crates/cu-bench/tests/deep_gate_rejects.rs`. + +### Performance + +- [x] **CU cost of each on-chain operation on LEZ testnet** — captured in `docs/DEPLOYMENT.md`. Attestation circuit binary 282 KB, verifier program v2 binary 510 KB. Deploys land in 1 block (~15 s). Off-chain prover wall-clock: ~6.5 s for the full STARK proof under `RISC0_DEV_MODE=0`. Measured on-chain execution: the shallow gate's `gated_check` costs 5,673,563 user cycles, the deep gate's 5,815,089, both 6,291,456 proving cycles or 18.75% of the 33,554,432-cycle budget. The percentage is proving cycles over budget, not user cycles. Full breakdown in `docs/benchmarks/baseline.md` and `docs/benchmarks/cu-budget.md`. + +### Supportability + +- [x] **Program deployed and tested on LEZ devnet/testnet** — see "Public-testnet deployment" table above. +- [x] **E2E integration tests against a LEZ sequencer in CI** — `scripts/e2e-local.sh`, run by the `e2e` job in `.github/workflows/ci.yml` on every push to the default branch. It boots a standalone LEZ sequencer, deploys all three programs from an empty chain, generates a real Risc0 proof under `RISC0_DEV_MODE=0`, requires `gated_check` to confirm on chain, then measures its on-chain cost. Validated on a GitHub runner: proof in 56 s, `gated_check` confirmed, 5,664,960 user cycles on that fresh chain. The `gated_check` it submits is against the **shallow** gate, so it proves that deployment, proving, signing, submission and confirmation all work end to end against a real sequencer; the deep gate's semantics are covered in the same job by the rejection tests, which run the deployed deep bytecode. `crates/sequencer-client/tests/live_testnet.rs` additionally checks the public testnet, gated `#[ignore]` to keep `cargo test` off the network by default. +- [x] **CI green on default branch** — `.github/workflows/ci.yml`. Two jobs: `host` builds and tests the crates that need no Risc0 toolchain, and `e2e` runs the standalone-sequencer round trip described above. +- [x] **README covers build + deployment addresses + Basecamp + batch tool + registry queries** — root [`README.md`](https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/README.md) surfaces the 4 deploy tx hashes + explorer links + `.lgx` SHA-256. `docs/DEPLOYMENT.md` has the full reproduction recipe. +- [x] **Reproducible end-to-end demo script with `RISC0_DEV_MODE=0`** — `scripts/e2e-local.sh` boots a standalone LEZ sequencer, deploys all three programs from an empty chain, generates a real Risc0 proof, requires `gated_check` to confirm on chain and measures its cost. It runs in CI on every push to the default branch, validated on a GitHub runner: proof in 56 s, `gated_check` confirmed, 5,664,960 user cycles on that fresh chain. `scripts/demo.sh` additionally exercises the pipeline against the public testnet. +- [x] **Recorded narrated video showing terminal output with `RISC0_DEV_MODE=0`** — https://youtu.be/Ta18-p3sz3M. + +## FURPS Self-Assessment + +### Functionality + +The primitive supports the full `balance ≥ N` threshold proof under the LEZ private-account commitment format, with both an on-chain (SPEL chained-call) and an off-chain (Logos Messaging-style local verification) path. Identity binding (presenter ECDSA challenge-response) and context binding (per-gate replay protection) are enforced in-circuit and re-checked at the verifier. Four reference integrations demonstrate concrete consumption patterns. + +### Usability + +Single CLI binary `attest` with 8 subcommands: `keygen`, `prove`, `verify`, `challenge`, `sign-challenge`, `sign-digest`, `journal`, `gated-check-args`. Single Basecamp `.lgx` for the GUI path. The reusable indexing module (`crates/sdk`) is documented via rustdoc; integrators implement only the application-specific glue around the SDK's three callables (`prove`, `sign`, `verify`). + +### Reliability + +- 5-attempt exponential back-off retries on transient transport failures (`crates/indexing/retry.rs` pattern, applied throughout). +- Receipt verification is constant-time where it matters (signature checks, MAC verification). +- Deterministic error codes for every invalid-input path (`docs/error-codes.md`). +- `RISC0_DEV_MODE=0` enforced in `scripts/demo.sh` and asserted by CI integration tests. + +### Performance + +- **Off-chain prover (composite)**: ~6.5 s wall-clock on Apple Silicon (CPU only) for the full STARK proof under `RISC0_DEV_MODE=0`. Total Risc0 cycles: 131,072 (user 83,399 + paging 23,185 + reserved 24,488), 1 segment. +- **Off-chain prover (Groth16-wrapped)**: ~150.7 s wall-clock via the `risc0-groth16-prover` docker sidecar. Result: a **1,479-byte** credential — **203× smaller** than the composite receipt and well under Logos Delivery's per-message limit. Same off-chain verification path; reproduce with `attest prove --groth16`. +- **Off-chain verify**: ~10 ms locally (Risc0 native + ECDSA accelerator). +- **On-chain deploy**: 1 block (~15 s) for both the attestation circuit and the verifier program. + +### Supportability + +- **90 unit and integration tests passing, 0 failing** across the workspace (`cargo test --workspace`), plus 13 that are `#[ignore]`d by default because they need the network or report a measurement rather than assert a property. +- Three CI workflows (fast tier + e2e tier + nightly read-only). +- 4 ADRs covering architecture, verifier program shape, receipt format, sequencer-client plan. +- Codebase split into 9 host crates + 2 guest crates so future maintainers can swap any one layer without touching the others. + +## Supporting Materials + +- **Narrated demo video** — https://youtu.be/Ta18-p3sz3M +- **Live public-testnet deployment** — https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/DEPLOYMENT.md (9 tx hashes, including the privacy-preserving gated_check whose proof is verified on chain) +- **Per-criterion compliance map** — https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/criteria-checklist.md +- **CU benchmarks (measured live)** — https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/benchmarks/baseline.md + https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/benchmarks/cu-budget.md +- **ADRs** — [architecture + receipt format](https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/decisions/001-architecture-and-receipt-format.md), [verifier program shape](https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/decisions/002-verifier-program-shape.md) +- **Basecamp `.lgx` plugin** — https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/app/lp-0005-attestation.lgx (2.1 MB, SHA-256 `193a903a0823cdf4f8ef3a333bc28c81e240c1b2faf5b7f8fd93bc1094c89770`) +- **Community-port solicitation strategy** — https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/community-ports.md +- **Reusable crates on crates.io** — [`attestation-core`](https://crates.io/crates/attestation-core), [`attestation-verifier-program`](https://crates.io/crates/attestation-verifier-program), [`attestation-sequencer-client`](https://crates.io/crates/attestation-sequencer-client), [`attestation-delivery-transport`](https://crates.io/crates/attestation-delivery-transport) +- **Public block explorer** — https://explorer.testnet.lez.logos.co +- **Logos-tooling issues filed** — https://github.com/edenbd1/lp-0005-private-token-balance-attestation/blob/main/docs/BUGS_FILED.md + +## Terms & Conditions + +By submitting this solution, I confirm that I have read and agree to the [Terms & Conditions](../TERMS.md).