Skip to content

feat: add share-decryption circuit gen and refactoring [skip-line-limit]#1269

Merged
cedoor merged 15 commits into
mainfrom
port/dkg-c4
Feb 10, 2026
Merged

feat: add share-decryption circuit gen and refactoring [skip-line-limit]#1269
cedoor merged 15 commits into
mainfrom
port/dkg-c4

Conversation

@0xjei

@0xjei 0xjei commented Feb 5, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added a share-decryption circuit to the ZK toolkit with sample generation, witness/config generation, and CLI support for end-to-end codegen.
  • Refactor

    • Consolidated decryption bit-parameter into a single unified configuration across presets.
    • Streamlined sample-generation APIs and test/sample flows for multiple circuits, improving consistency of generated witnesses and configs.

@0xjei 0xjei added this to the PHASE 2: PROVE & VERIFY milestone Feb 5, 2026
@0xjei 0xjei requested a review from cedoor February 5, 2026 16:52
@0xjei 0xjei self-assigned this Feb 5, 2026
@vercel

vercel Bot commented Feb 5, 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 9, 2026 10:52am
enclave-docs Ready Ready Preview, Comment Feb 9, 2026 10:52am

Request Review

@coderabbitai

coderabbitai Bot commented Feb 5, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

Consolidates share-decryption bit-message constants into a single SHARE_DECRYPTION_BIT_MSG, updates Noir circuit callsites, and adds a new DKG share-decryption circuit (circuit, computation, codegen, sample) plus supporting zk-helpers utilities, tests, and CLI registration.

Changes

Cohort / File(s) Summary
Config Consolidation
circuits/lib/src/configs/insecure/dkg.nr, circuits/lib/src/configs/secure/dkg.nr
Removed two per-circuit SHARE_DECRYPTION_BIT_MSG_* constants and introduced one consolidated SHARE_DECRYPTION_BIT_MSG with updated bit values.
Noir Circuit Callsites
circuits/bin/dkg/e_sm_share_decryption/src/main.nr, circuits/bin/dkg/sk_share_decryption/src/main.nr
Updated imports and generic type parameters to use SHARE_DECRYPTION_BIT_MSG instead of circuit-specific constants.
New Share-Decryption Circuit
crates/zk-helpers/src/circuits/dkg/share_decryption/{mod.rs,circuit.rs,codegen.rs,computation.rs,sample.rs}
Added ShareDecryptionCircuit, input type, computation (Configs/Bits/Bounds/Witness), codegen (TOML/configs), sample generation, and tests.
CLI & Registry
crates/zk-helpers/src/bin/zk_cli.rs, crates/zk-helpers/src/circuits/dkg/mod.rs
Registered ShareDecryptionCircuit in CLI/registry and added pub mod share_decryption. Updated generate/codegen flows to use *CircuitInput::generate_sample.
CRT / Ring Utilities
crates/zk-helpers/src/crt.rs, crates/zk-helpers/src/ring.rs, crates/zk-helpers/src/lib.rs
Added compute_k0is, fhe_poly_to_crt_centered, cyclotomic_polynomial, decompose_residue helpers and new crt/ring modules with re-exports.
Sample & Test Refactors
crates/zk-helpers/src/circuits/**/sample.rs, crates/zk-helpers/src/circuits/**/codegen.rs, crates/zk-helpers/src/circuits/**/computation.rs
Standardized tests to use *CircuitInput::generate_sample, removed several legacy sample wrappers and prepare_* helpers, and adapted codegen tests to accept generated samples.
Public API Re-exports
crates/zk-helpers/src/circuits/mod.rs, various mod.rs files
Removed multiple public re-exports of legacy sample helpers and some previous public sample types; tightened public re-export surface and added share_decryption exports.
Computation Helpers Integration
crates/zk-helpers/src/circuits/dkg/{pk,share_encryption,share_computation}/..., crates/zk-helpers/src/circuits/threshold/...
Replaced repeated CRT/polynomial logic with centralized helpers (compute_k0is, cyclotomic_polynomial, decompose_residue, fhe_poly_to_crt_centered) and moved parity matrix logic to utils.
JSON Helpers
crates/zk-helpers/src/utils.rs
Added bigint_2d_to_json_values to serialize 2D BigInt arrays into JSON string values.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI/zk_cli
    participant Sample as SampleGen
    participant Compute as CircuitComputation
    participant Codegen as CircuitCodegen
    participant Artifacts as ArtifactsStore

    CLI->>Sample: generate_sample(preset, committee, dkg_input_type)
    Sample-->>CLI: ShareDecryptionCircuitInput

    CLI->>Compute: compute(preset, input)
    Compute->>Compute: Configs::compute / Bits::compute / Bounds::compute / Witness::compute
    Compute-->>CLI: ShareDecryptionOutput

    CLI->>Codegen: codegen(preset, input)
    Codegen->>Codegen: generate_toml(witness), generate_configs(preset, configs)
    Codegen-->>Artifacts: Artifacts { toml, configs }
    Artifacts-->>CLI: Ready for prover
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

