feat(circom): CRL non-membership via full fixed-depth SMT (benchmark) - #875
Draft
kilianglas wants to merge 1 commit into
Draft
feat(circom): CRL non-membership via full fixed-depth SMT (benchmark)#875kilianglas wants to merge 1 commit into
kilianglas wants to merge 1 commit into
Conversation
Candidate 2 of 3 for credential revocation list support. The credential id is the leaf position in a depth-64 sparse Merkle tree, so proving the credential is not revoked is just inclusion of an empty leaf at that position — no comparators, no low leaf, no auxiliary witness. This needs no new gadget: it is BinaryMerkleRoot(64) with leaf = 0 and index = cred_id. A second template drops the dynamic-depth machinery so its cost can be measured separately. Not for merge — opened to measure constraint cost against the indexed Merkle tree and compressed SMT candidates. Co-Authored-By: Claude Opus 5 (1M context) <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.
Candidate 2 of 3 for credential revocation list (CRL) support. Not for merge — one of three branches opened so the designs can be compared on measured constraint cost. Related:
bench/crl-imt-low-leaf,bench/crl-smt-compressed.What this proves
The credential is not revoked, where the revocation list is a full fixed-depth sparse Merkle tree keyed on the
u64credential id. The key is the leaf position, so the tree depth equals the key width (64) and a live credential is simply an empty (0) leaf at that position.Non-membership is therefore one plain Merkle inclusion proof of an empty leaf — no comparators, no low leaf, no auxiliary witness. Soundness comes from position = key: no sibling set reproduces the root with
leaf = 0if the committed leaf is1.This variant needs no new gadget. It is
BinaryMerkleRoot(64)withleaf = 0andindex = cred_id, both already in the repo and already exercised by the account-tree path. That makes it by far the lowest-risk candidate to implement.Measured cost
circom 2.2.3, R1CS totals (non-linear + linear):
OPRFNullifierProofonmainBinaryMerkleRoot(64))OPRFNullifierProofwith gadgetThis is the most expensive of the three candidates, and its cost is independent of how many credentials are actually revoked — depth is the key width, so a CRL with 10 entries costs the same as one with a million.
The second template drops the dynamic-depth machinery in
BinaryMerkleRoot(per-levelIsEqual, trailing index-range constraints), which this variant does not need. It saves only 387 constraints (1.2%), so that machinery is not worth avoiding. Depth sweep: d32 15,780 · d48 23,668 · d64 31,556 — about 493 constraints per level, the same rate as the other two candidates.A hashed key is not viable here: it would demand ~254 levels, roughly 125k constraints for the gadget alone. That constrains this variant to a per-issuer tree, since a raw
u64cred_idis only unique per issuer.Scope
Data structure and in-circuit gadget only. Tree maintenance — maintainer service, on-chain root anchoring, insert batching, reorg handling — is deliberately out of scope.
Un-revocation is trivial in this design: clear the leaf back to
0, one independent write.Tests
circom/tests/tests/crl_non_membership_smt_full.test.jscovers an unrevoked key plus rejection of a revoked key, a mismatched sibling path and a wrong root, for both templates. The existingoprf_nullifierKAT is extended with the CRL witness and still produces the same nullifier. Full suite green.Fixtures were generated with
taceo-poseidon2, the crate the Rust side already uses, so they independently confirm the circom Poseidon2 tree math.