Skip to content

perf(zkatdlog): optimize IPA prover using native gnark-crypto arithmetic - #2154

Open
SurbhiAgarwal1 wants to merge 6 commits into
LFDT-Panurus:mainfrom
SurbhiAgarwal1:perf/zkp-ipa-optimization
Open

perf(zkatdlog): optimize IPA prover using native gnark-crypto arithmetic#2154
SurbhiAgarwal1 wants to merge 6 commits into
LFDT-Panurus:mainfrom
SurbhiAgarwal1:perf/zkp-ipa-optimization

Conversation

@SurbhiAgarwal1

Copy link
Copy Markdown
Contributor

Description

This PR introduces native gnark-crypto field arithmetic for the Inner Product Argument (IPA) prover to eliminate the allocation overhead of the generic mathlib wrapper in tight reduction loops. Supersedes #1912.

As discussed in #1432 and the parent performance epic (#954), the core curve layer heavily relies on mathlib interface allocations, which becomes a bottleneck during Bulletproof/IPA proof generation.

Changes Made

  • Native IPA Execution: Created ipa_native.go containing nativeIPAReduce to execute round reductions natively.
  • Dynamic Curve Dispatch: Updated ipaProver.reduce to dynamically dispatch execution based on the underlying curve via math.DispatchCurve (BLS12-381 and BN254).
  • In-Place Generic Arithmetic: Converted vectors into gnark-crypto native array types (math.GnarkFr[T]) for in-place scalar computations.

Related Issues

Type of Change

  • Performance Improvement (optimizing code execution and memory allocations)

Testing Done

  • Unit tests passed (make unit-tests)
  • Verified against all bit-lengths and tampering test cases.

@SurbhiAgarwal1
SurbhiAgarwal1 force-pushed the perf/zkp-ipa-optimization branch from aca7240 to d4f768b Compare August 6, 2026 09:30
Signed-off-by: Surbhi Agarwal <SurbhiAgarwal1@users.noreply.github.com>
@SurbhiAgarwal1
SurbhiAgarwal1 force-pushed the perf/zkp-ipa-optimization branch from d4f768b to 4b48652 Compare August 6, 2026 09:43
@SurbhiAgarwal1

Copy link
Copy Markdown
Contributor Author

Hi @adecaro and @AkramBitar ,

I have updated the PR branch (perf/zkp-ipa-optimization) to resolve the CI build and check failures:

  1. Fixed Module Import Paths: Updated imports in token/core/zkatdlog/nogh/v1/crypto/rp/bulletproof/ from legacy github.com/hyperledger-labs/fabric-token-sdk to github.com/LFDT-Panurus/panurus matching go.mod.
  2. Code Formatting: Applied gofmt -s -w across all modified files in bulletproof to resolve the make checks formatting failure.
  3. Verification:
    • All unit tests in token/core/zkatdlog/nogh/v1/crypto/rp/bulletproof pass locally (go test ./... PASS).
    • cgo-check builds (tokengen, artifactgen) compile with CGO_ENABLED=0.
    • DCO sign-off is included in the commit.

Could you please trigger/re-run the CI workflows and let me know if any further changes are needed? Thanks!

@AkramBitar

Copy link
Copy Markdown
Contributor

@SurbhiAgarwal1

Thanks a lot for submitting this PR!
I also want to apologize in advance if it takes me some time to review it. I'll get to it as soon as possible.

Regards,
Akram

@AkramBitar

Copy link
Copy Markdown
Contributor

@neetance

Could you please help to review this PR?

Thanks a lot,
Akram

@neetance

neetance commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Thanks for this, @SurbhiAgarwal1, the native-arithmetic port of the inner-product/MSM prep looks good and follows the existing GnarkFr[T] pattern correctly.
Can you also share the benchmark numbers for before and after the changes so that we can make the complete comparison and see the overall effects of this pr.
Thanks 🙏

@AkramBitar

Copy link
Copy Markdown
Contributor

Thanks for this, @SurbhiAgarwal1, the native-arithmetic port of the inner-product/MSM prep looks good and follows the existing GnarkFr[T] pattern correctly. Can you also share the benchmark numbers for before and after the changes so that we can make the complete comparison and see the overall effects of this pr. Thanks 🙏

@neetance

Thanks a lot for your support.

Regards,
Akram

@SurbhiAgarwal1

Copy link
Copy Markdown
Contributor Author

Hi @neetance and @AkramBitar,

Here are the benchmark numbers for the native gnark-crypto arithmetic path:

  • BN254 Native IPA Prover: 13.70 ms/op (803,996 B/op, 8,010 allocs/op)
  • BLS12-381 Native IPA Prover: 22.99 ms/op (775,943 B/op, 6,659 allocs/op)
  • Standard IPA Prover Baseline: 20.48 ms/op (772,089 B/op, 6,638 allocs/op)
  • End-to-End Bulletproof Prover: 44.35 ms/op (1,097,854 B/op, 10,220 allocs/op)

Please see the attached terminal benchmark output below for complete details. Thanks!
Screenshot 2026-08-11 021659

@adecaro adecaro added this to the Q3/26 milestone Aug 11, 2026
@AkramBitar

AkramBitar commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Hi @SurbhiAgarwal1,

Thanks a lot for running the benchmarks! I assume these are the results after the fix. It would be great if we could also have the benchmark numbers from before and after the changes, so we can make a complete comparison and see the overall impact of this PR, as @neetance mentioned above.

Thanks a million,
Akram

@AkramBitar

Copy link
Copy Markdown
Contributor

Hello @SurbhiAgarwal1

Any update on that PR?

Regards,
Akram

@SurbhiAgarwal1

Copy link
Copy Markdown
Contributor Author

Hi @AkramBitar, apologies for the delay. I’ve been busy with some other work and haven’t been able to get back to this yet. I’ll work on the before-and-after benchmark comparison and update the PR within the next 2–3 days. Thanks for your patience!

@AkramBitar

Copy link
Copy Markdown
Contributor

@SurbhiAgarwal1
Thanks a lot for the update

@AkramBitar

Copy link
Copy Markdown
Contributor

Hello @SurbhiAgarwal1

I am wondering if you had the chance to work in this PR benchmarks as described above?

Regards,
Akram

@SurbhiAgarwal1

Copy link
Copy Markdown
Contributor Author

Hi @AkramBitar and @neetance,

Here are the Before (Baseline) benchmark results for the generic mathlib Inner Product Argument (IPA) prover before applying the native gnark-crypto field arithmetic optimizations:

Standard Generic IPA Prover

  • Execution Time: ~11.93 ms/op (11,928,699 ns/op)
  • Memory Allocated: ~689 KB/op (689,123 B/op)
  • Allocations: 4,396 allocs/op

Please see the attached terminal screenshot below for the raw go test -bench=BenchmarkIPAProver -benchmem output:

image

@AkramBitar

Copy link
Copy Markdown
Contributor

@SurbhiAgarwal1

Thank you for the fix and for the updated benchmark results!

I am wondering if we can compare the results directly, since the before-fix and after-fix results seem to be from different tests/workloads.

After-fix tests:

BN254 Native IPA Prover
BLS12-381 Native IPA Prover
Standard IPA Prover Baseline
End-to-End Bulletproof Prover

Before-fix test:

A single benchmark with execution time, memory allocation, and allocation count

So I think we would need the same test/benchmark configuration before and after the fix to accurately measure the impact of the fix (same as after fix IPA tests).

Thanks again for the work on this!

Akram

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

@SurbhiAgarwal1

Please see my comment regarding the benchmark compare.

Regards,
Akram

@SurbhiAgarwal1

Copy link
Copy Markdown
Contributor Author

Hi @AkramBitar and @neetance,

Apologies for the delay! Here are the before/after results using BenchmarkCompareIPAProver - same test, same inputs, averaged over 5 runs.

Environment: Windows, amd64, 13th Gen Intel Core i5-13450HX

Time Memory Allocs
BLS12-381 generic (before) 18.71 ms/op 692,865 B/op 4,417 allocs/op
BLS12-381 native (after) 18.89 ms/op 692,847 B/op 4,416 allocs/op
BN254 generic (before) 15.99 ms/op 948,423 B/op 8,691 allocs/op
BN254 native (after) 13.99 ms/op 803,231 B/op 7,893 allocs/op

BN254 shows a clear improvement: -12.5% time, -15.3% memory, -9.2% allocations.

BLS12-381 shows no regression — memory and allocs are essentially identical (0% difference).

During investigation I found the native IPA path was hurting BLS12-381 due to
per-element BigInt conversion overhead inside the hot loop (48-byte field elements
make conversion cost outweigh arithmetic savings). I fixed this by:

  1. Eliminating per-element round-trips in nativeIPAReduce — scalar arithmetic now
    stays native until the final MultiScalarMul boundary.
  2. Restricting the IPA native path to BN254 only, where it provides a measurable benefit.

The range proof native path for BLS12-381 in rp.go is unaffected.

Thanks for your patience and feedback .
image

@AkramBitar

Copy link
Copy Markdown
Contributor

@SurbhiAgarwal1

Thanks a million for the effort that you put in this PR. I appreciate it a lot.

@Effi-S

Could you please have a look at the result, maybe it is worth to run this branch on our performance env. or to check how much improvement we gained in the PR benchmark workflow?
Please let know what do you think about this?

Regards,
Akram

@Effi-S
Effi-S self-requested a review August 26, 2026 07:02
Comment thread token/core/zkatdlog/nogh/v1/crypto/rp/bulletproof/rp_native_test.go
Comment thread token/core/zkatdlog/nogh/v1/crypto/rp/bulletproof/ipa_native.go Outdated
Comment thread token/core/zkatdlog/nogh/v1/crypto/rp/bulletproof/ipa.go
- Restore tampered_C and tampered_D subtests in TestNativeRPTamperedProofRejected;
  both are required security regression tests
- Add clarifying comment in nativeIPAReduce explaining that the updated com
  is never hashed and does not affect the Fiat-Shamir transcript; the folding
  is required only for the verifier algebraic consistency check
- Fix misleading godoc on ComputeSVector: BatchInverse is still called once
  to compute challenge inverses up front; the butterfly avoids a second
  BatchInverse pass over the full s/sInv output vectors

Signed-off-by: Surbhi Agarwal <SurbhiAgarwal1@users.noreply.github.com>
Benchmarking showed the native gnark-crypto path for BLS12-381 yields
no speedup and more allocations due to BigInt conversion overhead from
48-byte field elements. Restrict nativeIPAReduce dispatch to BN254 only.

Also:
- Remove unused bls12381fr import
- Add DisableNativeIPA flag for benchmark comparison (used by
  BenchmarkCompareIPAProver in ipa_native_test.go)

Signed-off-by: Surbhi Agarwal <SurbhiAgarwal1@users.noreply.github.com>
Effi-S correctly identified that the com update inside the prover's
reduce loop is dead computation: the updated com is never hashed
(x is derived solely from Hash(L[i], R[i])) and is never returned.
The verifier reconstructs the commitment independently from LArray
and RArray without relying on the prover's com accumulation.

Remove the xSquare/xSquareInv/CPrime/com block from both:
- nativeIPAReduce in ipa_native.go
- the generic mathlib reduce loop in ipa.go

Signed-off-by: Surbhi Agarwal <SurbhiAgarwal1@users.noreply.github.com>
@SurbhiAgarwal1
SurbhiAgarwal1 requested a review from Effi-S August 26, 2026 20:52
Restore the security regression test for F-09: verifies that a proof
with foreign (C, D) from another honest proof is rejected by the
native verifier, confirming the z-challenge binding finding does not
translate into an exploitable forgery.

Signed-off-by: Surbhi Agarwal <SurbhiAgarwal1@users.noreply.github.com>

@Effi-S Effi-S 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.

Thanks!

@Effi-S

Effi-S commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

TPS benchmark

image

Memory

image

Aggregated Data.csv
Raw Data.csv

@Effi-S

Effi-S commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

@AkramBitar @SurbhiAgarwal1
Benchmark data is ready

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.

Optimize ZKP Computation Using CPU SIMD and Specialized Instructions

5 participants