perf: optimize scalar mul circuits - #1797
Conversation
There was a problem hiding this comment.
Pull request overview
Performance-focused update to gnark’s curve/arithmetic gadgets, primarily targeting proof-critical scalar multiplication and slope computation paths by introducing fixed-base signed-digit comb methods and cheaper deferred zero assertions for emulated-field polynomial checks.
Changes:
- Added fixed-base signed-digit comb scalar multiplication with compile-time table generation and automatic dispatch for constant base points (native BLS12-377 and emulated SW curves, plus emulated BN254/BLS12-381 G2).
- Introduced
Field.AssertEvalIsZeroand extended the multivariate evaluation hint to support “assert remainder is zero” mode (avoids materializing remainder witnesses). - Reworked emulated SW slope computations to use hinted witnesses certified by single deferred zero assertions (replacing many
Div-based patterns) and added an implicit-y accumulator optimization in incomplete scalar-mul loops.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| std/math/emulated/field.go | Adds UnsafeFromLimbs and ConstantValue to support comb selection without limb range checks and enable constant-input specialization. |
| std/math/emulated/field_mul.go | Adds AssertEvalIsZero and extends multivariate hint plumbing to optionally omit remainder outputs when asserting zero. |
| std/algebra/native/sw_bls12377/hints.go | Registers the new comb recoding hint for native BLS12-377 fixed-base comb. |
| std/algebra/native/sw_bls12377/g2.go | Adds constant-point comb dispatch for ScalarMul and comb-backed ScalarMulBase fast path. |
| std/algebra/native/sw_bls12377/g1.go | Adds constant-point comb dispatch for ScalarMul (native G1). |
| std/algebra/native/sw_bls12377/fixedbase.go | Implements native G1 fixed-base signed-digit comb with cached precomputed tables and recoding hint. |
| std/algebra/native/sw_bls12377/fixedbase_g2.go | Implements native G2 fixed-base signed-digit comb with cached precomputed tables. |
| std/algebra/native/sw_bls12377/fixedbase_g2_test.go | Adds correctness/count tests for native G2 comb-backed base multiplication. |
| std/algebra/native/sw_bls12377/fixedbase_count_test.go | Adds constraint-count and correctness tests for native G1 comb-backed base multiplication. |
| std/algebra/emulated/sw_emulated/slopes.go | Introduces hinted slope witnesses and implicit-y double-and-add helpers to reduce deferred-check cost. |
| std/algebra/emulated/sw_emulated/point.go | Switches slope computations to asserted ratios/hinted slopes; adds comb dispatch for constant points and comb integration into MSM/joint scalar paths; adds implicit-y optimization. |
| std/algebra/emulated/sw_emulated/joint_comb_count_test.go | Adds constraint-count tests for joint scalar multiplication modes and constant-point routing. |
| std/algebra/emulated/sw_emulated/hints.go | Registers new slope/comb/implicit-chain hints used by the updated emulated SW implementation. |
| std/algebra/emulated/sw_emulated/fixedbase.go | Implements emulated fixed-base signed-digit comb (table computation, selection, implicit-y chain, parity fold). |
| std/algebra/emulated/sw_emulated/fixedbase_test.go | Adds extensive correctness and constraint-count coverage for emulated comb paths and constant-point MSM routing. |
| std/algebra/emulated/sw_bn254/hints.go | Registers BN254 G2 comb recode/chain hints. |
| std/algebra/emulated/sw_bn254/g2.go | Adds constant-point comb dispatch for BN254 emulated G2 ScalarMul. |
| std/algebra/emulated/sw_bn254/fixedbase_g2.go | Implements BN254 emulated G2 fixed-base comb (tables, selection, implicit-y chain). |
| std/algebra/emulated/sw_bn254/fixedbase_g2_test.go | Adds correctness/count tests for BN254 emulated G2 comb-backed scalar multiplication. |
| std/algebra/emulated/sw_bls12381/hints.go | Registers BLS12-381 G2 comb recode/chain hints. |
| std/algebra/emulated/sw_bls12381/g2.go | Adds constant-point comb dispatch for BLS12-381 emulated G2 ScalarMul. |
| std/algebra/emulated/sw_bls12381/fixedbase_g2.go | Implements BLS12-381 emulated G2 fixed-base comb (tables, selection, implicit-y chain). |
| std/algebra/emulated/sw_bls12381/fixedbase_g2_test.go | Adds correctness/count tests for BLS12-381 emulated G2 comb-backed scalar multiplication. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit bf770b2. Configure here.

Summary
This PR ports the scalar-multiplication optimizations developed for zk.golf challenges 5 and 6 into gnark's short-Weierstrass circuits. The main theme is to exploit compile-time constant bases aggressively and to reduce the cost of the remaining variable-base loops.
Optimizations
AddUnifiedtail for the cases that can cancel or double.Ycoordinate implicitly through the incomplete chain. Consecutive slope hints are pinned by one deferred zero assertion and the finalYis materialized only once.w=8), while SCS/PLONK uses a smaller window (w=5) to avoid paying too many constant-linear-combination gates.Curve.ScalarMulBasethrough the comb for supported curves, making the fixed generator path complete and much cheaper than the previous variable-base fallback.Curve.ScalarMulthrough the comb when the input point is a compile-time constant, finite, prime-order point. Non-subgroup or unsupported constant points fall back to the existing variable-base code.JointScalarMulBaseby computing the fixed-generator term with the comb, computing the variable-base term separately, and merging them withAddUnified.MultiScalarMulby peeling off compile-time constant points and using comb scalar multiplication for those terms, while preserving the existing joint-scalar folding for the remaining variable points.emulated.Field.AssertEvalIsZero, a zero-remainder variant of multivariateEval, so slope constraints can avoid materializing an intermediate remainder witness and equality check.AddUnified, tangent, chord, and incomplete loop slope checks without changing their exceptional-case semantics.Yimplicitly in j-invariant-0 variable-base incomplete double-and-add loops, saving one materialization per loop step.Ychain, and complete tail.ScalarMulBaseand constant-pointScalarMulnow use complete fixed-base paths where supported.Benchmarks
Benchmarks were run against
masterat2ea1515eee1b1e4e8564ec0c1e15b7fd29bbb918and this branch at the current working tree based on655a64032. Each row compiles the same circuit once withr1cs.NewBuilderandscs.NewBuilder; the table reportsGetNbConstraints()andGetNbInstructions(). The numbers were collected with temporary same-package benchmark harnesses that were removed after collection.Harness command shape:
Emulated G1 methods
ScalarMulBase/completer1csScalarMulBase/completescsScalarMulBase/incompleter1csScalarMulBase/incompletescsScalarMul/constant/completer1csScalarMul/constant/completescsScalarMul/constant/incompleter1csScalarMul/constant/incompletescsScalarMul/variable/completer1csScalarMul/variable/completescsScalarMul/variable/incompleter1csScalarMul/variable/incompletescsJointScalarMulBase/completer1csJointScalarMulBase/completescsJointScalarMulBase/incompleter1csJointScalarMulBase/incompletescsMultiScalarMul/mixed/completer1csMultiScalarMul/mixed/completescsMultiScalarMul/mixed/incompleter1csMultiScalarMul/mixed/incompletescsEmulated G2 methods
BN254 G2 ScalarMul/constantr1csBN254 G2 ScalarMul/constantscsBLS12-381 G2 ScalarMul/constantr1csBLS12-381 G2 ScalarMul/constantscsNative BLS12-377 methods
G1ScalarMulBaser1csG1ScalarMulBasescsG1ScalarMul/constantr1csG1ScalarMul/constantscsG2ScalarMulBaser1csG2ScalarMulBasescsG2ScalarMul/constantr1csG2ScalarMul/constantscsEVM precompile circuits
bn254_ecmul/bn254r1csbn254_ecmul/bn254scsbn254_ecmul/bls12_377r1csbn254_ecmul/bls12_377scsecrecover/bn254r1csecrecover/bn254scsecrecover/bls12_377r1csecrecover/bls12_377scsbls12381_g1_msm_2/bn254r1csbls12381_g1_msm_2/bn254scsbls12381_g1_msm_2/bls12_377r1csbls12381_g1_msm_2/bls12_377scsRecursion circuits
groth16/BW6_in_BN254_constant_vkr1csgroth16/BW6_in_BN254_constant_vkscsplonk/BLS12_377_in_BW6_761r1csplonk/BLS12_377_in_BW6_761scsplonk/BLS12_381_in_BN254r1csplonk/BLS12_381_in_BN254scsplonk/BW6_761_in_BN254r1csplonk/BW6_761_in_BN254scsNotes:
Note
High Risk
Changes core elliptic-curve scalar multiplication and addition soundness paths (comb recoding, subgroup checks, slope hints) across emulated and native curves; incorrect constraints would break proof soundness for many circuits.
Overview
This PR cuts constraint cost for short-Weierstrass scalar multiplication by routing compile-time constant bases through a signed-digit fixed-base comb (precomputed windows, odd scalar recode, parity fold in the top window, implicit-
Yincomplete chain, complete tail) and by cheaper slope checks in the remaining variable-base paths.Emulated G1 (
sw_emulated): newfixedbase.gopowersScalarMulBase, constant-pointScalarMul, completeJointScalarMulBase(comb + variable term +AddUnified), andMultiScalarMulpeeling of constant points. Emulated G2 (BN254, BLS12-381) gets the same comb on constant subgroup points viafixedbase_g2.goand early dispatch inScalarMul.Native BLS12-377 adds G1/G2 fixed-base combs for
ScalarMulBaseand constantScalarMul, with tests that reject malicious wrapped scalar recodes.Supporting API:
emulated.Field.AssertEvalIsZero,UnsafeFromLimbs, andConstantValue;sw_emulated/slopes.goreplaces manyDiv-based slopes with hinted slopes + deferred zero assertions; j=0 incomplete loops use implicitYin double-and-add.Stats in
internal/stats/latest_stats.csvreflect large drops for fixed-base G1 scalar-mul benchmarks (e.g. BN254/secp256k1/P256).Reviewed by Cursor Bugbot for commit 4e381d7. Bugbot is set up for automated code reviews on this repo. Configure here.