Tracking the VCVio pin
Standing checklist for issue #21 in improvements.md. VCVio is young and the
plan's rule is "pin a commit and don't float on main", so a bump is a
deliberate, reviewed act — not something a lake update should do behind our
backs.
The current pin
| Package | Verified-zkEVM/VCVio |
| Revision | a5f474fd0e9a26266cc599d100267411690dfeb7 |
| Upstream date | 2026-07-15 |
| Toolchain | leanprover/lean4:v4.32.0 |
| Validated | 2026-07-25 (week-1 gate: lake build LatticeCrypto in the VCVio checkout, green) |
| Transitive deps | frozen in lake-manifest.json (mathlib v4.32.0, loom2, PolyFun, …) |
lakefile.toml also sets autoImplicit = false, relaxedAutoImplicit = false
and linter.missingDocs = true package-wide (issue #14), matching VCVio's own
package options.
Bump procedure
- Edit the pin. Change
revinlean/lakefile.toml. Leave the comment above it in place and update the date it quotes. lake update VCVio— refresheslake-manifest.json. Check the diff: a VCVio bump usually drags mathlib and the toolchain with it. Iflean-toolchainmoves, install the new toolchain first.lake exe cache get— mathlib oleans. VCVio itself is not in the cache and compiles from source; a cold build is hours, not minutes.lake build— must be green with nowarning:orerror:fromPqStealth/.- The axiom guard re-runs itself. There is no separate command: the 114
#guard_msgs (whitespace := lax) in #print axioms …blocks inPqStealth/Axioms.leanare part of the build, and a changed axiom list or asorryanywhere in a headline theorem's dependency cone is a compile error. If a guard fails, do not edit the expected list to make it pass until you understand which upstream change moved it. - Diff two upstream files for things to adopt:
LatticeCrypto/MLKEM/Security.lean— the threesorrys at the pin (kpke_ind_cpa_security,kpke_delta_correct,ind_cca_security). If any of them becomes a proof,docs/spr-two-hop.mdandMLKEM.lean's "missing upstream lemma" section need rewriting.VCVio/CryptoFoundations/KeyEncapMech.lean— at the pin it stops atIND_CPA_*/IND_CCA_*and has no anonymity notion at all. If a KEM-level anonymity / ANO-CCA definition lands, adopt it in place of ourKEM.AnonExp/KEM.anonAdvantage(KEMAnonymity.lean) rather than keeping a parallel one.
- Record the new pin and validation date in
README.md("Pin" section) and in the table above.
Cadence: once a month, or whenever upstream announces KEM anonymity or proves one of the ML-KEM security placeholders.
The three upstream asks
Things we carry locally only because VCVio does not export them. Each is a small upstream PR; when one lands, delete our copy at the next bump.
(The full list, eleven items, is in vcvio-upstream.md; item 8 there was
retracted on 2026-08-19 — VCVio's programming-oracle bridge exists at the pin.)
-
Ship
DecidableEqnext tomlkem768Encoding.LatticeCrypto/MLKEM/Concrete/Instance.leansets every encoded type toByteArray, but as a plaindef, so the equality instance does not transport. We re-derive all three by hand inMLKEM.lean(instDecidableEqMlkem768Encoded{THat,U,V}, eachinferInstanceAs (DecidableEq ByteArray)). They belong beside the encoding. -
De-privatize
byteEncode_size..lake/packages/VCVio/LatticeCrypto/MLKEM/Concrete/Encoding.lean:242,private theorem byteEncode_size (d : Nat) (f : Rq) : (byteEncode d f).size = 32 * d. It is exactly the fact we need for thevhalf of the FIPS 203 ciphertext layout; because it isprivatewe take that byte count from the standard instead of proving it, andMLKEM.lean's docstring lists it under "Assumed". Making it (or a public corollary) visible closes the only non-cryptographic assumption in that file. -
A KEM-level
kem_ind_cpa_security.Security.leansupplieskpke_ind_cpa_security, which is about K-PKE, not the KEM, and issorryat the pin. The statement we want is recorded verbatim inMLKEM.leanunder "The missing upstream lemma" — it elaborates as written against the pinned VCVio:theorem MLKEM.kem_ind_cpa_security {params : Params} (ring : NTTRingOps) (encoding : Encoding params) (prims : Primitives params encoding) [DecidableEq encoding.EncodedTHat] [DecidableEq encoding.EncodedU] [DecidableEq encoding.EncodedV] [SampleableType SharedSecret] : ∃ mlwe : LearningWithErrors.Problem (TqMatrix params.k params.k) (TqVec params.k) (TqVec params.k), ∀ cpaAdv : (MLKEM.asKEMScheme ring encoding prims).IND_CPA_Adversary, ∃ mlweAdv : LearningWithErrors.Adversary mlwe, KEMScheme.IND_CPA_Advantage ProbCompRuntime.probComp cpaAdv ≤ |LearningWithErrors.advantage mlwe mlweAdv|The gap between the two is the T-transform half of Fujisaki–Okamoto. The day it lands,
mlkem768_unlinkAdvantage_le_indCpacomposes with it in a one-linecalc.
What in our tree is sensitive to a bump
Ordered by how loudly it fails.
PqStealth/Axioms.lean— 114 frozen#print axiomsmessages. Failing loudly is the whole point, but note the shape is fragile too: the blocks use a 3-line form with#print axiomsindented onto a continuation line. That is structurally incompatible with mathlib'sstyle.commandStartlinter — withweak.linter.mathlibStandardSet = truethe linter's own warning is captured by#guard_msgsand turns every block into an error. Verified on this toolchain; that is why the lakefile enableslinter.missingDocsbut not the mathlib standard set. If a bump turns those linters on by default, reformatAxioms.leanto one-line commands rather than suppressing the linter.PqStealth/ROMUpToBad.leanandSPRTwoHop.lean— the round-4 VCVio reach.ROMUpToBadbuilds onProgramLogic/Relational/ProgrammingOracle.lean(QueryImpl.withProgramming,withCachingTrackingPolicy, the enginetvDist_simulateQ_run_le_probEvent_output_bad,relTriple_simulateQ_run') and re-proves upstream'sprivateper-step agreement lemma; itssimp onlylists namewithProgramming_apply,withCachingTrackingPolicy_apply,StateT.run_mk,QueryImpl.withCaching_apply.SPRTwoHopimportsVCVio.ProgramLogic.Tacticsand usesby_equiv/rvcstep swap left/rvcgenfor the branch reorder — the only tactic-layer use in the tree, so a tactic-surface change upstream shows up there first.- Three VCVio modules that entered the cone in round 3.
OracleComp/QueryTracking/RandomOracle/Basic.lean(OracleSpec.randomOracle, used byBlindingROM.lean) andOracleComp/QueryTracking/RandomOracle/DeferredSampling.lean(+ its importProbeEps.lean;evalDist_bind_const_neverFails, used byMultiRecipient.lean). Neither is reached by VCVio's own root import, so a bump that moves or renames them fails at import resolution, not at a lemma name.dksapRomImpl(andBlindingROM.romImpl) mirror VCVio'sPRF.prfIdealQueryImplline for line — if that handler's shape changes, copy the change across. PqStealth/Demo.lean— 8#guard_msgsaround#eval. Frozen numeric output. Only breaks ifZMod 23arithmetic orReproutput changes.- The pinned
simp onlylists (issue #18). After the round-2 pass there is no baresimpleft inPqStealth/, so every list is explicit and a rename upstream is a named-lemma error rather than a silent proof change. The exposed ones are the VCVio-internal names:probOutput_bind_const,probFailure_of_liftM_PMF,probOutput_map_const,bind_map_leftindksap_perfectlyComplete(DKSAP.lean), andsupport_bind/probFailure_bind_eq_zero_iff/bind_map_leftinControls.lean. PqStealth/SharedSecretHiding.lean— theevalDistnormal-form proofs.evalDist_uniformSample_bind_const,evalDist_bind_pull_front,indCpaGame_eq_evalDist_normalForm,evalDist_rorGame_eq_normalFormreorder independent samples underneathevalDistusingevalDist_bind_bind_swap/evalDist_bind_congr'. These are the deepest reach into VCVio's monadic API in the tree; if theevalDistAPI or theOracleCompbind normal form is reworked upstream, this file is where the work is.PqStealth/MLKEM.lean— everything that names concrete ML-KEM-768 internals. The threeDecidableEqinstances become redundant (and possibly ambiguous) the moment upstream ships its own; theCiphertextinfiniteness argument depends on the encoded types stayingByteArray; the 1088-byte simulator depends on the FIPS 203 layout VCVio implements.- Reduction lemmas that name VCVio hypotheses:
probOutput_bind_bijective_uniform_cross(DKSAP.lean),probOutput_bind_add_right_uniform(ConstructionA.lean),KEMScheme.PerfectlyCorrect ProbCompRuntime.probComp(perfectlyComplete_ofKEMFull),SIS.matrixProblem(Ownership.lean). - Known lint findings we do not act on (they resurface if the linter set
widens): mathlib's non-default
explicitVarsOfIffwantsownership_iff_signingandownershipValid_mldsaShort_iff_isSigningKeyto take implicit binders — we keep them explicit because both are headline statements cited fromdocs/; andlinter.unusedSectionVarsflags[DecidableEq G]on the three DKSAP soundness theorems inSoundness.lean, whose fix would be anomit, which the tree deliberately does not use.