Skip to content

feat: add parity matrix to config circuit [skip-line-limit]#1342

Merged
0xjei merged 1 commit into
mainfrom
configs/parity_matrix
Feb 18, 2026
Merged

feat: add parity matrix to config circuit [skip-line-limit]#1342
0xjei merged 1 commit into
mainfrom
configs/parity_matrix

Conversation

@zahrajavar

@zahrajavar zahrajavar commented Feb 17, 2026

Copy link
Copy Markdown
Contributor

Adds the parity matrix verification to the config circuit.

Summary by CodeRabbit

  • New Features
    • Added parity matrix verification for DKG configuration validation to strengthen cryptographic integrity checks
    • Implemented cross-configuration consistency validation to ensure parameter alignment between different configuration layers
    • Introduced public utility function for centered value computation

@zahrajavar zahrajavar requested a review from 0xjei February 17, 2026 20:36
@vercel

vercel Bot commented Feb 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
crisp Ready Ready Preview, Comment Feb 18, 2026 2:26pm
enclave-docs Ready Ready Preview, Comment Feb 18, 2026 2:26pm

Request Review

@coderabbitai

coderabbitai Bot commented Feb 17, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

This PR updates the DKG config verification circuit to add parity matrix verification logic, introduces a public centering function for modular arithmetic, restructures configuration imports, and enhances cross-config consistency checks. The changes also refactor threshold and verification bounds to use updated parameter references.

Changes

Cohort / File(s) Summary
DKG Parity Matrix Verification
circuits/bin/config/src/main.nr
Added verify_dkg_parity_matrix() function that constructs a Vandermonde generator matrix and validates H · G^T = 0 (mod q_l) for each CRT modulus; integrated into DKG derived-values verification flow. Updated imports to include PARITY_MATRIX and related DKG configuration symbols.
Centering and Configuration Utilities
circuits/bin/config/src/main.nr
Added public center() function to normalize field values from [0, modulus) to [-modulus/2, modulus/2). Introduced DKG_Q_MOD_T_CENTERED handling and cross-config consistency verification for N, L, and QIS arrays between DKG and Threshold configurations.
Verification Bounds Updates
circuits/bin/config/src/main.nr
Updated e_sm_bound and related threshold verification logic to use PARAMS_SEARCH_N instead of prior N_PARTIES references; refreshed DKG configuration imports and removed obsolete bounds-related exports.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • cedoor

Poem

🐰 Parity checks dance in modular moonlight,
Vandermonde matrices spin symmetric and tight,
Centering values with rabbity care,
DKG harmonies float through the air,
Verification whispers—the config's now fair! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding parity matrix verification to the config circuit, which aligns with the primary additions (verify_dkg_parity_matrix function and related validations).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch configs/parity_matrix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (10)
crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs (1)

215-248: test_configs_generation_contains_expected does not assert Q_MOD_T_CENTERED.

The new Q_MOD_T_CENTERED global is emitted by the codegen but not validated by the test. A single additional assertion would catch any future drift between compute and codegen output.

🧪 Suggested assertion
 assert!(artifacts.configs.contains("SHARE_ENCRYPTION_CONFIGS"));
+assert!(artifacts.configs.contains("Q_MOD_T_CENTERED"));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs` around lines
215 - 248, Add an assertion in test_configs_generation_contains_expected that
the codegen emitted Q_MOD_T_CENTERED; after computing bounds
(Bounds::compute(...)) check artifacts.configs contains the formatted constant
using the test's prefix and bounds.q_mod_t_centered value, e.g.
assert!(artifacts.configs.contains(format!("{}_Q_MOD_T_CENTERED: u32 = {}",
prefix, bounds.q_mod_t_centered).as_str())); this ties
ShareEncryptionCircuit.codegen output to Bounds::compute.
circuits/lib/src/configs/secure/dkg.nr (1)

17-18: Same documentation gap as insecure/dkg.nr: add a comment explaining Q_MOD_T_CENTERED == Q_MOD_T.

Q mod T = 1082658244788225 < T/2 ≈ 9007199254740992, so the centered value is the same as the raw value. A brief inline comment prevents future maintainers from questioning whether this is a copy-paste bug.

✏️ Suggested comment
 pub global Q_MOD_T: Field = 1082658244788225;
-pub global Q_MOD_T_CENTERED: Field = 1082658244788225;
+// Q_MOD_T_CENTERED == Q_MOD_T because Q mod T < T/2 (already in the positive half of the centered range)
+pub global Q_MOD_T_CENTERED: Field = 1082658244788225;

Based on learnings, documenting centered-range encoding conventions is the established practice in these config files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@circuits/lib/src/configs/secure/dkg.nr` around lines 17 - 18, Add a short
inline comment next to the global constants Q_MOD_T and Q_MOD_T_CENTERED
explaining that Q_MOD_T_CENTERED equals Q_MOD_T because Q_MOD_T
(1082658244788225) is less than T/2, so the centered representation is
identical; update the declarations for Q_MOD_T and Q_MOD_T_CENTERED to include
this explanatory note so future maintainers won't treat the duplication as a
copy/paste bug.
circuits/lib/src/configs/insecure/dkg.nr (1)

17-18: Add a comment clarifying why Q_MOD_T_CENTERED == Q_MOD_T.

Since Q mod T = 2415755265 < T/2 ≈ 34359701504, the centered representation is identical to the raw value. Without a comment, this looks like an accidental copy-paste. The same applies to secure/dkg.nr.

✏️ Suggested comment
 pub global Q_MOD_T: Field = 2415755265;
-pub global Q_MOD_T_CENTERED: Field = 2415755265;
+// Q_MOD_T_CENTERED == Q_MOD_T because Q mod T < T/2 (already in the positive half of the centered range)
+pub global Q_MOD_T_CENTERED: Field = 2415755265;

Based on learnings, the convention in this codebase is to document the centered-range encoding in config files when adding new constants of this kind.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@circuits/lib/src/configs/insecure/dkg.nr` around lines 17 - 18, Add a short
comment next to the Q_MOD_T and Q_MOD_T_CENTERED declarations explaining that
Q_MOD_T_CENTERED equals Q_MOD_T because the constant value 2415755265 is less
than T/2 so its centered (signed) representation matches the raw value; update
the same comment pattern in secure/dkg.nr as well so future readers know this is
intentional and not a copy-paste error (refer to symbols Q_MOD_T and
Q_MOD_T_CENTERED).
crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs (1)

634-643: test_constants_json_roundtrip doesn't assert on q_mod_t_centered.

The roundtrip test checks q_mod_t, moduli, k0is, bits, and bounds, but the newly added q_mod_t_centered field is not verified. Consider adding:

 assert_eq!(decoded.q_mod_t, constants.q_mod_t);
+assert_eq!(decoded.q_mod_t_centered, constants.q_mod_t_centered);
 assert_eq!(decoded.moduli, constants.moduli);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs` around
lines 634 - 643, The test test_constants_json_roundtrip omits asserting the
newly added q_mod_t_centered field; update the test to compare
decoded.q_mod_t_centered with constants.q_mod_t_centered (alongside the existing
assertions for q_mod_t, moduli, k0is, bits, and bounds) so the JSON roundtrip
verifies that field as well.
crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs (1)

142-154: Minor: compute_q_mod_t_centered recomputes q internally.

q is already computed on Line 142, but compute_q_mod_t_centered(&moduli, t) on Line 144 recomputes it internally. Not a problem since this is config-generation code (not hot-path), but if you ever refactor, a variant that accepts a pre-computed q would avoid the redundancy.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs`
around lines 142 - 154, compute_q_mod_t_centered recomputes q even though q is
already computed into the local variable q by compute_q_product(&moduli); update
the call site to use a variant that accepts the precomputed q (e.g., add and
call compute_q_mod_t_centered_with_q(q.clone(), &moduli, t) or similar) or
modify compute_q_mod_t_centered to accept Option<&BigInt>/q parameter so it
reuses the existing q; change the caller here (where q, q_mod_t,
q_mod_t_centered are set) to pass the precomputed q to the new/overloaded
function (referencing compute_q_product, q, compute_q_mod_t_centered) so the
redundant recomputation is avoided.
circuits/benchmarks/scripts/generate_report.sh (2)

211-261: Empty Config tables rendered in insecure mode.

The Config section is always emitted (Lines 212-261), but in insecure mode no config JSON files exist, resulting in table headers with no data rows (visible in the insecure report). The comment on Line 211 acknowledges this. Consider conditionally emitting the section only when config data exists, but this is minor since it doesn't affect functionality.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@circuits/benchmarks/scripts/generate_report.sh` around lines 211 - 261, The
Config tables are always written even when no "config" JSONs exist; before
emitting the Config section headers and running the two for-loops, scan
INPUT_DIR for any json file where category_of "$json_file" == "config" (or run a
short pre-loop that sets a flag like has_config=true when you find one), and
only output the "### Config" / "#### Timing Metrics" and "#### Size & Circuit
Metrics" headers and run the detailed loops if that flag is true; use the
existing symbols category_of, INPUT_DIR, OUTPUT_FILE and the same json-file
checks to keep behavior identical when config files are present.

89-90: Broad glob pattern for config category detection.

The pattern *"config"* would match any circuit path containing "config". This is fine given current naming conventions, but if a future circuit name happens to include "config" (e.g., "threshold/reconfiguration"), it would be mis-categorized.

