perf(crypto): optimize bn128 arithmetic and subgroup checks
What does this PR do?
Optimizes BN128 field arithmetic and G2 input validation:
- Specializes
Fp2.squared(),mul(),inverse(), and multiplication by9+i; corrects its documentation and diagnostictoString()to usea+bi. - Defines extension non-residues in their coefficient fields, removes the duplicate
Fp6.NON_RESIDUE, and reusesFp2.mulByNonResidue()throughoutFp6andFp12. Documents that the Fp2 helper multiplies by9+i, while the Fp6 helper multiplies byv, withv^3=9+i. - Replaces the 254-bit G2 subgroup multiplication with one multiplication by the 63-bit BN seed and three Frobenius applications. Preserves the infinity fast path and compares Jacobian points by subtraction.
Why are these changes required?
The fixed BN128 parameters permit equivalent arithmetic with fewer BigInteger multiplications. The G2 subgroup check runs for every decoded pairing term and can exploit the existing twist endomorphism.
For z=4965661367192848881 and psi=mulByP, the criterion is [z+1]P + psi([z]P) + psi^2([z]P) = psi^3([2z]P) (paper, Sections 3 and 5.1). On this sextic twist, g(psi)=0 for g(X)=X^4-X^2+1; the integer resultant of g and f(X)=z+1+zX+zX^2-2zX^3 is exactly r. The resulting integral Bezout identity excludes additional torsion in the acceptance set. Conversely, psi=[p] on G2 and f(p)=0 mod r.
This PR has been tested by:
- 30 focused BN128 tests passed on x86_64/JDK8: independent arithmetic formulas, full-order subgroup oracles, arbitrary twist points, all four prime cofactor components, projective Frobenius behavior, sparse/cyclotomic arithmetic, precompile outputs and invalid inputs, and deadlines.
- 3,793 baseline/final comparisons matched with all zksnark classes overridden: arithmetic, derived parameters, precompile outputs/failures/Energy, and CPU-policy branches. The intentional diagnostic-text correction was checked separately.
- Local clean build, the exact framework/plugins CI Checkstyle gate, and Math checks passed.
- The full-suite attempt was interrupted after 1,341 logged passes and five integration timeouts (RPC HTTP/2, native-library loading, and LevelDB/RocksDB node tests). All five failed cases passed when rerun with one Gradle worker. A complete full-suite pass is still pending.
Follow up
Historical replay, mixed-version producer/validator testing near CPU deadlines, and ARM64/JDK17 performance measurements remain pending.
Extra details
Compatibility and consensus
The intended point acceptance set, coordinate validation, input encoding, binary results, public method signatures, and Energy pricing are unchanged. Fp2.toString() intentionally corrects diagnostic text; protocol serialization does not use it. The removed Fp6 constant is internal to a package-private implementation.
Faster execution can change outcomes near local CPU deadlines. Signed-block replay has configurable CPU allowances, retry, and receipt matching, but an older validator can still reject a result it cannot reproduce within its allowance. No deterministic arithmetic or validation-rule change was identified; mixed-version timeout compatibility has not been fully validated.
Performance comparison (final combined implementation)
Baseline: release_v4.8.3 at 409a33f21e6341e31dff5a6341f34f6d0b212d50. Final source tree: 273858d778a1519f9639c28a0c152a074f0ba9f1. Host: Intel i7-9750H, macOS x86_64, Oracle JDK 1.8.0_221, 512 MiB heap, Parallel GC, -Xbatch.
Three fresh JVMs compared isolated baseline/final runtimes after warmup, using five paired rounds with alternating order and varied inputs. All zksnark classes were overridden together; the remaining runtime was identical. Input/output corpus hashes matched across both variants and all forks. Source hashes were recorded and the published Git tree was verified against the staged, tested tree.
Before/after are medians of per-JVM median latencies. Reduction is the median of within-JVM paired reductions, so it is not the ratio of the displayed medians.
| Precompile / workload | Before (ms/op) | After (ms/op) | Median paired latency reduction |
|---|---|---|---|
| BN128Addition | 0.047435 | 0.048366 | -0.31% |
| BN128Multiplication | 4.388712 | 4.846322 | -0.06% |
| BN128Pairing, 1 pair | 70.813124 | 45.357372 | 37.95% |
| BN128Pairing, 2 pairs | 78.437754 | 43.266948 | 44.84% |
| BN128Pairing, 4 pairs | 149.592134 | 81.610406 | 46.88% |
Pairing improved in all three JVMs. Allocated bytes/op decreased by 26.19%, 36.29%, 38.49% for 1/2/4 terms. Addition and multiplication use Fp and serve as controls; their allocations were identical. Individual multiplication control timings varied by roughly -10% to +6%, indicating host noise. The host had background activity, so absolute timings should not be compared with the earlier Fp2-only run.
These are single-host, warmed execute() measurements including decoding and subgroup checks, rather than transaction TPS or Energy reductions. The table replaces the earlier Fp2-only measurements. Benchmark tooling and raw data remain outside the PR.