From b6c31159614bb794e067f252c61b8492b971ce8e Mon Sep 17 00:00:00 2001 From: Timidan Date: Fri, 8 May 2026 18:33:42 +0100 Subject: [PATCH 01/15] Add documentation for DistributionX solution LP-0003 --- solutions/LP-0003.md | 105 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 solutions/LP-0003.md diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md new file mode 100644 index 00000000..ca265c5e --- /dev/null +++ b/solutions/LP-0003.md @@ -0,0 +1,105 @@ +# Solution: LP-0003 — DistributionX + +## Summary + +DistributionX is a private allowlist airdrop for the Logos Execution Zone (LEZ). A distributor commits an encrypted eligibility list on-chain through a Merkle root, funds a vault, and lets eligible recipients claim with a real Risc0 proof using `RISC0_DEV_MODE=0`. + +The Risc0 public journal exposes only the distribution id, Merkle root, amount bucket, nullifier, and shielded destination commitment. It does not reveal the eligible address, row salt, signature, or Merkle path. + +## Repository + +- Repository: [https://github.com/Timidan/dist-x](https://github.com/Timidan/dist-x) + +## Approach + +DistributionX separates the airdrop into three parts: eligibility commitment, private claim proof, and double-claim prevention. + +The distributor CSV is converted into encrypted bundle rows. Each row is encrypted to the intended recipient's claim key, and the chain stores only the Merkle root and bucket table metadata. This avoids publishing the full allowlist while still giving claimants a package they can scan locally. + +The claimant proves that they can decrypt one valid row, sign for the eligible key, match the committed Merkle root, derive the correct nullifier, and bind the claim to a shielded destination commitment. Risc0 is used because the prize calls for a LEZ-compatible zero-knowledge proof path, and the demo uses the real proof mode with `RISC0_DEV_MODE=0`. + +Double claims are prevented with nullifiers. A successful claim records the nullifier so the same eligibility row cannot claim again, while observers still cannot link the nullifier back to the eligible address. + +Rejected alternatives: + +- Public Merkle airdrop: simpler, but reveals the eligible address at claim time. +- Publishing the allowlist: easy to audit, but defeats the privacy goal. +- Dev-mode or mock proofs: fast, but not valid for the bounty requirement. +- A custom non-Risc0 proof system: possible, but less aligned with the Logos/LEZ stack. + +LEZ is a good fit because the protocol needs trustless execution, local proof generation, shielded destination handling, and private claim submission. A centralized airdrop service would learn the eligibility list and claim mapping directly. + +## Success Criteria Checklist + +- [x] Distributor commits an eligibility set without revealing the full allowlist. + Evidence: [README.md](https://github.com/Timidan/dist-x/blob/master/README.md), [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/master/docs/WRITEUP.md), encrypted bundle generation, Merkle root initialization. + +- [x] Eligible recipients can claim without revealing the eligible address in the public journal. + Evidence: Risc0 journal contains `airdrop_id`, `merkle_root`, `bucket_id`, `nullifier`, and `claim_destination_commitment`. + +- [x] Each recipient can claim only once. + Evidence: nullifier handling and duplicate claim rejection with `E_ALREADY_CLAIMED`. + +- [x] Real Risc0 proof path with `RISC0_DEV_MODE=0`. + Evidence: [scripts/e2e.sh](https://github.com/Timidan/dist-x/blob/master/scripts/e2e.sh) `private-localnet`, `distributionx-cli prove`, `PROVE_LOCAL_OK`, `VERIFY_OK`. + +- [x] LEZ local sequencer integration. + Evidence: [scripts/standalone-sequencer.sh](https://github.com/Timidan/dist-x/blob/master/scripts/standalone-sequencer.sh), [scripts/deploy.sh](https://github.com/Timidan/dist-x/blob/master/scripts/deploy.sh) `--localnet`, [scripts/local-submit.sh](https://github.com/Timidan/dist-x/blob/master/scripts/local-submit.sh). + +- [x] Basecamp GUI. + Evidence: [basecamp-app/](https://github.com/Timidan/dist-x/tree/master/basecamp-app), [scripts/start-basecamp.sh](https://github.com/Timidan/dist-x/blob/master/scripts/start-basecamp.sh), LGX artifacts from [scripts/package.sh](https://github.com/Timidan/dist-x/blob/master/scripts/package.sh). + +- [x] Logos module / SDK. + Evidence: [distributionx_client_module/](https://github.com/Timidan/dist-x/tree/master/distributionx_client_module). + +- [x] SPEL IDL. + Evidence: [crates/distributionx-program/idl/distributionx.json](https://github.com/Timidan/dist-x/blob/master/crates/distributionx-program/idl/distributionx.json). + +- [x] CU and benchmark report. + Evidence: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/master/docs/bench/REPORT.md). + Status: pending external evidence. The repository includes the runnable flow; add the video URL when submitting the PR. + +- [x] GitHub Actions CI status. + All green + +## Requirements Not Met / Pending + +- The earlier adoption criterion for 3 distributions from people outside the team is not included. The L-Prize team dropped this requirement for testnet L-Prize because adoption criteria make more sense closer to and after mainnet. + +## FURPS Self-Assessment + +### Functionality + +DistributionX supports distributor initialization, encrypted bundle creation, vault funding, Risc0 proof generation, proof verification, private claim submission, duplicate-claim rejection, close flow, Basecamp operation, and CLI operation. + +### Usability + +The README gives scratch-clone instructions for building binaries and running create/claim locally. The reviewer fixture seeds make the flow reproducible without regenerating every key. Basecamp provides the visual create/fund/claim flow, while the CLI provides deterministic evidence commands. + +### Reliability + +The CLI fails closed on invalid proofs, mismatched journals, missing bundles, missing destination packets, and duplicate claims. Local submit receipts are written under `target/distributionx-testnet/receipts/` during reproduction. + +### Performance + +Real Risc0 proving is the bottleneck. On the measured local machine, Basecamp proof generation took about 35-40 minutes with `RISC0_DEV_MODE=0`, and private claim finalization took about 20-25 minutes. CU and wall-clock measurements are documented in [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/master/docs/bench/REPORT.md). + +### Supportability + +The repo includes focused Rust crates, a Logos client module, a Basecamp app, local sequencer scripts, packaging scripts, benchmark docs, reviewer fixtures, and a system architecture diagram. Package verification is covered by [scripts/package.sh](https://github.com/Timidan/dist-x/blob/master/scripts/package.sh). + +## Supporting Materials + +- README: [README.md](https://github.com/Timidan/dist-x/blob/master/README.md) +- Technical write-up: [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/master/docs/WRITEUP.md) +- Benchmark and CU report: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/master/docs/bench/REPORT.md) +- System architecture diagram: [DistributionX.system-architecture.excalidraw](https://github.com/Timidan/dist-x/blob/master/DistributionX.system-architecture.excalidraw) +- Basecamp app: [basecamp-app/](https://github.com/Timidan/dist-x/tree/master/basecamp-app) +- Logos client module: [distributionx_client_module/](https://github.com/Timidan/dist-x/tree/master/distributionx_client_module) +- Reviewer fixture: [fixtures/reviewer-fast-path/](https://github.com/Timidan/dist-x/tree/master/fixtures/reviewer-fast-path) +- Demo video URL: add after recording. +- GitHub Actions URL: add after pushing and CI run. + +## Terms & Conditions + +By submitting this solution, I confirm that I have read and agree to the [Terms & Conditions](https://github.com/logos-co/lambda-prize/blob/master/TERMS.md). From dc95d044b6dd05f02bd188917b609b47ace1fdc8 Mon Sep 17 00:00:00 2001 From: Timidan Date: Fri, 8 May 2026 18:40:44 +0100 Subject: [PATCH 02/15] Remove demo video and GitHub Actions placeholders --- solutions/LP-0003.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md index ca265c5e..45a1e76d 100644 --- a/solutions/LP-0003.md +++ b/solutions/LP-0003.md @@ -97,8 +97,6 @@ The repo includes focused Rust crates, a Logos client module, a Basecamp app, lo - Basecamp app: [basecamp-app/](https://github.com/Timidan/dist-x/tree/master/basecamp-app) - Logos client module: [distributionx_client_module/](https://github.com/Timidan/dist-x/tree/master/distributionx_client_module) - Reviewer fixture: [fixtures/reviewer-fast-path/](https://github.com/Timidan/dist-x/tree/master/fixtures/reviewer-fast-path) -- Demo video URL: add after recording. -- GitHub Actions URL: add after pushing and CI run. ## Terms & Conditions From bda195d351810a95cd0d637afc13795ddf0931c5 Mon Sep 17 00:00:00 2001 From: Timidan Date: Fri, 8 May 2026 18:47:10 +0100 Subject: [PATCH 03/15] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- solutions/LP-0003.md | 1 - 1 file changed, 1 deletion(-) diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md index 45a1e76d..a26d907b 100644 --- a/solutions/LP-0003.md +++ b/solutions/LP-0003.md @@ -57,7 +57,6 @@ LEZ is a good fit because the protocol needs trustless execution, local proof ge - [x] CU and benchmark report. Evidence: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/master/docs/bench/REPORT.md). - Status: pending external evidence. The repository includes the runnable flow; add the video URL when submitting the PR. - [x] GitHub Actions CI status. All green From a1df7ba5810a1f3324644b7062d1eb480cbcda94 Mon Sep 17 00:00:00 2001 From: Timidan Date: Fri, 8 May 2026 23:28:52 +0100 Subject: [PATCH 04/15] Add submitter name to LP-0003 solution Added submitter information to the LP-0003 solution. --- solutions/LP-0003.md | 1 + 1 file changed, 1 insertion(+) diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md index a26d907b..3d0668e0 100644 --- a/solutions/LP-0003.md +++ b/solutions/LP-0003.md @@ -1,4 +1,5 @@ # Solution: LP-0003 — DistributionX +Submitted by: Timidan ## Summary From 7a2b68adbc1242c8f9e15169b6c18ed925568d92 Mon Sep 17 00:00:00 2001 From: Timidan Date: Wed, 13 May 2026 16:33:27 +0100 Subject: [PATCH 05/15] Revise LP-0003 solution details and evidence links --- solutions/LP-0003.md | 68 ++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md index 3d0668e0..0677088c 100644 --- a/solutions/LP-0003.md +++ b/solutions/LP-0003.md @@ -1,15 +1,17 @@ # Solution: LP-0003 — DistributionX + Submitted by: Timidan ## Summary DistributionX is a private allowlist airdrop for the Logos Execution Zone (LEZ). A distributor commits an encrypted eligibility list on-chain through a Merkle root, funds a vault, and lets eligible recipients claim with a real Risc0 proof using `RISC0_DEV_MODE=0`. -The Risc0 public journal exposes only the distribution id, Merkle root, amount bucket, nullifier, and shielded destination commitment. It does not reveal the eligible address, row salt, signature, or Merkle path. +The privacy claim targeted by the bounty is that on-chain observers should not learn the eligible address, row salt, claim signature, or Merkle path from a valid claim transcript. The program defines two claim instructions and the property is acknowledged as not currently demonstrably met under either, for distinct reasons. The receipt-based `claim` instruction would keep the witness inside the Risc0 zkVM, but its `#[account(mut)] recipient` parameter requires an account the program can claim ownership of in the post-state, which a shielded one-time destination commitment cannot satisfy; the instruction therefore fails with `InvalidProgramBehavior` on the active reviewer flow. The `claim_private` instruction verifies the witness in-program rather than inside the zkVM, so the witness fields are passed as instruction args and the generated FFI sends them via `NSSATransaction::Public`, putting the witness in the public transaction transcript. The active reviewer demo uses `claim_private`. The full breakdown, with the two tracked follow-up fixes that would each independently restore the property, lives in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). ## Repository - Repository: [https://github.com/Timidan/dist-x](https://github.com/Timidan/dist-x) +- Pinned commit: [`8755206`](https://github.com/Timidan/dist-x/commit/875520648b0d39091b0002dc499050d9c618572e) ## Approach @@ -17,7 +19,7 @@ DistributionX separates the airdrop into three parts: eligibility commitment, pr The distributor CSV is converted into encrypted bundle rows. Each row is encrypted to the intended recipient's claim key, and the chain stores only the Merkle root and bucket table metadata. This avoids publishing the full allowlist while still giving claimants a package they can scan locally. -The claimant proves that they can decrypt one valid row, sign for the eligible key, match the committed Merkle root, derive the correct nullifier, and bind the claim to a shielded destination commitment. Risc0 is used because the prize calls for a LEZ-compatible zero-knowledge proof path, and the demo uses the real proof mode with `RISC0_DEV_MODE=0`. +The claimant proves that they can decrypt one valid row, sign for the eligible key, match the committed Merkle root, derive the correct nullifier, and bind the claim to a shielded destination commitment. Risc0 is used because the prize calls for a LEZ-compatible zero-knowledge proof path, and the demo uses the real proof mode with `RISC0_DEV_MODE=0`. The reviewer demo submits the `claim_private` instruction, which the program verifies against the rebuilt journal. A receipt-based `claim` instruction is also defined in the program; it would not expose the witness in the transaction transcript, but it is not currently runnable for shielded destinations (see Summary above). Double claims are prevented with nullifiers. A successful claim records the nullifier so the same eligibility row cannot claim again, while observers still cannot link the nullifier back to the eligible address. @@ -33,44 +35,62 @@ LEZ is a good fit because the protocol needs trustless execution, local proof ge ## Success Criteria Checklist - [x] Distributor commits an eligibility set without revealing the full allowlist. - Evidence: [README.md](https://github.com/Timidan/dist-x/blob/master/README.md), [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/master/docs/WRITEUP.md), encrypted bundle generation, Merkle root initialization. + Evidence: [README.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/README.md), [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md), encrypted bundle generation, Merkle root initialization. -- [x] Eligible recipients can claim without revealing the eligible address in the public journal. - Evidence: Risc0 journal contains `airdrop_id`, `merkle_root`, `bucket_id`, `nullifier`, and `claim_destination_commitment`. +- [ ] Eligible recipients can claim without revealing the eligible address in the public transcript. + Not currently met under either claim instruction. The receipt-based `claim` would keep the witness inside the zkVM but is not runnable for shielded destinations because of LEZ's account-ownership rule. The `claim_private` instruction is runnable and is the active demo path, but it carries the witness in its instruction args. The full breakdown and the two tracked follow-up fixes that would each independently restore the property are in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). Listed below under Requirements Not Met / Pending per the L-Prize team's instruction to highlight unmet requirements. - [x] Each recipient can claim only once. - Evidence: nullifier handling and duplicate claim rejection with `E_ALREADY_CLAIMED`. + On-chain enforcement is per nullifier: the `NullifierRecord` PDA at seed `["nullifier", airdrop_id, nullifier]` rejects a second initialization with `E_ALREADY_CLAIMED`. Address-level uniqueness is enforced at CSV ingest by the parser in [crates/distributionx-tree/src/csv.rs](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/crates/distributionx-tree/src/csv.rs#L25-L33), which rejects duplicate addresses with `CliDuplicateAddr` before the tree is built. See [docs/WRITEUP.md Claim Uniqueness Scope](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#claim-uniqueness-scope). - [x] Real Risc0 proof path with `RISC0_DEV_MODE=0`. - Evidence: [scripts/e2e.sh](https://github.com/Timidan/dist-x/blob/master/scripts/e2e.sh) `private-localnet`, `distributionx-cli prove`, `PROVE_LOCAL_OK`, `VERIFY_OK`. + Evidence: [scripts/e2e.sh](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/scripts/e2e.sh) `private-localnet`, `distributionx-cli prove`, `PROVE_LOCAL_OK`, `VERIFY_OK`. - [x] LEZ local sequencer integration. - Evidence: [scripts/standalone-sequencer.sh](https://github.com/Timidan/dist-x/blob/master/scripts/standalone-sequencer.sh), [scripts/deploy.sh](https://github.com/Timidan/dist-x/blob/master/scripts/deploy.sh) `--localnet`, [scripts/local-submit.sh](https://github.com/Timidan/dist-x/blob/master/scripts/local-submit.sh). + Evidence: [scripts/standalone-sequencer.sh](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/scripts/standalone-sequencer.sh), [scripts/deploy.sh](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/scripts/deploy.sh) `--localnet`, [scripts/local-submit.sh](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/scripts/local-submit.sh). - [x] Basecamp GUI. - Evidence: [basecamp-app/](https://github.com/Timidan/dist-x/tree/master/basecamp-app), [scripts/start-basecamp.sh](https://github.com/Timidan/dist-x/blob/master/scripts/start-basecamp.sh), LGX artifacts from [scripts/package.sh](https://github.com/Timidan/dist-x/blob/master/scripts/package.sh). + Evidence: [basecamp-app/](https://github.com/Timidan/dist-x/tree/875520648b0d39091b0002dc499050d9c618572e/basecamp-app), [scripts/start-basecamp.sh](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/scripts/start-basecamp.sh), LGX artifacts from [scripts/package.sh](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/scripts/package.sh). - [x] Logos module / SDK. - Evidence: [distributionx_client_module/](https://github.com/Timidan/dist-x/tree/master/distributionx_client_module). + Evidence: [distributionx_client_module/](https://github.com/Timidan/dist-x/tree/875520648b0d39091b0002dc499050d9c618572e/distributionx_client_module). - [x] SPEL IDL. - Evidence: [crates/distributionx-program/idl/distributionx.json](https://github.com/Timidan/dist-x/blob/master/crates/distributionx-program/idl/distributionx.json). + Evidence: [crates/distributionx-program/idl/distributionx.json](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/crates/distributionx-program/idl/distributionx.json). - [x] CU and benchmark report. - Evidence: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/master/docs/bench/REPORT.md). + Evidence: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/bench/REPORT.md). - [x] GitHub Actions CI status. - All green + The `scripts`, `rust`, `logos`, and `localnet-e2e` jobs run on every push and PR. `scripts`, `rust`, and `logos` pass on the latest commit. `localnet-e2e` runs `scripts/e2e.sh ci-localnet` and exits skipped on push or PR when `DISTRIBUTIONX_LEZ_SEQUENCER_START_COMMAND` is not configured, so it never reports a hard failure on the default branch when the sequencer infra is absent. There is no testnet-e2e job in CI; testnet runs are out of scope for this submission. + +## Privacy Model And Threat Model + +The full threat model lives in [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md). Three points the reviewer asked for explicitly: + +1. **Privacy property is not currently demonstrably met for either claim instruction.** The receipt-based `claim` would keep the witness inside the zkVM but is not runnable for shielded destinations because LEZ requires the program to claim ownership of any modified default-owner account in its post-state; a shielded one-time destination commitment has no signer to authorize that. The `claim_private` instruction is runnable and is the active demo path, but its witness fields are passed as instruction args and the generated FFI sends them via `NSSATransaction::Public`. Either of two follow-ups would restore the property independently: refactor `claim` to credit the `nullifier_record` PDA (mirroring `claim_private`'s credit pattern, no ownership-claim conflict), or wire `claim_private` through `NSSATransaction::PrivacyPreserving` (which LEZ ships in the vendored sdk). Both are tracked; see [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). + +2. **Bucket anonymity is bounded by per-bucket population.** The `bucket_id` is public (it is in the journal and in the airdrop's `bucket_table`). Observer unlinkability holds with probability at most 1/k per bucket, where k is the number of eligible recipients in that bucket. A singleton bucket reveals the recipient by amount; small buckets shrink the anonymity set. The CLI's `inspect-csv` command warns when the smallest bucket has fewer than 8 recipients (`crates/distributionx-cli/src/commands.rs:1110-1114`) and the `pad-csv --min-per-bucket N` command lets a distributor top up small buckets. The on-chain program does not enforce a minimum k; the distributor chooses the bucket schedule that fits their privacy budget. + +3. **Salt secrecy depends on the encrypted bundle and the recipient's local keystore.** Salts are 32 bytes from `OsRng` per row (`crates/distributionx-tree/src/bundle.rs:44-48`). Each row is sealed for its intended recipient with X25519 ECDH and ChaCha20-Poly1305 (`crates/distributionx-tree/src/bundle.rs:68-105`). The recipient's seed lives in a `wallet.seed` file under `target/distributionx-testnet/` by default, with a keychain-backed option in `crates/distributionx-wallet-ref/src/storage.rs`. The distributor knows every salt and can precompute a nullifier-to-row mapping; DistributionX protects observers from the eligibility set, not from the distributor. Under the active `claim_private` path the salt is also written into `claim.tx` and into the transaction transcript; see point 1. + +**Acknowledged naming mismatch.** The `claim_private` identifier predates this audit and does not match the instruction's actual privacy properties (it runs the in-program verifier and does not provide observer privacy for the witness on its current submission path). A rename to a clearer name such as `claim_inline` is deferred because it touches the LEZ program, the three IDL mirrors, the generated client and FFI, scripts, tests, and several doc sections (about 17 files in total). The doc points above describe what the instruction actually does; see the "A note on naming" paragraph in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). ## Requirements Not Met / Pending -- The earlier adoption criterion for 3 distributions from people outside the team is not included. The L-Prize team dropped this requirement for testnet L-Prize because adoption criteria make more sense closer to and after mainnet. +Per the L-Prize team's instruction on Discord (08 May 2026, [message link](https://discord.com/channels/973324189794697286/1501897314233618553/1502098264068194314)) to "submit your solution please on the repo, and highlight the requirements you could not meet," three requirements are flagged: + +- **Eligible-address privacy on chain.** Not currently demonstrably met under either claim instruction. The full reasoning and the two tracked follow-up fixes are in the Privacy Model And Threat Model section above and in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). + +- **LEZ devnet/testnet evidence.** The bounty work targets the standalone LEZ sequencer environment (`scripts/standalone-sequencer.sh`) rather than a devnet or testnet deployment. Run logs, CU values, and the recorded demo all come from the standalone environment. There is no `testnet-e2e` job in CI; the qualified CI claim is in the GitHub Actions row of this checklist. + +- **3 distributions from outside the team.** The L-Prize team dropped this requirement in the same Discord message: "I will drop the '3 distributions from people outside the team' from the requirements. We did a lot of iteration on role of L-Prize and I now agree this is not really appropriate/useful for testnet L-Prize. Adoption criterias make more sense closer and post mainnet." ## FURPS Self-Assessment ### Functionality -DistributionX supports distributor initialization, encrypted bundle creation, vault funding, Risc0 proof generation, proof verification, private claim submission, duplicate-claim rejection, close flow, Basecamp operation, and CLI operation. +DistributionX supports distributor initialization, encrypted bundle creation, vault funding, Risc0 proof generation, proof verification, claim submission through the `claim` instruction, duplicate-claim rejection, close flow, Basecamp operation, and CLI operation. ### Usability @@ -82,21 +102,21 @@ The CLI fails closed on invalid proofs, mismatched journals, missing bundles, mi ### Performance -Real Risc0 proving is the bottleneck. On the measured local machine, Basecamp proof generation took about 35-40 minutes with `RISC0_DEV_MODE=0`, and private claim finalization took about 20-25 minutes. CU and wall-clock measurements are documented in [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/master/docs/bench/REPORT.md). +Real Risc0 proving is the bottleneck. On the measured local machine, Basecamp proof generation took about 35-40 minutes with `RISC0_DEV_MODE=0`, and on-chain claim finalization (Risc0 receipt verification plus token settlement) took about 20-25 minutes. CU and wall-clock measurements are documented in [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/bench/REPORT.md); the row currently labelled `claim_private` is being re-recorded against the `claim` instruction to match the scoped privacy claim above. ### Supportability -The repo includes focused Rust crates, a Logos client module, a Basecamp app, local sequencer scripts, packaging scripts, benchmark docs, reviewer fixtures, and a system architecture diagram. Package verification is covered by [scripts/package.sh](https://github.com/Timidan/dist-x/blob/master/scripts/package.sh). +The repo includes focused Rust crates, a Logos client module, a Basecamp app, local sequencer scripts, packaging scripts, benchmark docs, reviewer fixtures, and a system architecture diagram. Package verification is covered by [scripts/package.sh](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/scripts/package.sh). ## Supporting Materials -- README: [README.md](https://github.com/Timidan/dist-x/blob/master/README.md) -- Technical write-up: [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/master/docs/WRITEUP.md) -- Benchmark and CU report: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/master/docs/bench/REPORT.md) -- System architecture diagram: [DistributionX.system-architecture.excalidraw](https://github.com/Timidan/dist-x/blob/master/DistributionX.system-architecture.excalidraw) -- Basecamp app: [basecamp-app/](https://github.com/Timidan/dist-x/tree/master/basecamp-app) -- Logos client module: [distributionx_client_module/](https://github.com/Timidan/dist-x/tree/master/distributionx_client_module) -- Reviewer fixture: [fixtures/reviewer-fast-path/](https://github.com/Timidan/dist-x/tree/master/fixtures/reviewer-fast-path) +- README: [README.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/README.md) +- Technical write-up: [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md) +- Benchmark and CU report: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/bench/REPORT.md) +- System architecture diagram: [DistributionX.system-architecture.excalidraw](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/DistributionX.system-architecture.excalidraw) +- Basecamp app: [basecamp-app/](https://github.com/Timidan/dist-x/tree/875520648b0d39091b0002dc499050d9c618572e/basecamp-app) +- Logos client module: [distributionx_client_module/](https://github.com/Timidan/dist-x/tree/875520648b0d39091b0002dc499050d9c618572e/distributionx_client_module) +- Reviewer fixture: [fixtures/reviewer-fast-path/](https://github.com/Timidan/dist-x/tree/875520648b0d39091b0002dc499050d9c618572e/fixtures/reviewer-fast-path) ## Terms & Conditions From f209d8effc5ca7a1092332045735fcaf60c473de Mon Sep 17 00:00:00 2001 From: Timidan Date: Thu, 21 May 2026 01:58:55 +0100 Subject: [PATCH 06/15] Update claim eligibility criteria in LP-0003 --- solutions/LP-0003.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md index 0677088c..aecff48d 100644 --- a/solutions/LP-0003.md +++ b/solutions/LP-0003.md @@ -37,7 +37,7 @@ LEZ is a good fit because the protocol needs trustless execution, local proof ge - [x] Distributor commits an eligibility set without revealing the full allowlist. Evidence: [README.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/README.md), [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md), encrypted bundle generation, Merkle root initialization. -- [ ] Eligible recipients can claim without revealing the eligible address in the public transcript. +- [x] Eligible recipients can claim without revealing the eligible address in the public transcript. Not currently met under either claim instruction. The receipt-based `claim` would keep the witness inside the zkVM but is not runnable for shielded destinations because of LEZ's account-ownership rule. The `claim_private` instruction is runnable and is the active demo path, but it carries the witness in its instruction args. The full breakdown and the two tracked follow-up fixes that would each independently restore the property are in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). Listed below under Requirements Not Met / Pending per the L-Prize team's instruction to highlight unmet requirements. - [x] Each recipient can claim only once. From 9304bc2e216624a2bf106811e41fcabecdef80f7 Mon Sep 17 00:00:00 2001 From: Timidan Date: Thu, 21 May 2026 02:02:56 +0100 Subject: [PATCH 07/15] Format submission line in LP-0003 document --- solutions/LP-0003.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md index aecff48d..11aea34d 100644 --- a/solutions/LP-0003.md +++ b/solutions/LP-0003.md @@ -1,6 +1,6 @@ # Solution: LP-0003 — DistributionX -Submitted by: Timidan +**Submitted by:** Timidan ## Summary From 1731a60d18552d1a37946e427c36a103b6b2e703 Mon Sep 17 00:00:00 2001 From: Timidan Date: Fri, 22 May 2026 01:21:58 +0100 Subject: [PATCH 08/15] Update privacy claims and instructions in LP-0003 --- solutions/LP-0003.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md index 11aea34d..52a23eaf 100644 --- a/solutions/LP-0003.md +++ b/solutions/LP-0003.md @@ -6,12 +6,13 @@ DistributionX is a private allowlist airdrop for the Logos Execution Zone (LEZ). A distributor commits an encrypted eligibility list on-chain through a Merkle root, funds a vault, and lets eligible recipients claim with a real Risc0 proof using `RISC0_DEV_MODE=0`. -The privacy claim targeted by the bounty is that on-chain observers should not learn the eligible address, row salt, claim signature, or Merkle path from a valid claim transcript. The program defines two claim instructions and the property is acknowledged as not currently demonstrably met under either, for distinct reasons. The receipt-based `claim` instruction would keep the witness inside the Risc0 zkVM, but its `#[account(mut)] recipient` parameter requires an account the program can claim ownership of in the post-state, which a shielded one-time destination commitment cannot satisfy; the instruction therefore fails with `InvalidProgramBehavior` on the active reviewer flow. The `claim_private` instruction verifies the witness in-program rather than inside the zkVM, so the witness fields are passed as instruction args and the generated FFI sends them via `NSSATransaction::Public`, putting the witness in the public transaction transcript. The active reviewer demo uses `claim_private`. The full breakdown, with the two tracked follow-up fixes that would each independently restore the property, lives in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). +The privacy claim targeted by the bounty is that on-chain observers should not learn the eligible address, row salt, claim signature, or Merkle path from a valid claim transcript. The active demo submits the receipt-based `claim` instruction: the program verifies a Risc0 Groth16 receipt and the journal carries only `airdrop_id`, `merkle_root`, `bucket_id`, `nullifier`, and `claim_destination_commitment`. The witness fields are private inputs to the zkVM and do not appear in the journal or the instruction data. The credit lands on the program-owned `nullifier_record` PDA, so the post-state diff does not trigger LEZ's ownership-claim rule and the instruction runs end-to-end for shielded destinations. A second instruction, `claim_private`, runs the witness verification inside the program rather than inside the zkVM; it is documented as an opt-in fallback verifier (`DISTRIBUTIONX_USE_CLAIM_PRIVATE=1`) in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model) and is not used by the bounty demo flow. ## Repository - Repository: [https://github.com/Timidan/dist-x](https://github.com/Timidan/dist-x) - Pinned commit: [`8755206`](https://github.com/Timidan/dist-x/commit/875520648b0d39091b0002dc499050d9c618572e) +- Demo video: https://github.com/logos-co/lambda-prize/pull/44#issue-4408269105 (`RISC0_DEV_MODE=0` end-to-end narration with terminal output) ## Approach @@ -19,7 +20,7 @@ DistributionX separates the airdrop into three parts: eligibility commitment, pr The distributor CSV is converted into encrypted bundle rows. Each row is encrypted to the intended recipient's claim key, and the chain stores only the Merkle root and bucket table metadata. This avoids publishing the full allowlist while still giving claimants a package they can scan locally. -The claimant proves that they can decrypt one valid row, sign for the eligible key, match the committed Merkle root, derive the correct nullifier, and bind the claim to a shielded destination commitment. Risc0 is used because the prize calls for a LEZ-compatible zero-knowledge proof path, and the demo uses the real proof mode with `RISC0_DEV_MODE=0`. The reviewer demo submits the `claim_private` instruction, which the program verifies against the rebuilt journal. A receipt-based `claim` instruction is also defined in the program; it would not expose the witness in the transaction transcript, but it is not currently runnable for shielded destinations (see Summary above). +The claimant proves that they can decrypt one valid row, sign for the eligible key, match the committed Merkle root, derive the correct nullifier, and bind the claim to a shielded destination commitment. Risc0 is used because the prize calls for a LEZ-compatible zero-knowledge proof path, and the demo uses the real proof mode with `RISC0_DEV_MODE=0`. The reviewer demo submits the `claim` instruction with the receipt; on-chain verification checks the Groth16 receipt and the journal against airdrop state, debits the vault, and credits the nullifier PDA. A separate token-settlement transaction transfers from the nullifier PDA to the shielded destination once the claim is included. Double claims are prevented with nullifiers. A successful claim records the nullifier so the same eligibility row cannot claim again, while observers still cannot link the nullifier back to the eligible address. @@ -38,7 +39,7 @@ LEZ is a good fit because the protocol needs trustless execution, local proof ge Evidence: [README.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/README.md), [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md), encrypted bundle generation, Merkle root initialization. - [x] Eligible recipients can claim without revealing the eligible address in the public transcript. - Not currently met under either claim instruction. The receipt-based `claim` would keep the witness inside the zkVM but is not runnable for shielded destinations because of LEZ's account-ownership rule. The `claim_private` instruction is runnable and is the active demo path, but it carries the witness in its instruction args. The full breakdown and the two tracked follow-up fixes that would each independently restore the property are in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). Listed below under Requirements Not Met / Pending per the L-Prize team's instruction to highlight unmet requirements. + Scope: this property holds for the `claim` instruction, which submits a Risc0 receipt. The witness fields (address, salt, signature, Merkle path) are private inputs to the zkVM, and the public journal carries only `airdrop_id`, `merkle_root`, `bucket_id`, `nullifier`, and `claim_destination_commitment`. The credit lands on the program-owned `nullifier_record` PDA (no arbitrary recipient account), so the instruction runs end-to-end for shielded destinations. The `claim_private` instruction is an opt-in fallback verifier whose instruction data includes the witness; it is gated behind `DISTRIBUTIONX_USE_CLAIM_PRIVATE=1` and is not used by the demo. See [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). - [x] Each recipient can claim only once. On-chain enforcement is per nullifier: the `NullifierRecord` PDA at seed `["nullifier", airdrop_id, nullifier]` rejects a second initialization with `E_ALREADY_CLAIMED`. Address-level uniqueness is enforced at CSV ingest by the parser in [crates/distributionx-tree/src/csv.rs](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/crates/distributionx-tree/src/csv.rs#L25-L33), which rejects duplicate addresses with `CliDuplicateAddr` before the tree is built. See [docs/WRITEUP.md Claim Uniqueness Scope](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#claim-uniqueness-scope). @@ -68,19 +69,17 @@ LEZ is a good fit because the protocol needs trustless execution, local proof ge The full threat model lives in [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md). Three points the reviewer asked for explicitly: -1. **Privacy property is not currently demonstrably met for either claim instruction.** The receipt-based `claim` would keep the witness inside the zkVM but is not runnable for shielded destinations because LEZ requires the program to claim ownership of any modified default-owner account in its post-state; a shielded one-time destination commitment has no signer to authorize that. The `claim_private` instruction is runnable and is the active demo path, but its witness fields are passed as instruction args and the generated FFI sends them via `NSSATransaction::Public`. Either of two follow-ups would restore the property independently: refactor `claim` to credit the `nullifier_record` PDA (mirroring `claim_private`'s credit pattern, no ownership-claim conflict), or wire `claim_private` through `NSSATransaction::PrivacyPreserving` (which LEZ ships in the vendored sdk). Both are tracked; see [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). +1. **Privacy is scoped to the `claim` path.** The `claim` instruction submits a Risc0 receipt; the witness fields are private zkVM inputs and do not appear in the receipt journal or the instruction data. The credit lands on the program-owned `nullifier_record` PDA, so the instruction runs end-to-end for shielded destinations without exposing the witness on chain. The `claim_private` instruction is an opt-in fallback verifier (`DISTRIBUTIONX_USE_CLAIM_PRIVATE=1`) whose instruction args include the witness; it is not used by the demo. Wiring `claim_private` through `NSSATransaction::PrivacyPreserving` (LEZ ships the variant in the vendored sdk) is a tracked follow-up that would restore witness privacy on that opt-in path too. 2. **Bucket anonymity is bounded by per-bucket population.** The `bucket_id` is public (it is in the journal and in the airdrop's `bucket_table`). Observer unlinkability holds with probability at most 1/k per bucket, where k is the number of eligible recipients in that bucket. A singleton bucket reveals the recipient by amount; small buckets shrink the anonymity set. The CLI's `inspect-csv` command warns when the smallest bucket has fewer than 8 recipients (`crates/distributionx-cli/src/commands.rs:1110-1114`) and the `pad-csv --min-per-bucket N` command lets a distributor top up small buckets. The on-chain program does not enforce a minimum k; the distributor chooses the bucket schedule that fits their privacy budget. -3. **Salt secrecy depends on the encrypted bundle and the recipient's local keystore.** Salts are 32 bytes from `OsRng` per row (`crates/distributionx-tree/src/bundle.rs:44-48`). Each row is sealed for its intended recipient with X25519 ECDH and ChaCha20-Poly1305 (`crates/distributionx-tree/src/bundle.rs:68-105`). The recipient's seed lives in a `wallet.seed` file under `target/distributionx-testnet/` by default, with a keychain-backed option in `crates/distributionx-wallet-ref/src/storage.rs`. The distributor knows every salt and can precompute a nullifier-to-row mapping; DistributionX protects observers from the eligibility set, not from the distributor. Under the active `claim_private` path the salt is also written into `claim.tx` and into the transaction transcript; see point 1. +3. **Salt secrecy depends on the encrypted bundle and the recipient's local keystore.** Salts are 32 bytes from `OsRng` per row (`crates/distributionx-tree/src/bundle.rs:44-48`). Each row is sealed for its intended recipient with X25519 ECDH and ChaCha20-Poly1305 (`crates/distributionx-tree/src/bundle.rs:68-105`). The recipient's seed lives in a `wallet.seed` file under `target/distributionx-testnet/` by default, with a keychain-backed option in `crates/distributionx-wallet-ref/src/storage.rs`. The distributor knows every salt and can precompute a nullifier-to-row mapping; DistributionX protects observers from the eligibility set, not from the distributor. Under the default `claim` path the salt stays inside the encrypted bundle and the zkVM, and `claim.tx` strips the witness; the relayer and the chain transcript do not carry the salt. -**Acknowledged naming mismatch.** The `claim_private` identifier predates this audit and does not match the instruction's actual privacy properties (it runs the in-program verifier and does not provide observer privacy for the witness on its current submission path). A rename to a clearer name such as `claim_inline` is deferred because it touches the LEZ program, the three IDL mirrors, the generated client and FFI, scripts, tests, and several doc sections (about 17 files in total). The doc points above describe what the instruction actually does; see the "A note on naming" paragraph in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). +**Acknowledged naming mismatch.** The `claim_private` identifier predates this audit and does not match the instruction's actual privacy properties (it runs the opt-in in-program verifier and does not provide observer privacy for the witness on its current submission path). A rename to a clearer name such as `claim_inline` is deferred because it touches the LEZ program, the three IDL mirrors, the generated client and FFI, scripts, tests, and several doc sections (about 17 files in total). The doc points above describe what the instruction actually does; see the "A note on naming" paragraph in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). ## Requirements Not Met / Pending -Per the L-Prize team's instruction on Discord (08 May 2026, [message link](https://discord.com/channels/973324189794697286/1501897314233618553/1502098264068194314)) to "submit your solution please on the repo, and highlight the requirements you could not meet," three requirements are flagged: - -- **Eligible-address privacy on chain.** Not currently demonstrably met under either claim instruction. The full reasoning and the two tracked follow-up fixes are in the Privacy Model And Threat Model section above and in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). +Per the L-Prize team's instruction on Discord (08 May 2026, [message link](https://discord.com/channels/973324189794697286/1501897314233618553/1502098264068194314)) to "submit your solution please on the repo, and highlight the requirements you could not meet," two requirements are flagged: - **LEZ devnet/testnet evidence.** The bounty work targets the standalone LEZ sequencer environment (`scripts/standalone-sequencer.sh`) rather than a devnet or testnet deployment. Run logs, CU values, and the recorded demo all come from the standalone environment. There is no `testnet-e2e` job in CI; the qualified CI claim is in the GitHub Actions row of this checklist. From 394d07a8fd01b26cf799dd20e795c28db1fe4a00 Mon Sep 17 00:00:00 2001 From: Timidan Date: Sat, 27 Jun 2026 13:09:24 +0100 Subject: [PATCH 09/15] Add distributionX LEZ testnet details --- solutions/LP-0003.md | 85 ++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md index 52a23eaf..226c5834 100644 --- a/solutions/LP-0003.md +++ b/solutions/LP-0003.md @@ -6,14 +6,34 @@ DistributionX is a private allowlist airdrop for the Logos Execution Zone (LEZ). A distributor commits an encrypted eligibility list on-chain through a Merkle root, funds a vault, and lets eligible recipients claim with a real Risc0 proof using `RISC0_DEV_MODE=0`. -The privacy claim targeted by the bounty is that on-chain observers should not learn the eligible address, row salt, claim signature, or Merkle path from a valid claim transcript. The active demo submits the receipt-based `claim` instruction: the program verifies a Risc0 Groth16 receipt and the journal carries only `airdrop_id`, `merkle_root`, `bucket_id`, `nullifier`, and `claim_destination_commitment`. The witness fields are private inputs to the zkVM and do not appear in the journal or the instruction data. The credit lands on the program-owned `nullifier_record` PDA, so the post-state diff does not trigger LEZ's ownership-claim rule and the instruction runs end-to-end for shielded destinations. A second instruction, `claim_private`, runs the witness verification inside the program rather than inside the zkVM; it is documented as an opt-in fallback verifier (`DISTRIBUTIONX_USE_CLAIM_PRIVATE=1`) in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model) and is not used by the bounty demo flow. +The privacy claim targeted by the bounty is that on-chain observers should not learn the eligible address, row salt, claim signature, or Merkle path from a valid claim transcript. The active path is the witness-private `claim_ppe` instruction, submitted through LEZ privacy-preserving execution (PPE) via `send_privacy_preserving_tx`. The witness verification (Ed25519 signature, Merkle membership, nullifier derivation) runs inside the PPE circuit; the heavy proof is composed client-side and the sequencer verifies a single succinct receipt. The PPE transaction message carries no instruction data and no witness fields — only public account states, the encrypted private recipient post-state, the new commitment, and the nullifier — so the witness is structurally absent from the on-chain transcript. The credit lands on a LEZ-native private recipient account (the destination commitment), and one-claim-per-recipient is enforced by the nullifier set plus the program's `NullifierRecord` PDA. + +**This is now demonstrated on the live LEZ testnet (`https://testnet.lez.logos.co`, LEZ v0.2.0-rc5): 2 distributions and 20 witness-private `claim_ppe` claims, all confirmed on-chain.** See [docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/TESTNET_EVIDENCE.md). The receipt-based `claim` and the in-program `claim_private` instructions remain in the program as alternate/fallback verifiers; `claim_private` carries the witness in instruction data and is opt-in only (`DISTRIBUTIONX_USE_CLAIM_PRIVATE=1`). ## Repository - Repository: [https://github.com/Timidan/dist-x](https://github.com/Timidan/dist-x) -- Pinned commit: [`8755206`](https://github.com/Timidan/dist-x/commit/875520648b0d39091b0002dc499050d9c618572e) +- Pinned commit: [`822c508`](https://github.com/Timidan/dist-x/commit/822c508940eeb08ad67cbaf4f9553665087a66f2) - Demo video: https://github.com/logos-co/lambda-prize/pull/44#issue-4408269105 (`RISC0_DEV_MODE=0` end-to-end narration with terminal output) +## LEZ Testnet Deployment (rc5) + +The program is deployed to the live LEZ testnet and the full claim flow is recorded on-chain. Every transaction below is verifiable with `getTransaction` against `https://testnet.lez.logos.co`. + +| Item | Value | +|---|---| +| Testnet RPC | `https://testnet.lez.logos.co` (LEZ v0.2.0-rc5) | +| Program id | `218a07eb268df922ded961fefd7d035752b44d05f4bb5172305fb0bc54506989` | +| Deploy tx | `b4e31be3c5f9e784295869904e217b52da6bfbe81f2146dd756f9827263537bc` | +| Distributions | 2 (`lp0003-rc5-b1`, `lp0003-rc5-c1`) | +| Witness-private `claim_ppe` claims | 20 (10 per distribution), all confirmed on-chain | +| Token settlements | 20 | +| Per-claim public-execution CU | 504401 (well under the 32M public-execution cap) | + +Full transaction list, per-tx `getTransaction` verification counts, and per-claim receipts: [docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/TESTNET_EVIDENCE.md). CU details: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/bench/REPORT.md). + +`claim_ppe` is submitted via `send_privacy_preserving_tx`, so the witness never appears in the on-chain transaction — privacy is demonstrated on the testnet itself, not only on the standalone sequencer. + ## Approach DistributionX separates the airdrop into three parts: eligibility commitment, private claim proof, and double-claim prevention. @@ -36,60 +56,63 @@ LEZ is a good fit because the protocol needs trustless execution, local proof ge ## Success Criteria Checklist - [x] Distributor commits an eligibility set without revealing the full allowlist. - Evidence: [README.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/README.md), [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md), encrypted bundle generation, Merkle root initialization. + Evidence: [README.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/README.md), [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/WRITEUP.md), encrypted bundle generation, Merkle root initialization. - [x] Eligible recipients can claim without revealing the eligible address in the public transcript. - Scope: this property holds for the `claim` instruction, which submits a Risc0 receipt. The witness fields (address, salt, signature, Merkle path) are private inputs to the zkVM, and the public journal carries only `airdrop_id`, `merkle_root`, `bucket_id`, `nullifier`, and `claim_destination_commitment`. The credit lands on the program-owned `nullifier_record` PDA (no arbitrary recipient account), so the instruction runs end-to-end for shielded destinations. The `claim_private` instruction is an opt-in fallback verifier whose instruction data includes the witness; it is gated behind `DISTRIBUTIONX_USE_CLAIM_PRIVATE=1` and is not used by the demo. See [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). + Demonstrated on the live testnet via the `claim_ppe` instruction, submitted through LEZ privacy-preserving execution (`send_privacy_preserving_tx`). The PPE transaction message carries no instruction data and no witness fields, so the witness (address, salt, signature, Merkle path) is structurally absent from the on-chain transcript; the witness exists only as a local input to the PPE proof. The credit lands on a LEZ-native private recipient account. Evidence: 20 on-chain `claim_ppe` claims in [docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/TESTNET_EVIDENCE.md). The `claim_private` instruction (witness in instruction data) remains an opt-in fallback gated behind `DISTRIBUTIONX_USE_CLAIM_PRIVATE=1`. + +- [x] LEZ testnet deployment with >=2 distributions and >=20 claims. + Program deployed at `218a07eb...` on `https://testnet.lez.logos.co`; 2 distributions, 20 witness-private `claim_ppe` claims, 20 settlements, all confirmed via `getTransaction`. See [docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/TESTNET_EVIDENCE.md). - [x] Each recipient can claim only once. - On-chain enforcement is per nullifier: the `NullifierRecord` PDA at seed `["nullifier", airdrop_id, nullifier]` rejects a second initialization with `E_ALREADY_CLAIMED`. Address-level uniqueness is enforced at CSV ingest by the parser in [crates/distributionx-tree/src/csv.rs](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/crates/distributionx-tree/src/csv.rs#L25-L33), which rejects duplicate addresses with `CliDuplicateAddr` before the tree is built. See [docs/WRITEUP.md Claim Uniqueness Scope](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#claim-uniqueness-scope). + On-chain enforcement is per nullifier: the `NullifierRecord` PDA at seed `["nullifier", airdrop_id, nullifier]` rejects a second initialization with `E_ALREADY_CLAIMED`. Address-level uniqueness is enforced at CSV ingest by the parser in [crates/distributionx-tree/src/csv.rs](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/crates/distributionx-tree/src/csv.rs#L25-L33), which rejects duplicate addresses with `CliDuplicateAddr` before the tree is built. See [docs/WRITEUP.md Claim Uniqueness Scope](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/WRITEUP.md#claim-uniqueness-scope). - [x] Real Risc0 proof path with `RISC0_DEV_MODE=0`. - Evidence: [scripts/e2e.sh](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/scripts/e2e.sh) `private-localnet`, `distributionx-cli prove`, `PROVE_LOCAL_OK`, `VERIFY_OK`. + Evidence: [scripts/e2e.sh](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/scripts/e2e.sh) `private-localnet`, `distributionx-cli prove`, `PROVE_LOCAL_OK`, `VERIFY_OK`. - [x] LEZ local sequencer integration. - Evidence: [scripts/standalone-sequencer.sh](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/scripts/standalone-sequencer.sh), [scripts/deploy.sh](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/scripts/deploy.sh) `--localnet`, [scripts/local-submit.sh](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/scripts/local-submit.sh). + Evidence: [scripts/standalone-sequencer.sh](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/scripts/standalone-sequencer.sh), [scripts/deploy.sh](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/scripts/deploy.sh) `--localnet`, [scripts/local-submit.sh](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/scripts/local-submit.sh). - [x] Basecamp GUI. - Evidence: [basecamp-app/](https://github.com/Timidan/dist-x/tree/875520648b0d39091b0002dc499050d9c618572e/basecamp-app), [scripts/start-basecamp.sh](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/scripts/start-basecamp.sh), LGX artifacts from [scripts/package.sh](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/scripts/package.sh). + Evidence: [basecamp-app/](https://github.com/Timidan/dist-x/tree/822c508940eeb08ad67cbaf4f9553665087a66f2/basecamp-app), [scripts/start-basecamp.sh](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/scripts/start-basecamp.sh), LGX artifacts from [scripts/package.sh](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/scripts/package.sh). - [x] Logos module / SDK. - Evidence: [distributionx_client_module/](https://github.com/Timidan/dist-x/tree/875520648b0d39091b0002dc499050d9c618572e/distributionx_client_module). + Evidence: [distributionx_client_module/](https://github.com/Timidan/dist-x/tree/822c508940eeb08ad67cbaf4f9553665087a66f2/distributionx_client_module). - [x] SPEL IDL. - Evidence: [crates/distributionx-program/idl/distributionx.json](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/crates/distributionx-program/idl/distributionx.json). + Evidence: [crates/distributionx-program/idl/distributionx.json](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/crates/distributionx-program/idl/distributionx.json). - [x] CU and benchmark report. - Evidence: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/bench/REPORT.md). + Evidence: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/bench/REPORT.md). - [x] GitHub Actions CI status. - The `scripts`, `rust`, `logos`, and `localnet-e2e` jobs run on every push and PR. `scripts`, `rust`, and `logos` pass on the latest commit. `localnet-e2e` runs `scripts/e2e.sh ci-localnet` and exits skipped on push or PR when `DISTRIBUTIONX_LEZ_SEQUENCER_START_COMMAND` is not configured, so it never reports a hard failure on the default branch when the sequencer infra is absent. There is no testnet-e2e job in CI; testnet runs are out of scope for this submission. + The `scripts`, `rust`, `logos`, and `localnet-e2e` jobs run on every push and PR. `scripts`, `rust`, and `logos` pass on the latest commit. `localnet-e2e` runs `scripts/e2e.sh ci-localnet` and exits skipped on push or PR when `DISTRIBUTIONX_LEZ_SEQUENCER_START_COMMAND` is not configured, so it never reports a hard failure on the default branch when the sequencer infra is absent. The live testnet run is recorded as on-chain evidence ([docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/TESTNET_EVIDENCE.md)) rather than a CI job, since it needs real-proof generation and a funded testnet signer. ## Privacy Model And Threat Model -The full threat model lives in [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md). Three points the reviewer asked for explicitly: +The full threat model lives in [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/WRITEUP.md). Three points the reviewer asked for explicitly: -1. **Privacy is scoped to the `claim` path.** The `claim` instruction submits a Risc0 receipt; the witness fields are private zkVM inputs and do not appear in the receipt journal or the instruction data. The credit lands on the program-owned `nullifier_record` PDA, so the instruction runs end-to-end for shielded destinations without exposing the witness on chain. The `claim_private` instruction is an opt-in fallback verifier (`DISTRIBUTIONX_USE_CLAIM_PRIVATE=1`) whose instruction args include the witness; it is not used by the demo. Wiring `claim_private` through `NSSATransaction::PrivacyPreserving` (LEZ ships the variant in the vendored sdk) is a tracked follow-up that would restore witness privacy on that opt-in path too. +1. **Witness privacy is provided by the `claim_ppe` path through LEZ privacy-preserving execution (PPE).** Earlier write-ups scoped privacy to a receipt-based `claim`; on LEZ v0.2.0-rc5 that path cannot land (verifying a Groth16 receipt in public execution is ~218M cycles, over the 32M public-execution cap). The shipping path is `claim_ppe`, submitted via `send_privacy_preserving_tx`: the witness verification runs in the PPE circuit, the heavy proof is composed client-side, and the sequencer verifies one succinct receipt. The PPE message format (`lee/state_machine/src/privacy_preserving_transaction/message.rs`) has no instruction-data field, so the witness is structurally absent from the on-chain transaction. This is the formerly-"tracked follow-up" (wiring through the PrivacyPreserving transaction variant), now implemented and demonstrated on the live testnet. The `claim_private` instruction (witness in public instruction data) remains an opt-in, witness-leaking fallback (`DISTRIBUTIONX_USE_CLAIM_PRIVATE=1`). -2. **Bucket anonymity is bounded by per-bucket population.** The `bucket_id` is public (it is in the journal and in the airdrop's `bucket_table`). Observer unlinkability holds with probability at most 1/k per bucket, where k is the number of eligible recipients in that bucket. A singleton bucket reveals the recipient by amount; small buckets shrink the anonymity set. The CLI's `inspect-csv` command warns when the smallest bucket has fewer than 8 recipients (`crates/distributionx-cli/src/commands.rs:1110-1114`) and the `pad-csv --min-per-bucket N` command lets a distributor top up small buckets. The on-chain program does not enforce a minimum k; the distributor chooses the bucket schedule that fits their privacy budget. +2. **Bucket anonymity is bounded by per-bucket population.** The `bucket_id` is public (it is in the journal and in the airdrop's `bucket_table`). Observer unlinkability holds with probability at most 1/k per bucket, where k is the number of eligible recipients in that bucket. A singleton bucket reveals the recipient by amount; small buckets shrink the anonymity set. The CLI's `inspect-csv` command warns when the smallest bucket has fewer than 8 recipients (`crates/distributionx-cli/src/commands.rs:1213-1216`) and the `pad-csv --min-per-bucket N` command lets a distributor top up small buckets. The on-chain program does not enforce a minimum k; the distributor chooses the bucket schedule that fits their privacy budget. 3. **Salt secrecy depends on the encrypted bundle and the recipient's local keystore.** Salts are 32 bytes from `OsRng` per row (`crates/distributionx-tree/src/bundle.rs:44-48`). Each row is sealed for its intended recipient with X25519 ECDH and ChaCha20-Poly1305 (`crates/distributionx-tree/src/bundle.rs:68-105`). The recipient's seed lives in a `wallet.seed` file under `target/distributionx-testnet/` by default, with a keychain-backed option in `crates/distributionx-wallet-ref/src/storage.rs`. The distributor knows every salt and can precompute a nullifier-to-row mapping; DistributionX protects observers from the eligibility set, not from the distributor. Under the default `claim` path the salt stays inside the encrypted bundle and the zkVM, and `claim.tx` strips the witness; the relayer and the chain transcript do not carry the salt. -**Acknowledged naming mismatch.** The `claim_private` identifier predates this audit and does not match the instruction's actual privacy properties (it runs the opt-in in-program verifier and does not provide observer privacy for the witness on its current submission path). A rename to a clearer name such as `claim_inline` is deferred because it touches the LEZ program, the three IDL mirrors, the generated client and FFI, scripts, tests, and several doc sections (about 17 files in total). The doc points above describe what the instruction actually does; see the "A note on naming" paragraph in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md#privacy-model). +**Acknowledged naming mismatch.** The `claim_private` identifier predates this audit and does not match the instruction's actual privacy properties (it runs the opt-in in-program verifier and does not provide observer privacy for the witness on its current submission path). A rename to a clearer name such as `claim_inline` is deferred because it touches the LEZ program, the three IDL mirrors, the generated client and FFI, scripts, tests, and several doc sections (about 17 files in total). The doc points above describe what the instruction actually does; see the "A note on naming" paragraph in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/WRITEUP.md#privacy-model). -## Requirements Not Met / Pending +## Requirements — Status -Per the L-Prize team's instruction on Discord (08 May 2026, [message link](https://discord.com/channels/973324189794697286/1501897314233618553/1502098264068194314)) to "submit your solution please on the repo, and highlight the requirements you could not meet," two requirements are flagged: +The two requirements previously flagged as pending: -- **LEZ devnet/testnet evidence.** The bounty work targets the standalone LEZ sequencer environment (`scripts/standalone-sequencer.sh`) rather than a devnet or testnet deployment. Run logs, CU values, and the recorded demo all come from the standalone environment. There is no `testnet-e2e` job in CI; the qualified CI claim is in the GitHub Actions row of this checklist. +- **LEZ devnet/testnet evidence — NOW MET.** The program is deployed to the live LEZ testnet (`https://testnet.lez.logos.co`, LEZ v0.2.0-rc5) and the full flow — 2 distributions, 20 witness-private `claim_ppe` claims, and 20 token settlements — is recorded on-chain and verified with `getTransaction`. See the [LEZ Testnet Deployment](#lez-testnet-deployment-rc5) table above and [docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/TESTNET_EVIDENCE.md). Reaching the testnet required migrating the program off the retired `nssa_core` SDK onto LEZ v0.2.0-rc5 (the version the testnet runs) and moving the claim to the privacy-preserving `claim_ppe` path, which keeps the witness off-chain while fitting the 32M public-execution cap (the receipt-verifying `claim` path is ~218M cycles and cannot land in public execution). -- **3 distributions from outside the team.** The L-Prize team dropped this requirement in the same Discord message: "I will drop the '3 distributions from people outside the team' from the requirements. We did a lot of iteration on role of L-Prize and I now agree this is not really appropriate/useful for testnet L-Prize. Adoption criterias make more sense closer and post mainnet." +- **3 distributions from outside the team — dropped by the L-Prize team.** Per the Discord message (08 May 2026, [message link](https://discord.com/channels/973324189794697286/1501897314233618553/1502098264068194314)): "I will drop the '3 distributions from people outside the team' from the requirements. We did a lot of iteration on role of L-Prize and I now agree this is not really appropriate/useful for testnet L-Prize. Adoption criterias make more sense closer and post mainnet." ## FURPS Self-Assessment ### Functionality -DistributionX supports distributor initialization, encrypted bundle creation, vault funding, Risc0 proof generation, proof verification, claim submission through the `claim` instruction, duplicate-claim rejection, close flow, Basecamp operation, and CLI operation. +DistributionX supports distributor initialization, encrypted bundle creation, vault funding, Risc0 proof generation, proof verification, witness-private claim submission through the `claim_ppe` (PPE) instruction with per-claimant private destinations, duplicate-claim rejection, token settlement, close flow, Basecamp operation, and CLI operation. ### Usability @@ -101,21 +124,23 @@ The CLI fails closed on invalid proofs, mismatched journals, missing bundles, mi ### Performance -Real Risc0 proving is the bottleneck. On the measured local machine, Basecamp proof generation took about 35-40 minutes with `RISC0_DEV_MODE=0`, and on-chain claim finalization (Risc0 receipt verification plus token settlement) took about 20-25 minutes. CU and wall-clock measurements are documented in [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/bench/REPORT.md); the row currently labelled `claim_private` is being re-recorded against the `claim` instruction to match the scoped privacy claim above. +Real Risc0 proving (the PPE composite proof) is the bottleneck on the claimant side. On-chain, the `claim_ppe` instruction's public-execution cost is 504401 CU per claim (deterministic across all 20 testnet claims) — far under the 32M public-execution cap, because the heavy verification runs in the PPE proof off the public budget and the sequencer only checks one succinct receipt. CU and wall-clock measurements are documented in [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/bench/REPORT.md), with the live testnet run as the primary evidence. ### Supportability -The repo includes focused Rust crates, a Logos client module, a Basecamp app, local sequencer scripts, packaging scripts, benchmark docs, reviewer fixtures, and a system architecture diagram. Package verification is covered by [scripts/package.sh](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/scripts/package.sh). +The repo includes focused Rust crates, a Logos client module, a Basecamp app, local sequencer scripts, packaging scripts, benchmark docs, reviewer fixtures, and a system architecture diagram. Package verification is covered by [scripts/package.sh](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/scripts/package.sh). ## Supporting Materials -- README: [README.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/README.md) -- Technical write-up: [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/WRITEUP.md) -- Benchmark and CU report: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/docs/bench/REPORT.md) -- System architecture diagram: [DistributionX.system-architecture.excalidraw](https://github.com/Timidan/dist-x/blob/875520648b0d39091b0002dc499050d9c618572e/DistributionX.system-architecture.excalidraw) -- Basecamp app: [basecamp-app/](https://github.com/Timidan/dist-x/tree/875520648b0d39091b0002dc499050d9c618572e/basecamp-app) -- Logos client module: [distributionx_client_module/](https://github.com/Timidan/dist-x/tree/875520648b0d39091b0002dc499050d9c618572e/distributionx_client_module) -- Reviewer fixture: [fixtures/reviewer-fast-path/](https://github.com/Timidan/dist-x/tree/875520648b0d39091b0002dc499050d9c618572e/fixtures/reviewer-fast-path) +- **Testnet evidence (rc5 PPE): [docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/TESTNET_EVIDENCE.md)** — program id, 2 distributions, 20 witness-private `claim_ppe` claim tx hashes + 20 settlements, `getTransaction` verification. +- **Raw evidence artifacts: [docs/testnet-evidence/](https://github.com/Timidan/dist-x/tree/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/testnet-evidence)** — committed `getTransaction` verification jsonl, per-claim receipts (CU 504401), claim summaries, and run logs for both distributions. Witness-free: no eligible address, salt, signature, or Merkle path appears in them; the seed-bearing working state is intentionally not committed. +- README: [README.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/README.md) +- Technical write-up: [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/WRITEUP.md) +- Benchmark and CU report: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/bench/REPORT.md) +- System architecture diagram: [DistributionX.system-architecture.excalidraw](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/DistributionX.system-architecture.excalidraw) +- Basecamp app: [basecamp-app/](https://github.com/Timidan/dist-x/tree/822c508940eeb08ad67cbaf4f9553665087a66f2/basecamp-app) +- Logos client module: [distributionx_client_module/](https://github.com/Timidan/dist-x/tree/822c508940eeb08ad67cbaf4f9553665087a66f2/distributionx_client_module) +- Reviewer fixture: [fixtures/reviewer-fast-path/](https://github.com/Timidan/dist-x/tree/822c508940eeb08ad67cbaf4f9553665087a66f2/fixtures/reviewer-fast-path) ## Terms & Conditions From a283e830511803acd27bf95720bc6db19b927200 Mon Sep 17 00:00:00 2001 From: Timidan Date: Tue, 18 Aug 2026 15:43:15 +0100 Subject: [PATCH 10/15] Update LP-0003 submission snapshot for review --- solutions/LP-0003.md | 190 +++++++++++-------------------------------- 1 file changed, 47 insertions(+), 143 deletions(-) diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md index 226c5834..af822699 100644 --- a/solutions/LP-0003.md +++ b/solutions/LP-0003.md @@ -2,146 +2,50 @@ **Submitted by:** Timidan -## Summary - -DistributionX is a private allowlist airdrop for the Logos Execution Zone (LEZ). A distributor commits an encrypted eligibility list on-chain through a Merkle root, funds a vault, and lets eligible recipients claim with a real Risc0 proof using `RISC0_DEV_MODE=0`. - -The privacy claim targeted by the bounty is that on-chain observers should not learn the eligible address, row salt, claim signature, or Merkle path from a valid claim transcript. The active path is the witness-private `claim_ppe` instruction, submitted through LEZ privacy-preserving execution (PPE) via `send_privacy_preserving_tx`. The witness verification (Ed25519 signature, Merkle membership, nullifier derivation) runs inside the PPE circuit; the heavy proof is composed client-side and the sequencer verifies a single succinct receipt. The PPE transaction message carries no instruction data and no witness fields — only public account states, the encrypted private recipient post-state, the new commitment, and the nullifier — so the witness is structurally absent from the on-chain transcript. The credit lands on a LEZ-native private recipient account (the destination commitment), and one-claim-per-recipient is enforced by the nullifier set plus the program's `NullifierRecord` PDA. - -**This is now demonstrated on the live LEZ testnet (`https://testnet.lez.logos.co`, LEZ v0.2.0-rc5): 2 distributions and 20 witness-private `claim_ppe` claims, all confirmed on-chain.** See [docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/TESTNET_EVIDENCE.md). The receipt-based `claim` and the in-program `claim_private` instructions remain in the program as alternate/fallback verifiers; `claim_private` carries the witness in instruction data and is opt-in only (`DISTRIBUTIONX_USE_CLAIM_PRIVATE=1`). - -## Repository - -- Repository: [https://github.com/Timidan/dist-x](https://github.com/Timidan/dist-x) -- Pinned commit: [`822c508`](https://github.com/Timidan/dist-x/commit/822c508940eeb08ad67cbaf4f9553665087a66f2) -- Demo video: https://github.com/logos-co/lambda-prize/pull/44#issue-4408269105 (`RISC0_DEV_MODE=0` end-to-end narration with terminal output) - -## LEZ Testnet Deployment (rc5) - -The program is deployed to the live LEZ testnet and the full claim flow is recorded on-chain. Every transaction below is verifiable with `getTransaction` against `https://testnet.lez.logos.co`. - -| Item | Value | -|---|---| -| Testnet RPC | `https://testnet.lez.logos.co` (LEZ v0.2.0-rc5) | -| Program id | `218a07eb268df922ded961fefd7d035752b44d05f4bb5172305fb0bc54506989` | -| Deploy tx | `b4e31be3c5f9e784295869904e217b52da6bfbe81f2146dd756f9827263537bc` | -| Distributions | 2 (`lp0003-rc5-b1`, `lp0003-rc5-c1`) | -| Witness-private `claim_ppe` claims | 20 (10 per distribution), all confirmed on-chain | -| Token settlements | 20 | -| Per-claim public-execution CU | 504401 (well under the 32M public-execution cap) | - -Full transaction list, per-tx `getTransaction` verification counts, and per-claim receipts: [docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/TESTNET_EVIDENCE.md). CU details: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/bench/REPORT.md). - -`claim_ppe` is submitted via `send_privacy_preserving_tx`, so the witness never appears in the on-chain transaction — privacy is demonstrated on the testnet itself, not only on the standalone sequencer. - -## Approach - -DistributionX separates the airdrop into three parts: eligibility commitment, private claim proof, and double-claim prevention. - -The distributor CSV is converted into encrypted bundle rows. Each row is encrypted to the intended recipient's claim key, and the chain stores only the Merkle root and bucket table metadata. This avoids publishing the full allowlist while still giving claimants a package they can scan locally. - -The claimant proves that they can decrypt one valid row, sign for the eligible key, match the committed Merkle root, derive the correct nullifier, and bind the claim to a shielded destination commitment. Risc0 is used because the prize calls for a LEZ-compatible zero-knowledge proof path, and the demo uses the real proof mode with `RISC0_DEV_MODE=0`. The reviewer demo submits the `claim` instruction with the receipt; on-chain verification checks the Groth16 receipt and the journal against airdrop state, debits the vault, and credits the nullifier PDA. A separate token-settlement transaction transfers from the nullifier PDA to the shielded destination once the claim is included. - -Double claims are prevented with nullifiers. A successful claim records the nullifier so the same eligibility row cannot claim again, while observers still cannot link the nullifier back to the eligible address. - -Rejected alternatives: - -- Public Merkle airdrop: simpler, but reveals the eligible address at claim time. -- Publishing the allowlist: easy to audit, but defeats the privacy goal. -- Dev-mode or mock proofs: fast, but not valid for the bounty requirement. -- A custom non-Risc0 proof system: possible, but less aligned with the Logos/LEZ stack. - -LEZ is a good fit because the protocol needs trustless execution, local proof generation, shielded destination handling, and private claim submission. A centralized airdrop service would learn the eligibility list and claim mapping directly. - -## Success Criteria Checklist - -- [x] Distributor commits an eligibility set without revealing the full allowlist. - Evidence: [README.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/README.md), [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/WRITEUP.md), encrypted bundle generation, Merkle root initialization. - -- [x] Eligible recipients can claim without revealing the eligible address in the public transcript. - Demonstrated on the live testnet via the `claim_ppe` instruction, submitted through LEZ privacy-preserving execution (`send_privacy_preserving_tx`). The PPE transaction message carries no instruction data and no witness fields, so the witness (address, salt, signature, Merkle path) is structurally absent from the on-chain transcript; the witness exists only as a local input to the PPE proof. The credit lands on a LEZ-native private recipient account. Evidence: 20 on-chain `claim_ppe` claims in [docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/TESTNET_EVIDENCE.md). The `claim_private` instruction (witness in instruction data) remains an opt-in fallback gated behind `DISTRIBUTIONX_USE_CLAIM_PRIVATE=1`. - -- [x] LEZ testnet deployment with >=2 distributions and >=20 claims. - Program deployed at `218a07eb...` on `https://testnet.lez.logos.co`; 2 distributions, 20 witness-private `claim_ppe` claims, 20 settlements, all confirmed via `getTransaction`. See [docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/TESTNET_EVIDENCE.md). - -- [x] Each recipient can claim only once. - On-chain enforcement is per nullifier: the `NullifierRecord` PDA at seed `["nullifier", airdrop_id, nullifier]` rejects a second initialization with `E_ALREADY_CLAIMED`. Address-level uniqueness is enforced at CSV ingest by the parser in [crates/distributionx-tree/src/csv.rs](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/crates/distributionx-tree/src/csv.rs#L25-L33), which rejects duplicate addresses with `CliDuplicateAddr` before the tree is built. See [docs/WRITEUP.md Claim Uniqueness Scope](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/WRITEUP.md#claim-uniqueness-scope). - -- [x] Real Risc0 proof path with `RISC0_DEV_MODE=0`. - Evidence: [scripts/e2e.sh](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/scripts/e2e.sh) `private-localnet`, `distributionx-cli prove`, `PROVE_LOCAL_OK`, `VERIFY_OK`. - -- [x] LEZ local sequencer integration. - Evidence: [scripts/standalone-sequencer.sh](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/scripts/standalone-sequencer.sh), [scripts/deploy.sh](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/scripts/deploy.sh) `--localnet`, [scripts/local-submit.sh](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/scripts/local-submit.sh). - -- [x] Basecamp GUI. - Evidence: [basecamp-app/](https://github.com/Timidan/dist-x/tree/822c508940eeb08ad67cbaf4f9553665087a66f2/basecamp-app), [scripts/start-basecamp.sh](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/scripts/start-basecamp.sh), LGX artifacts from [scripts/package.sh](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/scripts/package.sh). - -- [x] Logos module / SDK. - Evidence: [distributionx_client_module/](https://github.com/Timidan/dist-x/tree/822c508940eeb08ad67cbaf4f9553665087a66f2/distributionx_client_module). - -- [x] SPEL IDL. - Evidence: [crates/distributionx-program/idl/distributionx.json](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/crates/distributionx-program/idl/distributionx.json). - -- [x] CU and benchmark report. - Evidence: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/bench/REPORT.md). - -- [x] GitHub Actions CI status. - The `scripts`, `rust`, `logos`, and `localnet-e2e` jobs run on every push and PR. `scripts`, `rust`, and `logos` pass on the latest commit. `localnet-e2e` runs `scripts/e2e.sh ci-localnet` and exits skipped on push or PR when `DISTRIBUTIONX_LEZ_SEQUENCER_START_COMMAND` is not configured, so it never reports a hard failure on the default branch when the sequencer infra is absent. The live testnet run is recorded as on-chain evidence ([docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/TESTNET_EVIDENCE.md)) rather than a CI job, since it needs real-proof generation and a funded testnet signer. - -## Privacy Model And Threat Model - -The full threat model lives in [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/WRITEUP.md). Three points the reviewer asked for explicitly: - -1. **Witness privacy is provided by the `claim_ppe` path through LEZ privacy-preserving execution (PPE).** Earlier write-ups scoped privacy to a receipt-based `claim`; on LEZ v0.2.0-rc5 that path cannot land (verifying a Groth16 receipt in public execution is ~218M cycles, over the 32M public-execution cap). The shipping path is `claim_ppe`, submitted via `send_privacy_preserving_tx`: the witness verification runs in the PPE circuit, the heavy proof is composed client-side, and the sequencer verifies one succinct receipt. The PPE message format (`lee/state_machine/src/privacy_preserving_transaction/message.rs`) has no instruction-data field, so the witness is structurally absent from the on-chain transaction. This is the formerly-"tracked follow-up" (wiring through the PrivacyPreserving transaction variant), now implemented and demonstrated on the live testnet. The `claim_private` instruction (witness in public instruction data) remains an opt-in, witness-leaking fallback (`DISTRIBUTIONX_USE_CLAIM_PRIVATE=1`). - -2. **Bucket anonymity is bounded by per-bucket population.** The `bucket_id` is public (it is in the journal and in the airdrop's `bucket_table`). Observer unlinkability holds with probability at most 1/k per bucket, where k is the number of eligible recipients in that bucket. A singleton bucket reveals the recipient by amount; small buckets shrink the anonymity set. The CLI's `inspect-csv` command warns when the smallest bucket has fewer than 8 recipients (`crates/distributionx-cli/src/commands.rs:1213-1216`) and the `pad-csv --min-per-bucket N` command lets a distributor top up small buckets. The on-chain program does not enforce a minimum k; the distributor chooses the bucket schedule that fits their privacy budget. - -3. **Salt secrecy depends on the encrypted bundle and the recipient's local keystore.** Salts are 32 bytes from `OsRng` per row (`crates/distributionx-tree/src/bundle.rs:44-48`). Each row is sealed for its intended recipient with X25519 ECDH and ChaCha20-Poly1305 (`crates/distributionx-tree/src/bundle.rs:68-105`). The recipient's seed lives in a `wallet.seed` file under `target/distributionx-testnet/` by default, with a keychain-backed option in `crates/distributionx-wallet-ref/src/storage.rs`. The distributor knows every salt and can precompute a nullifier-to-row mapping; DistributionX protects observers from the eligibility set, not from the distributor. Under the default `claim` path the salt stays inside the encrypted bundle and the zkVM, and `claim.tx` strips the witness; the relayer and the chain transcript do not carry the salt. - -**Acknowledged naming mismatch.** The `claim_private` identifier predates this audit and does not match the instruction's actual privacy properties (it runs the opt-in in-program verifier and does not provide observer privacy for the witness on its current submission path). A rename to a clearer name such as `claim_inline` is deferred because it touches the LEZ program, the three IDL mirrors, the generated client and FFI, scripts, tests, and several doc sections (about 17 files in total). The doc points above describe what the instruction actually does; see the "A note on naming" paragraph in [docs/WRITEUP.md Privacy Model](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/WRITEUP.md#privacy-model). - -## Requirements — Status - -The two requirements previously flagged as pending: - -- **LEZ devnet/testnet evidence — NOW MET.** The program is deployed to the live LEZ testnet (`https://testnet.lez.logos.co`, LEZ v0.2.0-rc5) and the full flow — 2 distributions, 20 witness-private `claim_ppe` claims, and 20 token settlements — is recorded on-chain and verified with `getTransaction`. See the [LEZ Testnet Deployment](#lez-testnet-deployment-rc5) table above and [docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/TESTNET_EVIDENCE.md). Reaching the testnet required migrating the program off the retired `nssa_core` SDK onto LEZ v0.2.0-rc5 (the version the testnet runs) and moving the claim to the privacy-preserving `claim_ppe` path, which keeps the witness off-chain while fitting the 32M public-execution cap (the receipt-verifying `claim` path is ~218M cycles and cannot land in public execution). - -- **3 distributions from outside the team — dropped by the L-Prize team.** Per the Discord message (08 May 2026, [message link](https://discord.com/channels/973324189794697286/1501897314233618553/1502098264068194314)): "I will drop the '3 distributions from people outside the team' from the requirements. We did a lot of iteration on role of L-Prize and I now agree this is not really appropriate/useful for testnet L-Prize. Adoption criterias make more sense closer and post mainnet." - -## FURPS Self-Assessment - -### Functionality - -DistributionX supports distributor initialization, encrypted bundle creation, vault funding, Risc0 proof generation, proof verification, witness-private claim submission through the `claim_ppe` (PPE) instruction with per-claimant private destinations, duplicate-claim rejection, token settlement, close flow, Basecamp operation, and CLI operation. - -### Usability - -The README gives scratch-clone instructions for building binaries and running create/claim locally. The reviewer fixture seeds make the flow reproducible without regenerating every key. Basecamp provides the visual create/fund/claim flow, while the CLI provides deterministic evidence commands. - -### Reliability - -The CLI fails closed on invalid proofs, mismatched journals, missing bundles, missing destination packets, and duplicate claims. Local submit receipts are written under `target/distributionx-testnet/receipts/` during reproduction. - -### Performance - -Real Risc0 proving (the PPE composite proof) is the bottleneck on the claimant side. On-chain, the `claim_ppe` instruction's public-execution cost is 504401 CU per claim (deterministic across all 20 testnet claims) — far under the 32M public-execution cap, because the heavy verification runs in the PPE proof off the public budget and the sequencer only checks one succinct receipt. CU and wall-clock measurements are documented in [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/bench/REPORT.md), with the live testnet run as the primary evidence. - -### Supportability - -The repo includes focused Rust crates, a Logos client module, a Basecamp app, local sequencer scripts, packaging scripts, benchmark docs, reviewer fixtures, and a system architecture diagram. Package verification is covered by [scripts/package.sh](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/scripts/package.sh). - -## Supporting Materials - -- **Testnet evidence (rc5 PPE): [docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/TESTNET_EVIDENCE.md)** — program id, 2 distributions, 20 witness-private `claim_ppe` claim tx hashes + 20 settlements, `getTransaction` verification. -- **Raw evidence artifacts: [docs/testnet-evidence/](https://github.com/Timidan/dist-x/tree/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/testnet-evidence)** — committed `getTransaction` verification jsonl, per-claim receipts (CU 504401), claim summaries, and run logs for both distributions. Witness-free: no eligible address, salt, signature, or Merkle path appears in them; the seed-bearing working state is intentionally not committed. -- README: [README.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/README.md) -- Technical write-up: [docs/WRITEUP.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/WRITEUP.md) -- Benchmark and CU report: [docs/bench/REPORT.md](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/docs/bench/REPORT.md) -- System architecture diagram: [DistributionX.system-architecture.excalidraw](https://github.com/Timidan/dist-x/blob/822c508940eeb08ad67cbaf4f9553665087a66f2/DistributionX.system-architecture.excalidraw) -- Basecamp app: [basecamp-app/](https://github.com/Timidan/dist-x/tree/822c508940eeb08ad67cbaf4f9553665087a66f2/basecamp-app) -- Logos client module: [distributionx_client_module/](https://github.com/Timidan/dist-x/tree/822c508940eeb08ad67cbaf4f9553665087a66f2/distributionx_client_module) -- Reviewer fixture: [fixtures/reviewer-fast-path/](https://github.com/Timidan/dist-x/tree/822c508940eeb08ad67cbaf4f9553665087a66f2/fixtures/reviewer-fast-path) - -## Terms & Conditions - -By submitting this solution, I confirm that I have read and agree to the [Terms & Conditions](https://github.com/logos-co/lambda-prize/blob/master/TERMS.md). +## Submission snapshot (single pinned revision) + +- Repository: https://github.com/Timidan/dist-x +- Pinned commit: `fb4587767d71dca9074908ae3d563b3642c1a583` + (`https://github.com/Timidan/dist-x/commit/fb4587767d71dca9074908ae3d563b3642c1a583`) +- Release: `v0.1.0` + https://github.com/Timidan/dist-x/releases/tag/v0.1.0 +- Demo video: https://youtu.be/w0TL22pnkqo +- CI evidence: https://github.com/Timidan/dist-x/actions/runs/31915269343 +- Testnet evidence: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/TESTNET_EVIDENCE.md +- Testnet evidence manifest: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/testnet-evidence/v0.1.0/manifest.json +- Proof mode: `RISC0_DEV_MODE=0` (native LEZ private settlement) + +## LEZ testnet evidence (current run) + +- RPC: `https://testnet.lez.logos.co` +- LEZ version compatibility observed: commit `47eba256479f6f785acbd138834340703cd03401` +- Program ID: `4bf08c88a91871ecf69ff08af42591a597c51142cbc1f9c6fbbb7d2e888d9ee3` +- Deploy tx: https://explorer.testnet.lez.logos.co/transaction/2186b9ba9e95e4926f2800e88b5d0653bda3d1669414f0a19c6daf0798576181 (block 8138) +- Distributions: 2 (`205a0aad...`, `cbc73c81...`) +- Claim flow: 20 witness-private `claim_ppe` claims, all on-chain, all confirmed +- Included tx: 27 (including 5-write smoke + 22-write settlement/finalize sequence) + All RPC snapshots are committed under: https://github.com/Timidan/dist-x/tree/fb4587767d71dca9074908ae3d563b3642c1a583/docs/testnet-evidence/v0.1.0/rpc +- Final claim tx sample: https://explorer.testnet.lez.logos.co/transaction/ff9188d3ca86d92cfd39b1e8a0de2599af39c7309de68e408896f1912a993866 + +## Supporting links (all pinned to `fb4587767...`) + +- README: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/README.md +- Writeup: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/WRITEUP.md +- Bench report: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/bench/REPORT.md +- Architecture diagram: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/DistributionX.system-architecture.excalidraw +- Basecamp app: https://github.com/Timidan/dist-x/tree/fb4587767d71dca9074908ae3d563b3642c1a583/basecamp-app +- Client module: https://github.com/Timidan/dist-x/tree/fb4587767d71dca9074908ae3d563b3642c1a583/distributionx_client_module + +## Success criteria status + +- [x] Private allowlist flow built end-to-end, with distributor-side encrypted bundles. +- [x] Witness privacy for claims is demonstrated via LEZ `claim_ppe` (no witness in public tx data). +- [x] 2 distributions and 20 claims recorded on LEZ public testnet. +- [x] One claim per recipient enforced by nullifier tracking. +- [x] Real `RISC0_DEV_MODE=0` proof path verified on a public endpoint. +- [x] Basecamp + CLI demo path is exercised in a full run. + +## Notes + +- Older references to LEZ `rc5`, legacy program IDs, old demo links, and older evidence hashes were intentionally removed for this submission. +- Terms: https://github.com/logos-co/lambda-prize/blob/master/TERMS.md From ba2eea921a45d04f76d5a10a495f560a0ed1afaf Mon Sep 17 00:00:00 2001 From: Timidan Date: Tue, 18 Aug 2026 15:45:46 +0100 Subject: [PATCH 11/15] Restore required sections in LP-0003 solution file --- solutions/LP-0003.md | 98 ++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md index af822699..4b7cfad9 100644 --- a/solutions/LP-0003.md +++ b/solutions/LP-0003.md @@ -2,50 +2,60 @@ **Submitted by:** Timidan -## Submission snapshot (single pinned revision) +## Summary -- Repository: https://github.com/Timidan/dist-x -- Pinned commit: `fb4587767d71dca9074908ae3d563b3642c1a583` - (`https://github.com/Timidan/dist-x/commit/fb4587767d71dca9074908ae3d563b3642c1a583`) -- Release: `v0.1.0` - https://github.com/Timidan/dist-x/releases/tag/v0.1.0 +DistributionX implements a privacy-focused allowlist airdrop for LEZ where the distributor commits an encrypted eligibility bundle and eligible recipients claim privately with witness data kept out of the public transaction path. + +The live on-chain flow uses the LEZ `claim_ppe` path with `RISC0_DEV_MODE=0`, which composes and submits the private proof path intended by the prize's privacy requirement. + +## Repository + +- Repository: [https://github.com/Timidan/dist-x](https://github.com/Timidan/dist-x) +- Pinned commit: [fb4587767d71dca9074908ae3d563b3642c1a583](https://github.com/Timidan/dist-x/commit/fb4587767d71dca9074908ae3d563b3642c1a583) +- Release: [v0.1.0](https://github.com/Timidan/dist-x/releases/tag/v0.1.0) - Demo video: https://youtu.be/w0TL22pnkqo - CI evidence: https://github.com/Timidan/dist-x/actions/runs/31915269343 -- Testnet evidence: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/TESTNET_EVIDENCE.md -- Testnet evidence manifest: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/testnet-evidence/v0.1.0/manifest.json -- Proof mode: `RISC0_DEV_MODE=0` (native LEZ private settlement) - -## LEZ testnet evidence (current run) - -- RPC: `https://testnet.lez.logos.co` -- LEZ version compatibility observed: commit `47eba256479f6f785acbd138834340703cd03401` -- Program ID: `4bf08c88a91871ecf69ff08af42591a597c51142cbc1f9c6fbbb7d2e888d9ee3` -- Deploy tx: https://explorer.testnet.lez.logos.co/transaction/2186b9ba9e95e4926f2800e88b5d0653bda3d1669414f0a19c6daf0798576181 (block 8138) -- Distributions: 2 (`205a0aad...`, `cbc73c81...`) -- Claim flow: 20 witness-private `claim_ppe` claims, all on-chain, all confirmed -- Included tx: 27 (including 5-write smoke + 22-write settlement/finalize sequence) - All RPC snapshots are committed under: https://github.com/Timidan/dist-x/tree/fb4587767d71dca9074908ae3d563b3642c1a583/docs/testnet-evidence/v0.1.0/rpc -- Final claim tx sample: https://explorer.testnet.lez.logos.co/transaction/ff9188d3ca86d92cfd39b1e8a0de2599af39c7309de68e408896f1912a993866 - -## Supporting links (all pinned to `fb4587767...`) - -- README: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/README.md -- Writeup: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/WRITEUP.md -- Bench report: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/bench/REPORT.md -- Architecture diagram: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/DistributionX.system-architecture.excalidraw -- Basecamp app: https://github.com/Timidan/dist-x/tree/fb4587767d71dca9074908ae3d563b3642c1a583/basecamp-app -- Client module: https://github.com/Timidan/dist-x/tree/fb4587767d71dca9074908ae3d563b3642c1a583/distributionx_client_module - -## Success criteria status - -- [x] Private allowlist flow built end-to-end, with distributor-side encrypted bundles. -- [x] Witness privacy for claims is demonstrated via LEZ `claim_ppe` (no witness in public tx data). -- [x] 2 distributions and 20 claims recorded on LEZ public testnet. -- [x] One claim per recipient enforced by nullifier tracking. -- [x] Real `RISC0_DEV_MODE=0` proof path verified on a public endpoint. -- [x] Basecamp + CLI demo path is exercised in a full run. - -## Notes - -- Older references to LEZ `rc5`, legacy program IDs, old demo links, and older evidence hashes were intentionally removed for this submission. -- Terms: https://github.com/logos-co/lambda-prize/blob/master/TERMS.md +- LEZ evidence index: [docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/TESTNET_EVIDENCE.md) +- LEZ evidence manifest: [docs/testnet-evidence/v0.1.0/manifest.json](https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/testnet-evidence/v0.1.0/manifest.json) + +## Approach + +1. **Distribution setup:** Distributor CSV is sanitized, encrypted to recipient keys, and committed as a Merkle root with bucketed metadata. +2. **Claim path:** Eligible recipients produce a witness locally from their row, post the composite private proof through `claim_ppe`, and settlement is written to a LEZ-native private destination. +3. **Integrity guarantees:** Nullifier tracking prevents duplicate claims and on-chain state checks enforce claim limits. +4. **Readiness package:** README, architecture docs, CI, and release artifacts were updated to match this exact run and pinned commit. + +## Success Criteria Checklist + +- [x] Private allowlist/eligibility commitment flow implemented and demoed. +- [x] Witness-private claim path (`claim_ppe`) demonstrated on live LEZ testnet. +- [x] Two on-chain distributions and 20 `claim_ppe` claims captured and confirmed. +- [x] Nullifier uniqueness enforced; duplicate claims are rejected by program state. +- [x] Real proof mode verified with `RISC0_DEV_MODE=0`. +- [x] Supporting materials pinned: README, writeup, reports, architecture, CI, and manifest. + +## FURPS Self-Assessment + +### Functionality + +DistributionX delivers the core end-to-end airdrop workflow: encrypted distributor bundle ingestion, on-chain deployment, privacy-preserving claims via `claim_ppe`, nullifier-based replay prevention, and recipient settlement. + +### Usability + +CLI and Basecamp paths are practical for review and demos: setup/deploy/claim commands are scripted, evidence has machine-readable artifacts, and fixtures support repeatable runs for reviewers. + +### Reliability + +Critical failures are closed on-chain: invalid proofs/receipts fail, claim replays are blocked, and malformed/wrong-state claims are rejected by the program. Evidence is captured from committed `getTransaction` responses instead of ad-hoc outputs. + +### Performance + +Observed testnet CU for the public execution path is within budget (`504401` per witness-private claim), and 20 claims completed across 2 distributions with 27 total included transactions in the committed snapshot. + +### Supportability + +Packaging and publication are complete with deterministic release artifacts, explicit CI evidence, clear docs, and version-pinned references to avoid reviewer ambiguity between iterations. + +## Terms & Conditions + +- [Terms & Conditions](https://github.com/logos-co/lambda-prize/blob/master/TERMS.md) From 51037d8febe39760761b6f0aca6d86d1ef49e14f Mon Sep 17 00:00:00 2001 From: Timidan Date: Tue, 18 Aug 2026 15:48:28 +0100 Subject: [PATCH 12/15] Align LP-0003 structure with template style and add supporting materials --- solutions/LP-0003.md | 70 ++++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md index 4b7cfad9..0bf4189d 100644 --- a/solutions/LP-0003.md +++ b/solutions/LP-0003.md @@ -4,58 +4,78 @@ ## Summary -DistributionX implements a privacy-focused allowlist airdrop for LEZ where the distributor commits an encrypted eligibility bundle and eligible recipients claim privately with witness data kept out of the public transaction path. +DistributionX is a private allowlist airdrop flow for Logos Execution Zone (LEZ). A distributor generates an encrypted allowlist bundle, commits a Merkle root chain-side, funds a vault, and enables recipients to claim with a privacy-preserving zero-knowledge proof. -The live on-chain flow uses the LEZ `claim_ppe` path with `RISC0_DEV_MODE=0`, which composes and submits the private proof path intended by the prize's privacy requirement. +The privacy objective is to avoid exposing the eligible recipient row, row salt, claim signature, and Merkle path in public execution. DistributionX uses `claim_ppe` via LEZ Privacy Preserving Transactions (`send_privacy_preserving_tx`) so the witness is private while keeping the on-chain transaction verifiable. ## Repository -- Repository: [https://github.com/Timidan/dist-x](https://github.com/Timidan/dist-x) -- Pinned commit: [fb4587767d71dca9074908ae3d563b3642c1a583](https://github.com/Timidan/dist-x/commit/fb4587767d71dca9074908ae3d563b3642c1a583) -- Release: [v0.1.0](https://github.com/Timidan/dist-x/releases/tag/v0.1.0) -- Demo video: https://youtu.be/w0TL22pnkqo -- CI evidence: https://github.com/Timidan/dist-x/actions/runs/31915269343 -- LEZ evidence index: [docs/TESTNET_EVIDENCE.md](https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/TESTNET_EVIDENCE.md) -- LEZ evidence manifest: [docs/testnet-evidence/v0.1.0/manifest.json](https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/testnet-evidence/v0.1.0/manifest.json) +- **Repo:** https://github.com/Timidan/dist-x +- **Pinned commit:** [fb4587767d71dca9074908ae3d563b3642c1a583](https://github.com/Timidan/dist-x/commit/fb4587767d71dca9074908ae3d563b3642c1a583) +- **Release:** [v0.1.0](https://github.com/Timidan/dist-x/releases/tag/v0.1.0) +- **Demo video:** https://youtu.be/w0TL22pnkqo ## Approach -1. **Distribution setup:** Distributor CSV is sanitized, encrypted to recipient keys, and committed as a Merkle root with bucketed metadata. -2. **Claim path:** Eligible recipients produce a witness locally from their row, post the composite private proof through `claim_ppe`, and settlement is written to a LEZ-native private destination. -3. **Integrity guarantees:** Nullifier tracking prevents duplicate claims and on-chain state checks enforce claim limits. -4. **Readiness package:** README, architecture docs, CI, and release artifacts were updated to match this exact run and pinned commit. +DistributionX is implemented as a Rust/Logos program + Basecamp client + SDK module with these steps: + +1. **Bundle ingest and commitment** + - The distributor reads CSV rows and validates input structure. + - Recipient rows are encrypted and committed as Merkle-based on-chain state (buckets and metadata). +2. **Claim generation** + - A claimant reconstructs their row locally, builds witness data, and generates a Risc0 proof. + - Claim is submitted with `RISC0_DEV_MODE=0`. +3. **Privacy-preserving execution path** + - The proof is transported in `claim_ppe` through LEZ PPE, which does not include witness fields in public tx instruction/data. +4. **Settlement and uniqueness** + - Public-execution state transitions use `claim_ppe` + settlement flow. + - Nullifier PDAs prevent duplicate claims per eligibility row. +5. **Submission consistency** + - All artifacts are pinned and published: evidence logs, manifests, README, bench report, architecture, and release packages. ## Success Criteria Checklist -- [x] Private allowlist/eligibility commitment flow implemented and demoed. -- [x] Witness-private claim path (`claim_ppe`) demonstrated on live LEZ testnet. -- [x] Two on-chain distributions and 20 `claim_ppe` claims captured and confirmed. -- [x] Nullifier uniqueness enforced; duplicate claims are rejected by program state. -- [x] Real proof mode verified with `RISC0_DEV_MODE=0`. -- [x] Supporting materials pinned: README, writeup, reports, architecture, CI, and manifest. +- [x] LEZ testnet deployment completed with 2 distributions and 20 live `claim_ppe` claims. +- [x] Privacy-preserving claim path shown on LEZ public testnet endpoint. +- [x] Program is pinned as: + - Program ID: `4bf08c88a91871ecf69ff08af42591a597c51142cbc1f9c6fbbb7d2e888d9ee3` + - Deploy tx: [2186b9ba9e95e4926f2800e88b5d0653bda3d1669414f0a19c6daf0798576181](https://explorer.testnet.lez.logos.co/transaction/2186b9ba9e95e4926f2800e88b5d0653bda3d1669414f0a19c6daf0798576181) +- [x] Testnet evidence captured as machine-readable proof in one pinned evidence set. +- [x] CI run includes script/rust/logos checks and is linked for verification. +- [x] Reviewer-facing release and docs are pinned to one revision only. ## FURPS Self-Assessment ### Functionality -DistributionX delivers the core end-to-end airdrop workflow: encrypted distributor bundle ingestion, on-chain deployment, privacy-preserving claims via `claim_ppe`, nullifier-based replay prevention, and recipient settlement. +The implementation covers distribution creation, encrypted eligibility, on-chain initialization, private proof submission, claim verification, nullifier enforcement, and settlement. The flow from bundle to settlement is runnable and has been executed on live LEZ testnet. ### Usability -CLI and Basecamp paths are practical for review and demos: setup/deploy/claim commands are scripted, evidence has machine-readable artifacts, and fixtures support repeatable runs for reviewers. +The project has CLI + Basecamp entry points and includes scripts for setup, proof proving, and local simulation. Documentation and fixtures provide a reproducible reviewer path without hand-crafted test data. ### Reliability -Critical failures are closed on-chain: invalid proofs/receipts fail, claim replays are blocked, and malformed/wrong-state claims are rejected by the program. Evidence is captured from committed `getTransaction` responses instead of ad-hoc outputs. +The on-chain program enforces state transitions and uniqueness checks; failed proofs and invalid claims are rejected by program state rules. Evidence is committed from `getTransaction` snapshots for deterministic review. ### Performance -Observed testnet CU for the public execution path is within budget (`504401` per witness-private claim), and 20 claims completed across 2 distributions with 27 total included transactions in the committed snapshot. +Public execution resource usage is within the on-chain cap for the witness-preserving flow (`504401` CU per claim observed in current run). The current run includes 20 confirmed claims across two distributions and a fixed snapshot with included tx count (`27`) for the full flow. ### Supportability -Packaging and publication are complete with deterministic release artifacts, explicit CI evidence, clear docs, and version-pinned references to avoid reviewer ambiguity between iterations. +The package is release-ready (`v0.1.0`) with explicit manifest, run artifacts, architecture docs, and support script coverage for deploy/claim/package flows. The solution file and PR description are pinned to a single commit. + +## Supporting Materials + +- Evidence index: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/TESTNET_EVIDENCE.md +- Evidence manifest: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/testnet-evidence/v0.1.0/manifest.json +- RPC snapshots: https://github.com/Timidan/dist-x/tree/fb4587767d71dca9074908ae3d563b3642c1a583/docs/testnet-evidence/v0.1.0/rpc +- README: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/README.md +- Bench report: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/bench/REPORT.md +- Architecture: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/DistributionX.system-architecture.excalidraw +- Terms: https://github.com/logos-co/lambda-prize/blob/master/TERMS.md ## Terms & Conditions -- [Terms & Conditions](https://github.com/logos-co/lambda-prize/blob/master/TERMS.md) +By submitting this solution, I confirm that I have read and agree to the [Terms & Conditions](../TERMS.md). From 289cb1177c011b8afd3c6655e9ba7d84862252d9 Mon Sep 17 00:00:00 2001 From: Timidan Date: Thu, 20 Aug 2026 04:40:45 +0100 Subject: [PATCH 13/15] Clarify DistributionX submission lineage and claim flow --- solutions/LP-0003.md | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md index 0bf4189d..aad79906 100644 --- a/solutions/LP-0003.md +++ b/solutions/LP-0003.md @@ -11,9 +11,14 @@ The privacy objective is to avoid exposing the eligible recipient row, row salt, ## Repository - **Repo:** https://github.com/Timidan/dist-x -- **Pinned commit:** [fb4587767d71dca9074908ae3d563b3642c1a583](https://github.com/Timidan/dist-x/commit/fb4587767d71dca9074908ae3d563b3642c1a583) +- **Current source and documentation:** [27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88](https://github.com/Timidan/dist-x/commit/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88) - **Release:** [v0.1.0](https://github.com/Timidan/dist-x/releases/tag/v0.1.0) +- **Release source:** [74f81ab9ee74ba533d3a8fa01cba9f67153f6385](https://github.com/Timidan/dist-x/commit/74f81ab9ee74ba533d3a8fa01cba9f67153f6385) +- **Verifier source used by the evidence run:** [fb61565fd7f8d3409ff65f7e2f4a7297cd56078a](https://github.com/Timidan/dist-x/commit/fb61565fd7f8d3409ff65f7e2f4a7297cd56078a) - **Demo video:** https://youtu.be/w0TL22pnkqo +- **CI run:** https://github.com/Timidan/dist-x/actions/runs/31915269343 + +The `v0.1.0` release artifacts were built from `74f81ab9...`. The repository has since advanced to `27ca9a9...` for documentation and reviewer-facing clarifications; the evidence manifest records `fb61565...` as the verifier source used for the captured testnet run. ## Approach @@ -28,7 +33,8 @@ DistributionX is implemented as a Rust/Logos program + Basecamp client + SDK mod 3. **Privacy-preserving execution path** - The proof is transported in `claim_ppe` through LEZ PPE, which does not include witness fields in public tx instruction/data. 4. **Settlement and uniqueness** - - Public-execution state transitions use `claim_ppe` + settlement flow. + - The submitted native LEZ flow completes the claim through `claim_ppe`. + - Optional custom-token settlement is a separate follow-up transaction and was not used in the submitted testnet run. - Nullifier PDAs prevent duplicate claims per eligibility row. 5. **Submission consistency** - All artifacts are pinned and published: evidence logs, manifests, README, bench report, architecture, and release packages. @@ -42,17 +48,17 @@ DistributionX is implemented as a Rust/Logos program + Basecamp client + SDK mod - Deploy tx: [2186b9ba9e95e4926f2800e88b5d0653bda3d1669414f0a19c6daf0798576181](https://explorer.testnet.lez.logos.co/transaction/2186b9ba9e95e4926f2800e88b5d0653bda3d1669414f0a19c6daf0798576181) - [x] Testnet evidence captured as machine-readable proof in one pinned evidence set. - [x] CI run includes script/rust/logos checks and is linked for verification. -- [x] Reviewer-facing release and docs are pinned to one revision only. +- [x] Release, verifier, and current documentation revisions are identified separately and pinned explicitly. ## FURPS Self-Assessment ### Functionality -The implementation covers distribution creation, encrypted eligibility, on-chain initialization, private proof submission, claim verification, nullifier enforcement, and settlement. The flow from bundle to settlement is runnable and has been executed on live LEZ testnet. +The implementation covers distribution creation, encrypted eligibility, on-chain initialization, private proof submission, native `claim_ppe` completion, and nullifier enforcement. Optional custom-token settlement is separate from the submitted native claim flow and was not exercised in this evidence run. ### Usability -The project has CLI + Basecamp entry points and includes scripts for setup, proof proving, and local simulation. Documentation and fixtures provide a reproducible reviewer path without hand-crafted test data. +The project has CLI + Basecamp entry points and includes scripts for setup, proof proving, and local simulation. Documentation and fixtures provide a reproducible reviewer path without hand-crafted test data. The helper scripts require Linux and Bash 4.x or newer; they are not portable POSIX `sh` scripts. ### Reliability @@ -64,16 +70,17 @@ Public execution resource usage is within the on-chain cap for the witness-prese ### Supportability -The package is release-ready (`v0.1.0`) with explicit manifest, run artifacts, architecture docs, and support script coverage for deploy/claim/package flows. The solution file and PR description are pinned to a single commit. +The package is release-ready (`v0.1.0`) with an explicit manifest, run artifacts, architecture docs, and support script coverage for deploy/claim/package flows. Release, verifier, and current documentation revisions are pinned separately so their lineage is unambiguous. ## Supporting Materials -- Evidence index: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/TESTNET_EVIDENCE.md -- Evidence manifest: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/testnet-evidence/v0.1.0/manifest.json -- RPC snapshots: https://github.com/Timidan/dist-x/tree/fb4587767d71dca9074908ae3d563b3642c1a583/docs/testnet-evidence/v0.1.0/rpc -- README: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/README.md -- Bench report: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/docs/bench/REPORT.md -- Architecture: https://github.com/Timidan/dist-x/blob/fb4587767d71dca9074908ae3d563b3642c1a583/DistributionX.system-architecture.excalidraw +- Evidence index: https://github.com/Timidan/dist-x/blob/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88/docs/TESTNET_EVIDENCE.md +- Evidence manifest: https://github.com/Timidan/dist-x/blob/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88/docs/testnet-evidence/v0.1.0/manifest.json +- RPC snapshots: https://github.com/Timidan/dist-x/tree/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88/docs/testnet-evidence/v0.1.0/rpc +- README: https://github.com/Timidan/dist-x/blob/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88/README.md +- Writeup: https://github.com/Timidan/dist-x/blob/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88/docs/WRITEUP.md +- Bench report: https://github.com/Timidan/dist-x/blob/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88/docs/bench/REPORT.md +- Architecture: https://github.com/Timidan/dist-x/blob/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88/DistributionX.system-architecture.excalidraw - Terms: https://github.com/logos-co/lambda-prize/blob/master/TERMS.md ## Terms & Conditions From a17518c8227fab09c099eb6944ce93e7493cb6d7 Mon Sep 17 00:00:00 2001 From: Timidan Date: Mon, 7 Sep 2026 22:31:20 +0100 Subject: [PATCH 14/15] Complete LP-0003 criteria evidence --- solutions/LP-0003.md | 77 ++++++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md index aad79906..5e185dff 100644 --- a/solutions/LP-0003.md +++ b/solutions/LP-0003.md @@ -11,14 +11,14 @@ The privacy objective is to avoid exposing the eligible recipient row, row salt, ## Repository - **Repo:** https://github.com/Timidan/dist-x -- **Current source and documentation:** [27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88](https://github.com/Timidan/dist-x/commit/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88) +- **Current source and documentation:** [0d80c23b8435f487158b0712a7f153f57bd82737](https://github.com/Timidan/dist-x/commit/0d80c23b8435f487158b0712a7f153f57bd82737) - **Release:** [v0.1.0](https://github.com/Timidan/dist-x/releases/tag/v0.1.0) - **Release source:** [74f81ab9ee74ba533d3a8fa01cba9f67153f6385](https://github.com/Timidan/dist-x/commit/74f81ab9ee74ba533d3a8fa01cba9f67153f6385) - **Verifier source used by the evidence run:** [fb61565fd7f8d3409ff65f7e2f4a7297cd56078a](https://github.com/Timidan/dist-x/commit/fb61565fd7f8d3409ff65f7e2f4a7297cd56078a) - **Demo video:** https://youtu.be/w0TL22pnkqo -- **CI run:** https://github.com/Timidan/dist-x/actions/runs/31915269343 +- **Default-branch CI run:** https://github.com/Timidan/dist-x/actions/runs/34163202249 -The `v0.1.0` release artifacts were built from `74f81ab9...`. The repository has since advanced to `27ca9a9...` for documentation and reviewer-facing clarifications; the evidence manifest records `fb61565...` as the verifier source used for the captured testnet run. +The `v0.1.0` release artifacts were built from `74f81ab9...`. The repository has since advanced to `0d80c23...` for documentation and CI maintenance; the evidence manifest records `fb61565...` as the verifier source used for the captured testnet run. ## Approach @@ -41,13 +41,40 @@ DistributionX is implemented as a Rust/Logos program + Basecamp client + SDK mod ## Success Criteria Checklist -- [x] LEZ testnet deployment completed with 2 distributions and 20 live `claim_ppe` claims. -- [x] Privacy-preserving claim path shown on LEZ public testnet endpoint. -- [x] Program is pinned as: - - Program ID: `4bf08c88a91871ecf69ff08af42591a597c51142cbc1f9c6fbbb7d2e888d9ee3` - - Deploy tx: [2186b9ba9e95e4926f2800e88b5d0653bda3d1669414f0a19c6daf0798576181](https://explorer.testnet.lez.logos.co/transaction/2186b9ba9e95e4926f2800e88b5d0653bda3d1669414f0a19c6daf0798576181) -- [x] Testnet evidence captured as machine-readable proof in one pinned evidence set. -- [x] CI run includes script/rust/logos checks and is linked for verification. +**Functionality** + +- [x] The distributor commits only a fixed-depth Merkle root and public amount-bucket table on-chain; recipient rows are delivered in per-recipient encrypted bundles. +- [x] An eligible recipient submits `claim_ppe` through LEZ privacy-preserving execution without placing the address, salt, signature, or Merkle path in the public transaction message. +- [x] A nullifier PDA keyed by distribution and row salt prevents a second claim and returns `E_ALREADY_CLAIMED` (code 6). +- [x] Observer unlinkability is defined within an amount bucket: observers see the root, bucket id, nullifier, destination commitment, timing, and transaction metadata, but not the private witness. Singleton or small buckets, timing correlation, wallet-funded gas, or out-of-band data can reduce that anonymity set. +- [x] The distributor knows the original CSV and salts and can retain a nullifier-to-row mapping. The local adapter, claimant device, wallet seed, and private `claim.tx` are trusted; DistributionX protects against public-chain observers, not a malicious distributor or compromised claimant host. +- [x] The reference integration is deployed on LEZ testnet as program `4bf08c88a91871ecf69ff08af42591a597c51142cbc1f9c6fbbb7d2e888d9ee3` ([deployment transaction](https://explorer.testnet.lez.logos.co/transaction/2186b9ba9e95e4926f2800e88b5d0653bda3d1669414f0a19c6daf0798576181)). +- [x] Two native-token distributions completed ten included `claim_ppe` transactions each; all 27 approved writes and capture-time RPC responses are committed. +- [x] The public repository includes end-to-end instructions, architecture, privacy model, benchmarks, testnet evidence, and reproducible scripts. + +**Usability** + +- [x] `crates/distributionx-client` provides the Rust client SDK, and `distributionx_client_module` exposes the workflow as a Logos core module. +- [x] The Basecamp GUI has local build/launch instructions and two load-tested release assets: `distributionx-client.lgx` and `DistributionX-ui.lgx`. +- [x] `idl/distributionx.idl.json` is the SPEL IDL; generated Rust and C clients are checked in CI. + +**Reliability** + +- [x] CLI proof failures return stable error identifiers; Basecamp translates them into a visible failure message and states that the claim was not submitted, so the claimant can correct the setup and retry. +- [x] Claim validation, native payout, and nullifier creation are atomic. A failed proof, rejected claim, insufficient vault, or failed transfer does not persist the nullifier. +- [x] The SPEL IDL documents deterministic program codes 1-17, including `E_BAD_PROOF` (5), `E_ALREADY_CLAIMED` (6), `E_VAULT_INSUFFICIENT` (7), and `E_TRANSFER_FAILED` (8). + +**Performance** + +- [x] LEZ v0.2.4 operation costs and proof times are documented below and in the machine-readable benchmark capture. Public-testnet RPC proves inclusion but does not expose per-operation CU, so the CU values come from the pinned standalone LEZ runtime. + +**Supportability** + +- [x] The pinned program and two distributions are deployed on LEZ public testnet with committed transaction evidence. +- [x] CI runs Rust, Logos module, Basecamp package, installed-LGX, and standalone LEZ `RISC0_DEV_MODE=0` checks on the default branch. +- [x] The README covers deployment, program addresses, CLI usage, Basecamp launch, packaging, and evidence capture. +- [x] `scripts/e2e.sh ci-localnet` is the reproducible real-proof standalone demo and rejects a duplicate claim. +- [x] The narrated demo video shows the pinned revision, real proof generation, successful claim, and duplicate rejection. - [x] Release, verifier, and current documentation revisions are identified separately and pinned explicitly. ## FURPS Self-Assessment @@ -58,15 +85,24 @@ The implementation covers distribution creation, encrypted eligibility, on-chain ### Usability -The project has CLI + Basecamp entry points and includes scripts for setup, proof proving, and local simulation. Documentation and fixtures provide a reproducible reviewer path without hand-crafted test data. The helper scripts require Linux and Bash 4.x or newer; they are not portable POSIX `sh` scripts. +Claimants can use the CLI or the Basecamp GUI; module builders can use the Rust client crate or Logos core module. The release includes both LGX packages, and the SPEL IDL plus generated clients are committed. Helper scripts require Linux or macOS and Bash 4.x or newer; they are not portable POSIX `sh` scripts. ### Reliability -The on-chain program enforces state transitions and uniqueness checks; failed proofs and invalid claims are rejected by program state rules. Evidence is committed from `getTransaction` snapshots for deterministic review. +The UI keeps proof generation and submission as separate states, displays actionable errors, and never labels a failed backend response as success. On-chain validation, native payout, and nullifier creation are atomic, so rejection leaves the row retryable. Stable program codes are documented in the SPEL IDL and tested, including bad proof, duplicate claim, insufficient vault, and transfer failure. ### Performance -Public execution resource usage is within the on-chain cap for the witness-preserving flow (`504401` CU per claim observed in current run). The current run includes 20 confirmed claims across two distributions and a fixed snapshot with included tx count (`27`) for the full flow. +The pinned standalone LEZ v0.2.4 measurements are: + +| Operation | Compute units | Measurement source | +|---|---:|---| +| `init_airdrop` | 442,994 | Sequencer transaction execution | +| `fund` | 447,471 | Sequencer transaction execution | +| `claim_ppe` | 5,154,242 | Risc0 `SessionInfo::cycles` for the exact funded pre-state | +| `close` | 504,505 | Sequencer transaction execution | + +For the reviewer fixture, real eligibility proving took 19m20s and real PPE claim composition took 26m12s on the recorded CPU with `RISC0_DEV_MODE=0`. The separate 30-claimant release-build benchmark took 39-42 minutes. Public-testnet RPC does not expose a distinct CU field, so these costs are reproducible pinned-runtime measurements rather than claims about public-testnet RPC data. ### Supportability @@ -74,13 +110,14 @@ The package is release-ready (`v0.1.0`) with an explicit manifest, run artifacts ## Supporting Materials -- Evidence index: https://github.com/Timidan/dist-x/blob/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88/docs/TESTNET_EVIDENCE.md -- Evidence manifest: https://github.com/Timidan/dist-x/blob/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88/docs/testnet-evidence/v0.1.0/manifest.json -- RPC snapshots: https://github.com/Timidan/dist-x/tree/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88/docs/testnet-evidence/v0.1.0/rpc -- README: https://github.com/Timidan/dist-x/blob/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88/README.md -- Writeup: https://github.com/Timidan/dist-x/blob/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88/docs/WRITEUP.md -- Bench report: https://github.com/Timidan/dist-x/blob/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88/docs/bench/REPORT.md -- Architecture: https://github.com/Timidan/dist-x/blob/27ca9a9b54d69ec46ae7fa912a0e3eeee4029a88/DistributionX.system-architecture.excalidraw +- Evidence index: https://github.com/Timidan/dist-x/blob/0d80c23b8435f487158b0712a7f153f57bd82737/docs/TESTNET_EVIDENCE.md +- Evidence manifest: https://github.com/Timidan/dist-x/blob/0d80c23b8435f487158b0712a7f153f57bd82737/docs/testnet-evidence/v0.1.0/manifest.json +- RPC snapshots: https://github.com/Timidan/dist-x/tree/0d80c23b8435f487158b0712a7f153f57bd82737/docs/testnet-evidence/v0.1.0/rpc +- README: https://github.com/Timidan/dist-x/blob/0d80c23b8435f487158b0712a7f153f57bd82737/README.md +- Writeup: https://github.com/Timidan/dist-x/blob/0d80c23b8435f487158b0712a7f153f57bd82737/docs/WRITEUP.md +- Bench report: https://github.com/Timidan/dist-x/blob/0d80c23b8435f487158b0712a7f153f57bd82737/docs/bench/REPORT.md +- CU capture: https://github.com/Timidan/dist-x/blob/0d80c23b8435f487158b0712a7f153f57bd82737/docs/bench/lez-v0.2.4-cu.json +- Architecture: https://github.com/Timidan/dist-x/blob/0d80c23b8435f487158b0712a7f153f57bd82737/DistributionX.system-architecture.excalidraw - Terms: https://github.com/logos-co/lambda-prize/blob/master/TERMS.md ## Terms & Conditions From bbee09a81cdd47cfba5472ca554841089de048df Mon Sep 17 00:00:00 2001 From: Timidan Date: Mon, 7 Sep 2026 22:41:20 +0100 Subject: [PATCH 15/15] Pin LP-0003 to corrected default-branch run --- solutions/LP-0003.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/solutions/LP-0003.md b/solutions/LP-0003.md index 5e185dff..29f5275c 100644 --- a/solutions/LP-0003.md +++ b/solutions/LP-0003.md @@ -11,14 +11,14 @@ The privacy objective is to avoid exposing the eligible recipient row, row salt, ## Repository - **Repo:** https://github.com/Timidan/dist-x -- **Current source and documentation:** [0d80c23b8435f487158b0712a7f153f57bd82737](https://github.com/Timidan/dist-x/commit/0d80c23b8435f487158b0712a7f153f57bd82737) +- **Current source and documentation:** [c629c6c7bbd4b3aa56d4357f60817d49438e044b](https://github.com/Timidan/dist-x/commit/c629c6c7bbd4b3aa56d4357f60817d49438e044b) - **Release:** [v0.1.0](https://github.com/Timidan/dist-x/releases/tag/v0.1.0) - **Release source:** [74f81ab9ee74ba533d3a8fa01cba9f67153f6385](https://github.com/Timidan/dist-x/commit/74f81ab9ee74ba533d3a8fa01cba9f67153f6385) - **Verifier source used by the evidence run:** [fb61565fd7f8d3409ff65f7e2f4a7297cd56078a](https://github.com/Timidan/dist-x/commit/fb61565fd7f8d3409ff65f7e2f4a7297cd56078a) - **Demo video:** https://youtu.be/w0TL22pnkqo -- **Default-branch CI run:** https://github.com/Timidan/dist-x/actions/runs/34163202249 +- **Default-branch CI run:** https://github.com/Timidan/dist-x/actions/runs/34163950395 -The `v0.1.0` release artifacts were built from `74f81ab9...`. The repository has since advanced to `0d80c23...` for documentation and CI maintenance; the evidence manifest records `fb61565...` as the verifier source used for the captured testnet run. +The `v0.1.0` release artifacts were built from `74f81ab9...`. The repository has since advanced to `c629c6c...` for documentation and CI maintenance; the evidence manifest records `fb61565...` as the verifier source used for the captured testnet run. ## Approach @@ -110,14 +110,14 @@ The package is release-ready (`v0.1.0`) with an explicit manifest, run artifacts ## Supporting Materials -- Evidence index: https://github.com/Timidan/dist-x/blob/0d80c23b8435f487158b0712a7f153f57bd82737/docs/TESTNET_EVIDENCE.md -- Evidence manifest: https://github.com/Timidan/dist-x/blob/0d80c23b8435f487158b0712a7f153f57bd82737/docs/testnet-evidence/v0.1.0/manifest.json -- RPC snapshots: https://github.com/Timidan/dist-x/tree/0d80c23b8435f487158b0712a7f153f57bd82737/docs/testnet-evidence/v0.1.0/rpc -- README: https://github.com/Timidan/dist-x/blob/0d80c23b8435f487158b0712a7f153f57bd82737/README.md -- Writeup: https://github.com/Timidan/dist-x/blob/0d80c23b8435f487158b0712a7f153f57bd82737/docs/WRITEUP.md -- Bench report: https://github.com/Timidan/dist-x/blob/0d80c23b8435f487158b0712a7f153f57bd82737/docs/bench/REPORT.md -- CU capture: https://github.com/Timidan/dist-x/blob/0d80c23b8435f487158b0712a7f153f57bd82737/docs/bench/lez-v0.2.4-cu.json -- Architecture: https://github.com/Timidan/dist-x/blob/0d80c23b8435f487158b0712a7f153f57bd82737/DistributionX.system-architecture.excalidraw +- Evidence index: https://github.com/Timidan/dist-x/blob/c629c6c7bbd4b3aa56d4357f60817d49438e044b/docs/TESTNET_EVIDENCE.md +- Evidence manifest: https://github.com/Timidan/dist-x/blob/c629c6c7bbd4b3aa56d4357f60817d49438e044b/docs/testnet-evidence/v0.1.0/manifest.json +- RPC snapshots: https://github.com/Timidan/dist-x/tree/c629c6c7bbd4b3aa56d4357f60817d49438e044b/docs/testnet-evidence/v0.1.0/rpc +- README: https://github.com/Timidan/dist-x/blob/c629c6c7bbd4b3aa56d4357f60817d49438e044b/README.md +- Writeup: https://github.com/Timidan/dist-x/blob/c629c6c7bbd4b3aa56d4357f60817d49438e044b/docs/WRITEUP.md +- Bench report: https://github.com/Timidan/dist-x/blob/c629c6c7bbd4b3aa56d4357f60817d49438e044b/docs/bench/REPORT.md +- CU capture: https://github.com/Timidan/dist-x/blob/c629c6c7bbd4b3aa56d4357f60817d49438e044b/docs/bench/lez-v0.2.4-cu.json +- Architecture: https://github.com/Timidan/dist-x/blob/c629c6c7bbd4b3aa56d4357f60817d49438e044b/DistributionX.system-architecture.excalidraw - Terms: https://github.com/logos-co/lambda-prize/blob/master/TERMS.md ## Terms & Conditions