A stricter match like "config" (exact) or "config/"* could be considered, but this is unlikely to be a problem in practice.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@circuits/benchmarks/scripts/generate_report.sh` around lines 89 - 90, The
current branch that categorizes a circuit as "config" uses a broad glob ([[
"$path" == *"config"* ]]) and can misclassify paths that merely contain the
substring "config"; update the condition that checks the variable path (the elif
block examining "$path") to use a stricter match such as exact equality to
"config" or a prefix match like "config/"* (i.e., replace the *"config"* glob
with either == "config" or == "config/"*), so only true config directories are
categorized as "config".
examples/CRISP/circuits/src/utils.nr (1)

19-24: Doc comment is slightly misleading.

Line 19 says "The field representation of 1 in centered form", but the value is the centered representation of q mod t, not necessarily 1. It equals 1 only for certain parameter sets. Consider:

-/// * `q_mod_t_centered` - The field representation of 1 in centered form
+/// * `q_mod_t_centered` - The centered representation of q mod t
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/CRISP/circuits/src/utils.nr` around lines 19 - 24, The doc comment
for the parameter q_mod_t_centered in check_coefficient_values_with_balance is
misleading — it currently says "The field representation of 1 in centered form"
but this parameter is the centered field representation of q mod t (not
necessarily 1); update the doc comment for q_mod_t_centered to state that it is
the centered representation of q mod t (or "the field representation of q mod t
in centered form") and clarify it may only equal 1 for certain parameter sets so
callers shouldn't assume it's always 1.
circuits/benchmarks/results_insecure/report.md (1)

56-66: Empty Config section expected in insecure mode.

The Config section with empty tables is consistent with the design that config circuit runs only in secure mode. Based on learnings, benchmark reports should filter or skip incomplete data for circuits running in non-target configurations. The current approach (empty tables) is acceptable but slightly noisy.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@circuits/benchmarks/results_insecure/report.md` around lines 56 - 66, Remove
or suppress the "Config" section (the "Timing Metrics" and "Size & Circuit
Metrics" empty tables) when generating reports for insecure mode; update the
report generation logic that emits the "Config" header and those two tables so
it checks the run configuration (insecure vs secure) and either skips rendering
the section entirely or replaces it with a single concise placeholder line like
"Config: not applicable in insecure mode" rather than emitting empty tables,
locating the generation points that produce the "Config" header/"Timing
Metrics"/"Size & Circuit Metrics" blocks in the report template or renderer and
adding the conditional there.
circuits/bin/config/src/main.nr (1)

317-318: Consider using the centered Q_MOD_T for a tighter smudging noise bound.

Now that THRESHOLD_Q_MOD_T_CENTERED is available, this bound formula could potentially use |Q_MOD_T_CENTERED| instead of the raw THRESHOLD_Q_MOD_T for a tighter b_c (and consequently e_sm_bound). When Q_MOD_T > t/2, the centered value's magnitude is smaller, yielding a stricter bound.

This may be intentionally deferred — just flagging for consideration.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@circuits/bin/config/src/main.nr` around lines 317 - 318, The current
computation of b_c uses the raw THRESHOLD_Q_MOD_T which can be looser; update
the formula in the b_c assignment (where b_c is computed from num_ciphertexts,
b_fresh, THRESHOLD_Q_MOD_T) to use the magnitude of the centered constant (i.e.,
|THRESHOLD_Q_MOD_T_CENTERED|) instead of THRESHOLD_Q_MOD_T so the smudging bound
(e_sm_bound) is tighter when the centered residue is smaller; ensure
THRESHOLD_Q_MOD_T_CENTERED is defined/imported and use its absolute value in the
multiplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@circuits/benchmarks/config.json`:
- Line 3: The "config" benchmark is effectively never run under the default
"mode": "insecure" because run_benchmarks.sh guards it; make this behavior
explicit by updating the circuits/benchmarks/config.json entry for "config" to
indicate it is secure-only (e.g., replace the plain "config" string with a
descriptor that includes a modes or secureOnly flag such as
{"name":"config","modes":["secure"]}) and/or add a brief note in the benchmarks
README; reference the "config" entry in circuits/benchmarks/config.json and the
mode check in run_benchmarks.sh so the intent is clear to future readers.

In `@circuits/benchmarks/results_insecure/report.md`:
- Around line 5-6: The report's Git metadata lines currently reference branch
`configs/fixqmt` and commit `689e56cb90251b34e67af87cb7abfed03bedcd1c` which do
not match this PR's branch `configs/parity_matrix`; update the report.md
metadata to reflect the correct branch and commit for this PR by regenerating
the benchmarks (preferred) or manually replacing the `configs/fixqmt` branch and
`689e56cb90251b34e67af87cb7abfed03bedcd1c` commit entries with the current
branch name and commit hash so the report accurately corresponds to this change
set.

---

Nitpick comments:
In `@circuits/benchmarks/results_insecure/report.md`:
- Around line 56-66: Remove or suppress the "Config" section (the "Timing
Metrics" and "Size & Circuit Metrics" empty tables) when generating reports for
insecure mode; update the report generation logic that emits the "Config" header
and those two tables so it checks the run configuration (insecure vs secure) and
either skips rendering the section entirely or replaces it with a single concise
placeholder line like "Config: not applicable in insecure mode" rather than
emitting empty tables, locating the generation points that produce the "Config"
header/"Timing Metrics"/"Size & Circuit Metrics" blocks in the report template
or renderer and adding the conditional there.

In `@circuits/benchmarks/scripts/generate_report.sh`:
- Around line 211-261: The Config tables are always written even when no
"config" JSONs exist; before emitting the Config section headers and running the
two for-loops, scan INPUT_DIR for any json file where category_of "$json_file"
== "config" (or run a short pre-loop that sets a flag like has_config=true when
you find one), and only output the "### Config" / "#### Timing Metrics" and
"#### Size & Circuit Metrics" headers and run the detailed loops if that flag is
true; use the existing symbols category_of, INPUT_DIR, OUTPUT_FILE and the same
json-file checks to keep behavior identical when config files are present.
- Around line 89-90: The current branch that categorizes a circuit as "config"
uses a broad glob ([[ "$path" == *"config"* ]]) and can misclassify paths that
merely contain the substring "config"; update the condition that checks the
variable path (the elif block examining "$path") to use a stricter match such as
exact equality to "config" or a prefix match like "config/"* (i.e., replace the
*"config"* glob with either == "config" or == "config/"*), so only true config
directories are categorized as "config".

In `@circuits/bin/config/src/main.nr`:
- Around line 317-318: The current computation of b_c uses the raw
THRESHOLD_Q_MOD_T which can be looser; update the formula in the b_c assignment
(where b_c is computed from num_ciphertexts, b_fresh, THRESHOLD_Q_MOD_T) to use
the magnitude of the centered constant (i.e., |THRESHOLD_Q_MOD_T_CENTERED|)
instead of THRESHOLD_Q_MOD_T so the smudging bound (e_sm_bound) is tighter when
the centered residue is smaller; ensure THRESHOLD_Q_MOD_T_CENTERED is
defined/imported and use its absolute value in the multiplication.

In `@circuits/lib/src/configs/insecure/dkg.nr`:
- Around line 17-18: Add a short comment next to the Q_MOD_T and
Q_MOD_T_CENTERED declarations explaining that Q_MOD_T_CENTERED equals Q_MOD_T
because the constant value 2415755265 is less than T/2 so its centered (signed)
representation matches the raw value; update the same comment pattern in
secure/dkg.nr as well so future readers know this is intentional and not a
copy-paste error (refer to symbols Q_MOD_T and Q_MOD_T_CENTERED).

In `@circuits/lib/src/configs/secure/dkg.nr`:
- Around line 17-18: Add a short inline comment next to the global constants
Q_MOD_T and Q_MOD_T_CENTERED explaining that Q_MOD_T_CENTERED equals Q_MOD_T
because Q_MOD_T (1082658244788225) is less than T/2, so the centered
representation is identical; update the declarations for Q_MOD_T and
Q_MOD_T_CENTERED to include this explanatory note so future maintainers won't
treat the duplication as a copy/paste bug.

In `@crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs`:
- Around line 215-248: Add an assertion in
test_configs_generation_contains_expected that the codegen emitted
Q_MOD_T_CENTERED; after computing bounds (Bounds::compute(...)) check
artifacts.configs contains the formatted constant using the test's prefix and
bounds.q_mod_t_centered value, e.g.
assert!(artifacts.configs.contains(format!("{}_Q_MOD_T_CENTERED: u32 = {}",
prefix, bounds.q_mod_t_centered).as_str())); this ties
ShareEncryptionCircuit.codegen output to Bounds::compute.

In `@crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs`:
- Around line 634-643: The test test_constants_json_roundtrip omits asserting
the newly added q_mod_t_centered field; update the test to compare
decoded.q_mod_t_centered with constants.q_mod_t_centered (alongside the existing
assertions for q_mod_t, moduli, k0is, bits, and bounds) so the JSON roundtrip
verifies that field as well.

In
`@crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs`:
- Around line 142-154: compute_q_mod_t_centered recomputes q even though q is
already computed into the local variable q by compute_q_product(&moduli); update
the call site to use a variant that accepts the precomputed q (e.g., add and
call compute_q_mod_t_centered_with_q(q.clone(), &moduli, t) or similar) or
modify compute_q_mod_t_centered to accept Option<&BigInt>/q parameter so it
reuses the existing q; change the caller here (where q, q_mod_t,
q_mod_t_centered are set) to pass the precomputed q to the new/overloaded
function (referencing compute_q_product, q, compute_q_mod_t_centered) so the
redundant recomputation is avoided.

In `@examples/CRISP/circuits/src/utils.nr`:
- Around line 19-24: The doc comment for the parameter q_mod_t_centered in
check_coefficient_values_with_balance is misleading — it currently says "The
field representation of 1 in centered form" but this parameter is the centered
field representation of q mod t (not necessarily 1); update the doc comment for
q_mod_t_centered to state that it is the centered representation of q mod t (or
"the field representation of q mod t in centered form") and clarify it may only
equal 1 for certain parameter sets so callers shouldn't assume it's always 1.

Comment thread circuits/benchmarks/config.json
Comment thread circuits/benchmarks/results_insecure/report.md
@0xjei 0xjei added this to the PHASE 2: PROVE & VERIFY milestone Feb 18, 2026
@0xjei 0xjei linked an issue Feb 18, 2026 that may be closed by this pull request
@0xjei 0xjei enabled auto-merge (squash) February 18, 2026 10:36
@0xjei 0xjei requested review from cedoor and ctrlc03 February 18, 2026 10:41
@0xjei 0xjei merged commit 75dda28 into main Feb 18, 2026
26 checks passed
@ctrlc03 ctrlc03 deleted the configs/parity_matrix branch February 18, 2026 15:00
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.

Parity Matrix verification

3 participants