Skip to content

Fast EC arithmetic: projective addition, Pippenger MSM, 8-limb Montgomery fields - #13

Merged
daira merged 9 commits into
daira:mainfrom
mitschabaude-bot:fast-pasta-arithmetic
Jul 27, 2026
Merged

Fast EC arithmetic: projective addition, Pippenger MSM, 8-limb Montgomery fields#13
daira merged 9 commits into
daira:mainfrom
mitschabaude-bot:fast-pasta-arithmetic

Conversation

@mitschabaude-bot

@mitschabaude-bot mitschabaude-bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Human summary

This adds some very fast, certified curve arithmetic, motivated by the need in zcash/ironwood#99 to check correctness of dozens of size $2^{12}$ MSMs with native_decide (for the verifying key pin). In a first iteration that took 1.5 hours to build, these additions got it down to < 1min.

Interesting bits that I learned while doing this:

  • You can do multi-threading in Lean quite easily, without breaking out of formally verified world
  • precompileModules lets you compile modules and later import them normally in Lean. This is necessary for some stuff, like the Uint32-backed limb field arithmetic, which happens to be not very fast in Lean's interpreter

Additional AI summary

What is proven

Two tiers, the second proven equal to the first — and the first proven against the repository's pre-existing complete affine group law on SWPoint Vesta.curve (its nsmul for the ladder; the MSM specs are generic over [AddCommMonoid M] and consume the same instance at G). No new reference semantics is introduced, and no Mathlib bridge is involved.

  • Curves/Pasta/Fast/Projective.lean — Renes–Costello–Batina complete addition (add-2015-rcb, a = 0) on (X : Y : Z) over 𝔽_q: toAffine_padd against SWPoint Vesta.curve, closure, leaf bridges, pnsmulFast_spec. Completeness rests on Vesta having no 2-torsion (Vesta.no_onCurve_y_zero); the polynomial cofactors were found by an offline Gröbner search and are re-checked here by ring, so they are re-derivable rather than trusted.
  • Msm.lean / MsmProj.lean — windowed Pippenger, pippenger_eq_msm-proven equal to the naive MSM, generic over [AddCommMonoid M]; MsmProj runs the interior in projective coordinates (measured at n = 2048: naive 242 s → projective scatter 4.3 s → windows-parallel 0.8 s).
  • ProjectiveMontDefs.lean / ProjectiveMontEquiv.lean — the same schedules on eight-limb Montgomery residues, importing nothing beyond Lean core, with every kernel operation proven to compute the reference: toPVesM_padd/_pid/_pneg, pnsmulM_spec, msmM_spec. The bridge is one hop — the vendored field's toField is a ring isomorphism onto ZMod q, so the coordinate map pushes through the RCB formulas, and the schedules are structural inductions against their MsmProj twins.

No sorry; #print axioms on every headline spec gives exactly propext / Classical.choice / Quot.sound — nothing extends the trust base. This is a build-time check: CompElliptic/TrustBoundary.lean pins the seven headline specs (toAffine_padd, smulFast_eq, padd_eq_paddFast, pippengerFastPar_eq_msm, commitLagrangeFastWith_eq, pnsmulM_spec, msmM_spec) at the flagless assert_axioms tier, so reaching native_decide or sorry anywhere in the fast tier fails the build.

Vendored CompPoly (CompElliptic/Vendor/)

The Montgomery tier needs eight-limb arithmetic not in the pinned CompPoly, so Vendor/CompPoly/Montgomery/ vendors branch fast_multilimb_fields @ b3850f0, namespaces left at upstream's Montgomery.*. Deletion criterion: the CompPoly pin advancing past CompPoly #258 + #274 — then the directory is deleted and the imports re-point; only Pasta.lean was rewritten (to reuse this repo's Pratt certificates, keeping a single Field instance). Details in CompElliptic/Vendor/README.md.

The precompile lane

lean_lib FastFieldNative precompiles the two zero-import definition modules so the arithmetic evaluates as compiled C rather than in the interpreter. Two invariants, both enforced by scripts/check_native_lane.sh and commented in the lakefile: the lane's import closure must stay core-only (native codegen over a mathlib closure OOMs), and the root module must be named after the library (the dynlib initializer symbol is derived from the .so name). Opt-in — import CompElliptic pulls in none of it — and easy to drop wholesale if unwanted.

Notes

  • Not a CoordinateSystem instance: PVes is a bare triple with proven commutation. When the planned abstraction lands, the reusable part here is the RCB completeness argument; the two should be reconciled.
  • Msm.lean is curve-generic apart from its commit_lagrange wrapper; it sits under Curves/Pasta/Fast/ only to keep the tree in one place.
  • API stability: the Fast interfaces are provisional — they are not guaranteed to remain public, and may be folded into the existing API or otherwise changed incompatibly (see the CoordinateSystem reconciliation, Reconcile the fast projective arithmetic with the CoordinateSystem abstraction #15).

[Edited by @daira (assisted by Claude Fable 5): stated the correctness anchor (the pre-existing SWPoint affine law) up front, noted the trust-boundary pin added on the branch, and added the API-stability note.]

Comment thread FastFieldNative.lean Outdated
Comment thread lakefile.lean Outdated
Comment thread README.md Outdated
Comment thread CompElliptic/Curves/Pasta/Fast/ProjectiveMontDefs.lean Outdated
@mitschabaude-bot
mitschabaude-bot marked this pull request as ready for review July 26, 2026 20:23
mitschabaude and others added 5 commits July 26, 2026 22:51
Adds `CompElliptic/Curves/Pasta/Fast/`: Vesta group arithmetic meant to be
computed with, not only reasoned about, in three tiers each proven equal to
the one above it.

  * `Projective.lean` -- complete Renes-Costello-Batina addition on `(X:Y:Z)`
    over 𝔽_q, with `toAffine_padd`/`valid_padd` against `SWPoint Vesta.curve`.
    Completeness rests on Vesta having no 2-torsion.
  * `Msm.lean` / `MsmProj.lean` -- windowed Pippenger MSM, proven equal to the
    naive MSM, plus its all-projective instantiation (one inversion per MSM
    instead of one per add).
  * `NatKernel.lean` / `ProjectiveMontDefs.lean` -- zero-import transplants of
    the same schedules onto ℕ representatives and onto eight-limb Montgomery
    residues, with `NatKernelEquiv.lean` / `ProjectiveMontEquiv.lean` proving
    every kernel operation computes its statement-surface counterpart.

`CompElliptic/Vendor/CompPoly/Montgomery/` vendors the eight-limb Montgomery
field from CompPoly `fast_multilimb_fields` (`b3850f0`) pending a pin bump past
CompPoly #258 + #274; namespaces are left at upstream's `Montgomery.*` so the
migration is an import-path rewrite. See the README there.

`FastFieldNative` is a new `precompileModules` library over the three zero-import
definition modules, so the Montgomery tier evaluates at native speed. Its
core-only invariant is what keeps native codegen off a mathlib closure;
`scripts/check_native_lane.sh` checks it.

Opt-in throughout: `import CompElliptic` does not pull any of this in.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The roadmap notes (CoordinateSystem relationship, generalizing Msm out
of the Pasta tree) move to the PR description where suggestions belong.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ctly

The `Nat`-carrier kernel was historical: its specs existed first, so the
Montgomery kernel's correctness was transported through it. The vendored
Montgomery field ships `FastField.toField` and its `ringEquiv : FastField ≃+*
ZMod modulus`, so the limbs-to-𝔽_q bridge is one hop, and the meaning of the
Pippenger schedules already lives in `Msm.lean` / `MsmProj.lean`. Two tiers, not
three.

  * `ProjectiveMontEquiv.lean` now relates `PM` to `Projective.PVes` directly,
    along `RM p P := WFP p ∧ toPVesM p = P`. `toPVesM_padd` — the one commuting
    square of substance, ~40 field operations of RCB pushed through the ring
    homomorphism — is unchanged; the bucket-scatter, downsweep and window
    inductions now land on `MsmProj.pscatterStep` / `paccStep` /
    `pwindowValueFast`, and the ladder and the Horner recombination are proved
    through `toGM = toAffine ∘ toPVesM` where the group law lives.
  * `NatKernel.lean` and `NatKernelEquiv.lean` are deleted, and the `Nat` kernel
    is dropped from the `FastFieldNative` lane (three core-only modules now, not
    four), from `scripts/check_native_lane.sh`, and from the README.

Public statements are unchanged: `msmM_spec`, `pnsmulM_spec`, `toPVesM_padd`,
`toPVesM_pid`, `toPVesM_pneg` and the field-level cast lemmas keep their exact
shapes. Only the transport API (`RM`, `RM2`, `RA` and their closure lemmas),
which used to mention the `Nat` triples, is retargeted at `PVes`. `#print axioms`
on the specs stays [propext, Classical.choice, Quot.sound].

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`PM.fft` and `PM.bitreverse` in
`CompElliptic/Curves/Pasta/Fast/ProjectiveMontDefs.lean` had no spec here and
no consumer: a group-point FFT belongs with a reference group DFT to be proven
against, which this repository does not have. `pneg` stays -- it is part of the
group API and has its spec in `toPVesM_pneg`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review feedback: the lakefile lane comment, the `check_native_lane.sh` header
and the module docstrings of `FastFieldNative.lean`, `Fast/Projective.lean`,
`Fast/Msm.lean`, `Fast/MsmProj.lean`, `Fast/ProjectiveMontDefs.lean` and
`Fast/ProjectiveMontEquiv.lean` were essays. Cut to what each thing is, the one
invariant that is load-bearing (core-only import closure; the dynlib
initializer name), and where the proofs live.

`README.md`: the Fast/ entry now reads like its neighbours -- one plain
sentence, no bold, no self-marking.

No statement or proof changed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mitschabaude-bot added a commit to mitschabaude-bot/ironwood that referenced this pull request Jul 26, 2026
Pin CompElliptic to daira/CompElliptic#13's head (a descendant of the
previously pinned rev; CompPoly and mathlib pins unchanged) and delete
the vendored copies it replaces. Vendor/CompPoly keeps only the scalar
FFT defs, which no pin provides until CompPoly#274 lands. The local
precompile lane becomes Zcash.FastFieldNative: a lib named exactly like
the pin's FastFieldNative collides silently (module names are global)
and the local dylib never gets built.

