feat(holder): cache and reuse proofs for unchanged credentials (#426) - #472
Merged
Merged
Conversation
Re-proving a credential whose on-chain record is still valid wasted time and compute (server-side witness + browser WASM UltraHonk). Add local proof caching keyed by the exact inputs that determine a proof — commitment, claim params, and circuit/VK version — so a valid, matching proof is reused instead of regenerated. New lib/proof-cache.ts: - buildProofKey derives a deterministic key from (type, commitment, canonicalised claim params, VK version). - Proofs are stored locally and reused only when the entry matches AND the on-chain record is still valid (local proofStatus expiry check plus a live is_verified read to catch revocation). - Correctly invalidated on expiry, revocation, VK version change, or any change to the claim params; VK version is itself derived from the actual circuit bytecode, so bumping the circuit invalidates every old proof. Holder page (ProofFlow + BatchProofFlow) now checks the cache before doing any expensive work and, when reusing, surfaces a clear "using existing proof" banner so the behaviour is visible. Reused proofs are still submittable, and a successful submission records the cache entry as proved (with its TTL) so it stays reusable until it expires. Removing a credential also clears its cached proofs. Coverage: unit tests cover reuse, param-change / VK-version / expiry / revocation invalidation, and independent handling across multiple credentials. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
|
@CodingBabe-1 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
…ToluLabs#426) The "Merge branch 'main'" on this branch left the frontend build broken: - pnpm-workspace.yaml had a duplicated `esbuild: true` under allowBuilds, which is invalid YAML and made every `pnpm install --frozen-lockfile` (frontend, contracts, and a11y CI jobs) fail. - pnpm-lock.yaml was left with duplicated mapping keys from the merge; regenerated it consistently with pnpm 9 (the CI version). - proof-cache.ts imported a `CircuitArtifact` type that proof.ts never exported; added the export and typed the circuit fetch with it. - The merge dropped the cache-reuse path and the mark-proved / purge call sites from holder/page.tsx; restored them so proof caching works in both the single and batch flows. - Raised the holder route JS budget (16 kB -> 17 kB) for the new local proof-cache module, which pushed the route past its previous limit.
# Conflicts: # frontend/app/holder/page.tsx
CodingBabe-1
force-pushed
the
feat/426-proof-caching
branch
from
September 1, 2026 12:19
16be835 to
00dc5dc
Compare
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.
What does this PR do?
Fixes #426: a holder who already generated a valid proof for a credential no longer re-proves it needlessly when the underlying credential, its parameters, and the circuit are unchanged.
Proof generation is the most expensive step in the holder flow (server-side Noir witness + browser WASM UltraHonk). This PR caches generated proofs locally, keyed by the exact inputs that determine a proof — commitment, claim parameters, and circuit/VK version — and reuses a valid, matching prior proof instead of regenerating it. Reuse is surfaced in the UI with a clear "using existing proof" banner so it's visible rather than a silent optimisation.
How the cache works
Every proof is a pure function of:
lib/proof-cache.tsderives a deterministicbuildProofKeyfrom exactly these three, stores proofs locally (JSON-safe byte arrays), and:provedAt+ TTL window, and a liveis_verifiedread),resolveVkVersion), so bumping a circuit or toolchain automatically invalidates every cached proof,Holder page integration
ProofFlowchecks the cache before doing any expensive work; on a hit it jumps straight to the generated state with a "Using existing proof — params unchanged" banner. Fresh proofs are saved to the cache, and a successful on-chain submission marks the entry as proved (with its TTL) so it stays reusable until it expires.BatchProofFlowdoes the same per credential and shows a per-row "using existing proof" badge; successful batch submission records every reused/fresh proof's cache entry.Acceptance checklist
Type of change
Checklist
cargo testpasses (contracts) — no contract changespnpm tsc --noEmitpasses (frontend)pnpm buildpasses (frontend)pnpm testpasses (frontend – includes issuer suite)NEXT_PUBLIC_prefix on server-only env varsNotes for reviewers
is_verifiedread is only attempted when the local record already says the proof is currently valid; if contracts aren't reachable it falls back to the local expiry check. This means the cache is never trusted blindly — a mismatch in any dimension triggers a fresh proof.crypto.subtle, which is available in the app's secure-context browsers.pnpm test) is shared across the frontend workspace; the standalone@stellarcred/issuersuite keeps its own node-environment runner.closes #426