Skip to content

Bls script#698

Draft
vivianjeng wants to merge 5 commits into
mainfrom
bls-script
Draft

Bls script#698
vivianjeng wants to merge 5 commits into
mainfrom
bls-script

Conversation

@vivianjeng

@vivianjeng vivianjeng commented Feb 25, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added test coverage for witness generation and proof verification.
    • Added demo circuits for multiplexer functionality.
    • Added build infrastructure for compiling circuits and generating proofs.
  • Improvements

    • Enhanced proof computation logic for better compatibility.

Review Change Stack

…ripts

- Added .gitignore to exclude output directory
- Created package.json and package-lock.json for dependency management
- Implemented multiplexer circuit in multiplexer.circom
- Added build script for compiling circuits and generating proof parameters
- Included test vectors for multiplexer in the circom-prover
…generation

- Modified build_circuits.sh to include multiple contributions for BLS power series
- Enhanced QAP root of unity calculation in qap.rs to accommodate different field sizes
- Updated multiplexer_final.zkey with new proof parameters
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Feb 25, 2026

Copy link
Copy Markdown

Deploying mopro with  Cloudflare Pages  Cloudflare Pages

Latest commit: 31c3b16
Status: ✅  Deploy successful!
Preview URL: https://c662efc8.mopro.pages.dev
Branch Preview URL: https://bls-script.mopro.pages.dev

View logs

- Introduced mux.circom circuit implementing a multiplexer
- Added input_mux.json for input data
- Updated package.json and package-lock.json to include circomlib dependency
- Modified build_circuits.sh to compile the new mux circuit and generate proof parameters
- Added new test vectors for mux in the circom-prover
@vivianjeng

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
✅ 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 commented Jun 3, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR adds FFT algorithm improvements to support snarkjs-compatible root selection in the Arkworks prover, introduces a test mux circuit, and validates the changes with a Rust integration test. The build infrastructure compiles the circuit, generates a Groth16 proof, and verifies it; the Rust test exercises this flow end-to-end.

Changes

Snarkjs-Compatible FFT and Mux Circuit Validation

