Skip to content

refactor: porting of greco circuit (aka greco) [skip-line-limit]#1260

Merged
cedoor merged 24 commits into
mainfrom
refactor/greco
Feb 4, 2026
Merged

refactor: porting of greco circuit (aka greco) [skip-line-limit]#1260
cedoor merged 24 commits into
mainfrom
refactor/greco

Conversation

@cedoor

@cedoor cedoor commented Feb 3, 2026

Copy link
Copy Markdown
Contributor

Re #1259
Closes #1227

Summary by CodeRabbit

  • New Features

    • Added a complete user-data-encryption circuit: computation, codegen (Prover.toml + configs), sample/witness generation, public key generation, wasm/SDK bindings, and tests.
  • Refactor

    • Replaced GRECO-based input flow with a preset-driven, circuit-based pipeline; standardized codegen types and JSON serialization across circuits; simplified Noir input handling.
  • Chores

    • Removed legacy GRECO helpers/crates and related exports; added utilities for bit-widths, commitments, JSON merging, and updated manifests.

@cedoor cedoor requested a review from 0xjei February 3, 2026 19:47
@vercel

vercel Bot commented Feb 3, 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 4, 2026 8:09pm
enclave-docs Ready Ready Preview, Comment Feb 4, 2026 8:09pm

Request Review

@cedoor cedoor marked this pull request as ready for review February 3, 2026 19:47
@coderabbitai

coderabbitai Bot commented Feb 3, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds a new UserDataEncryption circuit (circuit, computation, codegen, sample, utils), migrates CRISP GRECO-based input flows to the new circuit and preset-driven APIs, removes greco-helpers and related workspace deps, adapts codegen/computation traits, updates clients/SDK/Noir configs, and introduces BFV↔Greco conversion and commitments.

Changes

