Skip to content

ethereum/py-arkworks-bls12381

Repository files navigation

py_arkworks_bls12381

Python bindings for BLS12-381 curve operations using arkworks. Built with PyO3 and maturin.

The main usage of this library at this moment is to generate test vectors for EIP4844 in the consensus-specs. The library itself is generic, so feel free to use it for other purposes.

Requires Python >= 3.11.

G1/G2 Points

from py_arkworks_bls12381 import G1Point, G2Point, Scalar

# G1Point and G2Point have the same methods implemented on them.
# For brevity, most examples below use G1Point only.

# Point initialization -- defaults to the generator
g1_generator = G1Point()
g2_generator = G2Point()

# Identity element
identity = G1Point.identity()

# Equality
assert g1_generator == g1_generator
assert g1_generator != identity

# Printing -- __str__ returns hex
print("identity: ", identity)
print("g1 generator: ", g1_generator)
print("g2 generator: ", g2_generator)

# Arithmetic -- add/sub/neg/scalar mul
gen = G1Point()
double_gen = gen + gen
assert double_gen - gen == gen
neg_gen = -gen
assert neg_gen + gen == identity

scalar = Scalar(4)
four_gen = gen * scalar
assert four_gen == gen + gen + gen + gen

# Subgroup check
assert gen.is_in_subgroup()

# Multi-scalar multiplication
result = G1Point.multiexp_unchecked([gen, gen], [Scalar(2), Scalar(3)])
assert result == gen * Scalar(5)

Serialization

from py_arkworks_bls12381 import G1Point, G2Point, Scalar

gen = G1Point()

# Compressed (48 bytes for G1, 96 bytes for G2)
compressed = gen.to_compressed_bytes()
assert gen == G1Point.from_compressed_bytes(compressed)
# Skip subgroup check for trusted bytes:
assert gen == G1Point.from_compressed_bytes_unchecked(compressed)

# Uncompressed xy coordinates (96 bytes for G1, 192 bytes for G2)
# Explicit endianness: _be for big-endian, _le for little-endian
xy_be = gen.to_xy_bytes_be()
assert gen == G1Point.from_xy_bytes_be(xy_be)

xy_le = gen.to_xy_bytes_le()
assert gen == G1Point.from_xy_bytes_le(xy_le)

# Unchecked variants (skip subgroup check, still checks on-curve)
assert gen == G1Point.from_xy_bytes_unchecked_be(xy_be)
assert gen == G1Point.from_xy_bytes_unchecked_le(xy_le)

# Scalar serialization -- big-endian and little-endian
s = Scalar(42)
assert s == Scalar.from_le_bytes(s.to_le_bytes())
assert s == Scalar.from_be_bytes(s.to_be_bytes())

Map to Curve

from py_arkworks_bls12381 import G1Point, G2Point

# Map an Fp element (48 bytes) to G1 using SWU map + cofactor clearing
fp_bytes = bytes(47) + bytes([1])
g1 = G1Point.map_from_fp_be(fp_bytes)   # big-endian input
g1 = G1Point.map_from_fp_le(fp_bytes)   # little-endian input

# Map an Fp2 element (96 bytes: c0 || c1) to G2
fp2_bytes = bytes(47) + bytes([1]) + bytes(47) + bytes([2])
g2 = G2Point.map_from_fp2_be(fp2_bytes)
g2 = G2Point.map_from_fp2_le(fp2_bytes)

Pairing

from py_arkworks_bls12381 import G1Point, G2Point, GT, Scalar

# Generator pairing
gt_gen = GT()
assert gt_gen == GT.pairing(G1Point(), G2Point())

# Multi-pairing (single final exponentiation)
g1s = [G1Point()]
g2s = [G2Point()]
assert gt_gen == GT.multi_pairing(g1s, g2s)

# Pairing check: returns True if product of pairings equals identity
# e(G1, G2) * e(-G1, G2) == 1
g1 = G1Point()
g2 = G2Point()
assert GT.pairing_check([g1, -g1], [g2, g2])
assert not GT.pairing_check([g1], [g2])

# Bilinearity
a = Scalar(1234)
b = Scalar(4566)
c = a * b

g = G1Point() * a
h = G2Point() * b
p = GT.pairing(g, h)

assert p == GT.pairing(G1Point() * c, G2Point())
assert p == GT.pairing(G1Point(), G2Point() * c)

Scalar

from py_arkworks_bls12381 import Scalar

scalar = Scalar(12345)

# Equality
assert scalar == scalar
assert Scalar(1234) != Scalar(4567)

# Arithmetic
a = Scalar(3)
b = Scalar(4)
c = Scalar(5)
assert a.square() + b.square() == c.square()
assert a * a + b * b == c * c

neg_a = -a
assert a + neg_a == Scalar(0)
assert (a + neg_a).is_zero()

# Division, pow, inverse
assert a / a == Scalar(1)
assert a.pow(Scalar(2)) == a.square()
assert a * a.inverse() == Scalar(1)

# Convert to/from int
assert int(Scalar(42)) == 42

Development

First, activate the virtual environment:

python3 -m venv .env
source .env/bin/activate

Then, install maturin which is needed to build the project:

pip install maturin

Next, build the rust package and install it in your virtual environment:

maturin develop

Finally, run a file in the examples folder:

python3 examples/point.py

Benchmarks

This is to be executed in the virtual environment above.

First, install py_ecc which is used as a comparison:

pip install py_ecc

Then, run the benchmarks with this command:

python3 -m examples.benches.bench

About

Python wrapper around arkwork's implementation of bls12-381

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors