Skip to content

DO NOT MERGE: reproduction branch for the K=21 production ceremony - #3

Closed
mellowcroc wants to merge 2 commits into
audit/ed25519-key-validationfrom
test/production-path-10min-beacon-lead
Closed

DO NOT MERGE: reproduction branch for the K=21 production ceremony#3
mellowcroc wants to merge 2 commits into
audit/ed25519-key-validationfrom
test/production-path-10min-beacon-lead

Conversation

@mellowcroc

@mellowcroc mellowcroc commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

DO NOT MERGE

This branch weakens a security control on purpose. It exists to be checked out and run, not landed. Nothing here needs approval; the question for a reviewer is whether the run reproduces and whether the evidence holds up.

What it changes

Two commits on top of #1.

Add a tiny rehearsal circuit so the ceremony is testable registers a second circuit that proves x^3 = pub at a small domain, so the whole ceremony lifecycle runs in minutes instead of days. It carries exactly one Groth16 commitment, matching destination-v2, because finalization exports a Cardano verifying key whose BSB22 encoding assumes a single commitment — without it the rehearsal could not reach the finalize stage at all.

Production must never see this circuit, and three independent things keep it out: CeremonyDefinition.validate rejects any key version other than destination-v2 when mode is production, and does so before any environment-dependent check; the CLI refuses --key-version rehearsal-tiny-v1 unless --mode rehearsal; and the exact-k21-rehearsal gate in the production decision still demands domain 2^21, which a small run cannot satisfy.

It also tightens CircuitBinding.Validate to check key version and circuit id as a pair rather than each against a single constant — checking them independently would let a definition name one circuit's version with another's id and pass both checks while describing nothing that exists.

TEST BRANCH: cut the production witness lead to 10 minutes reduces ProductionMinimumWitnessLeadSeconds from 24 hours to 10 minutes.

Why the lead had to be cut

The released 24 hours is the window in which a public witness can observe a phase closure before the randomness that seals that phase exists. Two phase closes make 48 hours of mandated waiting, and that is the point rather than an accident.

But it also means a ceremony cannot be exercised end to end in under four days, and the consequence showed: the finalize, audit, release and decision stages had never been executed against real inputs. Two blocking defects were sitting in them, both now fixed in #1:

  • a close names its beacon round before the multi-hour replay that decides whether the round is still in the future — 4h32m of discarded work across two occurrences
  • mpc-finalization-evidence derived its credential at the wrong derivation path, so finalize complete could never accept the evidence it requires

Neither is reachable by a unit test, a small-domain rehearsal, or any check that does not spend the hours.

Three boundary tests also hardcoded a 24-hour offset instead of deriving it from the constant, so they passed regardless of what the constant said. They now derive it, which is what they were meant to assert.

What the run produced

A complete K=21 production-mode ceremony: two phases, three contributors, finalization, two independent audits, a signed release. Both audits passed with zero findings, each recompiling the circuit from source and reproducing the proving key, verifying key and Cardano BSB22 encoding byte for byte.

phase 1            13h55m    init, 3 contributions, close, beacon, seal
phase 2            20h43m    init, 3 contributions, close
finalization       12h19m    prepare, evidence, complete, bundle, 2 audits, release
compute            46h59m
beacon waits        5h11m
discarded           4h32m
total elapsed      56h42m

Roughly 25 of those 47 hours are one deterministic value being recomputed: every phase-2 operation rebuilds the phase-2 genesis from the sealed commons, 3h39m at a time, about eight times. The value is already pinned by dual digest in the coordinator-signed chain-0000.json. That is a proposal, not a defect, and is not addressed here.

Network cost was measured separately against R2 at 59.2 MB/s down and 40.3 MB/s up: the entire distributed ceremony moves 18 GB down and 6.5 GB up, about eight minutes. Distributing this is nearly free in bytes; what it costs is sequencing.

Reproducing it

The .ccs and both genesis files are deterministic, so a reviewer can rebuild them and compare digests without running the ceremony. To run it, use the tiny circuit first — it exercises the same orchestration in minutes.

Note that a ceremony pins source_commit and the tool binary hash, so a rebuild mid-run invalidates a pinned ceremony. Build once, up front.

What it does not show

A single-host run proves resource and coherence properties only. It proves nothing about participant independence, and the production decision for this run would be NO-GO: participant-independence, live-twenty-party-ceremony, public-witnessing and immutable-independent-mirrors are all false when every identity is a key file in one directory. Those four gates carry no code at all — they are assertions four named humans sign, which is exactly why they cannot be satisfied here.

@mellowcroc
mellowcroc changed the base branch from main to audit/ed25519-key-validation August 18, 2026 04:44
The destination-v2 circuit has 1,789,750 constraints and forces a 2^21
domain, which makes every ceremony operation expensive: a single phase-1
contribution takes 56 minutes and moves 576 MiB, and a full lifecycle is a
multi-day exercise. Testing the orchestration at that size is impractical,
so in practice it was not tested at all.

Register a second circuit that proves x^3 = pub at a small domain, so the
same lifecycle runs in minutes. It carries exactly one Groth16 commitment,
matching destination-v2, because finalization exports a Cardano verifying
key whose BSB22 encoding assumes a single commitment; without it the
rehearsal could not reach the finalize stage at all.

Production must never see this circuit, and three independent things keep
it out. CeremonyDefinition.validate rejects any key version other than
destination-v2 when mode is production, and does so before any
environment-dependent check. The CLI refuses --key-version rehearsal-tiny-v1
unless --mode rehearsal. The exact-k21-rehearsal gate in the production
decision still demands domain 2^21, which a small run cannot satisfy.

CircuitBinding.Validate now checks key version and circuit id as a pair
rather than each against a single constant. Checking them independently
would let a definition name one circuit's version with another's id and
pass both checks while describing nothing that exists.

The four executor sites that previously hardcoded CompileDestinationV2 now
compile the circuit the signed definition names, so an operator cannot
select a circuit the ceremony was not created with.
DO NOT MERGE. Any transcript produced from this branch is a test artifact
and must never be presented as a ceremony.

The released ProductionMinimumWitnessLeadSeconds is 24 hours. It is the
window in which a public witness can observe a phase closure before the
randomness that seals that phase exists, which is what stops a coordinator
who already knows the beacon output from closing the phase around it. Two
phase closes make it 48 hours of mandated waiting, and that is the point
rather than an accident.

That cost makes the production path effectively untestable: a run cannot
finish in less than three days, so the finalize, audit, release, and
decision stages have never been exercised. A rehearsal reaches none of
them, because the production arm of CeremonyDefinition.validate is the
only place several of those checks live.

Reducing the constant to 10 minutes brings a full production run inside a
single working session. The witness observation window introduced on the
base branch is cut proportionally, from one hour to two minutes, so the
reproduction still exercises the reserved-window check without restoring
the multi-hour wait. Nothing else is relaxed: the clean-tree requirement,
the pinned build profile, the destination-v2 circuit binding, and every
other production gate remain exactly as released.
@mellowcroc
mellowcroc force-pushed the test/production-path-10min-beacon-lead branch from 97111e1 to 5afe1c8 Compare August 18, 2026 09:13
@mellowcroc mellowcroc closed this Aug 18, 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.

1 participant