You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Expose fast additive inverse for P1/P2 in Go bindings #282
I noticed that the C API exposes fast conditional negation for projective points via:
blst_p1_cnegblst_p2_cneg
However, the Go bindings do not seem to expose equivalent public methods for P1 and P2.
At the moment, if one wants to compute the additive inverse of a point in Go, i.e. P -> -P, the closest public approach seems to be using subtraction, for example computing 0 - P through SubAssign.
This works mathematically, but it is significantly more expensive than direct group negation, because subtraction goes through point addition logic.
I tested a small local patch exposing direct negation in the Go bindings, using a small cgo wrapper around blst_p1_cneg.
Here is the trouble. In Go one wants to minimize the calls to ultrafast C subroutines, because the cgo call overhead overshadows the actual useful computation. One can also put it as following, if such short C subroutine ends up on critical path, then you want to do something different. Could you clarify what is it you're trying to do? If you negate a point and then add it to some other point, then you can just as well subtract it. If you negate, multiply and add, then you can multiply and subract...
Thanks for the reply and the clear explanation. You are absolutely right about the cgo call overhead overshadowing the math for such a tiny operation.
To clarify my use case: I am not using the negated point for standard elliptic curve arithmetic. If I were, I would definitely use SubAssign to minimize cgo crossings as you suggested.
Instead, I am implementing a NIZK verifier where I need to check pairing equations, e.g., $e(A, B) = e(C, D)$.
To optimize this and avoid multiple expensive $\mathbb{F}_{p^{12}}$ exponentiations, I am batching the pairings using Miller loops and a single final exponentiation, evaluating: $e(A, B) \cdot e(-C, D) = 1$
Because of this, I need to explicitly compute $-C$ and pass it directly as an isolated input to the Miller loop function.
Right now, the only public Go API way I found to get $-C$ is by doing P1.SubAssign(C), after having initialized P1 to its zero value (computing $0 - C$), which goes through the full point addition logic rather than a simple conditional negation.
Given this specific use case for pairing verifications, what would be your recommended approach? Would you be open to exposing a negation method, or is there perhaps a way to negate the point purely in Go (manipulating the struct limbs directly) to avoid the cgo overhead entirely?
I looked a bit more into the C implementation, and I think I now understand the role of Fp12FinalVerify better. For comparing two Miller-loop outputs, it is clearly the intended API, since it avoids the naive approach of doing two separate final exponentiations.
My original point was about a slightly different cost comparison. In both approaches the Miller-loop outputs still have to be multiplied/combined before the final exponentiation. The difference I had in mind is where the “inverse/sign change” is applied: if the equation can be arranged by negating one input point before the Miller loop, this only costs a point negation, while Fp12FinalVerify handles the corresponding operation on the Fp12 side before the final exponentiation.
So my intuition is that there may be a small constant-factor advantage in the point-negation approach in that specific case. That said, I understand that this is likely small compared to the total cost of the Miller loops and the final exponentiation, and in Go the extra cgo calls needed to do this manually may reduce or even eliminate that advantage in practice.
So I agree that Fp12FinalVerify is the right interface for comparing pairing products. My main point is just that exposing point negation in the Go bindings still seems useful as a natural group operation, especially since the C API already exposes blst_p1_cneg and blst_p2_cneg.
Thanks again for your time and for the explanation.
So my intuition is that there may be a small constant-factor advantage in the point-negation approach in that specific case.
No, because the number of cgo transitions would be higher.
exposing point negation in the Go bindings still seems useful as a natural group operation
Again, "if such short C subroutine ends up on critical path, then you want to do something different." Or in other words, you don't want the interfaces to be exposed ;-)
Activity
dot-asm commentedon May 14, 2026
Here is the trouble. In Go one wants to minimize the calls to ultrafast C subroutines, because the cgo call overhead overshadows the actual useful computation. One can also put it as following, if such short C subroutine ends up on critical path, then you want to do something different. Could you clarify what is it you're trying to do? If you negate a point and then add it to some other point, then you can just as well subtract it. If you negate, multiply and add, then you can multiply and subract...
leoruss0902-lab commentedon May 14, 2026
Hi,
Thanks for the reply and the clear explanation. You are absolutely right about the cgo call overhead overshadowing the math for such a tiny operation.
To clarify my use case: I am not using the negated point for standard elliptic curve arithmetic. If I were, I would definitely use
SubAssignto minimize cgo crossings as you suggested.Instead, I am implementing a NIZK verifier where I need to check pairing equations, e.g.,$e(A, B) = e(C, D)$ .$\mathbb{F}_{p^{12}}$ exponentiations, I am batching the pairings using Miller loops and a single final exponentiation, evaluating:
$e(A, B) \cdot e(-C, D) = 1$
To optimize this and avoid multiple expensive
Because of this, I need to explicitly compute$-C$ and pass it directly as an isolated input to the Miller loop function.
Right now, the only public Go API way I found to get$-C$ is by doing $0 - C$ ), which goes through the full point addition logic rather than a simple conditional negation.
P1.SubAssign(C), after having initializedP1to its zero value (computingGiven this specific use case for pairing verifications, what would be your recommended approach? Would you be open to exposing a negation method, or is there perhaps a way to negate the point purely in Go (manipulating the struct limbs directly) to avoid the cgo overhead entirely?
Thanks again for your time!
dot-asm commentedon May 15, 2026
Ah! The answer to the question is Fp12FinalVerify. Not only does it accept$e(A,B)$ and $e(C,D)$ as inputs, but it even takes you all the way to ==1.
leoruss0902-lab commentedon May 28, 2026
Hi,
thank you again for the clarification.
I looked a bit more into the C implementation, and I think I now understand the role of
Fp12FinalVerifybetter. For comparing two Miller-loop outputs, it is clearly the intended API, since it avoids the naive approach of doing two separate final exponentiations.My original point was about a slightly different cost comparison. In both approaches the Miller-loop outputs still have to be multiplied/combined before the final exponentiation. The difference I had in mind is where the “inverse/sign change” is applied: if the equation can be arranged by negating one input point before the Miller loop, this only costs a point negation, while
Fp12FinalVerifyhandles the corresponding operation on theFp12side before the final exponentiation.So my intuition is that there may be a small constant-factor advantage in the point-negation approach in that specific case. That said, I understand that this is likely small compared to the total cost of the Miller loops and the final exponentiation, and in Go the extra cgo calls needed to do this manually may reduce or even eliminate that advantage in practice.
So I agree that
Fp12FinalVerifyis the right interface for comparing pairing products. My main point is just that exposing point negation in the Go bindings still seems useful as a natural group operation, especially since the C API already exposesblst_p1_cnegandblst_p2_cneg.Thanks again for your time and for the explanation.
dot-asm commentedon Jun 3, 2026
No, because the number of cgo transitions would be higher.
Again, "if such short C subroutine ends up on critical path, then you want to do something different." Or in other words, you don't want the interfaces to be exposed ;-)