I am implementing BLS signature verification on smart contracts and I have a question regarding the way that Ethereum verifies the signature. Recall that bls signature works as

  • $e(P_2,H(m)_1)_T=e(G_2, S_1)_T$ where $_2$ and $_1$ denote points of $G_2$ and $G_1$, and $_T$ for $G_T$.
  • Off-chain, you take your secret $x$ , and do $x\,G_2\to P_2$ (your public key).
  • You then provide your public key $P_2$ to the on-chain contract.
  • You then generate your signature, $x\,H(m)_1\to S_1$.
  • You provide signature to on-chain contract.
  • It verifies $e(P_2,H(m)_1)_T=e(G_2, S_1)_T$.

However, Ethereum verifies the equality little differently than the last line. Specifically they check if:


The pseudo-code that does all this can be written by

from py_ecc.bn128 import *
p = curve_order
x = randint(1, p-1) # out secret key
H_m = multiply(G1, randint(1, p-1)) # lets pretend it's HashToPoint
P = multiply(G2, x) # our public key in G2
S = multiply(H_m, x) # our signature in G1
a = pairing(P, H_m)
b = pairing(G2, S)
assert a == b # Verify signature

To use equivalent of ECPAIRING, you’d then do:

c = pairing(G2, neg(S))
assert a * c == FQ12.one()

What I don't understand is how $e(A_2,B_1)*e(C_2,D_1)==1_T$. I far as I know the neg stands for negation operation. The ECPAIRING is the opcode they named. Can someone help me to see what I am missing?

Moderator's note: some of this question matches that post


1 Answer 1


It all comes down to the properties of the pairing operation (commonly denoted as $e$, called pairing in the code).

$e$ is a function that takes two elliptic curve points as inputs, and generates a value for which multiplication and exponentiation makes sense (in practice, it is an element within an 'extension field', however you don't need to worry what that means)

One property that $e$ has is this identity:

$$e(aG, bH) = e(G,H)^{ab}$$

This identity is true for any integers $a, b$, and any two points $G, H$.

The BLS signature verification logic relies on this: if the public key is $P_2 = xG_2$ (and $G_2$ is also known by the verifier), and the message is mapped to a point $H(m)_1$, and the signature is $S_1 = xH(m)_1$, then the standard BLS verify (assuming a valid signature) computes the two pairings:

$$e(P_2, H(m)_1) = e(xG_2, H(m)_1) = e(G_2, H(m)_1)^x$$

$$e(G_2, S_1) = e(G_2, xH(m)_1) = e(G_2, H(m)_1)^x$$

If the signature is invalid, the second equation evaluates to something else.

If $S_1$ is in fact $xH(m)_1$ (that is, if the signature is valid, these two values are the same.

Now, the Ethereum BLS verification logic is equivalent, but works slightly differently: it instead computes (again, if the signature is valid):

$$e(G_2, -S_1) = e(G_2, -xH(m)_1) = e(G_2, H(m)_1)^{-x}$$

Remember, negating a point is the same as multiplying it by -1.

And again, if the signature is invalid, it evaluates to something else.

And so, if the signature is valid, the verifier has just computed the two values $e(G_2, H(m)_1)^x$ and $e(G_2, H(m)_1)^{-x}$; these are multiplicative inverses of each other, and so multiplying them together evaluates to one.

  • $\begingroup$ This was great explanation. Thank you! $\endgroup$ Oct 25, 2022 at 13:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.