Layer / File(s) Summary
FFT Algorithm Enhancement
circom-prover/src/prover/ark_circom/qap.rs
witness_map_from_matrices selects between snarkjs-compatible manual FFT transforms (when k != 1) and arkworks domain transforms (when k == 1). New helpers implement bit-reversal permutation, Cooley–Tukey stages, inverse transforms, and power application for coset shifts.
Mux Circuit and Input
circom/circuits/multiplexer.circom, circom/circuits/mux.circom, circom/input_mux.json
A multiplexer demo circuit passes through constant-scaled input; the mux circuit instantiates Multiplexer(1,2) with selector 1 to route in to output. Test input supplies "in": 42.
Build Infrastructure
circom/package.json, circom/scripts/build_circuits.sh
npm package metadata and a shell script that compiles mux.circom to R1CS, generates/finalizes BLS12-381 ptau, performs Groth16 setup and witness computation, then generates and verifies a Groth16 proof.
Rust Integration Test
circom-prover/src/lib.rs
A gated test (rustwitness + arkworks) loads the mux circuit, generates witness, proves via CircomProver::prove with mux.zkey, verifies the proof, and asserts success.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A mux to prove, a circuit bright,
FFT roots shining snarkjs-right!
From witness born, the proof takes flight,
Arkworks and circom both unite! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Bls script' is vague and generic, referring only to a tooling/infrastructure aspect without conveying the primary substantive change. Revise the title to reflect the main change more clearly, such as 'Add BLS12-381 circuit tests and build infrastructure' or 'Add Circom mux circuit with BLS12-381 proof generation and verification'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bls-script

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@circom-prover/src/prover/ark_circom/qap.rs`:
- Around line 81-93: Add an explicit radix-2 precondition check before taking
the manual FFT path: verify that domain_size is a power of two when k != 1 and
return or panic with a clear error if not. Modify the branch around the k != 1
handling (the block using manual_ifft, apply_powers, manual_fft) to call a small
predicate (or inline check) that asserts bit_reverse/manual_fft-safe domain
sizes; reference the symbols domain_size, k, manual_ifft, manual_fft,
bit_reverse and ensure the same guard is added for the other occurrence
mentioned (the block covering lines ~155-211) so non-power-of-two domains fail
fast instead of producing invalid polynomials.
- Around line 64-79: The exponent math can underflow when computing the coset
shift (inc) because s - power - 1 may be negative; add a guarded check before
computing snarkjs_omega / inc: ensure power < s (use the existing s and power
locals) and if not return Err(SynthesisError::PolynomialDegreeTooLarge). Use
this check before the block that computes snarkjs_omega_inv and the inc exponent
so you avoid unsigned underflow when calculating exp for inc and fail early with
SynthesisError::PolynomialDegreeTooLarge.

In `@circom/circuits/multiplexer.circom`:
- Around line 7-8: The signal x is underconstrained because it uses witness-only
assignment (`x <-- 1`) so no R1CS ties x to 1; change that to a constrained
assignment (use `x <== 1` or otherwise add an explicit constraint enforcing x ==
1) so the circuit constrains x; keep the existing multiplication constraint `out
<== in * x` intact so out is correctly tied to in and the now-constrained x.

In `@circom/package.json`:
- Around line 5-7: The package.json "test" script is hardcoded to fail; update
the "test" entry in package.json (the "test" script under "scripts") to either
run a real test command for this package (e.g., invoke your CI test runner or a
small verification that build artifacts exist) or replace the failing command
with a no-op success command so npm test exits 0; ensure the "build" script
("build": "sh scripts/build_circuits.sh") remains unchanged unless you want the
test to call it, and verify npm test in CI/local passes after the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5f5689d4-dc0b-4714-905a-572e29afe6d4

📥 Commits

Reviewing files that changed from the base of the PR and between 93a27c5 and 31c3b16.

⛔ Files ignored due to path filters (4)
  • circom-prover/test-vectors/multiplexer.wasm is excluded by !**/*.wasm
  • circom-prover/test-vectors/multiplier2_bls.wasm is excluded by !**/*.wasm
  • circom-prover/test-vectors/mux.wasm is excluded by !**/*.wasm
  • circom/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • circom-prover/src/lib.rs
  • circom-prover/src/prover/ark_circom/qap.rs
  • circom-prover/test-vectors/multiplexer_final.zkey
  • circom-prover/test-vectors/mux.zkey
  • circom/.gitignore
  • circom/circuits/multiplexer.circom
  • circom/circuits/mux.circom
  • circom/input_mux.json
  • circom/package.json
  • circom/scripts/build_circuits.sh

Comment on lines +64 to +79
let s = F::TWO_ADICITY as u64;
let power = ark_std::log2(domain_size) as u64;
let k: u64 = if s == 32 { 5 } else { 1 };

// Get snarkjs-compatible generator for this domain size
let snarkjs_omega = {
let exp = 1u64 << (s - power);
F::TWO_ADIC_ROOT_OF_UNITY.pow([k]).pow([exp])
};
D::distribute_powers_and_mul_by_const(&mut a, root_of_unity, F::one());
D::distribute_powers_and_mul_by_const(&mut b, root_of_unity, F::one());
let snarkjs_omega_inv = snarkjs_omega.inverse().unwrap();

domain.fft_in_place(&mut a);
domain.fft_in_place(&mut b);
// Coset shift = snarkjs w[power+1]
let inc = {
let exp = 1u64 << (s - power - 1);
F::TWO_ADIC_ROOT_OF_UNITY.pow([k]).pow([exp])
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard exponent math to avoid unsigned underflow in shift counts.

At Line 77, s - power - 1 can underflow for boundary sizes (power >= s), which can break proof generation with invalid shift counts. Add checked arithmetic and fail early with SynthesisError::PolynomialDegreeTooLarge.

Suggested fix
-        let s = F::TWO_ADICITY as u64;
-        let power = ark_std::log2(domain_size) as u64;
+        let s = F::TWO_ADICITY as u64;
+        let power = ark_std::log2(domain_size) as u64;
+        if power >= s {
+            return Err(SynthesisError::PolynomialDegreeTooLarge);
+        }

         let k: u64 = if s == 32 { 5 } else { 1 };

         // Get snarkjs-compatible generator for this domain size
         let snarkjs_omega = {
-            let exp = 1u64 << (s - power);
+            let exp = 1u64 << (s - power);
             F::TWO_ADIC_ROOT_OF_UNITY.pow([k]).pow([exp])
         };
         let snarkjs_omega_inv = snarkjs_omega.inverse().unwrap();

         // Coset shift = snarkjs w[power+1]
         let inc = {
-            let exp = 1u64 << (s - power - 1);
+            let exp = 1u64 << (s - power - 1);
             F::TWO_ADIC_ROOT_OF_UNITY.pow([k]).pow([exp])
         };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@circom-prover/src/prover/ark_circom/qap.rs` around lines 64 - 79, The
exponent math can underflow when computing the coset shift (inc) because s -
power - 1 may be negative; add a guarded check before computing snarkjs_omega /
inc: ensure power < s (use the existing s and power locals) and if not return
Err(SynthesisError::PolynomialDegreeTooLarge). Use this check before the block
that computes snarkjs_omega_inv and the inc exponent so you avoid unsigned
underflow when calculating exp for inc and fail early with
SynthesisError::PolynomialDegreeTooLarge.

Comment on lines +81 to +93
if k != 1 {
// Use manual DFT with snarkjs's roots
let n_inv = F::from(domain_size as u64).inverse().unwrap();

manual_ifft(&mut a, snarkjs_omega_inv, n_inv, domain_size);
manual_ifft(&mut b, snarkjs_omega_inv, n_inv, domain_size);

apply_powers(&mut a, inc);
apply_powers(&mut b, inc);

manual_fft(&mut a, snarkjs_omega, domain_size);
manual_fft(&mut b, snarkjs_omega, domain_size);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add an explicit radix-2 precondition before manual FFT path.

manual_fft/bit_reverse assume n is a power of two. In the k != 1 path this is currently implicit; add a hard guard so wrong domains fail fast instead of producing invalid witness polynomials.

Suggested fix
         if k != 1 {
+            if !domain_size.is_power_of_two() {
+                return Err(SynthesisError::PolynomialDegreeTooLarge);
+            }
             // Use manual DFT with snarkjs's roots
             let n_inv = F::from(domain_size as u64).inverse().unwrap();

Also applies to: 155-211

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@circom-prover/src/prover/ark_circom/qap.rs` around lines 81 - 93, Add an
explicit radix-2 precondition check before taking the manual FFT path: verify
that domain_size is a power of two when k != 1 and return or panic with a clear
error if not. Modify the branch around the k != 1 handling (the block using
manual_ifft, apply_powers, manual_fft) to call a small predicate (or inline
check) that asserts bit_reverse/manual_fft-safe domain sizes; reference the
symbols domain_size, k, manual_ifft, manual_fft, bit_reverse and ensure the same
guard is added for the other occurrence mentioned (the block covering lines
~155-211) so non-power-of-two domains fail fast instead of producing invalid
polynomials.

Comment on lines +7 to +8
signal x <-- 1;
out <== in * x;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Circom 2.x, what is the semantic difference between <--and<==for signals, and does<-- add an R1CS constraint?

💡 Result:

In Circom 2.x, the primary semantic difference between the assignment operators <-- and <== lies in whether they generate an R1CS (Rank-1 Constraint System) constraint during compilation [1]. The operator <== performs an assignment to a signal and automatically adds an R1CS constraint asserting that the signal is equal to the provided expression [2][1]. Because R1CS constraints must be quadratic (i.e., they must be expressible as products of two linear combinations), you can only use <== with expressions that satisfy this property [3]. In contrast, the operator <-- performs only an assignment to a signal without adding any R1CS constraint [2][1]. This operation is often referred to as a "hint" or "out-of-circuit computation" [3]. It is useful for computations that are difficult or impossible to represent directly as quadratic constraints (such as bitwise operations, divisions, or ternary logic) [3][1][4]. However, because <-- adds no constraints to the circuit, failing to manually add the corresponding constraints using the === operator will result in an underconstrained circuit, which is a critical security vulnerability [5][1][4]. In summary: - <-- does NOT add an R1CS constraint [2][1]. It is used for witness generation only [1]. - <== DOES add an R1CS constraint [2][1]. It is the safer, preferred method for quadratic assignments [1]. Developers are strongly discouraged from using <-- unless absolutely necessary and are advised to pair it with explicit === constraints to ensure the circuit is correctly constrained [1][4]. Tools such as circom --inspect can help identify assignments using <-- that could potentially be replaced by <== [6][5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
ls -la
rg -n "multiplexer\.circom" -S . || true
FILE="circom/circuits/multiplexer.circom"
if [ -f "$FILE" ]; then
  echo "=== $FILE (context) ==="
  nl -ba "$FILE" | sed -n '1,120p'
else
  echo "File not found: $FILE"
fi

Repository: zkmopro/mopro

Length of output: 1922


Fix underconstrained assignment of x

Line 7 uses <-- (witness-only assignment) so it generates no R1CS constraint tying x to 1; line 8 only constrains out = in * x, leaving x free and allowing arbitrary out values consistent with the equation.

Suggested fix
-    signal x <-- 1;
-    out <== in * x;
+    var x = 1;
+    out <== in * x;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
signal x <-- 1;
out <== in * x;
var x = 1;
out <== in * x;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@circom/circuits/multiplexer.circom` around lines 7 - 8, The signal x is
underconstrained because it uses witness-only assignment (`x <-- 1`) so no R1CS
ties x to 1; change that to a constrained assignment (use `x <== 1` or otherwise
add an explicit constraint enforcing x == 1) so the circuit constrains x; keep
the existing multiplication constraint `out <== in * x` intact so out is
correctly tied to in and the now-constrained x.

Comment thread circom/package.json
Comment on lines +5 to +7
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "sh scripts/build_circuits.sh"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

npm test is hardcoded to fail.

Line 6 will always fail local/CI test entrypoints. Replace with a real check (or a no-op success) so the package doesn’t report false failures.

Suggested fix
   "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1",
+    "test": "npm run build",
     "build": "sh scripts/build_circuits.sh"
   },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "sh scripts/build_circuits.sh"
"scripts": {
"test": "npm run build",
"build": "sh scripts/build_circuits.sh"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@circom/package.json` around lines 5 - 7, The package.json "test" script is
hardcoded to fail; update the "test" entry in package.json (the "test" script
under "scripts") to either run a real test command for this package (e.g.,
invoke your CI test runner or a small verification that build artifacts exist)
or replace the failing command with a no-op success command so npm test exits 0;
ensure the "build" script ("build": "sh scripts/build_circuits.sh") remains
unchanged unless you want the test to call it, and verify npm test in CI/local
passes after the change.

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.

1 participant