Skip to content

Solution: LP-0002 Private M-of-N Multisig - #125

Closed
edenbd1 wants to merge 33 commits into
logos-co:masterfrom
edenbd1:lp-0002-submission
Closed

Solution: LP-0002 Private M-of-N Multisig#125
edenbd1 wants to merge 33 commits into
logos-co:masterfrom
edenbd1:lp-0002-submission

Conversation

@edenbd1

@edenbd1 edenbd1 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

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.

Repository https://github.com/edenbd1/lp-0002-private-multisig
Reviewed commit e9e8fcd
Basecamp module app/lp-0002-multisig.lgx — 2.5 MB, darwin-arm64 and linux-amd64, verified loading in Basecamp 0.2.2
CI — host tests + deployed-binary audit run 33221831189 ✅ — six jobs, all green, on the reviewed commit e9e8fcd: 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 path
CI — full lifecycle vs a standalone LEZ sequencer run 33098969090 ✅ — 2 h 39 at RISC0_DEV_MODE=0, on 1759015, an ancestor of the reviewed commit; git diff 1759015 e9e8fcd --stat is 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 it
Solution file solutions/LP-0002.md

Redeployed on 2026-08-25, and the gate now prices what it releases. The
multisig anchors a spending tier: at or below a cap, fewer approvals than
the default threshold. This deployment is 3-of-3 and the transfer cleared with
two approvals, which is the tier doing something — counted in proofs not
generated rather than asserted in prose.

The tier is anchored, not merely applied. config_hash here is 1628d88b…;
the same members and threshold without the tier give 3dff16d7… — a different
PDA, an account nobody created. So a caller who invents a more generous table
must present a config_hash committing to it, and that hash is the address they
then have to read the multisig from. The gate still moves value: treasury
2 → 1, recipient 1 → 2, both sides in the execute transaction.
Everything below is that deployment, in blocks 23028-23554.

Only the verifier was redeployed. membership_lez is byte-for-byte the binary
it has been since block 4459, so MEMBERSHIP_LEZ_PROGRAM_ID — the pin that
stops a chained call reaching anything but the audited membership program —
never moved. Keeping it there took work: with lto = "fat", unreachable code
in a linked crate shifts the ELF, and the first build of this revision moved
the membership ImageID purely because multisig-core had gained an
account-layout module the membership guest never calls. Gating that behind a
feature the membership guest does not enable brings the ImageID back exactly.

Two minutes eight, the whole lifecycle, two real proofs, narrated. One take
of scripts/e2e-local-sequencer.sh — the script CI runs — at c72ff1c, whose
hash 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), and
RISC0_DEV_MODE=0 is also on screen before any proving starts. No funded
account, 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.sh and then
verify-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 as
watched. The hash on screen is c72ff1c. The reason it is
not 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 reviewed
commit for the same reason; run git diff from either against the reviewed commit to see that only
documents, the transcript and the anchor lie between.

docs/recording-evidence.md
carries the git diff that says so, the measured transcript check, and what it
does not establish.

lp-0002-threshold-moves-value-sub.mp4

What is verified on chain

Each approve declares a ChainedCall to a LEZ-native membership program, so
the privacy circuit composes it with a real env::verify and the sequencer
checks the receipt against the node-pinned PRIVACY_PRESERVING_CIRCUIT_ID. The
member 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

Step Transaction Block
deploy membership_lez fb8eb10f7f394286c109cb6502a1c95294180523f30d06f707fc087a589bea98 4459
deploy multisig_verifier 268834b601f78b59090e90f8f10fd8ce3b526528e1224983edba95224be31aa3 23028
create_multisig dce8fd4dc4b53216d7271466ba66290b3bbfb2cf125701cd7c97a68cb69d1db0 23029
fund_treasury 993d1f7c2b27fcab1abf759513f7bf7c64449a547b608e692deae67ec94f640b 23030
create_proposal 54a300eb8c0bec27adb40b3ab36ff653b6234dc4deab2a47ac89a0a665c4fdd3 23031
approve (member A, privacy tx) 1a5e529d8b9c87ec781b6e8cc2d4bc71c149e7f45f9ce66711108a22dbf6fcd5 23039
approve (member B, privacy tx) 28a07e8df970322b643d7d5d6c74640f49e6d0260964255909181fdc815e8397 23065
execute d0bab2943f09d3a27a10610c49c2a6cce1a2c94b93ded6f341ce29005ba8ca7c 23066
create_proposal (rotation) 7a9331a9db536f710689b31861e7a3c94462fa1e090140e9fc3af66bc9eee773 23527
approve (member A, privacy tx) 664fa866c041d39733dadadf84e266b1ec9a36e033a2cc271d2032c87dc2b563 23536
approve (member B, privacy tx) 87aeffe96c98d4ed58601bbb5707f5c6516ca602e4a7aff1e274ba2188bb58a0 23544
approve (member C, privacy tx) 6fcc674ae1c198651181afc74b47e005dc90aee4729301e79fb709019a314160 23553
rotate_config cb8a3f8b07db5039b48aefc32e3770e9919b2ce7af0792ef31fb8b47ea942554 23554

