Solution: LP-0002 Private M-of-N Multisig - #125
Closed
edenbd1 wants to merge 33 commits into
Closed
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 was referenced Aug 20, 2026
Closed
This document lives in lambda-prize/solutions/, so a link written as docs/DEPLOYMENT.md resolves there and 404s. Eight of its nine repository links did, including every "here is how to check this" pointer — the worst place to lose a reader. Rewritten to absolute URLs into edenbd1/lp-0002-private-multisig, which is what the merged LP-0005 does (23 absolute links, one relative: ../TERMS.md, the only path that genuinely lives in this repository and the only one left alone here). Each URL was fetched before it was written, blob then tree, and every one of the nine distinct targets answers 200.
…ckable The criterion named three PDAs, said they were owned by the verifier, and told the reader to re-verify with ./scripts/verify-onchain.sh. Read as a first-time reviewer with nothing but a clone, both halves needed work. The explorer has an account view and renders all three. Each shows Program Owner 7AyJ7x4DuAa58ALGqLXYwqdhEvQLCz5A2GFdcDrwyzUZ, which base58-decodes to 5bb40082…a5966f82 — exactly what "spel program-id" prints for the verifier binary committed in the repository. That is the entire claim, checkable in a browser, and this document offered no link to it. It does now. And verify-onchain.sh needs the working directory a lifecycle run leaves behind. From a clean clone it says so and exits 1 — correct behaviour, wrong instruction: the criterion pointed a reviewer at the one command they cannot run. A clone plus ./scripts/demo.sh does the same check from public data, and that is what the criterion names now.
Open
6 tasks
The paragraph on transaction paths ended by gesturing at other people's outcomes, which is not evidence and is not this submission's to cite. The argument stands on its own and is stronger for it: on the public path the sequencer re-executes the program host-side, so the approval logic runs in the clear and there is neither a proof nor any privacy. That is a fact about the runtime, checkable at the file and lines already quoted.
…therwise Three things in here described a deployment that no longer exists. The reference-integration section argued that performing the transfer was deliberately out of scope: the multisig proved a threshold, wrote an execution marker, and left the transfer to a caller. That argument was honest and the primitive is still general, but the criterion names a treasury transfer as its example, and a treasury transfer is a better answer than an essay about one. The multisig now owns a treasury, and `execute` moves value out of it into a recipient held by the native transfer program — so what the gate releases is spendable rather than stranded. The balance falling from 2 to 1 is a fact a reviewer reads off the chain in one call. The account addresses were the previous verifier's. A program's identity on LEZ is its ImageID and every PDA derives from it, so redeploying moved all six. The six current ones are named, with the program owner that base58-decodes to the ImageID `spel program-id` computes from the binary committed here. And the explorer paragraph still claimed the site was a WASM shell that made a size comparison meaningless. That was true when the check script was written and is why it drives a browser; it stopped being true on 2026-08-15. A document that keeps a superseded measurement without dating it is asking to be trusted on the wrong one. Test count corrected from 61 to the 97 the workspace now runs.
…ich video shows what Twenty-three links pointed at `main`, so a reviewer reading this later would have been reading whatever `main` had become rather than the version under review. Every one now names the commit, and every path behind them was checked to exist there. The header also names the CI run that went green on that commit — "CI is green" is a claim about a branch at a moment, a run id is a thing anyone can open — and it separates the two videos. The ten-minute walkthrough was recorded before the 2026-08-24 redeploy, so every identifier it shows on screen is superseded; a short reading of the current deployment is attached to the pull request. Leaving the older one unlabelled invited a reviewer to check a hash from the screen against a chain that no longer has it.
Inside a `- [x] …` item the content column is 2, so a new paragraph indented six spaces sits four beyond it and GitHub renders it as an indented code block. Ten blocks were caught that way — the whole explanation of what the threshold gates, the treasury's balance, the explorer measurement, the Basecamp load test — each displayed as monospace text in a horizontally scrolling box instead of as prose. Re-indented to two spaces. Not a word changed; this is what the document already said, shown as it was meant to be read.
…iewed commit The header named only the host suite. The end-to-end job, which builds LEZ at a pinned revision, starts a standalone sequencer and drives the whole 2-of-3 lifecycle against it with RISC0_DEV_MODE at zero, finished green on the same commit in two hours and forty-eight minutes. A run id is checkable; a claim that the lifecycle works is not.
It was described as a fifty-second silent reading because that is what it was when the line was written. It now carries a voice track, and it runs forty-seven seconds. A duration in a submission is a small claim, but it is a checkable one.
edenbd1
force-pushed
the
lp-0002-submission
branch
2 times, most recently
from
August 24, 2026 19:02
0ad3376 to
469ff71
Compare
…he runs that go with the commit The 47-second film attached to the pull request is evidence a reviewer has to watch. Its narration is now committed as `recordings/lp-0002-threshold-moves-value.srt`, so it can be read instead, and `scripts/check-transcript.py` ties that text to this film rather than asserting it belongs: cues ordered and landing inside the film, and four anchors requiring the frame to show what the narration is talking about at the second it says it — where the line is "both approvals carry the privacy-preserving variant", the frame must show the variant column. The film shows `ad2267e` on screen and the reviewed commit is later, because a film's transcript can only be committed after the film exists. Everything between the two is documentation: the transcript, the checker, an evidence document, and one correction to the explorer section of `docs/DEPLOYMENT.md`, which had illustrated a `curl` check with a hash from the superseded deployment and a size that holds only for transactions carrying a proof. `docs/recording-evidence.md` says so, and `git diff` confirms it. Every link is re-pinned to the commit that carries all of it, and the two runs named are the ones that went green on that commit.
edenbd1
force-pushed
the
lp-0002-submission
branch
from
August 24, 2026 19:33
469ff71 to
b8a307d
Compare
Collaborator
|
@edenbd1 please, ensure CI is green |
37,199 bytes to 23,090. Checked mechanically rather than by eye: all 21 criteria are byte-identical, all 14 on-chain identifiers are kept, and so is every measured figure. What it drops is elaboration that the linked docs already carry in full, plus two passages that said the same thing twice — the explorer's indexing delay, and the lifecycle itself. The error-code count said thirteen in three places. The repository documents twenty-two.
Contributor
Author
|
@weboko my bad, thanks for the ping. Green now. |
The verifier redeployed on 2026-08-25 folds a tier table into `config_hash`, so every PDA moved with it. Eight transactions in blocks 23028-23066, both approvals PrivacyPreserving, treasury 2 -> 1, and `execute` taking two approvals where the default threshold is three — the tier counted in proofs not generated rather than asserted in prose. Each row was read back off the sequencer: hash, variant and block number. Three figures here were wrong and are corrected against a fresh measurement, not carried: `approve` costs 541,207 user cycles rather than 337,105, `execute` scales at 81,046 per approval rather than ~48,600, and the share of the public budget is 3.12 % rather than 1.56 %. The tier work is what made them worse, and it also pushed `execute` at M=5 from one segment to two — the one figure that changes what an operator must plan for. Said plainly rather than left for a reviewer to recompute. Rotation is documented as what it is: covered by 23 tests and 13 mutations, and not yet exercised on chain, because the testnet wallet holds too few usable approver accounts to fund a second round of proofs. All 21 criteria are byte-identical to the previous revision, every reference now resolves at the pinned commit, and the file stays under the size where this repository's validator becomes unreliable.
The file was 23,965 bytes and the validator's section check is a SIGPIPE race against its own size: echo "$SOL_CONTENT" | grep -qF under pipefail reports a section that is present as missing. Measured in a container, this file went red on roughly one run in forty; at the size it is now, five in four thousand. The rotation is no longer only tested. The same 3-of-3 replaced its own configuration on the public testnet, so the lifecycle is thirteen transactions in blocks 23028-23554 with five on the proving path, not eight with two. Corrected: the proving times quoted for the deployed run were the re-measurement of a different one (484 s and 550 s, not 440 and 469); the error-code count read twenty-two where the guest declares twenty-seven; and the test inventory said eight suites where the README lists nine.
…sed them Each checkbox now carries the prize's own sentence, character for character, so an evaluator can tick this list against theirs without deciding whether two wordings mean the same thing. The evidence under each is unchanged. The paraphrases were dropping the qualifying clauses that are the thing being checked: "documents its full privacy model" lost the enumeration of what each party learns, the error-code criterion lost "for all invalid-proof and double-claim cases", and the video criterion lost everything after "the recording must show". Those clauses are where a pass/fail is decided.
The video criterion asks for terminal output including proof generation, to confirm RISC0_DEV_MODE=0 was active. Neither artefact satisfied it on the submitted version: the 47-second film shows the on-chain verifiers and proves nothing, and the ten-minute walkthrough that does show proving predates the redeploy, which the file admitted in the same breath. So the lifecycle was filmed again, at the reviewed commit and showing that hash on screen: scripts/e2e-local-sequencer.sh from nothing — a real standalone sequencer, both programs deployed onto a fresh chain, two real Risc0 approvals, execute, accounts read back — closing on PASS (2-of-3, RISC0_DEV_MODE=0). The session ran 25 min 52; only the stretch where the prover works and the screen does not change is sped up, its factor written across the picture.
The page named the script as the thing that reads the accounts back, without the two arguments it takes, so a reviewer who ran the name got a usage line instead of the decode. It now carries them, and the line as printed exits 0 from a clean clone. Its siblings were checked the same way: verify-onchain-lifecycle.sh and demo.sh both run as written, with no arguments, and exit 0.
The film that plays in the pull request compresses twenty-four minutes of proving into twenty seconds, with the factor written across the picture while it plays. That is honest but it is not verifiable: a reader has to take the caption's word for what the skipped stretch contained. The same session is now published unedited and unaccelerated, so the compression can be checked rather than trusted. Three films, one job each: the two-minute cut is the demo and plays inline; the full take is the evidence behind its one edit; the narrated walkthrough carries the commentary and says plainly that it predates this redeploy. That last line used to read "kept for its commentary rather than the identifiers it shows", which was defensive because it was then the only long-form artefact and it was stale. It is a normal choice now that a current one sits beside it.
…60b2f A reviewer opening the lifecycle sees five privacy transactions carrying a STARK each and one small public execute, and the obvious question — why does the transaction that moves the value carry no proof — was answered nowhere near where it is asked. It is now a paragraph directly under the transaction table: an approval is where membership is proven, execute presents marker accounts that only a verified proof could have created and re-derives each address, and putting a proof there would prove nothing new while publishing on the public path exactly what the approvals exist to keep off it. The write-up gave proposal_ref as H(multisig_id || proposal_id || action_hash). The code is H(PREFIX || multisig_id || config_hash || proposal_id || action_hash), and two paragraphs later this same page said proposal_ref derives through config_hash — so it contradicted itself about its own construction. Repinned to 2960b2f, where create_multisig, fund_treasury and create_proposal finally carry measured CU numbers instead of an argument, and where verify-onchain.sh names the tier that priced the transfer rather than closing on a line that reads like a threshold bypass. CI and a 2 h 48 standalone-sequencer e2e are both green on it.
This document said the five approvals carry the privacy-preserving variant, "which is the part a block explorer cannot show you". The explorer shows it: an approval page reads Type: Privacy-Preserving Transaction with Proof Size: 264907 bytes, and execute reads Public Transaction and 0 bytes. Saying otherwise to reviewers whose first move is to paste a hash into the explorer was the worst available thing to be wrong about, and it was said aloud in the film's narration as well. Both are corrected. The recipient's balance was given as 0 -> 1. getAccount says 2. The README, the pull-request body and the film's own transcript all said 1 -> 2; this file was the only place carrying the wrong number, and the only one a reviewer must read. It also disowned its own evidence: it said the full lifecycle against a standalone sequencer ran four commits back, when a green run exists whose head commit is the one under review. Volunteering "the expensive job did not run on what you are reading" to a reviewer whose documented rejection line is exactly that -- when it did run -- is a self-inflicted wound. The deployment date was 2026-08-24 in three documents. Block 23028 closed at 2026-08-25 11:50:38 UTC and 23554 at 20:38:24 the same day; 08-24 belongs to the tierless deployment already recorded as superseded. The same call puts block 4459 -- the membership_lez deploy listed first -- at 2026-08-12, so "thirteen transactions in blocks 23028-23554" was never true of thirteen of them. Both films were reshot. The old pair showed "2 approval(s) recorded against a threshold of 3" for 27 of 46 seconds while the narration said "passing the gate moves value", and the end-to-end film had no audio at all.
The recordings this document used to link were made before the deployment it describes: one carries superseded on-screen identifiers by its own admission, and the other predates the sixth on-chain check by twelve days, so it shows a five-check run of a script that now runs six. Both films that matter were reshot for this submission, both are narrated, and both play inline in the pull request with their own commit legible on screen. Pointing a reviewer at a recording that contradicts the one above costs more than the extra commentary is worth, so the links are removed rather than annotated. The criterion asks for a recorded demo showing terminal output and proof generation with RISC0_DEV_MODE=0; the inline films carry that on 120 and 49 frames respectively, measured by reading every frame rather than sampling.
verify-onchain.sh exits 1 when run bare -- it requires a work directory and a proposal id. The narration said otherwise, spoken and burned in as a caption, and the line had been carried forward from an earlier recording without being checked against the script it describes. Scene five was re-recorded and the film re-cut from the same take; it is 76 seconds now and says the more useful thing, which is that the second script can be pointed at any run rather than only this one. Three citations named the wrong commit. The reviewed-commit row was labelled with one hash and linked to another. The CI run cited as being on the reviewed commit ran three commits earlier, and a green run on the reviewed commit does exist. The end-to-end film is shot at b4f53ba, not at the reviewed commit, and the body said it was. Commit distances were wrong in three separate sentences. The mutation-testing evidence pointed at a commit the history rewrite orphaned. It now names the commit that record became, which is an ancestor of the reviewed one and still carries `13 mutations, 13 caught` in its message.
The document said this film is a single take at the reviewed commit, whose hash is on screen. The hash on screen is b4f53ba, four commits earlier -- the film's own transcript can only be committed after the film exists, so the reviewed commit is necessarily later. The repository's own docs/recording-evidence.md and the pull-request body both say b4f53ba; only this graded file said otherwise, and the frame it points a reviewer at is what refutes it. The closing line was also misquoted: the screen reads PASS (http://localhost:3141, 2-of-3, RISC0_DEV_MODE=0), and the document dropped the RPC endpoint.
The document pinned ad45806, where docs/DEPLOYMENT.md prints a ProgramId that does not derive from the ImageID beside it -- the retired 52b64613... against ImageID a8a87f8b...61500750. c72ff1c corrects it to 8b7fa8a8..., the little-endian rearrangement of that ImageID, and fixes two phantom hashes in cu-costs.md. The reviewed-commit pin now points at c72ff1c so the tree read matches the corrected deployment. The self-falsifying 'four commits later' distance is replaced by the commit name and the exact git diff; the verifier binary is byte-identical across the range, so only the documents read change.
…ee back The re-pin moved the reviewed commit to c72ff1c but this paragraph still ran git diff 1759015 08eaca5 -- three commits behind -- so the document contradicted its own pin. It now diffs 1759015..c72ff1c, which is five files (the deployment and CU-cost docs, the film transcript and its recording-evidence anchor, and the transcript-checker gate) -- still no program, no demo script, no chain artefact, and both program binaries hash identically across the range.
recording-evidence.md's OCR table had a column and a row for a film outside this repository; the fix is commit 511434b, now the reviewed commit. The pin, and the two git diff commands that name the reviewed commit, move to it. The delta from 1759015 is still five files and from b4f53ba still four documents -- no program, no script, no chain artefact -- so every count and description holds.
…po docs/ Trim the solution document to a tighter self-contained write-up: the on-chain transaction table, all criteria and evidence hashes are kept, while the long-form design rationale and benchmark detail are referenced in the repository's docs/ (security.md, cu-costs.md, DEPLOYMENT.md) where a reviewer verifying a claim goes.
Collaborator
|
Hi @edenbd1 , thanks for the submission. Looking through it my main concerns are:
Looking forward to your re-submission! |
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.
The exact version under review
Everything below is pinned to one commit. Nothing in this submission points at a
branch, so nothing under review can move after it is opened.
e9e8fcdapp/lp-0002-multisig.lgx— 2.5 MB,darwin-arm64andlinux-amd64, verified loading in Basecamp 0.2.2e9e8fcd: 131 tests, 84 of them through the sequencer's own executor, plus the gates that hold these documents to the code; and the full standalone-sequencer lifecycle ran on the reviewed commit itself — run 33249480393 ✅,RISC0_DEV_MODE=0, no skip pathRISC0_DEV_MODE=0, on1759015, an ancestor of the reviewed commit;git diff 1759015 e9e8fcd --statis five files — the deployment and CU-cost notes, the film's transcript and its recording-evidence anchor, and the transcript-checker gate — with no program, no demo script and no chain artefact among them, so the run exercised the code under review: it builds LEZ at a pinned revision, starts a standalone sequencer, and drives deploy → create → propose → real Risc0 approvals → execute against itsolutions/LP-0002.mdTwo minutes eight, the whole lifecycle, two real proofs, narrated. One take
of
scripts/e2e-local-sequencer.sh— the script CI runs — atc72ff1c, whosehash is on screen in the opening seconds; the reviewed commit is later because a
film's transcript and the measurement of it can only be written once the film
exists. It starts a real LEZ sequencer in
standalone mode, deploys both programs onto that fresh chain, creates the
multisig, proposes a transfer, gathers two real Risc0 approvals, executes,
and reads the accounts back off the same chain. It closes on
e2e against a real local sequencer: PASS (http://localhost:3141, 2-of-3, RISC0_DEV_MODE=0), andRISC0_DEV_MODE=0is also on screen before any proving starts. No fundedaccount, no public testnet, nothing mocked. The session ran 31 min 40; only the
stretch where the prover works and the screen does not change is sped up — 1,259
seconds compressed to 20, a factor of 63 written across the picture for the whole
of it, so a sped-up shot is announced rather than slipped past.
lp-0002-e2e-real-proofs-vo.mp4
Seventy-six seconds, two scripts. One terminal session running
verify-onchain-lifecycle.shand thenverify-onchain.sh: the thirteen transactions and the variant each carries —five of them
PrivacyPreserving— then the six accounts decoded field by field,down to the recipient's balance of two. Nothing is sped up; the two scripts
answer in about eleven seconds between them. It is narrated, and the narration was
re-recorded for this deployment rather than carried over — it says thirteen
transactions and a recipient holding two, which is what the screen shows. The same
words are burned in as captions and committed as
recordings/lp-0002-threshold-moves-value.srt, so the film can be read as well aswatched. The hash on screen is
c72ff1c. The reason it isnot the reviewed commit is structural: a film's transcript can only be committed
after the film exists, so the commit that carries the transcript is necessarily
later than the take. What came after this take is exactly that — the transcript
and the anchor that holds it to the picture. The e2e film above was shot at
c72ff1c, a different take at the same commit, also earlier than the reviewedcommit for the same reason; run
git difffrom either against the reviewed commit to see that onlydocuments, the transcript and the anchor lie between.
docs/recording-evidence.mdcarries the
git diffthat says so, the measured transcript check, and what itdoes not establish.
lp-0002-threshold-moves-value-sub.mp4
What 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 — the lifecycle and the rotation, on the current public testnet
membership_lezfb8eb10f7f394286c109cb6502a1c95294180523f30d06f707fc087a589bea98multisig_verifier268834b601f78b59090e90f8f10fd8ce3b526528e1224983edba95224be31aa3create_multisigdce8fd4dc4b53216d7271466ba66290b3bbfb2cf125701cd7c97a68cb69d1db0fund_treasury993d1f7c2b27fcab1abf759513f7bf7c64449a547b608e692deae67ec94f640bcreate_proposal54a300eb8c0bec27adb40b3ab36ff653b6234dc4deab2a47ac89a0a665c4fdd3approve(member A, privacy tx)1a5e529d8b9c87ec781b6e8cc2d4bc71c149e7f45f9ce66711108a22dbf6fcd5approve(member B, privacy tx)28a07e8df970322b643d7d5d6c74640f49e6d0260964255909181fdc815e8397executed0bab2943f09d3a27a10610c49c2a6cce1a2c94b93ded6f341ce29005ba8ca7ccreate_proposal(rotation)7a9331a9db536f710689b31861e7a3c94462fa1e090140e9fc3af66bc9eee773approve(member A, privacy tx)664fa866c041d39733dadadf84e266b1ec9a36e033a2cc271d2032c87dc2b563approve(member B, privacy tx)87aeffe96c98d4ed58601bbb5707f5c6516ca602e4a7aff1e274ba2188bb58a0approve(member C, privacy tx)6fcc674ae1c198651181afc74b47e005dc90aee4729301e79fb709019a314160rotate_configcb8a3f8b07db5039b48aefc32e3770e9919b2ce7af0792ef31fb8b47ea942554Check any of them:
Three approvals on the rotation, two on the transfer. The anchored tier
prices transfers; it never prices governance, and
rotate_configdoes not readthe tier table for its own count.
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
All six are owned by the verifier program.
./scripts/verify-onchain.shreadsthem back and decodes every record against the offsets published in
docs/account-layout.md.C9CgRDNfrCUJMzYG1YbskwViRTpDypty74YC2KThcxL3FyTtsh2h5fC85vrei19ThtM4yRThHEH4MGtXH1azrdhM5kM4uZdHgwjanoX82PtmeRUptkekm4EfjCzvmz6r7MHz5YajQiNzWfsy5VnDQxEKh7UPN6mXcqFsY3XTWp9GhWZg3fWFBfi8VWy9NadC6vE5KXtmjj9MzYkC9p2XWHX6ANitJ1N6WWpHVPh6pBpXmKAe522JBEWaaRT3jymaWcczg5AGA seventh account is not the program's, and it is the point of the exercise: the
recipient,
8kexXda8j5hPegPeHXzUM9PhvjYNFLpN8wN8PvG5iDhn, went 1 → 2. Itis held by the native transfer program rather than by the verifier, which is what
makes what it received spendable instead of stranded.
The treasury's balance is the one number nothing except a real execution could
produce:
fund_treasuryput 2 in andexecutemoved 1 out, and the two sides ofthat move are visible in two accounts owned by two different programs. 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 block explorer, measured rather than asserted
All thirteen render, and each carries the type it should: two
Deployment, fivePrivacy-Preserving Transaction— the transfer's twoapprovals and the rotation's three — and six
Public Transaction. Checkedat 22:24 UTC on 2026-08-25, against an impossible hash as the control, which still answers
Transaction not found.The index trails the sequencer — on this chain, roughly an hour and three
quarters — so a transaction submitted recently answers "Transaction not found"
there while
getTransactionalready resolves it. That is an indexing delay, nota gap, and it affects anything recent by anyone. These thirteen were written in
blocks 23028-23554 and each was behind the index for about three hours.
And a
curlsize comparison under-reports public transactions, which isworth stating because this description used to recommend one. Three sizes,
measured on these very hashes:
curlType: Public Transaction,Program ID, instruction-data size, signature count, and every account with its nonceA public transaction's page is small because a public transaction carries no
proof, not because anything is missing from it. So the size tells you which kind
of transaction you fetched, and it does not tell you whether it is there —
Type:does.scripts/check-explorer.pyclassifies on that marker rather thanon a length, with an impossible hash as the control, and aborts if the control
ever classifies as found.
Correction. An earlier version of this description said the explorer was a
WASM application returning an identical shell for every
/transaction/<hash>URL, so an indexed transaction and one that cannot exist were byte-identical over
curl. That was true whencheck-explorer.pywas written — it is why the scriptdrives a browser at all. It stopped being true on 2026-08-15, when the
explorer began rendering server-side; and as the table above shows, it stopped
being true only partly, which is the sort of half-change that turns a shortcut
into a wrong answer.
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
131 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.Eighty-four of the 131 run the built verifier binary through the sequencer's
own executor rather than the host crate: 30 rejections and honest controls, 22 on
the state it writes and the value it moves, 23 on the spending tiers and the
rotation, 6 on the IDL and the docs, 3 pinning the committed membership binary and
every ImageID the documentation quotes.
Thirteen mutations, thirteen caught. Every guard the tier and rotation work
added was watched fail when removed — each rebuilt through the reproducible Docker
build with the binary's hash required to move first, because a mutation that
leaves the artefact untouched proves nothing. CU costs are measured rather than
estimated, per instruction and per machine, and this revision made them worse:
every instruction is 45,000-52,000 user cycles dearer and
executeat M=5 crossedfrom one segment to two. That is in
docs/cu-costs.mdwith the numbers, notrounded in the submission's favour. The Basecamp module ships as a
.lgxwithdarwin-arm64 and linux-amd64 variants and was verified loading in Basecamp 0.2.2,
not merely built.
And the documents are gated against the code, because on this repository they
have drifted from it. Every quoted path, link and cited CI run must resolve;
every explorer link must resolve against the chain it names; nothing in the tree
may name another submission; and every row of the README's test inventory, with
its total and the addition printed under it, must be what the suites actually
pass. That last gate was rewritten after it checked four rows of nine and
reported "every stated count is what its suite passes" — it now reads the table,
and a row it cannot map is a failure rather than a skip.
Happy to address anything in review.