Fixture-check speedups verified at parity after the swap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mitschabaude-bot mitschabaude-bot changed the title Fast Vesta group arithmetic: complete projective addition, Pippenger MSM, and proven native kernels Fast EC arithmetic: projective addition, Pippenger MSM, 8-limb Montgomery fields Jul 26, 2026
Add the seven headline correctness theorems of the fast Vesta arithmetic
(projective addition, scalar ladder, Pippenger MSM, the raw-numeral spelling,
and the Montgomery kernel) to TrustBoundary.lean at the standard-axioms tier,
making the claim that nothing in the fast tier extends the trust base a
build-time check rather than a prose claim. Completeness routes through
no_onCurve_y_zero, a kernel decide, so no native_decide tier is reached.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

@daira daira left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff. Verdict: approve modulo the small items below. The trust discipline is exactly right throughout, and the two places where fast code could have quietly extended the trust base — native overrides and parallelism — both take the proven route instead.

One point worth stating up front, since it is the natural first question about a "proven fast arithmetic" PR: the correctness anchor is CompElliptic's pre-existing reference, not the PR's own definitions. toAffine_padd and smulFast_eq are stated against the existing complete affine group law on SWPoint Vesta.curve and its nsmul; the MSM specs are generic over [AddCommMonoid M], so at G they consume that same existing structure. Only the Montgomery tier is proven against the PR's own projective tier — which is in turn anchored as above. No Mathlib bridge is involved (unchanged from the rest of the repo).

What I verified

  • Projective.lean in full: the RCB closed forms at a = 0, b = 5, b3 = 15; the Z = 1 core equivalence with all four case splits (distinct x, doubling, inverse, identity); completeness via the cube-root-of-−5 arguments (Z_ne_zero_dist, Y_ne_zero_inv), which are the paper's exceptional-cases-force-2-torsion claim made concrete against Vesta.no_onCurve_y_zero; homogeneity lifting to general valid points; and the toAffine homomorphism into SWPoint Vesta.curve. The offline Gröbner cofactors are re-verified in-kernel by linear_combination/ring, so nothing rests on the search. Citation checks out (EUROCRYPT 2016; EFD add-2015-rcb; the file states the polynomial form rather than the paper's operation schedule).
  • padd_eq_paddFast is the proven counterpart of implemented_by — the fast raw- spelling never becomes the statement surface, and the decision not to register it @[csimp] yet is justified by measurement and documented with the exact condition under which to flip it.
  • parMap: the trust analysis in the docstring is correct — Task is logically the one-field structure, the spawn/get round-trip is definitional, so parMap_eq_map makes parallelism purely an evaluation strategy inside the existing native_decide boundary. The kernel's view is literally List.map.
  • Msm.lean/MsmProj.lean statement surface: pippengerFast(Par)_eq_msm against the naive MSM, generic over [AddCommMonoid M]; commitLagrangeFastWith_eq against the Pedersen spec; the projective interior via psum/pwindowValue specs.
  • The Montgomery tier's statement surface (WF/montVal algebra, msmM_spec, pnsmulM_spec) and the vendoring discipline: deletion criterion pinned to CompPoly #258 + #274, per-file change table, upstream namespaces preserved, and the Pasta.lean rewrite keeping a single Field (ZMod PALLAS_SCALAR_CARD) instance.
  • The precompile lane is opt-in (import CompElliptic pulls none of it), the core-only invariant is real (codegen over a mathlib closure), and check_native_lane.sh enforces the lane's import closure inductively.