noir

Suggested reviewers

  • cedoor

Poem

🐇 I stitched two bits into one, hopped through code at speed,

I built the witness, TOML, and configs that you need.
From samples to codegen I danced across the floor —
A rabbit-made circuit delivered at the door! ✨

🚥 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 clearly summarizes the main change: adding share-decryption circuit generation and performing related refactoring work.
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 port/dkg-c4

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.

@0xjei

0xjei commented Feb 6, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 3

🤖 Fix all issues with AI agents
In `@crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs`:
- Line 167: The current call
input.secret_key.try_decrypt(&party_cts[mod_idx]).unwrap() will panic on
failure; replace unwrap() with the ? operator to propagate the decryption error
(e.g., let decrypted_pt = input.secret_key.try_decrypt(&party_cts[mod_idx])?;),
update the enclosing function's signature to return a Result type (propagating
the try_decrypt error type or mapping it into your crate's error enum), and
update any callers to handle the returned Result accordingly so decryption
failures are handled instead of panicking.
- Around line 150-189: In compute (fn compute) avoid panicking by replacing the
.unwrap() on input.secret_key.try_decrypt with error propagation: call
try_decrypt(...).map_err(|e| CircuitsErrors::Sample(e.to_string()))? so
decryption failures return Err instead of panic; keep building
expected_commitments and decrypted_shares and return Ok(Witness {
expected_commitments, decrypted_shares }) as before.

In `@crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs`:
- Around line 174-176: The assertion incorrectly compares
sample.sum_ciphertexts.len() to committee.threshold; instead assert it equals
the number of CRT moduli used to build it (i.e.,
threshold_params.moduli().len()). Update both tests
(test_generate_secret_key_sample and test_generate_smudging_noise_sample) to
replace the second assert_eq! so it checks sample.sum_ciphertexts.len() ==
threshold_params.moduli().len(), referencing sample.sum_ciphertexts,
threshold_params.moduli(), and committee.threshold to locate the code.
🧹 Nitpick comments (5)
crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs (2)

61-115: Inner loop generates the same share_row for every TRBFV basis — intentional?

The modulus loop (line 65) iterates threshold_params.moduli().len() times, but the share_row computed inside (e.g., sk_sss_u64[0].row(0) at line 78) doesn't vary with the loop index. Every ciphertext within a single party encrypts the same plaintext data, just with different encryption randomness.

For a test sample generator this is probably fine, but the comment on line 130 ("The message should be the same for all TRBFV bases") is a consequence of this decision, not an inherent property of the scheme. A brief comment at line 65 noting that the same share is reused across bases for simplicity would help future readers.


94-96: rng.clone() produces a duplicate RNG state.

Cloning the RNG at line 96 means generate_secret_shares_from_poly receives a copy with the same state. If the original rng is used afterward in the same iteration, both could produce identical sequences. This is acceptable in test sample code per project conventions (learnings confirm RNG reuse in tests is fine), but a brief // clone is fine for test samples comment would prevent future reviewers from flagging it again.

crates/zk-helpers/src/bin/zk_cli.rs (1)

334-353: New generation branch looks correct and consistent.

The match arm mirrors the ShareEncryptionCircuit pattern (lines 288–313), correctly fetching search_defaults, generating a sample, and constructing ShareDecryptionCircuitInput with the right fields.

As a minor note, the search_defaults / sample-generation / codegen pattern is now duplicated across three arms (ShareComputation, ShareEncryption, ShareDecryption). If more circuits follow, consider extracting a shared helper. Not blocking for this PR.

crates/zk-helpers/src/circuits/dkg/share_decryption/codegen.rs (1)

51-72: dkg_counterpart().unwrap() can panic for non-DKG presets on a pub function.

generate_configs is public, but the two .unwrap() calls on line 67–68 will panic if a caller passes a preset without a DKG counterpart. The CLI validates this upstream, but a standalone caller of this API won't.

Consider returning a Result or, at minimum, extracting the counterpart once and using expect with a clear message.