Cohort / File(s) Summary
Circuit root & exports
crates/zk-helpers/src/circuits/mod.rs
Expose CodegenConfigs/CodegenToml, reduce legacy computation re-exports, add threshold module, stop re-exporting TomlJson.
New UserDataEncryption module
crates/zk-helpers/src/circuits/threshold/mod.rs, crates/zk-helpers/src/circuits/threshold/user_data_encryption/*
Add user_data_encryption module with circuit, codegen, computation, sample, utils; public re-exports.
Circuit type & sample
.../user_data_encryption/circuit.rs, .../sample.rs
Add UserDataEncryptionCircuit, UserDataEncryptionCircuitInput, UserDataEncryptionSample and tests.
Computation pipeline (large)
crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs
New Configs, Bounds, Bits, Witness, UserDataEncryptionComputationOutput; implement Computation/CircuitComputation, JSON serialization and many computation details/tests.
Codegen & artifacts
.../user_data_encryption/codegen.rs, crates/zk-helpers/src/circuits/codegen.rs
Add generate_toml/generate_configs, tests; introduce CodegenToml/CodegenConfigs aliases; update Artifacts/CircuitCodegen signatures.
Greco ↔ BFV utils & commitments
.../user_data_encryption/utils.rs, crates/zk-helpers/src/utils.rs
Add BFV↔Greco conversions, compute_public_key_commitment/compute_ciphertext_commitment, change calculate_bit_width to BigInt, add compute_pk_bit and polynomial→TOML JSON helpers; extend utils errors.
Samples & preset-driven APIs
crates/zk-helpers/src/circuits/*/sample.rs
Convert sample generators to preset-driven signatures (remove Arc), simplify test helpers.
DKG / share computation & pk modules
crates/zk-helpers/src/circuits/dkg/*
Rename associated type BfvThresholdParametersPresetPreset, adapt compute/codegen signatures, switch Toml/Configs to Codegen* types, add to_json serializers, update tests and exports (remove TomlJson).
Remove greco-helpers & workspace edits
crates/greco-helpers/*, Cargo.toml, crates/Dockerfile
Delete greco-helpers crate/manifest, remove workspace member and git greco dependency, remove Docker COPY entries for greco-helpers.
CRISP examples migration
examples/CRISP/crates/zk-inputs/*, examples/CRISP/crates/zk-inputs-wasm/*, examples/CRISP/server/Dockerfile
Replace GRECO flows with UserDataEncryptionCircuit outputs; rename CiphertextAdditionInputsCiphertextAdditionWitness; remove old serialization module; add merge_json_objects.
BFV client & CLI wiring
crates/bfv-client/src/client.rs, crates/zk-helpers/src/bin/zk_cli.rs, crates/wasm/src/lib.rs
Switch client/CLI/wasm to circuit-based processing, add generate_public_key wrappers, compute commitments via new helpers, register UserDataEncryptionCircuit, adopt preset-based sample generation.
Noir configs & params
circuits/lib/src/configs/insecure/threshold.nr, crates/fhe-params/src/constants.rs, examples/CRISP/circuits/src/main.nr
Adjust PLAINTEXT_MODULUS and many USER_DATA_ENCRYPTION bit widths/bounds, add USER_DATA_ENCRYPTION_Q_MOD_T_MOD_P, update config constructor/uses and main.nr imports.
Frontend / enclave SDK
packages/enclave-sdk/src/greco.ts, packages/enclave-sdk/src/index.ts, packages/enclave-sdk/src/enclave-sdk.ts, packages/enclave-sdk/src/types.ts
Remove polynomial-conversion helpers/exports, add pk_commitment/e0_quotients to CircuitInputs, call noir.execute with raw string arrays, add generatePublicKey() to SDK and update tests.
Computation trait & errors
crates/zk-helpers/src/circuits/computation.rs, crates/zk-helpers/src/circuits/errors.rs
Remove Toml/Configs aliases, rename associated types to Preset, add default Computation::to_json, add CircuitsErrors::SerdeJson.
Examples serialization removal
examples/CRISP/crates/zk-inputs/src/serialization.rs
Remove GRECO serialization module, ZKInputs, helpers and associated tests (file deleted).

Sequence Diagram

sequenceDiagram
    rect rgba(200,200,255,0.5)
    participant User as User/Caller
    end
    rect rgba(200,255,200,0.5)
    participant Circuit as UserDataEncryptionCircuit
    participant Computation as ComputationPipeline
    participant Codegen as Codegen
    participant Artifacts as ArtifactsStore
    end

    User->>Circuit: codegen(preset, input)
    Circuit->>Computation: compute(preset, input)
    Computation->>Computation: derive Configs → Bounds → Bits → Witness
    Computation-->>Circuit: witness, bits, configs
    Circuit->>Codegen: generate_toml(witness)
    Codegen->>Codegen: format CodegenToml
    Circuit->>Codegen: generate_configs(preset, configs)
    Codegen->>Codegen: format CodegenConfigs
    Codegen-->>Artifacts: Artifacts{toml, configs}
    Artifacts-->>User: return artifacts
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

noir

Suggested reviewers

  • 0xjei

Poem

🐇 I hopped from GRECO tunnels into code so bright,
I pack keys and plain and stitch a witness tight,
Bits and bounds I nibble, TOML I hum along,
I bundle configs and artifacts — a carrot-sized song! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor: porting of greco circuit (aka greco)' relates to the main changes, which involve consolidating greco-helpers functionality into zk-helpers as submodules and removing greco-related dependencies.
Linked Issues check ✅ Passed The PR successfully addresses issue #1227 by converting e3-greco-helpers into a submodule (threshold/user_data_encryption) within e3-zk-helpers, removing the separate crate, and consolidating related functionality.
Out of Scope Changes check ✅ Passed All changes are scoped to consolidating greco functionality into zk-helpers as submodules and updating dependent code. Minor updates to Noir circuit constants and example code are necessary follow-ups to the consolidation.
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 docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/greco

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.

@cedoor cedoor changed the title refactor: porting of user_data_encryption circuit (aka greco) refactor: porting of user_data_encryption circuit (aka greco) [skip-size-limit] Feb 3, 2026
@cedoor cedoor changed the title refactor: porting of user_data_encryption circuit (aka greco) [skip-size-limit] refactor: porting of user_data_encryption circuit (aka greco) [skip-line-limit] Feb 3, 2026
@theinterfold theinterfold deleted a comment from github-actions Bot Feb 3, 2026
@vercel vercel Bot temporarily deployed to Preview – crisp February 3, 2026 19:49 Inactive
@vercel vercel Bot temporarily deployed to Preview – enclave-docs February 3, 2026 19:49 Inactive

@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 `@examples/CRISP/crates/zk-inputs/src/lib.rs`:
- Around line 115-117: Replace redundant calculate_bit_width calls and re-use
the precomputed ct_bit from the circuit output: wherever code sets bit_ct from
calculate_bit_width(&bounds.pk_bounds[0].to_string()) (the local variable bit_ct
and the bounds = UserDataEncryptionBounds::compute(...) usage), instead read
user_data_encryption_computation_output.bits.ct_bit and assign that to bit_ct;
do the same replacement for the second occurrence that duplicates this logic.
For the third spot (the place currently creating Bits from a string), instead
compute a Bits value consistent with the circuit by constructing it from
user_data_encryption_computation_output.bits (e.g., derive the Bits instance
from ct_bit or the circuit bits structure) so the runtime matches the
circuit-determined widths; finally remove the now-unused
UserDataEncryptionBounds::compute call and any related temporary variables
(bounds, calculate_bit_width usage) to clean up dead code.
🧹 Nitpick comments (5)
crates/zk-helpers/src/utils.rs (1)

189-202: Consider adding unit tests for polynomial_to_toml_json.

The function is well-implemented, but unlike crt_polynomial_to_toml_json, there are no tests covering this new utility. Consider adding a test case to verify the JSON structure matches expectations, especially since the output format differs (individual { "coefficient": ... } objects vs. { "coefficients": [...] } arrays).

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

22-25: Consider deriving Debug for UserDataEncryptionCircuitInput.

UserDataEncryptionCircuit derives Debug, but UserDataEncryptionCircuitInput does not. Adding #[derive(Debug)] (if PublicKey and Plaintext support it) would improve consistency and aid debugging.

crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs (2)

782-784: Unnecessary clone before sorting.

results is already an owned Vec, so the clone is redundant. You can sort it in place directly.

-let mut results = results.clone();
+let mut results = results;
 results.sort_by_key(|(i, ..)| *i);

865-865: Redundant field name syntax.

The k1: k1 uses redundant field initialization syntax.

-k1: k1,
+k1,
examples/CRISP/crates/zk-inputs/src/ciphertext_addition.rs (1)

182-193: Align test bit_ct with the circuit bit width.

The test helper derives bit_ct from pk_bounds[0]. To keep tests aligned with the circuit configs, prefer Bits::ct_bit from the user-data-encryption computation.

♻️ Suggested refactor
@@
-    let bounds = Bounds::compute(params, &()).unwrap();
-    calculate_bit_width(&bounds.pk_bounds[0].to_string()).unwrap()
+    let bounds = Bounds::compute(params, &()).unwrap();
+    let bits = e3_zk_helpers::threshold::Bits::compute(params, &bounds).unwrap();
+    bits.ct_bit
🧹 Cleanup (if no longer used)
-use e3_zk_helpers::utils::calculate_bit_width;

Comment thread crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs Outdated
Comment thread examples/CRISP/crates/zk-inputs/src/lib.rs Outdated
@cedoor cedoor changed the title refactor: porting of user_data_encryption circuit (aka greco) [skip-line-limit] refactor: porting of greco circuit (aka greco) [skip-line-limit] Feb 4, 2026

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

🤖 Fix all issues with AI agents
In
`@crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs`:
- Around line 151-159: The loop that computes k0qi and pushes into k0is uses
k0qi.to_u64().unwrap_or(0), which silently tolerates conversion failures;
replace the unwrap_or(0) with a checked conversion that returns an error instead
(e.g., k0qi.to_u64().ok_or_else(||
CircuitsErrors::Fhe(fhe::Error::MathError(fhe_math::Error::Default("Failed to
convert k0qi to u64".into()))))? ) and propagate the error with ? so failures in
converting k0qi (computed in the ctx.moduli_operators() loop) do not become 0s;
update the push to push the successful u64 into k0is (referencing variables k0qi
and k0is in computation.rs).
- Around line 566-567: Replace the panic-prone `.unwrap()` on the modular
inverse call so failures are propagated instead of panicking: change the code
that computes `koqi_u64 = qi.inv(qi.neg(t.modulus())).unwrap()` to handle the
`Option`/`Result` return from `qi.inv` (e.g., map/and_then or `match`) and
propagate an error (return a `Result`) up from the enclosing closure/function,
then construct `k0qi` only on success; update the enclosing closure/function
signature to return `Result<..., E>` and convert callers accordingly so errors
from `qi.inv(...)` are consistently handled instead of unwrapping.
- Around line 519-544: The closures building e0i and e0i_from_poly call
.as_slice().ok_or_else(...).unwrap() inside what is run in parallel, which can
panic and lose context; change these closures (where e0i and e0i_from_poly are
constructed using qi.center_vec_vt, e0_coeffs and e0_poly_coeffs) to return
Result<Vec<BigInt>, String> (or a suitable error type) instead of unwrapping,
propagate any as_slice() or center_vec_vt errors out of the closure, and then
collect the parallel results with collect::<Result<_,_>>() (or equivalent) so
callers can handle/report errors instead of panicking. Ensure you reference
qi.center_vec_vt, e0_coeffs, e0_poly_coeffs, e0i and e0i_from_poly when applying
the change.
- Around line 258-263: The code currently uses to_u128().unwrap() when computing
e0_bound (see e0_bound, params.get_error1_variance, and uniform_bound), which
can panic if uniform_bound > u128::MAX; change this to a safe, non-panicking
conversion and propagate the failure: replace the unwrap with a fallible
conversion (e.g., try_from or to_u128().ok_or(...)) and return or propagate an
error from the surrounding function instead of panicking, ensuring e0_bound
computation handles the Result/Option and keeps cbd_bound as the fallback
branch.
- Around line 324-354: The Bounds construction currently calls
.to_u128().unwrap() in multiple places (pk_bounds, k1_low_bound, k1_up_bound,
r1_low_bounds, r1_up_bounds, r2_bounds, p1_bounds, p2_bounds) which can panic on
overflow; update these conversions to use fallible conversions (e.g.
.to_u128().ok_or(...) or TryInto with .map_err(...)) and propagate a Result from
the enclosing function instead of panicking so overflows return a clear error;
locate the Bounds return in computation.rs (the Bounds { ... } block) and change
each map/assignment that uses .to_u128().unwrap() to return a mapped error on
failure and ensure the surrounding function signature and callers handle the
Result error accordingly.
🧹 Nitpick comments (2)
crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs (2)

276-276: Remove unused variable _num_moduli.

The variable is declared but never used.

♻️ Suggested fix
-        let _num_moduli = ctx.moduli().len();

868-868: Use field shorthand for cleaner code.

k1: k1 can be simplified to k1.

♻️ Suggested fix
-            k1: k1,
+            k1,

Comment thread crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.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/threshold/user_data_encryption/computation.rs`:
- Around line 382-385: The current code uses .to_bigint().unwrap() on e0_mod_q
which can panic; change the conversion to return a Result and propagate errors
instead: replace the map+collect into something like e0_mod_q.iter().map(|c|
c.to_bigint().ok_or_else(|| CircuitsErrors::Other(format!("failed converting
BigUint to BigInt: {:?}", c)))).collect::<Result<Vec<BigInt>,
CircuitsErrors>>()? so e0_bigints is produced without unwrap, then pass that
Vec<BigInt> into Poly::from_bigints(&e0_bigints, &ctx) as before and propagate
any errors from Poly::from_bigints using the existing map_err(...)?. Reference
symbols: e0_mod_q, e0_bigints, Poly::from_bigints, CircuitsErrors::Other.

In `@crates/zk-helpers/src/utils.rs`:
- Around line 195-207: The function polynomial_to_toml_json currently emits one
object per coefficient ("coefficient": ...), but consumers expect the schema
with a single object containing a "coefficients" array like
crt_polynomial_to_toml_json; change polynomial_to_toml_json to produce a single
serde_json::Value with key "coefficients" whose value is an array of the
coefficient strings (e.g., collect polynomial.coefficients().iter().map(|c|
c.to_string()) into a Vec and wrap it in serde_json::json!({ "coefficients": vec
})). Ensure the function still returns Vec<serde_json::Value> (e.g., return a
single-element Vec containing that object) and keep using the
Polynomial::coefficients() accessor.
🧹 Nitpick comments (2)
crates/zk-helpers/src/circuits/threshold/user_data_encryption/sample.rs (1)

25-37: Avoid unwrap() in a public generator; return a Result instead.

This is a public API and will panic if encoding fails. Prefer returning a Result and letting callers decide how to handle the error.

♻️ Suggested change (update call sites accordingly)
-    pub fn generate(params: &Arc<BfvParameters>) -> Self {
+    pub fn generate(params: &Arc<BfvParameters>) -> Result<Self, fhe::Error> {
         let mut rng = thread_rng();
 
         let secret_key = SecretKey::random(&params, &mut rng);
         let public_key = PublicKey::new(&secret_key, &mut rng);
 
-        let plaintext = Plaintext::try_encode(&[1u64], Encoding::poly(), &params).unwrap();
+        let plaintext = Plaintext::try_encode(&[1u64], Encoding::poly(), &params)?;
 
-        Self {
+        Ok(Self {
             public_key,
             plaintext,
-        }
+        })
     }
-        let sample = Sample::generate(&params);
+        let sample = Sample::generate(&params).expect("Failed to generate sample");
crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs (1)

867-867: Minor: redundant binding k1: k1.

The field initialization can use shorthand syntax.

✨ Suggested fix
-            k1: k1,
+            k1,

Comment thread crates/zk-helpers/src/utils.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/threshold/user_data_encryption/computation.rs`:
- Line 392: The current conversion q_mod_t = (ctx.modulus() %
t.modulus()).to_u64().unwrap() can panic; replace the unwrap with a safe
conversion that returns a Result and propagate the error instead of panicking
(e.g., call to_u64().ok_or(...)? or try_from and use ?), following the file's
existing error-handling pattern; update the code around q_mod_t, referencing
ctx.modulus(), t.modulus(), and to_u64, so the function returns an error instead
of panicking when the modulus exceeds u64::MAX.

In `@crates/zk-helpers/src/circuits/threshold/user_data_encryption/utils.rs`:
- Around line 93-99: compute_pk_bit currently assumes params.moduli()[0] is the
largest modulus which can undercompute the bit width and cause truncated
commitments; update compute_pk_bit to first check that params.moduli() is
non-empty (return a ZkHelpersUtilsError on empty), iterate through all moduli to
find the maximum modulus, convert that max modulus into a BigInt, compute bound
= (max_modulus - 1) / 2 and pass that to calculate_bit_width; reference the
compute_pk_bit function and mirror the max-modulus selection pattern used in the
pk_bfv computation (crates/zk-helpers/src/circuits/pk_bfv/computation.rs) when
locating where to change the logic.
🧹 Nitpick comments (6)
crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs (6)

196-218: Consider using iterator .max() for cleaner code.

The manual loops to find maximum bit widths could be simplified using iterator methods.

♻️ Optional refactor using iterator pattern
         // For r1, use the maximum of all low and up bounds
-        let mut r1_bit = 0;
-        for bound in input.r1_low_bounds.iter().chain(input.r1_up_bounds.iter()) {
-            r1_bit = r1_bit.max(calculate_bit_width(BigInt::from(bound.clone())));
-        }
+        let r1_bit = input
+            .r1_low_bounds
+            .iter()
+            .chain(input.r1_up_bounds.iter())
+            .map(|b| calculate_bit_width(BigInt::from(b.clone())))
+            .max()
+            .unwrap_or(0);
 
         // For r2, use the maximum of all bounds
-        let mut r2_bit = 0;
-        for bound in &input.r2_bounds {
-            r2_bit = r2_bit.max(calculate_bit_width(BigInt::from(bound.clone())));
-        }
+        let r2_bit = input
+            .r2_bounds
+            .iter()
+            .map(|b| calculate_bit_width(BigInt::from(b.clone())))
+            .max()
+            .unwrap_or(0);
 
         // For p1, use the maximum of all bounds
-        let mut p1_bit = 0;
-        for bound in &input.p1_bounds {
-            p1_bit = p1_bit.max(calculate_bit_width(BigInt::from(bound.clone())));
-        }
+        let p1_bit = input
+            .p1_bounds
+            .iter()
+            .map(|b| calculate_bit_width(BigInt::from(b.clone())))
+            .max()
+            .unwrap_or(0);
 
         // For p2, use the maximum of all bounds
-        let mut p2_bit = 0;
-        for bound in &input.p2_bounds {
-            p2_bit = p2_bit.max(calculate_bit_width(BigInt::from(bound.clone())));
-        }
+        let p2_bit = input
+            .p2_bounds
+            .iter()
+            .map(|b| calculate_bit_width(BigInt::from(b.clone())))
+            .max()
+            .unwrap_or(0);

546-559: Assertions in production code may cause silent failures in parallel context.

Multiple assert! and assert_eq! calls throughout the parallel closure (lines 546, 559, 577, 582, 586, 596, 602, 616-617, 628-629, etc.) will panic on failure. In a parallel context, these panics can be difficult to diagnose. Consider converting critical assertions to Result returns, or documenting that these are invariant checks that indicate bugs rather than invalid inputs.


665-667: Consider more efficient leading zero removal.

Using remove(0) in a loop is O(n²) worst case. For polynomials with many leading zeros, this could be inefficient.

♻️ Optional: More efficient approach
-                while !ct0i_calculated.is_empty() && ct0i_calculated[0].is_zero() {
-                    ct0i_calculated.remove(0);
-                }
+                let leading_zeros = ct0i_calculated.iter().take_while(|x| x.is_zero()).count();
+                ct0i_calculated.drain(..leading_zeros);

784-786: Unnecessary clone before sorting.

The results variable is already owned, so the clone is unnecessary.

♻️ Remove unnecessary clone
         // Sort by modulus index so CRT limbs are in order
-        let mut results = results.clone();
+        let mut results = results;
         results.sort_by_key(|(i, ..)| *i);

867-867: Use field init shorthand.

When the field name matches the variable name, shorthand syntax is preferred.

♻️ Use shorthand
-            k1: k1,
+            k1,

902-911: Test uses hardcoded expected value that may break if parameters change.

The assertion assert_eq!(bounds.pk_bounds[0], BigUint::from(34359701504u64)) will fail if DEFAULT_BFV_PRESET is modified. Consider documenting why this specific value is expected or deriving it from the parameters.

Comment thread crates/zk-helpers/src/circuits/threshold/user_data_encryption/utils.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: 1

🤖 Fix all issues with AI agents
In
`@crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs`:
- Around line 183-187: In compute (fn compute) the pk_bit and ct_bit are
computed from input.pk_bounds[0], which is incorrect when pk_bounds are not
ordered; change the logic to find the maximum modulus in input.pk_bounds (e.g.,
iterate input.pk_bounds to select the largest BigInt), then call
calculate_bit_width on that maximum to derive pk_bit and ct_bit (keep ct_bit
equal to the pk max as before); update references to pk_bounds[0] to use the
computed max value so pk_bit/ct_bit correctly reflect the largest pk bound.
🧹 Nitpick comments (1)
crates/zk-helpers/src/circuits/pk_bfv/computation.rs (1)

189-198: Test name may be misleading after refactor.

The test test_bound_and_bits_computation_consistency no longer verifies consistency between bounds and bits—it now independently checks each. The assertion bits.pk_bit == expected_bits is tautological since both use compute_pk_bit. Consider either:

  1. Renaming to test_bound_and_bits_computation to reflect the decoupled nature, or
  2. Adding an assertion that pk_bit is sufficient to represent pk_bound (e.g., pk_bound.bits() <= pk_bit as u64).

Comment thread crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs Outdated
@0xjei 0xjei added this to the PHASE 2: PROVE & VERIFY milestone Feb 4, 2026
@vercel vercel Bot temporarily deployed to Preview – crisp February 4, 2026 17:10 Inactive
@vercel vercel Bot temporarily deployed to Preview – enclave-docs February 4, 2026 17:10 Inactive

@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 `@examples/CRISP/crates/zk-inputs/src/ciphertext_addition.rs`:
- Around line 177-181: The docstring for the method to_json on the
CiphertextAddition witness incorrectly states it returns a "JSON string" while
the signature returns a serde_json::Value; update the doc comment for to_json to
say it returns a serde_json::Value (a JSON value/object representation) and
clarify that callers get a JSON value rather than a serialized string so they
can call to_string()/serde_json::to_string if they need a string.

In `@examples/CRISP/crates/zk-inputs/src/lib.rs`:
- Around line 95-101: The code calls UserDataEncryptionCircuit::compute with the
hard-coded DEFAULT_BFV_PRESET which can mismatch generators built with custom
params (see ZKInputsGenerator::from_set and ZKInputsGenerator::new); update the
calls (including the other occurrence around lines 159-165) to pass the
generator's actual BFV preset/params (self.bfv_params or the stored preset) into
UserDataEncryptionCircuit::compute instead of DEFAULT_BFV_PRESET so the computed
ciphertext/witness aligns with the generator's parameters.

Comment thread examples/CRISP/crates/zk-inputs/src/ciphertext_addition.rs
Comment thread examples/CRISP/crates/zk-inputs/src/lib.rs

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

🤖 Fix all issues with AI agents
In
`@crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs`:
- Line 189: The code uses let max_pk_bound =
input.pk_bounds.iter().max().unwrap(); which will panic if input.pk_bounds is
empty; replace this unwrap with an explicit check of input.pk_bounds.is_empty()
and handle the empty-case by returning a proper error (or Option) instead of
panicking—e.g., return Err(...) or propagate a descriptive error (create/return
a domain-specific error like EmptyPkBounds) from the surrounding function;
ensure all call sites handle the Result/Option and remove the unwrap on
max_pk_bound.
🧹 Nitpick comments (2)
crates/zk-helpers/src/circuits/dkg/pk/computation.rs (1)

194-203: Test verifies internal consistency but not cross-type consistency.

The test checks that bits.pk_bit == compute_pk_bit(&dkg_params), but this doesn't validate that pk_bit is actually sufficient to represent pk_bound. Consider adding an assertion that verifies the bit width can accommodate the bound:

// Verify pk_bit is sufficient to represent pk_bound
assert!(
    bounds.pk_bound < BigUint::from(1u32) << bits.pk_bit,
    "pk_bit {} insufficient to represent pk_bound {}",
    bits.pk_bit,
    bounds.pk_bound
);

This would catch any mismatch between Bounds::compute (which uses max modulus) and compute_pk_bit (which may not).

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

882-882: Redundant field initialization syntax.

The field k1: k1 can be simplified using shorthand syntax.

🛠️ Suggested fix
-            k1: k1,
+            k1,

@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/bfv-client/src/client.rs`:
- Around line 104-118: The code currently calls
UserDataEncryptionWitness::compute with the hardcoded DEFAULT_BFV_PRESET while
earlier code builds params from degree, plaintext_modulus, and moduli, causing a
mismatch; fix by deriving/constructing the same preset from those params (or
accept a preset identifier) and pass that derived preset into
UserDataEncryptionWitness::compute instead of DEFAULT_BFV_PRESET so the preset
used to deserialize the public key and encode the plaintext matches the preset
used to compute the witness (ensure consistency between params, the public key
deserialization, plaintext encoding, and the call to
UserDataEncryptionWitness::compute).

In `@packages/enclave-sdk/src/greco.ts`:
- Line 53: The current call uses a blind cast "circuitInputs as any" when
calling noir.execute, which disables type safety; replace it by importing and
using the proper InputMap type from `@noir-lang/noir_js` (or upgrade the package
if that version doesn't export InputMap) and cast/declare circuitInputs as
InputMap before calling noir.execute (referencing noir.execute and circuitInputs
in packages/enclave-sdk/src/greco.ts); if InputMap is unavailable in the beta,
either upgrade to a release that exports InputMap or create a local typed alias
matching InputMap shape and use that type for circuitInputs and the noir.execute
call.
🧹 Nitpick comments (2)
crates/wasm/src/lib.rs (1)

45-55: Consider adding detailed documentation for consistency.

Other wasm-exported functions in this file (e.g., bfv_encrypt_number, compute_pk_commitment) include comprehensive docstrings with Arguments, Returns, and Panics sections. This new function has only a single-line comment.

📝 Suggested documentation enhancement
-/// Generate a public key from JavaScript.
+/// Generate a BFV public key.
+///
+/// # Arguments
+///
+/// * `degree` - Polynomial degree for BFV parameters
+/// * `plaintext_modulus` - Plaintext modulus for BFV parameters
+/// * `moduli` - Moduli for BFV parameters
+///
+/// # Returns
+///
+/// Returns a `Result<Vec<u8>, JsValue>` containing the serialized public key bytes.
+///
+/// # Panics
+///
+/// Panics if key generation fails
 #[wasm_bindgen]
 pub fn generate_public_key(
crates/bfv-client/src/client.rs (1)

108-108: Redundant field name syntax.

Clippy will flag plaintext: plaintext as a redundant field name.

♻️ Minor fix
     let witness = UserDataEncryptionWitness::compute(
         DEFAULT_BFV_PRESET,
         &UserDataEncryptionCircuitInput {
             public_key: pk,
-            plaintext: plaintext,
+            plaintext,
         },
     )?;

Comment thread crates/bfv-client/src/client.rs
Comment thread packages/enclave-sdk/src/greco.ts

@ctrlc03 ctrlc03 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

utACK

@cedoor cedoor merged commit 7ced843 into main Feb 4, 2026
27 checks passed
@ctrlc03 ctrlc03 deleted the refactor/greco branch February 4, 2026 20:26
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.

use submodules instead of crates

3 participants