Solution: LP-0002 — Private M-of-N Multisig for LEZ - #144
Open
pramadanif wants to merge 2 commits into
Open
Conversation
Shielded members approve without revealing which member approved. The chain records a threshold and nullifiers, never identities. Full lifecycle live on the LEZ public testnet, all seven transactions rendering on the explorer; verify-onchain.sh re-reads the whole claim from public data alone. Narrated demo video with timestamps into proof generation and the Basecamp module. CI runs the same lifecycle against a real standalone sequencer with RISC0_DEV_MODE=0 on every push.
|
The IDL is now at artifacts/multisig.idl.json in the linked repository, the name .github/scripts/validate-submission.sh globs for.
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.
The full packet is
solutions/LP-0002.md, the single file this PR adds — readable in Files changed or rendered. Headline evidence, so it can be checked without reading it first:Repository: https://github.com/pramadanif/lp-0002-private-multisig —
main@138c683, MIT OR Apache-2.0Narrated demo video: https://youtu.be/7gRweVxWEL4 — narrated by me, not synthesised. Timestamped into the evidence rather than left to be hunted for:
./demo.shstarts —RISC0_DEV_MODE=0banner, real standalone sequencerdemo.shandverify-onchain.sheach print the commit in their own banner, so the recording and the solution file name the same commit and you can check that rather than take it on trust.Public-testnet lifecycle — all seven transactions render on the explorer:
membershipfe3a65ee4127a821847514d0350df479c86cb9b6d14c399c5608b36dde333fdcmultisigef9029b2a9d4ef8c261e02af21b9a099ceba510a58a05407ff4a60714bb08d4ecreate_multisig— 2-of-3007d9ff27063b39843af29443abbcd40923de9fc4a17d9963d0521b9217e8a13create_proposal— transfer 60aa14aa283a0cdb5de76fee512a24aff1da30e73a6ae88cc7079a621e2a3db1d3approve— privacy-preserving, anonymousa3eaeb3a773f35a48935944ca1b15bed683265dbded90633c094e4ef56aa4f4bapprove— privacy-preserving, anonymous2a283d3c8887ef552bf56f415bbd6b534a4424e0765b590f3b44f6f6215a0aaaexecute— threshold reached at full Md1a47fddeddbfebd1a387f52ac91ecaed43f8c20275d6fb82bf3acd2f460057eTreasury 100 → 40, payee 0 → 60.
./scripts/verify-onchain.shre-derives the whole claim from public chain data alone and exits 0: the config account rehashes to its own address, names the deployed verifier, the threshold was met at full M rather than a lowered tier, every nullifier is distinct, and the account holding the 60 is the recipient the proposal named — read out of the proposal account, never from an argument.What the chain does not record: any member identity. The proposal holds a count and two nullifiers. A nullifier is keyed to the member's secret, not to an account id, so a member cannot vote twice from another of their own addresses either.
executecarries no proof, deliberately. By the time it runs the threshold is already a fact on chain — a set of distinct, proof-backed nullifiers. Anyone may execute, including a non-member, and that is what keeps execution unlinkable to any approver. What stops a stranger redirecting the funds is that execute pins both ends: the funds leave the multisig's own account and the recipient must be the one the proposal named. The reasoning is set out in full in the solution file, since a previous submission for this prize was closed partly for an execute transaction with no proof and the difference has to be stated rather than hoped past.CI: the
e2e-sequencerjob runs the whole lifecycle against a real standalone LEZ sequencer withRISC0_DEV_MODE=0, on every push tomain— not on cron, not path-filtered. Green runs: 34302452494 (all eleven jobs, e2e 3 h 40 m, two 96-minute proofs) and 34275830322 (2 h 54 m).Basecamp:
app/private_multisig.lgx, 2,653,306 bytes, sha2561d8b806dfabdc369d8c1546e42ff933a96194eb74f7d78fdb58260768597204d, typeui_qml, variantdarwin-arm64. It installs, opens and reads the deployed multisig on camera. Two caveats stated rather than discovered: it ships one platform variant, and Basecamp needsQT_ENABLE_REGEXP_JIT=0to open it — the host's own hardened runtime lacks the JIT entitlement its QML sandbox uses, reproduced and written up indocs/BUGS_FILED.md§8 together with the undocumented replica-factory plugin aui_qmlmodule has to ship.CU cost, numeric:
create_multisig155,809 ·create_proposal257,625 ·execute315,293 ·verify_approval602,662. Client proving: 115.97 s standalone, ≈19 min 26 s composed at 8.74 GB peak.What this does not do is a first-class section of the solution file, not a footnote — it is unaudited, the member set is fixed at creation, the GUI deliberately cannot submit approvals because the witness is a spending key, and
docs/tried-failed.mdrecords what I got wrong, including shipping a version where a member's spending key was recoverable from the guest journal and a first test that missed it because it scanned raw bytes while risc0 word-encodes each one.