Asks

  1. Build-enforce the axiom claim — done on the branch (898f088). The body says #print axioms on every headline spec gives exactly propext/Classical.choice/Quot.sound; that is now a build-time check: TrustBoundary.lean pins toAffine_padd, smulFast_eq, pippengerFastPar_eq_msm, commitLagrangeFastWith_eq, padd_eq_paddFast, pnsmulM_spec, and msmM_spec at the standard tier, and the full build passes — confirming empirically that nothing in the fast tier reaches native_decide (completeness routes through no_onCurve_y_zero, a kernel decide).
  2. Wire check_native_lane.sh into ci.yml. The script exists but nothing runs it; the invariant it guards fails silently (a build that quietly becomes enormous), which is exactly the kind of thing CI should catch. One step next to the generated-fields freshness check.
  3. CI has not run on this branch — workflows now approved and running.
  4. Nit, padd_eq_paddFast docstring: "makes every compiled call site … runs the fused raw- form" → "run".

Posted with the assistance of Claude Fable 5.

daira and others added 2 commits July 27, 2026 03:36
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@daira
daira force-pushed the fast-pasta-arithmetic branch from 6d38e8f to d8d4d1e Compare July 27, 2026 03:16
@daira
daira merged commit fd99773 into daira:main Jul 27, 2026
4 checks passed
daira added a commit to mitschabaude-bot/ironwood that referenced this pull request Jul 27, 2026
…tic#13 merged

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
TalDerei pushed a commit to zcash/ironwood that referenced this pull request Jul 27, 2026
Two self-contained, temporarily-vendored trees under `Zcash/Vendor/`, each with a
README stating its provenance and its deletion criterion:

* `Zcash/Vendor/CompPoly/` — eight-limb (8 x 32-bit in `UInt64`) Montgomery
  arithmetic for 255-bit primes, vendored from the CompPoly branch
  `fast_multilimb_fields`, plus the radix-2 DIT scalar FFT developed here.
  Deleted once ironwood's CompPoly pin carries both CompPoly#258 (the field) and
  CompPoly#274 (the scalar FFT).
* `Zcash/Vendor/CompElliptic/` — RCB complete projective Vesta addition, the
  bucket-method Pippenger MSM, the zero-import `Nat`-carrier kernel and its
  operation-for-operation equivalence proof, and the same kernel again on
  Montgomery residues. Developed here but generic; deleted once ironwood's
  CompElliptic pin provides it (daira/CompElliptic#13).

Layering, mirroring the upstream dependency direction: `Vendor/CompElliptic/`
may import `Vendor/CompPoly/`, never the reverse, so the two migrations stay
independent. Neither tree imports Keygen, Soundness or Clean.

The `*Defs.lean` modules are split out from their proofs and import nothing
beyond Lean core, so they can be native-compiled without dragging a mathlib
import closure through codegen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
TalDerei pushed a commit to zcash/ironwood that referenced this pull request Jul 28, 2026
…anch

Closes out the fast-arithmetic arc. The branch already carried #111's content
through 95baf6c via prior direct merges; main adds only the two commits that
landed on the PR branch after that point:

* caaf766 re-pins CompElliptic to the canonical daira/CompElliptic after
  daira/CompElliptic#13 merged (fd997734, was mitschabaude-bot @ 53b35333).
  The two revisions differ only in docstrings and a TrustBoundary.lean
  `assert_axioms` block for the Fast tier -- no definition or proof changes.
* 308b6ac expands "CIOS" in the Zcash/Arithmetic/CommitLagrange.lean docstring.

No conflicts. The lakefile/manifest automerge keeps this branch's Clean pin
(18c8a6b9) and its FixtureCheck/CircuitCheck target layout while taking main's
CompElliptic re-pin.

Nothing else landed on main outside #111. Captured fixtures are byte-identical.
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.

3 participants