Check any of them:

curl -s -X POST https://testnet.lez.logos.co -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"getTransaction","params":["<hash>"]}'

Three approvals on the rotation, two on the transfer. The anchored tier
prices transfers; it never prices governance, and rotate_config does not read
the tier table for its own count.

The two deployment hashes are SHA256(borsh(bytecode)) of the binaries committed
under artifacts/programs/, so they can be recomputed from the repository
without trusting this table.

The accounts, which are the stronger evidence

All six are owned by the verifier program. ./scripts/verify-onchain.sh reads
them back and decodes every record against the offsets published in
docs/account-layout.md.

Account Address Balance
multisig C9CgRDNfrCUJMzYG1YbskwViRTpDypty74YC2KThcxL3 0
treasury FyTtsh2h5fC85vrei19ThtM4yRThHEH4MGtXH1azrdhM 2 → 1
proposal 5kM4uZdHgwjanoX82PtmeRUptkekm4EfjCzvmz6r7MHz 0
approval marker A 5YajQiNzWfsy5VnDQxEKh7UPN6mXcqFsY3XTWp9GhWZg 0
approval marker B 3fWFBfi8VWy9NadC6vE5KXtmjj9MzYkC9p2XWHX6ANit 0
execution marker J1N6WWpHVPh6pBpXmKAe522JBEWaaRT3jymaWcczg5AG 0

A seventh account is not the program's, and it is the point of the exercise: the
recipient, 8kexXda8j5hPegPeHXzUM9PhvjYNFLpN8wN8PvG5iDhn, went 1 → 2. It
is 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_treasury put 2 in and execute moved 1 out, and the two sides of
that 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, five Privacy-Preserving Transaction — the transfer's two
approvals and the rotation's three — and six Public Transaction. Checked
at 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 getTransaction already resolves it. That is an indexing delay, not
a 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 curl size comparison under-reports public transactions, which is
worth stating because this description used to recommend one. Three sizes,
measured on these very hashes:

what over curl why
a hash that cannot exist 2,416 bytes the page stops at the loading shell
a public transaction 4.9-7.0 kB complete — hash, Type: Public Transaction, Program ID, instruction-data size, signature count, and every account with its nonce
a deployment or a privacy-preserving transaction 372-694 kB the same page, plus the proof bytes, which are almost all of it

A 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.py classifies on that marker rather than
on a length, with an impossible hash as the control, and aborts if the control
ever classifies as found.

# where the explorer's index is, and where the sequencer is
curl -s https://explorer.testnet.lez.logos.co/ | grep -o 'block_id[^,]*' | head -1

# the control: a hash that cannot exist
curl -s "https://explorer.testnet.lez.logos.co/transaction/$(python3 -c 'print("ff"*32)')" | wc -c

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 when check-explorer.py was written — it is why the script
drives 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.md documents 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 execute at M=5 crossed
from one segment to two. That is in docs/cu-costs.md with the numbers, not
rounded in the submission's favour. The Basecamp module ships as a .lgx with
darwin-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.

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown

✅ Validation passed

A reviewer will assess against the prize criteria.
ℹ️ Solution submission for LP-0002.
ℹ️ Checking repo: https://github.com/edenbd1/lp-0002-private-multisig


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 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.
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
edenbd1 force-pushed the lp-0002-submission branch 2 times, most recently from 0ad3376 to 469ff71 Compare August 24, 2026 19:02
…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
edenbd1 force-pushed the lp-0002-submission branch from 469ff71 to b8a307d Compare August 24, 2026 19:33
@weboko

weboko commented Aug 24, 2026

Copy link
Copy Markdown
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.
@edenbd1

edenbd1 commented Aug 24, 2026

Copy link
Copy Markdown
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.
edenbd1 and others added 17 commits August 26, 2026 03:09
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.
@weboko

weboko commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Hi @edenbd1 , thanks for the submission.

Looking through it my main concerns are:

  • demo video doesn't cover Basecamp app but only CLI;
  • demo video is narrated using AI which is not fair comparing to other submitters;
  • I see there is a lot of information of various degree of usefulness in the PR description but main point of submission is solution file and from there I am missing some information, for example - transaction hashes are trimmed and do not have links, video link is missing etc;

Looking forward to your re-submission!

@weboko weboko closed this Sep 7, 2026
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.

2 participants