Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ citest: pyspec
mkdir -p $(TEST_REPORT_DIR);
ifdef fork
. venv/bin/activate; cd $(PY_SPEC_DIR); \
python3 -m pytest -n 16 --bls-type=milagro --preset=$(TEST_PRESET_TYPE) --fork=$(fork) --junitxml=test-reports/test_results.xml eth2spec
python3 -m pytest -n 16 --bls-type=fastest --preset=$(TEST_PRESET_TYPE) --fork=$(fork) --junitxml=test-reports/test_results.xml eth2spec
else
. venv/bin/activate; cd $(PY_SPEC_DIR); \
python3 -m pytest -n 16 --bls-type=milagro --preset=$(TEST_PRESET_TYPE) --junitxml=test-reports/test_results.xml eth2spec
python3 -m pytest -n 16 --bls-type=fastest --preset=$(TEST_PRESET_TYPE) --junitxml=test-reports/test_results.xml eth2spec
endif


Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -1174,5 +1174,6 @@ def run(self):
RUAMEL_YAML_VERSION,
"lru-dict==1.1.8",
MARKO_VERSION,
"py_arkworks_bls12381==0.3.4",
]
)
13 changes: 7 additions & 6 deletions specs/deneb/polynomial-commitments.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def g1_lincomb(points: Sequence[KZGCommitment], scalars: Sequence[BLSFieldElemen
BLS multiscalar multiplication. This function can be optimized using Pippenger's algorithm and variants.
"""
assert len(points) == len(scalars)
result = bls.Z1
result = bls.Z1()
for x, a in zip(points, scalars):
result = bls.add(result, bls.multiply(bls.bytes48_to_G1(x), a))
return KZGCommitment(bls.G1_to_bytes48(result))
Expand Down Expand Up @@ -371,10 +371,10 @@ def verify_kzg_proof_impl(commitment: KZGCommitment,
Verify KZG proof that ``p(z) == y`` where ``p(z)`` is the polynomial represented by ``polynomial_kzg``.
"""
# Verify: P - y = Q * (X - z)
X_minus_z = bls.add(bls.bytes96_to_G2(KZG_SETUP_G2[1]), bls.multiply(bls.G2, BLS_MODULUS - z))
P_minus_y = bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1, BLS_MODULUS - y))
X_minus_z = bls.add(bls.bytes96_to_G2(KZG_SETUP_G2[1]), bls.multiply(bls.G2(), BLS_MODULUS - z))
P_minus_y = bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), BLS_MODULUS - y))
return bls.pairing_check([
[P_minus_y, bls.neg(bls.G2)],
[P_minus_y, bls.neg(bls.G2())],
[bls.bytes48_to_G1(proof), X_minus_z]
])
```
Expand Down Expand Up @@ -415,14 +415,14 @@ def verify_kzg_proof_batch(commitments: Sequence[KZGCommitment],
proofs,
[BLSFieldElement((int(z) * int(r_power)) % BLS_MODULUS) for z, r_power in zip(zs, r_powers)],
)
C_minus_ys = [bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1, BLS_MODULUS - y))
C_minus_ys = [bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), BLS_MODULUS - y))
for commitment, y in zip(commitments, ys)]
C_minus_y_as_KZGCommitments = [KZGCommitment(bls.G1_to_bytes48(x)) for x in C_minus_ys]
C_minus_y_lincomb = g1_lincomb(C_minus_y_as_KZGCommitments, r_powers)

return bls.pairing_check([
[bls.bytes48_to_G1(proof_lincomb), bls.neg(bls.bytes96_to_G2(KZG_SETUP_G2[1]))],
[bls.add(bls.bytes48_to_G1(C_minus_y_lincomb), bls.bytes48_to_G1(proof_z_lincomb)), bls.G2]
[bls.add(bls.bytes48_to_G1(C_minus_y_lincomb), bls.bytes48_to_G1(proof_z_lincomb)), bls.G2()]
])
```

