Repository navigation
fix(cache): serve prepared points only for the points they came from - #35
Merged
Merged
Conversation
The setup multi-pairings took cached prepared points for any input no longer than the cache, without checking the input. A cache built from another setup gave wrong pairings, and an input longer than the cache panicked. The cache now keeps its source points. A lookup serves a prefix of them only, and the pairing otherwise prepares its input. init_cache replaces a cache that does not cover its input. The cache tests take a shared lock, and new tests pin the fallback, the replacement, and a proof that verifies after a foreign cache.
g1_prepared and g2_prepared were still public, so downstream code could index them without the prefix check that g1_prepared_for and g2_prepared_for apply. Make them private and add g1_len and g2_len for callers that only need the cache size. Add tests that pair prefixes of the cached points (the cache-hit path) against a naive pairing, and that offset or altered point sets miss the cache and still pair correctly.
quangvdao
marked this pull request as ready for review
October 29, 2026 21:09
# Conflicts: # CHANGELOG.md
This was referenced Nov 2, 2026
moodlezoup
pushed a commit
to a16z/jolt
that referenced
this pull request
Nov 2, 2026
* chore(deps): bump dory-pcs to 0.5.0 Picks up atomic first-writer-wins setup files (a16z/dory#34), the prepared-point cache prefix check (a16z/dory#35) and cyclotomic GT exponentiation in the verifier (a16z/dory#36). Refreshes the two scheme.rs comments that described the old dory behavior. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(fuzz): bump dory-pcs to 0.5.0 in fuzz lockfiles Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.
Summary
With the
cachefeature, the setup multi-pairings inark_pairing.rsuse the global prepared-point cache for any input no longer than the cache. They never check that the input is the cached points. So:init_cachekeeps any cache at least as large as the input, so a secondinit_cachecall cannot fix a wrong cache.This is the same problem #29 addressed. #29 was closed because jolt-dory never fills the cache, so Jolt cannot hit the problem. This PR is for dory itself:
tests/arkworks/cache.rsfills the cache with random points. Under plaincargo test, every test in thetest_arkworksbinary shares one process, so later tests in that binary pair against those points. Their proofs then fail to verify (on this sandbox, fiveevaluationtests failed). CI uses nextest, which runs each test in its own process, so CI never sees this.cachewho creates more than one setup in a process gets wrong results instead of an error.Changes
PreparedCachestores the source points beside their prepared forms. New methodsg1_prepared_for(points)andg2_prepared_for(points)return the prepared prefix only whenpointsis exactly a prefix of the cached points. Otherwise they returnNone.ark_pairing.rs(G1 and G2, sequential and parallel) use these lookups. OnNone, they prepare the input themselves, exactly as they do with no cache.init_cachekeeps the existing cache only when that cache covers the new points. Otherwise it replaces the cache.foreign_cache_falls_backchecks setup pairings against a naive pairing, with the cache filled from other points. It covers inputs both shorter and longer than the cache, from the G1 side and the G2 side.reinit_with_other_points_replaceschecks thatinit_cachereplaces a cache built from other points.proof_verifies_after_foreign_cacheruns prove and verify end to end after a foreign cache.cache_hit_matches_naive_pairingpairs prefixes of length 1, n/2 and n of the cached points (the cache-hit path) and checks them against a naive pairing.non_prefix_points_fall_backchecks that an offset slice of the cached points, and a prefix with its last point changed, miss the cache and still pair correctly.CHANGELOG.md: Unreleased entries under Fixed, Added and Changed.Breaking: all of
PreparedCache's fields are now private, includingg1_preparedandg2_prepared. Prepared points are reachable only throughg1_prepared_forandg2_prepared_for, so downstream code can't index them without the prefix check. Newg1_lenandg2_lenreturn the cache size. Code outside the crate can't build aPreparedCachewith a struct literal.init_cachebuilds it.Cost: each lookup compares the input with the cached points (
nprojective equality checks, in parallel). This is not free. In one release-mode measurement on a Mac, the check for 1024 G2 points took about 8 ms, against about 101 ms for the cache-hitmulti_pair_g2_setupit guards, so roughly 8% of that pairing. The prover repeats it for each setup pairing in every reduce round. Tagging the cache with a setup identity would remove most of this cost; this PR does not do that.Validation
On a Linux sandbox, against
mainat 5246da5:main,foreign_cache_falls_backandproof_verifies_after_foreign_cachefail, under nextest as well. They pass on this branch.cargo fmt --check;zk, and with--no-default-features;cargo docwith-D warnings;cargo nextestwith the transparent features (88 passed);cargo nextestwithzk: 4 of 5 cold runs passed (145 tests each). One run failedtest_workflow_without_precommitmentandtest_zero_polynomial. Those tests don't use the cache, and the failure did not reproduce. I didn't capture its output. The likely cause is the disk-persisted setup race fixed in fix(setup): publish setup files atomically and keep the first one saved #34.cargo teststill fails on this branch with only this change, because of that setup-file race. With fix(setup): publish setup files atomically and keep the first one saved #34 also applied, plaincargo test --features backends,parallel,cache,disk-persistence,zkpassed:--test-threads=1;🤖 Generated with Claude Code