Proposed fix
-pub fn generate_configs(preset: BfvPreset, configs: &Configs) -> CodegenConfigs {
+pub fn generate_configs(preset: BfvPreset, configs: &Configs) -> Result<CodegenConfigs, CircuitsErrors> {
     let prefix = <ShareDecryptionCircuit as Circuit>::PREFIX;
+    let dkg = preset
+        .dkg_counterpart()
+        .ok_or_else(|| CircuitsErrors::Sample("preset has no DKG counterpart".into()))?;
 
-    format!(
+    Ok(format!(
         r#"pub global N: u32 = {};
 pub global L: u32 = {};
 ...
 pub global {}_BIT_MSG: u32 = {};
 "#,
-        preset.dkg_counterpart().unwrap().metadata().degree,
-        preset.dkg_counterpart().unwrap().metadata().num_moduli,
+        dkg.metadata().degree,
+        dkg.metadata().num_moduli,
         prefix,
         configs.bits.msg_bit,
-    )
+    ))
 }

This would also require updating the call site in codegen() (line 32) to propagate the error.

crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs (1)

99-117: Configs::compute calls Bounds::compute(preset, &input) — the extra & is redundant.

input is already &ShareDecryptionCircuitInput, so &input introduces an extra indirection layer that is resolved via deref coercion. Passing input directly is clearer.

Proposed fix
-        let bounds = Bounds::compute(preset, &input)?;
+        let bounds = Bounds::compute(preset, input)?;

Comment thread crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs
Comment thread crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs Outdated
Comment thread crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs Outdated

@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

🤖 Fix all issues with AI agents
In `@crates/zk-helpers/src/circuits/crt.rs`:
- Around line 42-57: In reconstruct_crt_coefficients: stop silently falling back
to BigInt::from(1u64) when mod_inverse returns None and avoid unguarded
crt.limb(0) access; instead, validate that crt has at least one limb (return Err
or panic with a clear message if empty) and replace the unwrap_or_else on
mod_inverse with a failure that surfaces the problem (e.g., use expect or
propagate a Result) including context (i, qi, q_i) so missing inverses
(non-coprime or duplicate moduli) fail loudly and can be diagnosed; keep the
rest of the CRT loop logic the same.

In `@crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs`:
- Around line 140-142: The comment above the decryption call is misleading: in
this sample generator each TRBFV basis encrypts an independent plaintext (the
inner loop creates a fresh secret per (party, modulus)), so the messages are not
the same across bases; update the comment at the
`dkg_secret_key.try_decrypt(&sum_ciphertexts[0]).unwrap()` line to state that
only the aggregate plaintext for basis 0 is being decrypted/stored and that
other bases contain unrelated plaintexts (or, if intended, change the code to
decrypt each entry in `sum_ciphertexts` instead). Ensure you reference
`sum_ciphertexts` and `dkg_secret_key.try_decrypt` in the updated comment to
make the behavior clear.
🧹 Nitpick comments (11)
crates/zk-helpers/src/circuits/ring.rs (1)

33-87: Potential underflow in degree assertions when polynomial length is zero.

The pattern (poly.coefficients().len() as u64) - 1 (Lines 50, 58, 66, 72, 76) will wrap around to u64::MAX if the polynomial has zero coefficients, causing a confusing panic message instead of a meaningful error. While this scenario is not expected with valid FHE inputs, a guard or a descriptive message would help debugging if it ever occurs.

Example defensive check (optional)
-    let num_coeffs = xi.sub(xi_hat).coefficients().to_vec();
-    assert_eq!((num_coeffs.len() as u64) - 1, 2 * (n - 1));
+    let num_coeffs = xi.sub(xi_hat).coefficients().to_vec();
+    assert!(
+        !num_coeffs.is_empty(),
+        "decompose_residue: xi - xi_hat produced an empty polynomial"
+    );
+    assert_eq!((num_coeffs.len() as u64) - 1, 2 * (n - 1));
crates/zk-helpers/src/circuits/parity.rs (1)

44-65: Noir constant dimensions are hardcoded — ensure they stay in sync with the circuit template.

The dimension string [[[Field; N_PARTIES + 1]; N_PARTIES - T]; L_THRESHOLD] on Line 62 is embedded as a literal. If the Noir circuit template changes these constant names (e.g., L_THRESHOLDL), this will silently produce invalid Noir code. Consider extracting these dimension names as constants or parameters if the names are expected to evolve.

This isn't blocking since the same approach is used by the generate_configs function in codegen.rs.

crates/zk-helpers/src/bin/zk_cli.rs (1)

208-211: Stale comment: "Only share-computation has a witness-type choice" no longer accurate.

The comment on line 208 states that only share-computation has a witness-type choice, but has_witness_type now also includes ShareEncryptionCircuit and ShareDecryptionCircuit.

✏️ Suggested comment fix
-    // Only share-computation has a witness-type choice (secret-key vs smudging-noise). pk always uses secret key.
+    // share-computation, share-encryption, and share-decryption have a witness-type choice (secret-key vs smudging-noise).
crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs (1)

207-207: Minor inconsistency: circuit_input_from_sample is a free function here, but an associated method on other sample types.

ShareEncryptionCircuit uses circuit_input_from_sample(&sample, preset) as a free function, while PkSample::circuit_input_from_sample(...) and UserDataEncryptionSample::circuit_input_from_sample(...) are associated methods. This isn't a bug, but aligning the API surface would improve discoverability.

Also applies to: 227-227

crates/zk-helpers/src/circuits/threshold/user_data_encryption/sample.rs (1)

29-64: Non-deterministic encryption inside circuit_input_from_sample.

Each call to circuit_input_from_sample performs a fresh try_encrypt_extended with thread_rng() (line 41), producing different ciphertexts and randomness each time. This is fine for test/codegen use, but it differs from the ShareEncryptionSample pattern where the ciphertext is pre-stored in the sample.

If deterministic replay matters for debugging, consider storing the ciphertext and randomness in UserDataEncryptionSample (like ShareEncryptionSample does) and converting them here. Otherwise, the current approach is acceptable for its purpose.

crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs (1)

113-142: Inconsistent API placement: free function vs. associated method.

circuit_input_from_sample is a free function here, whereas ShareComputationSample, UserDataEncryptionSample, and PkSample (see crates/zk-helpers/src/circuits/dkg/pk/sample.rs lines 29–41) all define it as an associated method on their respective sample types. Consider moving this into impl ShareEncryptionSample for discoverability and consistency.

Suggested restructure
-/// Builds [`ShareEncryptionCircuitInput`] from a sample by converting FHE polynomials to CRT form
-/// (same pattern as [`crate::threshold::pk_generation::PkGenerationCircuitInput::generate_sample`]).
-pub fn circuit_input_from_sample(
-    sample: &ShareEncryptionSample,
-    preset: BfvPreset,
-) -> Result<ShareEncryptionCircuitInput, CircuitsErrors> {
+impl ShareEncryptionSample {
+    // ... existing generate() ...
+
+    /// Builds [`ShareEncryptionCircuitInput`] from a sample by converting FHE polynomials to CRT form.
+    pub fn circuit_input_from_sample(
+        sample: &ShareEncryptionSample,
+        preset: BfvPreset,
+    ) -> Result<ShareEncryptionCircuitInput, CircuitsErrors> {
+        // ... body unchanged ...
+    }
+}
crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs (1)

311-341: Multiple .to_u128().unwrap() calls on bounds could panic for large parameter sets.

All bound values are converted via .to_u128().unwrap(). If the cryptographic parameters ever produce bounds exceeding u128::MAX, these will panic. For current insecure/test presets this is fine, but production presets with larger moduli could be a concern.

Consider propagating errors instead of unwrapping, or at minimum adding a comment noting the u128 assumption.

crates/zk-helpers/src/circuits/dkg/share_decryption/codegen.rs (2)

54-72: Duplicate .unwrap() on preset.dkg_counterpart() — extract and propagate error.

Lines 67 and 68 each call preset.dkg_counterpart().unwrap() independently. If the preset has no DKG counterpart, this panics in codegen. Extract the result once and convert None to an error since generate_configs could return Result.

Alternatively, if the caller guarantees a valid preset, at least deduplicate:

Proposed fix: deduplicate and fail gracefully
 pub fn generate_configs(preset: BfvPreset, configs: &Configs) -> CodegenConfigs {
     let prefix = <ShareDecryptionCircuit as Circuit>::PREFIX;
+    let dkg_meta = preset.dkg_counterpart().expect("preset must have a DKG counterpart").metadata();
 
     format!(
         r#"pub global N: u32 = {};
 pub global L: u32 = {};
 
 /************************************
 -------------------------------------
 share_decryption_sk (CIRCUIT 4a - BFV DECRYPTION SK)
 share_decryption_e_sm (CIRCUIT 4b - BFV DECRYPTION E_SM)
 -------------------------------------
 ************************************/
 
 pub global {}_BIT_MSG: u32 = {};
 "#,
-        preset.dkg_counterpart().unwrap().metadata().degree,
-        preset.dkg_counterpart().unwrap().metadata().num_moduli,
+        dkg_meta.degree,
+        dkg_meta.num_moduli,
         prefix,
         configs.bits.msg_bit,
     )
 }

42-48: Nit: simplify map_err closure to method reference.

Minor style fix
     let json = witness
         .to_json()
-        .map_err(|e| CircuitsErrors::SerdeJson(e))?;
+        .map_err(CircuitsErrors::SerdeJson)?;
crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs (1)

344-579: Witness computation is structurally near-identical to user_data_encryption.

The limb-wise parallel CRT processing, e0 reconstruction, decompose_residue calls, and CrtPolynomial aggregation in this file closely mirror the same logic in threshold/user_data_encryption/computation.rs (lines 350–554 there). The key differences are:

  • Uses message field instead of k1
  • Different commitment functions (compute_dkg_pk_commitment + compute_share_encryption_commitment_from_message vs compute_pk_aggregation_commitment + compute_ciphertext_commitment)
  • Uses DKG params instead of threshold params

Given these circuits have genuinely different semantics, the duplication is understandable. Consider extracting the shared CRT-limb-processing core into a helper if a third circuit needs the same pattern.

crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs (1)

94-117: Inconsistent error mapping between Configs::compute and Witness::compute.

Line 101 maps build_pair_for_preset errors to ZkHelpersUtilsError::ParseBound, while line 152 maps the same call to CircuitsErrors::Sample. Using a consistent error mapping pattern within the same module would improve clarity.

Comment thread crates/zk-helpers/src/circuits/crt.rs Outdated
Comment thread crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs Outdated
@0xjei

0xjei commented Feb 6, 2026

Copy link
Copy Markdown
Contributor Author

@cedoor ready

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs (1)

135-160: ⚠️ Potential issue | 🔴 Critical

Bug: test_generate_smudging_noise_sample tests the wrong DkgInputType.

Line 141 passes DkgInputType::SecretKey instead of DkgInputType::SmudgingNoise. This is a copy-paste error — the smudging noise code path is completely untested.

🐛 Proposed fix
         let sample = ShareEncryptionCircuitInput::generate_sample(
             BfvPreset::InsecureThreshold512,
             CiphernodesCommitteeSize::Small,
-            DkgInputType::SecretKey,
+            DkgInputType::SmudgingNoise,
             sd.z,
             sd.lambda,
         );
🤖 Fix all issues with AI agents
In `@crates/zk-helpers/src/bin/zk_cli.rs`:
- Line 268: preset.search_defaults().unwrap() can panic in production; replace
the unwrap with graceful handling by checking the result of
preset.search_defaults() (e.g., match or if let) and emit a clear user-facing
error and non-zero exit when no search defaults are present. Locate the call
that binds sd from preset.search_defaults() in zk_cli.rs (the sd variable) and
change it to handle the None/Err case: print/log a descriptive message including
which preset failed and exit with a non-zero status (or return a Result::Err) so
the CLI doesn't panic.

In `@crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs`:
- Around line 164-179: The loop over mod_idx silently skips when threshold_l >
party_cts.len(), causing party_commitments and party_shares to be shorter than
expected; update the code in the block that builds
party_commitments/party_shares (the loop using threshold_l, party_cts,
input.secret_key.try_decrypt, compute_share_encryption_commitment_from_message)
to explicitly enforce the invariant by either (a) checking upfront that
party_cts.len() >= threshold_l and returning an Err or panicking with a clear
message, or (b) replacing the conditional inside the loop with an explicit error
when mod_idx >= party_cts.len(); ensure the function returns a proper Result or
panics consistently so downstream consumers always see exactly threshold_l
entries per party.

In `@crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs`:
- Around line 97-112: The computed but unused variable sum_ciphertexts should
either be removed and the module docstring updated, or actually stored in the
ShareDecryptionCircuitInput returned by build (i.e., add a sum_ciphertexts:
Vec<Ciphertext> field to the ShareDecryptionCircuitInput struct and populate it
when constructing the value in the function that builds the input); update any
struct constructors/serializers and call sites to handle the new field, and
update the module doc (the docstring that promises "sum ciphertexts" and
"message") to match the chosen behavior.
- Around line 132-136: The assertion for secret key degree uses
DEFAULT_BFV_PRESET but the test actually builds parameters for a specific preset
(e.g., BfvPreset::InsecureThreshold512); update the test to derive the expected
degree from the actual preset used to build the DKG parameters (call
build_pair_for_preset(...) with the explicit preset where the committee and
sample are created) and replace the DEFAULT_BFV_PRESET.metadata().degree
reference in the assertion comparing sample.secret_key.coeffs.len() with the
preset.metadata().degree so the test uses the same preset as the generated
secret key.
🧹 Nitpick comments (7)
crates/zk-helpers/src/circuits/dkg/share_computation/utils.rs (1)

44-45: Nit: simplify the map_err closure.

The closure |e| CircuitsErrors::Sample(e) can be replaced with a method reference.

♻️ Suggested simplification
-        .map_err(|e| CircuitsErrors::Sample(e))?;
+        .map_err(CircuitsErrors::Sample)?;
crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs (1)

82-87: Nit: _ct prefix is misleading since the value is used.

The underscore prefix in _ct conventionally signals an unused binding, but it's assigned to sample.ciphertext on line 87. Consider renaming to ct.

♻️ Suggested rename
-        let (_ct, u_rns, e0_rns, e1_rns) =
+        let (ct, u_rns, e0_rns, e1_rns) =
             dkg_public_key.try_encrypt_extended(&pt, &mut rng).unwrap();
 
         ShareEncryptionCircuitInput {
             plaintext: pt,
-            ciphertext: _ct,
+            ciphertext: ct,
crates/zk-helpers/src/circuits/dkg/share_computation/sample.rs (1)

33-33: Nit: search_defaults() is computed unconditionally but only used in the SmudgingNoise branch.

Consider moving preset.search_defaults().unwrap() inside the SmudgingNoise arm to avoid unnecessary computation in the SecretKey path.

crates/zk-helpers/src/circuits/threshold/pk_generation/computation.rs (1)

349-358: pk1is is assigned a by move after a is cloned — consider a brief comment.

pk1is: a reuses the moved a (which equals pk1 in BFV). The clone on line 350 (a: a.clone()) goes to the a field while the original is moved to pk1is. This is correct and avoids a redundant clone, but the semantic equivalence of pk1is and a might surprise a future reader.

crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs (1)

74-76: rng.clone() is a no-op for ThreadRng.

ThreadRng delegates to the thread-local PRNG; cloning it produces another handle to the same underlying state. The .clone() on line 76 is misleading — it doesn't create an independent RNG. Just pass &mut rng directly.

Suggested fix
                         let esi_sss_u64 = share_manager
-                            .generate_secret_shares_from_poly(esi_poly.clone(), &mut rng.clone())
+                            .generate_secret_shares_from_poly(esi_poly.clone(), &mut rng)
crates/zk-helpers/src/ring.rs (1)

33-88: Consider documenting preconditions more explicitly for decompose_residue.

The function has strict mathematical preconditions on its inputs (e.g., xi must have degree n-1, xi_hat must have degree 2*(n-1), cyclo must be the cyclotomic polynomial of degree n). Violations cause panics at various assertion points, which is documented at a high level, but callers would benefit from knowing exactly what input shapes are expected.

The assertions themselves are valuable as they verify the mathematical invariants at each step — this is appropriate for ZK witness computation where silent corruption would be far worse than a panic.

crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs (1)

99-101: Inconsistent error handling in Configs::compute—use direct CircuitsErrors::Sample for consistency with Witness::compute.

Both Configs::compute (line 99) and Witness::compute (line 152) handle errors from build_pair_for_preset(preset) with the same trait return type (CircuitsErrors), but they map errors differently:

  • Configs::compute maps through ZkHelpersUtilsError::ParseBound (indirect, via From impl)
  • Witness::compute uses CircuitsErrors::Sample (direct)

The ParseBound variant is semantically misleading here—the error isn't about parsing bounds, it's about building parameters. Change to match the pattern established by Witness::compute:

Proposed fix
-        let (_, dkg_params) = build_pair_for_preset(preset)
-            .map_err(|e| crate::utils::ZkHelpersUtilsError::ParseBound(e.to_string()))?;
+        let (_, dkg_params) = build_pair_for_preset(preset)
+            .map_err(|e| CircuitsErrors::Sample(e.to_string()))?;

Comment thread crates/zk-helpers/src/bin/zk_cli.rs Outdated
Comment thread crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs Outdated
Comment thread crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs Outdated
Comment thread crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs

@cedoor cedoor 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.

utACK

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.

2 participants