Expand Down Expand Up @@ -561,3 +561,4 @@ def verify_blob_kzg_proof_batch(blobs: Sequence[Blob],

return verify_kzg_proof_batch(commitments, evaluation_challenges, ys, proofs)
```

11 changes: 9 additions & 2 deletions tests/core/pyspec/eth2spec/test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ def pytest_addoption(parser):
help="bls-default: make tests that are not dependent on BLS run without BLS"
)
parser.addoption(
"--bls-type", action="store", type=str, default="py_ecc", choices=["py_ecc", "milagro"],
help="bls-type: use 'pyecc' or 'milagro' implementation for BLS"
"--bls-type", action="store", type=str, default="py_ecc", choices=["py_ecc", "milagro", "arkworks", "fastest"],
help=(
"bls-type: use specified BLS implementation;"
"fastest' uses milagro for signatures and arkworks for everything else (e.g. KZG)"
)
)


Expand Down Expand Up @@ -88,5 +91,9 @@ def bls_type(request):
bls_utils.use_py_ecc()
elif bls_type == "milagro":
bls_utils.use_milagro()
elif bls_type == "arkworks":
bls_utils.use_arkworks()
elif bls_type == "fastest":
bls_utils.use_fastest()
else:
raise Exception(f"unrecognized bls type: {bls_type}")
223 changes: 196 additions & 27 deletions tests/core/pyspec/eth2spec/utils/bls.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,49 @@
from py_ecc.bls import G2ProofOfPossession as py_ecc_bls
from py_ecc.bls.g2_primatives import signature_to_G2 as _signature_to_G2
from py_ecc.optimized_bls12_381 import ( # noqa: F401
G1,
G2,
Z1,
Z2,
FQ,
add,
multiply,
neg,
pairing,
final_exponentiate,
FQ12
G1 as py_ecc_G1,
G2 as py_ecc_G2,
Z1 as py_ecc_Z1,
add as py_ecc_add,
multiply as py_ecc_mul,
neg as py_ecc_neg,
pairing as py_ecc_pairing,
final_exponentiate as py_ecc_final_exponentiate,
FQ12 as py_ecc_GT
)
from py_ecc.bls.g2_primitives import ( # noqa: F401
G1_to_pubkey as G1_to_bytes48,
pubkey_to_G1 as bytes48_to_G1,
G2_to_signature as G2_to_bytes96,
signature_to_G2 as bytes96_to_G2,
G1_to_pubkey as py_ecc_G1_to_bytes48,
pubkey_to_G1 as py_ecc_bytes48_to_G1,
G2_to_signature as py_ecc_G2_to_bytes96,
signature_to_G2 as py_ecc_bytes96_to_G2,
)
from py_arkworks_bls12381 import (
G1Point as arkworks_G1,
G2Point as arkworks_G2,
Scalar as arkworks_Scalar,
GT as arkworks_GT
)


import milagro_bls_binding as milagro_bls # noqa: F401 for BLS switching option

import py_arkworks_bls12381 as arkworks_bls # noqa: F401 for BLS switching option


class fastest_bls:
G1 = arkworks_G1
G2 = arkworks_G2
Scalar = arkworks_Scalar
GT = arkworks_GT
_AggregatePKs = milagro_bls._AggregatePKs
Sign = milagro_bls.Sign
Verify = milagro_bls.Verify
Aggregate = milagro_bls.Aggregate
AggregateVerify = milagro_bls.AggregateVerify
FastAggregateVerify = milagro_bls.FastAggregateVerify
SkToPk = milagro_bls.SkToPk


# Flag to make BLS active or not. Used for testing, do not ignore BLS in production unless you know what you are doing.
bls_active = True

Expand All @@ -43,6 +64,14 @@ def use_milagro():
bls = milagro_bls


def use_arkworks():
"""
Shortcut to use Arkworks as BLS library
"""
global bls
bls = arkworks_bls


def use_py_ecc():
"""
Shortcut to use Py-ecc as BLS library
Expand All @@ -51,6 +80,14 @@ def use_py_ecc():
bls = py_ecc_bls


def use_fastest():
"""
Shortcut to use Milagro for signatures and Arkworks for other BLS operations
"""
global bls
bls = fastest_bls


def only_with_bls(alt_return=None):
"""
Decorator factory to make a function only run when BLS is active. Otherwise return the default.
Expand All @@ -68,7 +105,10 @@ def entry(*args, **kw):
@only_with_bls(alt_return=True)
def Verify(PK, message, signature):
try:
result = bls.Verify(PK, message, signature)
if bls == arkworks_bls: # no signature API in arkworks
result = py_ecc_bls.Verify(PK, message, signature)
else:
result = bls.Verify(PK, message, signature)
except Exception:
result = False
finally:
Expand All @@ -78,7 +118,10 @@ def Verify(PK, message, signature):
@only_with_bls(alt_return=True)
def AggregateVerify(pubkeys, messages, signature):
try:
result = bls.AggregateVerify(list(pubkeys), list(messages), signature)
if bls == arkworks_bls: # no signature API in arkworks
result = py_ecc_bls.AggregateVerify(list(pubkeys), list(messages), signature)
else:
result = bls.AggregateVerify(list(pubkeys), list(messages), signature)
except Exception:
result = False
finally:
Expand All @@ -88,7 +131,10 @@ def AggregateVerify(pubkeys, messages, signature):
@only_with_bls(alt_return=True)
def FastAggregateVerify(pubkeys, message, signature):
try:
result = bls.FastAggregateVerify(list(pubkeys), message, signature)
if bls == arkworks_bls: # no signature API in arkworks
result = py_ecc_bls.FastAggregateVerify(list(pubkeys), message, signature)
else:
result = bls.FastAggregateVerify(list(pubkeys), message, signature)
except Exception:
result = False
finally:
Expand All @@ -97,12 +143,16 @@ def FastAggregateVerify(pubkeys, message, signature):

@only_with_bls(alt_return=STUB_SIGNATURE)
def Aggregate(signatures):
if bls == arkworks_bls: # no signature API in arkworks
return py_ecc_bls.Aggregate(signatures)
return bls.Aggregate(signatures)


@only_with_bls(alt_return=STUB_SIGNATURE)
def Sign(SK, message):
if bls == py_ecc_bls:
if bls == arkworks_bls: # no signature API in arkworks
return py_ecc_bls.Sign(SK, message)
elif bls == py_ecc_bls:
return bls.Sign(SK, message)
else:
return bls.Sign(SK.to_bytes(32, 'big'), message)
Expand All @@ -121,24 +171,143 @@ def AggregatePKs(pubkeys):
# milagro_bls._AggregatePKs checks KeyValidate internally
pass

if bls == arkworks_bls: # no signature API in arkworks
return milagro_bls._AggregatePKs(list(pubkeys))

return bls._AggregatePKs(list(pubkeys))


@only_with_bls(alt_return=STUB_SIGNATURE)
def SkToPk(SK):
if bls == py_ecc_bls:
return bls.SkToPk(SK)
if bls == py_ecc_bls or bls == arkworks_bls: # no signature API in arkworks
return py_ecc_bls.SkToPk(SK)
else:
return bls.SkToPk(SK.to_bytes(32, 'big'))


def pairing_check(values):
p_q_1, p_q_2 = values
final_exponentiation = final_exponentiate(
pairing(p_q_1[1], p_q_1[0], final_exponentiate=False)
* pairing(p_q_2[1], p_q_2[0], final_exponentiate=False)
)
return final_exponentiation == FQ12.one()
if bls == arkworks_bls or bls == fastest_bls:
p_q_1, p_q_2 = values
g1s = [p_q_1[0], p_q_2[0]]
g2s = [p_q_1[1], p_q_2[1]]
return arkworks_GT.multi_pairing(g1s, g2s) == arkworks_GT.one()
else:
p_q_1, p_q_2 = values
final_exponentiation = py_ecc_final_exponentiate(
py_ecc_pairing(p_q_1[1], p_q_1[0], final_exponentiate=False)
* py_ecc_pairing(p_q_2[1], p_q_2[0], final_exponentiate=False)
)
return final_exponentiation == py_ecc_GT.one()


def add(lhs, rhs):
"""
Performs point addition of `lhs` and `rhs`.
The points can either be in G1 or G2.
"""
if bls == arkworks_bls or bls == fastest_bls:
return lhs + rhs
return py_ecc_add(lhs, rhs)


def multiply(point, scalar):
"""
Performs Scalar multiplication between
`point` and `scalar`.
`point` can either be in G1 or G2
"""
if bls == arkworks_bls or bls == fastest_bls:
int_as_bytes = scalar.to_bytes(32, 'little')
scalar = arkworks_Scalar.from_le_bytes(int_as_bytes)
return point * scalar
return py_ecc_mul(point, scalar)


def neg(point):
"""
Returns the point negation of `point`
`point` can either be in G1 or G2
"""
if bls == arkworks_bls or bls == fastest_bls:
return -point
return py_ecc_neg(point)


def Z1():
"""
Returns the identity point in G1
"""
if bls == arkworks_bls or bls == fastest_bls:
return arkworks_G1.identity()
return py_ecc_Z1


def G1():
"""
Returns the chosen generator point in G1
"""
if bls == arkworks_bls or bls == fastest_bls:
return arkworks_G1()
return py_ecc_G1


def G2():
"""
Returns the chosen generator point in G2
"""
if bls == arkworks_bls or bls == fastest_bls:
return arkworks_G2()
return py_ecc_G2


def G1_to_bytes48(point):
"""
Serializes a point in G1.
Returns a bytearray of size 48 as
we use the compressed format
"""
if bls == arkworks_bls or bls == fastest_bls:
return point.to_compressed_bytes()
return py_ecc_G1_to_bytes48(point)


def G2_to_bytes96(point):
"""
Serializes a point in G2.
Returns a bytearray of size 96 as
we use the compressed format
"""
if bls == arkworks_bls or bls == fastest_bls:
return point.to_compressed_bytes()
return py_ecc_G2_to_bytes96(point)


def bytes48_to_G1(bytes48):
"""
Deserializes a purported compressed serialized
point in G1.
- No subgroup checks are performed
- If the bytearray is not a valid serialization
of a point in G1, then this method will raise
an exception
"""
if bls == arkworks_bls or bls == fastest_bls:
return arkworks_G1.from_compressed_bytes_unchecked(bytes48)
return py_ecc_bytes48_to_G1(bytes48)


def bytes96_to_G2(bytes96):
"""
Deserializes a purported compressed serialized
point in G2.
- No subgroup checks are performed
- If the bytearray is not a valid serialization
of a point in G2, then this method will raise
an exception
"""
if bls == arkworks_bls or bls == fastest_bls:
return arkworks_G2.from_compressed_bytes_unchecked(bytes96)
return py_ecc_bytes96_to_G2(bytes96)


@only_with_bls(alt_return=True)
Expand Down