Solution: LP-0002 Private M-of-N Multisig - #125
Open
edenbd1 wants to merge 5 commits into
Open
Conversation
✅ Validation passedA reviewer will assess against the prize criteria. Automated check. See solution template and TERMS. |
The video is recorded and linked, so the two places that still said it was outstanding are gone. The security write-up is presented by what the design defends against rather than by the order in which it was built; every property and every test it refers to is unchanged.
`lee/state_machine/src/program.rs` does not exist: the file is `program/mod.rs`, and the split predates the LEZ release this targets, so the path never resolved. `execution_state.rs:149` sat four lines above the `env::verify` it names; the call is at 153, and 149-155 brackets it with its comment. The executor suite is 25 adversarial rejections plus five honest controls, not 28 rejections.
The file said none of the seven transactions rendered on the block
explorer; that was true when written and is no longer. Re-measured on
2026-08-15 by rendering each /transaction/<hash> page in a headless
browser, with an impossible hash as the control:
- all seven render — the two approvals as Privacy-Preserving
Transaction, the two deploys as Program Deployment Transaction, the
other three as Public Transaction;
- the control still renders Transaction not found, so the positives
are distinguishable from the shell the WASM app serves for every URL;
- getTransaction returns a result for all seven on the RPC.
The explorer's front page was at block 8633 and the sequencer at 8676 on
the same pass — a 43-block lag, against 4351 vs 4496 when this was first
written. The indexing delay is kept in the file as history rather than as
a current claim, because it still applies to anything submitted recently.
… count Three numbers in this file were checked against docs/cu-costs.md in the linked repo on 2026-08-15 and two of them were wrong in the submission's favour, which is the worst direction to be wrong in. Proving time. The file led with 149 s and 154 s. cu-costs.md records those as the LEZ v0.2.0 local-sequencer measurement; the v0.2.4 run that actually produced the deployed lifecycle measured 440 s and 469 s on 2026-08-12. v0.2.4 is roughly three times more expensive than v0.2.0 and a second machine shows the same ratio (CI, 1264 s to 4033 s), so the old figure understated the current chain by about 3x. Now quoted at 440/469 with the version, the date and the contention it was measured under, and with the idle-laptop 437 s and the 935 s contended figure alongside so the number cannot be read as a best case. Cycle count. The file said approve costs 335,564 user cycles; cu-costs.md says 337,105. Re-ran the repo's own measurement, cargo test -p multisig-verifier-tests -- --ignored --nocapture, which reports 337,105 for approve (and 267,055 / 363,510 / 461,604 for execute at M=1/3/5, matching the doc). Corrected in both places. lifecycle.tsv. The file said the timings were written into it as though it were a committed artefact. It is not committed: deploy-and-run.sh writes it into WORK, which defaults to .testnet/, and .gitignore excludes /.testnet*/ deliberately because that directory also holds member keys. The claim now says what is true — the script writes it during the run, it is not committed, and the reviewable record is the table in cu-costs.md. Also dropped the stale note that the old estimate had been four times too pessimistic. That was true of v0.2.0 and is not true of v0.2.4, so it would have been a fourth wrong number.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A threshold multisig for LEZ where an approval is unlinkable to the member who
gave it — including to the other members — and the chain records only that the
threshold was met.
solutions/LP-0002.mdWhat is verified on chain
Each
approvedeclares aChainedCallto a LEZ-native membership program, sothe privacy circuit composes it with a real
env::verifyand the sequencerchecks the receipt against the node-pinned
PRIVACY_PRESERVING_CIRCUIT_ID. Themember set and the threshold are folded into the PDA address, so neither can be
invented nor lowered — a forged configuration resolves to an address nobody ever
created.
Transactions — a 2-of-3 lifecycle on the current public testnet
membership_lezfb8eb10f7f394286c109cb6502a1c95294180523f30d06f707fc087a589bea98multisig_verifier517efe12a0b592abe4d21a03246866b95c4379483e87af62fd9f26f7b8fe45ffcreate_multisig2930c1db4521b7c0b912278f4025e430704cfb9a7ebfcb5d22c374fd7ce85b70create_proposal68d5127e1e5570936f8d78e9a2da4d485562566cd8b7487a59322bf059406978approve(member A, privacy tx)41f5bb99346a0bef6aa0c69243473a554b84f0f0ad65e460bbb6890b11644942approve(member B, privacy tx)ae006465f5f945b8ba2666f28a5357d0a2aab4af05508c9c2811e0101d0ac649executeb43e46505f571e31d6051f7da43563db605b6a74b90c670da2d3582d53412ecdCheck any of them:
Every hash above links to its page on the block explorer, and all seven render there. The two privacy transactions show as
Privacy-Preserving Transaction— which is the part that matters: an approval is on the proving path, not the public one.The two deployment hashes are
SHA256(borsh(bytecode))of the binaries committedunder
artifacts/programs/, so they can be recomputed from the repositorywithout trusting this table.
The accounts, which are the stronger evidence
4wqJXoEhqqqYknt1s7gHcgBL6pkfwNJDfhbVVeAqwtnXE11Awng7j59dVft83VVrwftXp41roJPKY5QRMb45ZcoeDaG2Qan1ie5YhEpcti2LMCsvbkYi7WjWxnNKvxiqxi7BFMj5yL8cpcrQzN7xhENHC2vysTrNwbtokPbTYjr98rPtCpiuicNDii6uCeMXtjd1W6hek6Vq35HJ7k3mz1Q82Fui./scripts/verify-onchain.shreads all five and confirms the verifier programowns them. Each approval marker exists only because a membership proof was
verified on chain, and nothing in the pair names a member — read them and you
learn the threshold was met, and nothing about who met it.
On the explorer: all seven render. The explorer is a separate index and reaches a transaction later than the sequencer does — measured on this chain at about an hour and three quarters — so a hash submitted minutes ago shows "Transaction not found" there while
getTransactionalready returns it. That is an indexing delay, not a gap, and it affects anything recent by anyone.Correction. An earlier version of this description said the explorer was a WASM application that returned an identical shell for every
/transaction/<hash>URL, so that an indexed transaction and one that cannot exist were byte-identical overcurland a size or status comparison proved nothing. That was true whenscripts/check-explorer.pywas written — it is why the script drives a browser at all — and it is not true now: re-measured on 2026-08-15, the explorer server-side renders, so onecurlseparates the two cases. Ten seconds, no browser:scripts/check-explorer.pystill renders each page headless against that same impossible-hash control. It is now belt and braces on the rendered DOM rather than the only way to get an answer — and if that control ever renders as a found transaction it aborts rather than report verdicts against a baseline that would be meaningless.Note on versions
The testnet was reset onto a newer chain in August, so everything here was
rebuilt against LEZ v0.2.4 and redeployed. That is also why the repository
vendors SPEL: no published SPEL release builds against a current LEZ, and
vendor/spel/PATCH.mddocuments every change, how to reproduce the directory,and when it should be deleted.
Against the criteria
61 tests, CI green on Linux and macOS, including a workflow that runs the whole
lifecycle against a real standalone LEZ sequencer with
RISC0_DEV_MODE=0. CUcosts are measured rather than estimated, per instruction and per machine. The
Basecamp module ships as a
.lgxwith darwin-arm64 and linux-amd64 variants andwas verified loading in Basecamp 0.2.2, not merely built.
Happy to address anything in review.