Skip to content

Commit bf54e1d

Browse files
committed
update readme
1 parent 1d38d91 commit bf54e1d

File tree

1 file changed

+88
-50
lines changed

1 file changed

+88
-50
lines changed

README.md

Lines changed: 88 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,147 @@
11
# py_arkworks_bls12381
22

3+
Python bindings for BLS12-381 curve operations using [arkworks](https://github.com/arkworks-rs/algebra). Built with [PyO3](https://pyo3.rs/) and [maturin](https://www.maturin.rs/).
4+
35
The main usage of this library at this moment is to generate test vectors for EIP4844 in the [consensus-specs](https://github.com/ethereum/consensus-specs/tree/master). The library itself is generic, so feel free to use it for other purposes.
46

5-
## G1/G2Points
7+
Requires Python >= 3.11.
8+
9+
## G1/G2 Points
610

711
```python
812
from py_arkworks_bls12381 import G1Point, G2Point, Scalar
913

10-
# G1Point and G2Point have the same methods implemented on them
11-
# For brevity, I will only show one method using G1Point and G2Point
12-
# The rest of the code will just use G1Point
14+
# G1Point and G2Point have the same methods implemented on them.
15+
# For brevity, most examples below use G1Point only.
1316

14-
# Point initialization -- This will be initialized to the g1 generator
17+
# Point initialization -- defaults to the generator
1518
g1_generator = G1Point()
1619
g2_generator = G2Point()
1720

18-
# Identity element
21+
# Identity element
1922
identity = G1Point.identity()
2023

21-
# Equality -- We override eq and neq operators
24+
# Equality
2225
assert g1_generator == g1_generator
2326
assert g1_generator != identity
2427

25-
26-
# Printing an element -- We override __str__ so when we print
27-
# an element it prints in hex
28-
print("identity: ",identity)
28+
# Printing -- __str__ returns hex
29+
print("identity: ", identity)
2930
print("g1 generator: ", g1_generator)
3031
print("g2 generator: ", g2_generator)
3132

32-
# Point Addition/subtraction/Negation -- We override the add/sub/neg operators
33+
# Arithmetic -- add/sub/neg/scalar mul
3334
gen = G1Point()
3435
double_gen = gen + gen
3536
assert double_gen - gen == gen
3637
neg_gen = -gen
3738
assert neg_gen + gen == identity
3839

39-
# Scalar multiplication
40-
#
4140
scalar = Scalar(4)
4241
four_gen = gen * scalar
4342
assert four_gen == gen + gen + gen + gen
4443

45-
# Serialisation
46-
#
47-
# serialising to/from a g1 point
48-
# We don't expose the uncompressed form
49-
# because it seems like its not needed
50-
compressed_bytes = gen.to_compressed_bytes()
51-
deserialised_point = G1Point.from_compressed_bytes(compressed_bytes)
52-
# If the bytes being received are trusted, we can avoid
53-
# doing subgroup checks
54-
deserialised_point_unchecked = G1Point.from_compressed_bytes_unchecked(compressed_bytes)
55-
assert deserialised_point == deserialised_point_unchecked
56-
assert deserialised_point == gen
44+
# Subgroup check
45+
assert gen.is_in_subgroup()
46+
47+
# Multi-scalar multiplication
48+
result = G1Point.multiexp_unchecked([gen, gen], [Scalar(2), Scalar(3)])
49+
assert result == gen * Scalar(5)
50+
```
51+
52+
## Serialization
53+
54+
```python
55+
from py_arkworks_bls12381 import G1Point, G2Point, Scalar
56+
57+
gen = G1Point()
58+
59+
# Compressed (48 bytes for G1, 96 bytes for G2)
60+
compressed = gen.to_compressed_bytes()
61+
assert gen == G1Point.from_compressed_bytes(compressed)
62+
# Skip subgroup check for trusted bytes:
63+
assert gen == G1Point.from_compressed_bytes_unchecked(compressed)
64+
65+
# Uncompressed xy coordinates (96 bytes for G1, 192 bytes for G2)
66+
# Explicit endianness: _be for big-endian, _le for little-endian
67+
xy_be = gen.to_xy_bytes_be()
68+
assert gen == G1Point.from_xy_bytes_be(xy_be)
69+
70+
xy_le = gen.to_xy_bytes_le()
71+
assert gen == G1Point.from_xy_bytes_le(xy_le)
72+
73+
# Unchecked variants (skip subgroup check, still checks on-curve)
74+
assert gen == G1Point.from_xy_bytes_unchecked_be(xy_be)
75+
assert gen == G1Point.from_xy_bytes_unchecked_le(xy_le)
76+
77+
# Scalar serialization -- big-endian and little-endian
78+
s = Scalar(42)
79+
assert s == Scalar.from_le_bytes(s.to_le_bytes())
80+
assert s == Scalar.from_be_bytes(s.to_be_bytes())
81+
```
82+
83+
## Map to Curve
84+
85+
```python
86+
from py_arkworks_bls12381 import G1Point, G2Point
87+
88+
# Map an Fp element (48 bytes) to G1 using SWU map + cofactor clearing
89+
fp_bytes = bytes(47) + bytes([1])
90+
g1 = G1Point.map_from_fp_be(fp_bytes) # big-endian input
91+
g1 = G1Point.map_from_fp_le(fp_bytes) # little-endian input
92+
93+
# Map an Fp2 element (96 bytes: c0 || c1) to G2
94+
fp2_bytes = bytes(47) + bytes([1]) + bytes(47) + bytes([2])
95+
g2 = G2Point.map_from_fp2_be(fp2_bytes)
96+
g2 = G2Point.map_from_fp2_le(fp2_bytes)
5797
```
5898

5999
## Pairing
60100

61101
```python
62102
from py_arkworks_bls12381 import G1Point, G2Point, GT, Scalar
63103

64-
65-
# Initilisation -- This is the generator point
104+
# Generator pairing
66105
gt_gen = GT()
106+
assert gt_gen == GT.pairing(G1Point(), G2Point())
67107

68-
# Zero/One
69-
zero = GT.zero()
70-
one = GT.one()
71-
72-
# Computing a pairing using pairing and multi_pairing
73-
# multi_pairing does multiple pairings with only one final_exp
74-
assert gt_gen == GT.pairing(G1Point(), G2Point())
108+
# Multi-pairing (single final exponentiation)
75109
g1s = [G1Point()]
76110
g2s = [G2Point()]
77111
assert gt_gen == GT.multi_pairing(g1s, g2s)
78112

113+
# Pairing check: returns True if product of pairings equals identity
114+
# e(G1, G2) * e(-G1, G2) == 1
115+
g1 = G1Point()
116+
g2 = G2Point()
117+
assert GT.pairing_check([g1, -g1], [g2, g2])
118+
assert not GT.pairing_check([g1], [g2])
119+
79120
# Bilinearity
80121
a = Scalar(1234)
81122
b = Scalar(4566)
82123
c = a * b
83124

84-
85125
g = G1Point() * a
86126
h = G2Point() * b
87-
88127
p = GT.pairing(g, h)
89128

90-
c_g1 = G1Point() *c
91-
c_g2 = G2Point() *c
92-
93-
assert p == GT.pairing(c_g1, G2Point())
94-
assert p == GT.pairing(G1Point(), c_g2)
129+
assert p == GT.pairing(G1Point() * c, G2Point())
130+
assert p == GT.pairing(G1Point(), G2Point() * c)
95131
```
96132

97133
## Scalar
98134

99135
```python
100136
from py_arkworks_bls12381 import Scalar
101137

102-
# Initialisation - The default initialiser for a scalar is an u128 integer
103138
scalar = Scalar(12345)
104139

105-
# Equality -- We override eq and neq operators
140+
# Equality
106141
assert scalar == scalar
107142
assert Scalar(1234) != Scalar(4567)
108143

109-
# Scalar Addition/subtraction/Negation -- We override the add/sub/neg operators
144+
# Arithmetic
110145
a = Scalar(3)
111146
b = Scalar(4)
112147
c = Scalar(5)
@@ -117,13 +152,16 @@ neg_a = -a
117152
assert a + neg_a == Scalar(0)
118153
assert (a + neg_a).is_zero()
119154

120-
# Serialisation
121-
compressed_bytes = scalar.to_le_bytes()
122-
deserialised_scalar = Scalar.from_le_bytes(compressed_bytes)
123-
assert scalar == deserialised_scalar
155+
# Division, pow, inverse
156+
assert a / a == Scalar(1)
157+
assert a.pow(Scalar(2)) == a.square()
158+
assert a * a.inverse() == Scalar(1)
159+
160+
# Convert to/from int
161+
assert int(Scalar(42)) == 42
124162
```
125163

126-
## Development
164+
## Development
127165

128166
First, activate the virtual environment:
129167

0 commit comments

Comments
 (0)