Skip to content

Solution: LP-0002 — Private M-of-N Multisig for LEZ - #144

Open
pramadanif wants to merge 2 commits into
logos-co:masterfrom
pramadanif:solution/lp-0002
Open

Solution: LP-0002 — Private M-of-N Multisig for LEZ#144
pramadanif wants to merge 2 commits into
logos-co:masterfrom
pramadanif:solution/lp-0002

Conversation

@pramadanif

@pramadanif pramadanif commented Sep 9, 2026

Copy link
Copy Markdown

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-multisigmain @ 138c683, MIT OR Apache-2.0

Narrated demo video: https://youtu.be/7gRweVxWEL4 — narrated by me, not synthesised. Timestamped into the evidence rather than left to be hunted for:

./demo.sh starts — RISC0_DEV_MODE=0 banner, real standalone sequencer t=100
lifecycle completes — two anonymous approvals, execute at full M t=194
the Basecamp module reading the deployed multisig from chain t=246

demo.sh and verify-onchain.sh each 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:

# Action Transaction
1 Deploy membership fe3a65ee4127a821847514d0350df479c86cb9b6d14c399c5608b36dde333fdc
2 Deploy multisig ef9029b2a9d4ef8c261e02af21b9a099ceba510a58a05407ff4a60714bb08d4e
3 create_multisig — 2-of-3 007d9ff27063b39843af29443abbcd40923de9fc4a17d9963d0521b9217e8a13
4 create_proposal — transfer 60 aa14aa283a0cdb5de76fee512a24aff1da30e73a6ae88cc7079a621e2a3db1d3
5 approve — privacy-preserving, anonymous a3eaeb3a773f35a48935944ca1b15bed683265dbded90633c094e4ef56aa4f4b
6 approve — privacy-preserving, anonymous 2a283d3c8887ef552bf56f415bbd6b534a4424e0765b590f3b44f6f6215a0aaa
7 execute — threshold reached at full M d1a47fddeddbfebd1a387f52ac91ecaed43f8c20275d6fb82bf3acd2f460057e

Treasury 100 → 40, payee 0 → 60. ./scripts/verify-onchain.sh re-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.

execute carries 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-sequencer job runs the whole lifecycle against a real standalone LEZ sequencer with RISC0_DEV_MODE=0, on every push to main — 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, sha256 1d8b806dfabdc369d8c1546e42ff933a96194eb74f7d78fdb58260768597204d, type ui_qml, variant darwin-arm64. It installs, opens and reads the deployed multisig on camera. Two caveats stated rather than discovered: it ships one platform variant, and Basecamp needs QT_ENABLE_REGEXP_JIT=0 to open it — the host's own hardened runtime lacks the JIT entitlement its QML sandbox uses, reproduced and written up in docs/BUGS_FILED.md §8 together with the undocumented replica-factory plugin a ui_qml module has to ship.

CU cost, numeric: create_multisig 155,809 · create_proposal 257,625 · execute 315,293 · verify_approval 602,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.md records 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.

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.
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

⚠️ Warnings

Warnings (non-blocking):

  • ⚠️ Prize references a Logos mini-app; no module.json found.

ℹ️ Solution submission for LP-0002.
ℹ️ Checking repo: https://github.com/pramadanif/lp-0002-private-multisig


Automated check. See solution template and TERMS.

The IDL is now at artifacts/multisig.idl.json in the linked repository, the name
.github/scripts/validate-submission.sh globs for.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant