Correctness fixes, batched miller loop, tests, benchmarks#9
Open
Marchhill wants to merge 8 commits into
Open
Conversation
- Pairing.AsFp12 read past the end of the context buffer (it copied Pairing.Sz longs from the interior GT pointer instead of PT.Sz) and used the pointer after the span was unpinned; copy under fixed - new Pairing() bypassed the all-optional-parameter constructor and produced an instance backed by a null buffer that crashed the process on first use; add an explicit parameterless constructor - MultiMult passed npoints == 0 through to blst, which underflows and crashes; return infinity instead, and validate buffer lengths - Generator(Span)/PT.One(Span) copied into the caller's buffer before validating its length, corrupting memory for short buffers - Keygen*/DeriveMasterEip2333 silently produced an all-zero secret key for IKM shorter than 32 bytes (blst zeroes the output); throw instead - MapTo read past the end of inputs shorter than 48 bytes; validate - native library fallback path resolved runtimes/ against the current working directory instead of the application base directory - expose the error code on BlsException - fix RepositoryUrl typo (bls-bindings -> blst-bindings) Also readability: pool MSM scratch buffers instead of allocating per call, replace Buffer.MemoryCopy with bounds-checked span copies, use stackalloc pointer arrays for the pippenger wrappers, take MSM inputs as ReadOnlySpan, normalize modifier order, document the unvalidated decode paths and wrap-vs-parse constructors, drop dead code Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- sign/verify/aggregate roundtrips for min-pk signatures, including Pairing-based verification and rejection cases - EIP-2333 key derivation test vectors - RFC 9380 hash-to-curve test vectors for G1 and G2 - secret key range validation and encoding roundtrips - point serialization/compression roundtrips, infinity encoding, decode error cases, generator known-answer tests - scalar arithmetic identities and reduction - MSM vs single mult consistency, empty and invalid input handling - GT operations and Pairing.AsFp12 consistency Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BenchmarkDotNet project covering keygen, hash-to-curve, sign, verify, point arithmetic, decode/subgroup checks, pairings (including precomputed lines) and G1/G2 multi-scalar multiplication Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- PT.MillerLoopN exposes blst_miller_loop_n: computes the product of n Miller loops in one pass, sharing the Fp12 squarings across pairs (measured 1.14x-1.45x end-to-end for pairing checks of 2-16 pairs) - P1Affine/P2Affine raw affine Decode from field element bytes, so callers can build contiguous affine buffers without a Jacobian round trip - MultiMultAffine skips the Jacobian-to-affine batch conversion for callers that already hold affine points - bump version to 1.1.0 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The folded scalar's deeper-indented continuation line kept its newline, so -p:VersionSuffix=... executed as its own shell command whenever the preview input was set Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dotnet pack --no-build recomputes the package version, so without the suffix it packed and pushed a non-preview version Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Full review of the bindings with fixes, new performance APIs for the EIP-2537 precompiles, a much larger test suite (8 → 57 tests, all passing), and a BenchmarkDotNet project.
Correctness / safety fixes
new Pairing()crashed the process on first use: the only constructor has all-optional parameters, sonew Pairing()produceddefault(Pairing)backed by a null buffer; any call segfaulted (confirmed:blst_pairing_raw_aggregatefaulting at address0x4). Added an explicit parameterless constructor.MultiMultwithnpoints == 0crashed natively: blst's pippenger dereferencespoints[0]and underflowsnpoints. All-infinity MSM inputs are valid EIP-2537 calls; the client-side guard in Nethermind was the only protection. Now returns infinity, and buffer lengths are validated.Pairing.AsFp12out-of-bounds read + GC hole: copiedPairing.Szlongs (the whole context size) from blst's interior GT pointer instead ofPT.Sz, reading past the allocation, and used the pointer after the span was unpinned. Now copiesPT.Szlongs underfixed.Generator(Span)/PT.One(Span)corrupted memory on short buffers:Buffer.MemoryCopyran before length validation. Now validate first and use bounds-checked span copies.Keygen*variants andDeriveMasterEip2333; now throwsArgumentException.AppContext.BaseDirectory.MapTovalidates 48-byte field element lengths (native code read past shorter inputs).BlsExceptionexposes itsERRORcode; added missingPairing.FinalVerify()overload (the standard blst aggregate-verify flow, previously inexpressible); fixedRepositoryUrltypo.P1/P2.TryDecodeuncompressed,Decode(fp...)) and the wrap-vs-parse constructor overloads.New APIs (v1.1.0)
PT.MillerLoopN(qAffines, pAffines, npairs)— exposesblst_miller_loop_n, computing the product of n Miller loops in one pass with shared Fp12 squarings. Measured end-to-end pairing-check speedup (M2 Max, portable build): 1.14× at 2 pairs, 1.37× at 8, 1.45× at 16 vs the sequential loop currently used by Nethermind's pairing precompile.P1Affine/P2Affine.Decode(fp...)— raw affine decode from field-element bytes, so protocol code can build contiguous affine buffers without a Jacobian round trip.P1/P2.MultiMultAffine— MSM over affine inputs, skipping the redundantblst_p1s_to_affinebatch conversion (the EIP-2537 flow decodes affine coordinates, converts to Jacobian, then converts straight back).Performance
ArrayPool: zero managed allocations per call (previously ~100KB+ per 512-point G2 MSM).stackallocpointer arrays, bounds-checked span copies instead of rawBuffer.MemoryCopy, reducedunsafesurface.Tests (8 → 57)
Sign/verify/aggregate roundtrips with rejection cases, official EIP-2333 and RFC 9380 hash-to-curve vectors, generator known-answer tests, serialization/infinity/decode-error cases, scalar arithmetic, MSM/MillerLoopN consistency and edge cases,
AsFp12regression test.Benchmarks
New
Nethermind.Crypto.Bls.Bench(BenchmarkDotNet): keygen, hash-to-curve, sign, verify, point ops, decodes, pairings (incl. precomputed lines and sequential-vs-batched pairing check), G1/G2 MSM.Infra
.github/CODEOWNERS(supersedes Add CODEOWNERS file #8).🤖 Generated with Claude Code