NEP | Title | Authors | Status | DiscussionsTo | Type | Version | Created | LastUpdated |
---|---|---|---|---|---|---|---|---|
488 |
Host Functions for BLS12-381 Curve Operations |
Olga Kuniavskaia <olga.kunyavskaya@aurora.dev> |
Final |
Runtime Spec |
0.0.1 |
2023-07-17 |
2023-11-21 |
This NEP introduces host functions to perform operations on the BLS12-381 elliptic curve. It is a minimal set of functions needed to efficiently verify BLS signatures and zkSNARKs.
The primary aim of this NEP is to enable fast and efficient verification of BLS signatures and zkSNARKs based on the BLS12-3811,2,3 elliptic curve on NEAR.
To efficiently verify zkSNARKs4, host functions for operations on the BN254 elliptic curve (also known as Alt-BN128)5, 6 have already been implemented on NEAR7. For instance, the Zeropool8 project utilizes these host functions for verifying zkSNARKs on NEAR. However, recent research shows that the BN254 security level is lower than 100-bit9 and it is not recommended for use. BLS12-381, on the other hand, offers over 120 bits of security10 and is widely used11,12,13,14,15,16 as a robust alternative. Supporting operations for BLS12-381 elliptic curve will significantly enhance the security of projects similar to Zeropool.
Another crucial objective is the verification of BLS signatures. Initially, host functions for BN254 on NEAR were designed for zkSNARK verification and are insufficient for BLS signature verification. However, even if these host functions were sufficient for BLS signature verification on the BN254 elliptic curve, this would not be enough for compatibility with other projects. In particular, projects such as ZCash11, Ethereum12, Tezos14, and Filecoin15 incorporate BLS12-381 specifically within their protocols. If we aim for compatibility with these projects, we must also utilize this elliptic curve. For instance, to create a trustless bridge17 between Ethereum and NEAR, we must efficiently verify BLS signatures based on BLS12-381, as these are the signatures employed within Ethereum's protocol.
In this NEP, we propose to add the following host functions:
-
bls12381_p1_sum — computes the sum of signed points from
$E(F_p)$ elliptic curve. This function is useful for aggregating public keys or signatures in the BLS signature scheme. It can be employed for simple addition in$E(F_p)$ . It is kept separate from themultiexp
function due to gas cost considerations. -
bls12381_p2_sum — computes the sum of signed points from
$E'(F_{p^2})$ elliptic curve. This function is useful for aggregating signatures or public keys in the BLS signature scheme. -
bls12381_g1_multiexp — calculates
$\sum p_i s_i$ for points$p_i \in G_1 \subset E(F_p)$ and scalars$s_i$ . This operation can be used to multiply a group element by a scalar. -
bls12381_g2_multiexp — calculates
$\sum p_i s_i$ for points$p_i \in G_2 \subset E'(F_{p^2})$ and scalars$s_i$ . -
bls12381_map_fp_to_g1 — maps base field elements into
$G_1$ points. It does not perform the mapping of byte strings into field elements. -
bls12381_map_fp2_to_g2 — maps extension field elements into
$G_2$ points. This function does not perform the mapping of byte strings into extension field elements, which would be needed to efficiently map a message into a group element. We are not implementing thehash_to_field
18 function because the latter can be executed within a contract and various hashing algorithms can be used within this function. -
bls12381_p1_decompress — decompresses points from
$E(F_p)$ provided in a compressed form. Certain protocols offer points on the curve in a compressed form (e.g., the light client updates in Ethereum 2.0), and decompression is a time-consuming operation. All the other functions in this NEP only accept decompressed points for simplicity and optimized gas consumption. -
bls12381_p2_decompress — decompresses points from
$E'(F_{p^2})$ provided in a compressed form. -
bls12381_pairing_check — verifies that
$\prod e(p_i, q_i) = 1$ , where$e$ is a pairing operation and$p_i \in G_1 \land q_i \in G_2$ . This function is used to verify BLS signatures or zkSNARKs.
Functions required for verifying BLS signatures19:
- bls12381_p1_sum
- bls12381_p2_sum
- bls12381_map_fp2_to_g2
- bls12381_p1_decompress
- bls12381_p2_decompress
- bls12381_pairing_check
Functions required for verifying zkSNARKs:
- bls12381_p1_sum
- bls12381_g1_multiexp
- bls12381_pairing_check
Both zkSNARKs and BLS signatures can be implemented alternatively by swapping
An analogous proposal, EIP-253720, exists in Ethereum. The functions here have been designed with compatibility with that Ethereum's proposal in mind. This design approach aims to ensure future ease in supporting corresponding precompiles for Aurora21.
The field
The elliptic curve
together with an imaginary point at infinity
In the case of BLS12-381 the equation is
Parameters for our case:
$A = 0$ $B = 4$ $p = \mathtt{0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab}$
Let
The addition operation for Elliptic Curve is a function
- if
$P \ne Q$ and$P \ne -Q$ - draw a line passing through
$P$ and$Q$ . This line intersects the curve at a third point$R$ . - reflect the point
$R$ across the$x$ -axis by changing the sign of the$y$ -coordinate. The resulting point is$P+Q$ .
- draw a line passing through
- if
$P=Q$ - draw a tangent line through
$P$ for an elliptic curve. The line will intersect the curve at the second point$R$ . - reflect the point
$R$ across the$x$ -axis the same way to get point$2P$
- draw a tangent line through
-
$P = -Q$ -
$P + Q = P + (-P) = \mathcal{O}$ — the point on infinity
-
-
$Q = \mathcal{O}$ $P + Q = P + \mathcal{O} = P$
With the addition operation, Elliptic Curve forms a group.
Subgroup H is a subset of the group G with the following properties:
$\forall h_1, h_2 \in H\colon h_1 + h_2 \in H$ $0 \in H$ $\forall h \in H \colon -h \in H$
Notation:
Group/subgroup order is the number of elements in group/subgroup.
Notation: |G| or #G, where G represents the group.
For some technical reason (related to the pairing
operation which we will define later),
we will not operate over the entire
For the BLS12-381 Elliptic Curve, the order r of
$r = \mathtt{0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001}$
The field extension
The addition operation (
The multiplication
Notation:
In BLS12-381, we will require
To define these fields, we'll need to set up three irreducible polynomials22:
$F_{p^2} = F_p[u] / (u^2 + 1)$ $F_{p^6} = F_{p^2}[v] / (v^3 - u - 1)$ $F_{p^{12}} = F_{p^6}[w] / (w^2 - v)$
The second subgroup we'll utilize has order r and
resides within the same elliptic curve but with elements from
Storing elements from
We want to have
$\forall a, b \in E'(F_{p^2}) \colon \psi(a + b) = \psi(a) + \psi(b)$ $\forall a, b \in E'(F_{p^2}) \colon \psi(a) = \psi(b) \Rightarrow a = b$
This is referred to as an injective group homomorphism.
For BLS12-381, E’ is defined as22:
In most cases, we will be working with points from
If there exists an element
$x = \mathtt{0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb}$ $y = \mathtt{0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1}$
For
$x_0 = \mathtt{0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8}$ $x_1 = \mathtt{0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e}$ $y_0 = \mathtt{0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801}$ $y_1 = \mathtt{0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be}$
Cofactor is the ratio of the size of the entire group
Cofactor
Cofactor
Pairing is a necessary operation for the verification of BLS signatures and certain zkSNARKs. It performs the operation
The main properties of the pairing operation are:
$e(P, Q + R) = e(P, Q) \cdot e(P, R)$ $e(P + S, R) = e(P, R)\cdot e(S, R)$
To compute this function, we utilize an algorithm called Miller Loop.
For an affective implementation of this algorithm,
we require a key parameter for the BLS curve, denoted as
This parameter can be found in the following sources:
- 20 section specification, pairing parameters, Miller loop scalar
- 22 section 4.2.1 Parameter t
- 23 section BLS12-381, parameter u
- 2 section Curve equation and parameters, parameter x
The parameters for the BLS12-381 curve are as follows:
Base field modulus:
Main subgroup order:
Generator for
$x = \mathtt{0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb}$ $y = \mathtt{0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1}$
Generator for
$x_0 = \mathtt{0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8}$ $x_1 = \mathtt{0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e}$ $y_0 = \mathtt{0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801}$ $y_1 = \mathtt{0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be}$
Cofactor for
Cofactor for
Key BLS12-381 parameter used in Miller Loop:
All parameters were sourced from 20, 22, and 23, and they remain consistent across these sources.
This section delineates the functionality of the bls12381_map_fp_to_g1
and bls12381_map_fp2_to_g2
functions,
operating in accordance with the RFC9380 specification "Hashing to Elliptic Curves"24.
These functions map field elements in bls12381_map_fp_to_g1
/bls12381_map_fp2_to_g2
combine the functionalities
of map_to_curve
and clear_cofactor
from RFC938025.
fn bls12381_map_fp_to_g1(u):
let Q = map_to_curve(u);
return clear_cofactor(Q);
We choose not to implement the hash_to_field
function as a host function due to potential changes in hashing methods.
Additionally, executing this function within the contract consumes approximately 2 TGas, which is acceptable for our goals.
Specific implementation parameters for bls12381_map_fp_to_g1
and bls12381_map_fp2_to_g2
can be found in RFC9380
under sections 8.8.126 and 8.8.227, respectively.
The encoding rules for curve points and field elements align with the standards established in zkcrypto28 and the implementation in the milagro lib29.
For elements from
The sign of a point on the elliptic curve is represented as a u8 type in Rust, with two possible values: 0 for a positive sign and 1 for a negative sign. Any other u8 value is considered invalid and should be treated as incorrect.
A scalar value is encoded as little-endian [u8; 32]. All possible byte combinations are allowed.
Values from
An element
Points on the curve are represented by affine coordinates: [u8; 96]
as the byte concatenation of the x and y point coordinates, where
The second-highest bit within the encoding serves to signify a point at infinity. When this bit is set to 1, it designates an infinity point. In this case, all other bits should be set to 0.
Encoding the point at infinity:
let x: [u8; 96] = [0; 96];
x[0] = x[0] | 0x40;
The points on the curve are represented by affine coordinates: [u8; 48]
,
with big-endian encoded
- The highest bit indicates that the point is encoded in compressed form and thus must always be set to 1.
- The second-highest bit marks the point at infinity (if set to 1).
- For the point at infinity, all bits except the first two should be set to 0; other encodings should be considered as incorrect.
- To represent the sign of
$y$ , the third-highest bit in the x encoding is utilized.- If the bit is 0,
$y$ is positive; if 1,$y$ is negative. We'll consider the number positive by taking the smallest value between$y$ and$-y$ , after reducing them to$[0, p)$ .
- If the bit is 0,
The encoding for [u8; 48]
bytes follows the rules described in the section "Extension fields elements
Encoding a point on
let x: [u8; 48] = encodeFp(x)
x[0] = x[0] | 0x80;
x[0] = x[0] | 0x20;
Encoding the point at infinity:
let x: [u8; 48] = [0; 48];
x[0] = x[0] | 0x80;
x[0] = x[0] | 0x40;
The points on the curve are represented by affine coordinates:
The second-highest bit within the encoding serves to signify a point at infinity. When this bit is set to 1, it designates an infinity point. In this case, all other bits should be set to 0.
Encoding the point at infinity:
let x: [u8; 192] = [0; 192];
x[0] = x[0] | 0x40;
The points on the curve are represented by affine coordinates:
- The highest bit indicates if the point is encoded in compressed form and should be set to 1.
- The second-highest bit marks the point at infinity (if set to 1).
- For the point at infinity, all bits except the first two should be set to 0; other encodings should be considered as incorrect.
- To represent the sign of
$y$ , the third-highest bit in the x encoding is utilized.- If the bit is 0,
$y$ is positive; if 1,$y$ is negative. We'll consider the number positive by taking the smallest value between$y$ and$-y$ : first compare$c_1$ , then$c_0$ , after reduction to$[0, p)$ .
- If the bit is 0,
The encoding of
Encoding a point on
let x: [u8; 96] = encodeFp2(x);
x[0] = x[0] | 0x80;
x[0] = x[0] | 0x20;
Encoding the point at infinity:
let x: [u8; 96] = [0; 96];
x[0] = x[0] | 0x80;
x[0] = x[0] | 0x40;
Validating the input for the host functions within the contract can consume significant gas. For instance, verifying if a point belongs to the subgroup is gas-consuming. If an error is returned by the near host function, the entire execution is reverted. To mitigate this, when the input verification is complex, the host function will successfully complete its work but return an ERROR_CODE. This enables users to handle error cases independently. It's important to note that host functions might terminate with an error if it's straightforward to avoid it (e.g., incorrect input size).
The ERROR_CODE is an u64 and can hold the following values:
- 0: No error, execution was successful. For
bls12381_pairing_check
function, the pairing result equals the multiplicative identity. - 1: Execution finished with error due to:
- Incorrect encoding (e.g., incorrectly set compression/decompression bit, coordinate >= p, etc.).
- A point not on the curve (where applicable).
- A point not in the expected subgroup (where applicable).
- 2: Can be returned only in
bls12381_pairing_check
. No error, execution was successful, but the pairing result doesn't equal the multiplicative identity.
In all functions, the input is fetched from memory, beginning at value_ptr
and extending to value_ptr + value_len
.
If value_len
is u64::MAX
, input will come from the register with id value_ptr
.
Execution ends only if there's an incorrect input length,
input extends beyond memory bounds, or gas limits are reached.
Otherwise, execution completes successfully, providing the ERROR_CODE
.
If the ERROR_CODE
equals 0, the output data will be written to
the register with the register_id
identifier.
Otherwise, nothing will be written to the register.
Gas Estimation:
The algorithms described above exhibit linear complexity concerning the number of elements. Gas estimation can be calculated using the following formula:
let k = input_bytes/item_size
let gas_consumed = A + B * k
Here, A and B denote empirically calculated constants unique to each algorithm.
For gas estimation, the benchmark vectors outlined in EIP-253730 can be used where applicable.
Error cases (execution is terminated):
For all functions, execution will terminate in the following cases:
- The input length is not divisible by
item_size
. - The input is beyond memory bounds.
Description:
The function calculates the sum of signed elements on the BLS12-381 curve. It accepts an arbitrary number of pairs
The operations, including the
Note: This function accepts points from the entire curve and is not restricted to points in
Input:
The sequence of pairs
Output:
The ERROR_CODE is returned.
- ERROR_CODE = 0: the input is correct
-
Output: 96 bytes represent one point
$\in E(F_p)$ in its decompressed form. In case of an empty input, it outputs a point on infinity (refer to the Curve Points Encoding section for more details).
-
Output: 96 bytes represent one point
- ERROR_CODE = 1:
- Points or signs are incorrectly encoded (refer to Curve points encoded section).
- Point is not on the curve.
Test cases:
Tests for the sum of two points
This section aims to verify the correctness of summing up two valid elements on the curve:
- Utilize points on the curve with known addition results for comparison, such as tests from EIP-253731,32.
- Generate random points on the curve and verify the commutative property: P + Q = Q + P.
- Validate that the sum of random points from
$G_1$ remains in$G_1$ . - Generate random points on the curve and use another library to cross-check the results.
Edge cases:
- Points not from
$G_1$ . -
$\mathcal{O} + \mathcal{O} = \mathcal{O}$ . -
$P + \mathcal{O} = \mathcal{O} + P = P$ . -
$P + (-P) = (-P) + P = \mathcal{O}$ . - P + P (tangent to the curve).
- The sum of two points P and (-(P + P)) (tangent to the curve at point P).
Tests for inversion
This section aims to validate the correctness of point inversion:
- Generate random points on the curve and verify
$P - P = -P + P = \mathcal{O}$ . - Generate random points on the curve and verify -(-P) = P.
- Generate random points from
$G_1$ and ensure that -P also belong to$G_1$ . - Utilize an external implementation, generate random points on the curve, and compare results.
Edge cases:
- Points not from
$G_1$ . $-\mathcal{O}$
Tests for incorrect data
This section aims to validate the handling of incorrect input data:
- Incorrect input length.
- Incorrect sign value (not 0 or 1).
- Erroneous coding of field elements: one of the first three bits set up incorrectly.
- Erroneous coding of field elements resulting in a correct element on the curve modulo p.
- Erroneous coding of field elements with an incorrect extra bit in the decompressed encoding.
- Point not on the curve.
- Incorrect encoding of the point at infinity.
- Input is beyond memory bounds.
Tests for the sum of an arbitrary amount of points
This section focuses on validating the summation functionality with an arbitrary number of points:
- Generate random points on the curve and verify that the sum of a random permutation matches.
- Generate random points on the curve and utilize another library to validate results.
- Create points and cross-check the outcome with the
multiexp
function. - Generate random points from
$G_1$ and confirm that the sum is also from$G_1$ .
Edge cases:
- Empty input
- Sum with the maximum number of elements
- A single point
Annotation:
pub fn bls12381_p1_sum(&mut self,
value_len: u64,
value_ptr: u64,
register_id: u64) -> Result<u64>;
Description:
The function computes the sum of the signed elements on the BLS12-381 curve. It accepts an arbitrary number of pairs
The
Note: The function accepts any points on the curve and is not limited to points in
Input:
The sequence of pairs
Output:
The ERROR_CODE is returned.
- ERROR_CODE = 0: the input is correct
-
Output: 192 bytes represent one point
$\in E'(F_{p^2})$ in its decompressed form. In case of an empty input, it outputs the point at infinity (refer to the Curve Points Encoding section for more details).
-
Output: 192 bytes represent one point
- ERROR_CODE = 1:
- Points or signs are incorrectly encoded (refer to Curve points encoded section).
- Point is not on the curve.
Test cases:
The test cases are identical to those of bls12381_p1_sum
, with the only alteration being the substitution of points from
Annotation:
pub fn bls12381_p2_sum(&mut self,
value_len: u64,
value_ptr: u64,
register_id: u64) -> Result<u64>;
Description:
The function accepts a list of pairs
The scalar multiplication operation signifies the addition of that point a scalar number of times:
The
Please note:
- The function accepts only points from
$G_1$ . - The scalar is an arbitrary unsigned integer and can exceed the group order.
- To enhance gas efficiency, the Pippenger’s algorithm33 can be utilized.
Input: The sequence of pairs
Output:
The ERROR_CODE is returned.
- ERROR_CODE = 0: the input is correct
-
Output: 96 bytes represent one point
$\in G_1 \subset E(F_p)$ in its decompressed form. In case of an empty input, it outputs the point at infinity (refer to the Curve Points Encoding section for more details).
-
Output: 96 bytes represent one point
- ERROR_CODE = 1:
- Points are incorrectly encoded (refer to Curve points encoded section).
- Point is not on the curve.
- Point is not from
$G_1$
Test cases:
Tests for multiplication
- Tests with known answers for multiplication from EIP-253731,32.
- Random small scalar n and point P:
- Check results with the sum function:
P + P + P + .. + P = n*P
. - Compare with results from another library.
- Check results with the sum function:
- Random scalar n and point P:
- Verify against results from another library.
- Implement multiplication by using the sum function and the double-and-add algorithm34.
Edge cases:
group_order * P = 0
(scalar + groupt_order) * P = scalar * P
P + P + P .. + P = N*P
0 * P = 0
1 * P = P
- Scalar is a MAX_INT
Tests for sum of two points
These are identical test cases to those in the bls12381_p1_sum
section, but only with points from
- Generate random points P and Q, then compare the results with the sum function.
Tests for the sum of an arbitrary amount of points
- Random number of points, random point values; compare results with the sum function.
- Empty input.
- Input of maximum size.
Tests for the multiexp of an arbitrary amount of points
- Tests with known answers from EIP-253731,32
- Random number of points, scalars, and points:
- Check with results from another library.
- Check with raw implementation based on the sum function and the double-and-add algorithm.
- Empty input
- Maximum number of scalars and points.
Tests for error cases
- The same test cases as those in the
bls12381_p1_sum
section. - Points not from
$G_1$ .
Annotation:
pub fn bls12381_g1_multiexp(
&mut self,
value_len: u64,
value_ptr: u64,
register_id: u64,
) -> Result<u64>;
Description:
The function takes a list of pairs
This scalar multiplication operation involves adding the point
The
Please note:
- The function accepts only points from
$G_2$ . - The scalar is an arbitrary unsigned integer and can exceed the group order.
- To enhance gas efficiency, the Pippenger’s algorithm33 can be utilized.
Input: the sequence of pairs
The expected input size is 224*k
bytes, interpreted as the byte concatenation of k
slices. Each slice is the concatenation of an uncompressed point from 192
bytes and a scalar — 32
bytes. More details are in the Curve Points Encoding section.
Output:
The ERROR_CODE is returned.
- ERROR_CODE = 0: the input is correct
-
Output: 192 bytes represent one point
$\in G_2 \subset E'(F_{p^2})$ in its decompressed form. In case of an empty input, it outputs the point at infinity (refer to the Curve Points Encoding section for more details).
-
Output: 192 bytes represent one point
- ERROR_CODE = 1:
- Points are incorrectly encoded (refer to Curve points encoded section).
- Point is not on the curve.
- Point is not in
$G_2$ subgroup.
Test cases:
The test cases are identical to those for bls12381_g1_multiexp
, except that the points from
Annotation:
pub fn bls12381_g2_multiexp(
&mut self,
value_len: u64,
value_ptr: u64,
register_id: u64,
) -> Result<u64>;
Description:
This function takes as input a list of field elements
Input:
The function expects 48*k
bytes as input, representing a list of element from
Output:
The ERROR_CODE is returned.
- ERROR_CODE = 0: the input is correct
-
Output:
96*k
bytes - represents a list of points$\in G_1 \subset E(F_p)$ in decompressed format. Further information is available in the Curve Points Encoding section.
-
Output:
- ERROR_CODE = 1:
$a_i \ge p$ .
Test cases:
Tests for general cases
- Validate the results for known answers from EIP-253731,32.
- Generate a random point
$a$ from$F_p$ :- Verify the result using another library.
- Check that the resulting point lies on the curve in
$G_1$ . - Compare the results for
$a$ and$-a$ ; they should share the same x-coordinates and have opposite y-coordinates.
Edge cases:
$a = 0$ $a = p - 1$
Tests for an arbitrary number of elements
- Empty input
- Maximum number of points.
- Generate a random number of field elements and compare the result with another library.
Tests for error cases
- Input length is not divisible by 48:
- Input is beyond memory bounds.
$a = p$ - Random number
$\ge p$
Annotation:
pub fn bls12381_map_fp_to_g1(
&mut self,
value_len: u64,
value_ptr: u64,
register_id: u64,
) -> Result<u64>;
Description:
This function takes as input a list of elements
Input: the function takes as input 96*k
bytes — the elements from
Output:
The ERROR_CODE is returned.
- ERROR_CODE = 0: the input is correct
-
Output:
192*k
bytes - represents a list of points$\in G_2 \subset E'(F_{p^2})$ in decompressed format. More details are in the Curve Points Encoding section.
-
Output:
- ERROR_CODE = 1: one of the values is not a valid extension field
$F_{p^2}$ element
Test cases:
Tests for general cases
- Validate the results for known answers from EIP-253731,32
- Generate a random point
$a$ from$F_{p^2}$ :- Verify the result with another library.
- Check that the resulting point lies in
$G_2$ . - Compare results for
$a$ and$-a$ ; they should have the same x-coordinates and opposite y-coordinates.
Edge cases:
$a = (0, 0)$ $a = (p - 1, p - 1)$
Tests for an arbitrary number of elements
- Empty input
- Maximum number of points.
- Generate a random number of field elements and compare the result with another library.
Tests for error cases
- Input length is not divisible by 96.
- Input is beyond memory bounds.
$a = (0, p)$ $a = (p, 0)$ - (random number
$\ge p$ , 0) - (0, random number
$\ge p$ )
Annotation:
pub fn bls12381_map_fp2_to_g2(
&mut self,
value_len: u64,
value_ptr: u64,
register_id: u64,
) -> Result<u64>;
Description:
The pairing function is a bilinear function
This function takes as input the sequence of pairs
We don’t need to calculate the pairing function itself as the result would lie on a huge field, and in all known applications only this validation check is necessary.
Input: A sequence of pairs
Output:
The ERROR_CODE is returned.
- ERROR_CODE = 0: the input is correct, the pairing result equals the multiplicative identity.
- ERROR_CODE = 1:
- Points encoded incorrectly (refer to the Curve Points Encoded section).
- Point not on the curve.
- Point not in
$G_1/G_2$ .
- ERROR_CODE = 2: the input is correct, the pairing result doesn't equal the multiplicative identity.
Test cases:
Tests for one pair
- Generate a random point
$P \in G_1$ : verify$e(P, \mathcal{O}) = 1$ - Generate a random point
$Q \in G_2$ : verify$e(\mathcal{O}, Q) = 1$ - Generate random points
$P \ne \mathcal{O} \in G_1$ and$Q \ne \mathcal{O} \in G_2$ : verify$e(P, Q) \ne 1$
Tests for two pairs
-
Generate random points
$P \in G_1$ ,$Q \in G_2$ and random scalars$s_1, s_2$ :$e(P, Q) \cdot e(P, -Q) = 1$ $e(P, Q) \cdot e(-P, Q) = 1$ $e(s_1P, s_2Q) \cdot e(-s_2P, s_1Q) = 1$ $e(s_1P, s_2Q) \cdot e(s_2P, -s_1Q) = 1$
-
$g_1 \in G_1$ ,$g_2 \in G_2$ are generators defined in section 'BLS12-381 Curve Specification', r is the order of$G_1$ and$G_2$ , and$p_1, p_2, q_1, q_2$ are randomly generated scalars:- if
$p_1 \cdot q_1 + p_2 \cdot q_2 \not \equiv 0 (\mod r)$ , verify$e(p_1 g_1, q_1 g_2) \cdot e(p_2 g_1, q_2 g_2) \ne 1$ - if
$p_1 \cdot q_1 + p_2 \cdot q_2 \equiv 0 (\mod r)$ , verify$e(p_1 g_1, q_1 g_2) \cdot e(p_2 g_1, q_2 g_2) = 1$
- if
Tests for an arbitrary number of pairs
- Empty input
- Test with the maximum number of pairs
- Tests using known answers from EIP-253731,32
- For all possible values of 'n', generate random scalars
$p_1 \cdots p_n$ and$q_1 \cdots q_n$ such that$\sum p_i \cdot q_i \not \equiv 0 (\mod r)$ :- Verify
$\prod e(p_i g_1, q_i g_2) \ne 1$
- Verify
- For all possible values of 'n', generate random scalars
$p_1 \cdots p_{n - 1}$ and$q_1 \cdots q_{n - 1}$ :- Verify
$(\prod e(p_i g_1, q_i g_2)) \cdot e(-(\sum p_i q_i) g_1, g_2) = 1$ - Verify
$(\prod e(p_i g_1, q_i g_2)) \cdot e(g_1, -(\sum p_i q_i) g_2) = 1$
- Verify
Tests for error cases
- The first point is on the curve but not in
$G_1$ . - The second point is on the curve but not in
$G_2$ . - The input length is not divisible by 288.
- The first point is not on the curve.
- The second point is not on the curve.
- Input length exceeds the memory limit.
- Incorrect encoding of the point at infinity.
- Incorrect encoding of a curve point:
- Incorrect decompression bit.
- Coordinates greater than or equal to 'p'.
Annotation:
pub fn bls12381_pairing_check(&mut self,
value_len: u64,
value_ptr: u64) -> Result<u64>;
Description: The function decompresses compressed points from
Input: A sequence of points
Output:
The ERROR_CODE is returned.
- ERROR_CODE = 0: the input is correct
-
Output: The sequence of points
$p_i \in E(F_p)$ , with each point encoded in decompressed form. An expected output of 96*k bytes, interpreted as the byte concatenation of k slices. Each slice represents the decompressed point from$E(F_p)$ . k is the same as in the input. More details are available in the Curve Points Encoding section.
-
Output: The sequence of points
- ERROR_CODE = 1:
- Points are incorrectly encoded (refer to the Curve points encoded section).
- Point is not on the curve.
Test cases:
Tests for decompressing a single point
- Generate random points on the curve from
$G_1$ and not from$G_1$ :- Check that the uncompressed point lies on the curve.
- Compare the result with another library.
- Generate random points with a negative y:
- Take the inverse and compare the y-coordinate.
- Compare the result with another library.
- Decompress a point on infinity.
Tests for decompression of an arbitrary number of points
- Empty input.
- Maximum number of points.
- Generate a random number of points on the curve and compare the result with another library.
Tests for error cases
- The input length is not divisible by 48.
- The input is beyond memory bounds.
- Point is not on the curve.
- Incorrect decompression bit.
- Incorrectly encoded point at infinity.
- Point with a coordinate larger than 'p'.
Annotation:
pub fn bls12381_p1_decompress(&mut self,
value_len: u64,
value_ptr: u64,
register_id: u64) -> Result<u64>;
Description: The function decompresses compressed points from
Input: A sequence of points 96*k
bytes, interpreted as the byte concatenation of k slices. Each slice represents the compressed point from
Output:
The ERROR_CODE is returned.
- ERROR_CODE = 0: the input is correct
-
Output: the sequence of point
$p_i \in E'(F_{p^2})$ , with each point encoded in decompressed form. The expected output is 192*k bytes, interpreted as the byte concatenation of k slices.k
corresponds to the value specified in the input section. Each slice represents the decompressed point from$E'(F_{p^2})$ . For more details, refer to the Curve Points Encoding section.
-
Output: the sequence of point
- ERROR_CODE = 1:
- Points are incorrectly encoded (refer to Curve points encoded section).
- Point is not on the curve.
Test cases:
The same test cases as bls12381_p1_decompress
, but with points from
Annotation:
pub fn bls12381_p2_decompress(&mut self,
value_len: u64,
value_ptr: u64,
register_id: u64) -> Result<u64>;
Primarily, concerning integration with nearcore, our interest lies in Rust language libraries. The current implementations of BLS12-381 in Rust are:
- Milagro Library 29.
- BLST 3536.
- Matter labs EIP-1962 implementation 37
- zCash origin implementation 38
- MCL Library 39
- FileCoin implementation 40
- zkCrypto 41
To compile the list, we used links from EIP-253742, the pairing-curves specification43, and an article containing benchmarks44. This list might be incomplete, but it should encompass the primary BLS12-381 implementations.
In addition, there are implementations in other languages that are less relevant to us in this context but can serve as references.
- C++, ETH2.0 Client, Chia library45
- Haskell, Adjoint Lib46
- Go, Go-Ethereum47
- JavaScript, Noble JS48
- Go, Matter Labs Go EIP-1962 implementation49
- C++, Matter Labs C++ EIP-1962 implementation50
One of the possible libraries to use is the blst library35. This library exhibits good performance44 and has undergone several audits51. You can find a draft implementation in nearcore, which is based on this library, through this link52.
The implementation's security depends on the chosen library's security, supporting operations with BLS curves.
Within this NEP, a constant execution time for all operations isn't mandated. All the computations executed by smart contract are entirely public anyway, so there would be no advantage to a constant-time algorithm.
BLS12-381 offers more security bits compared to the already existing pairing-friendly curve BN254. Consequently, the security of projects requiring a pairing-friendly curve will be enhanced.
In nearcore, host functions for another pairing-friendly curve, BN254, have already been implemented7. Some projects8 might consider utilizing the supported curve as an alternative. However, recent research indicates that this curve provides less than 100 bits of security and is not recommended for use9. Furthermore, projects involved in cross-chain interactions, like Rainbow Bridge, are mandated to employ the same curve as the target protocol, which, in the case of Ethereum, is currently BLS12-38112. Consequently, there is no viable alternative to employing a different pairing-friendly curve.
An alternative approach involves creating a single straightforward host function in nearcore for BLS signature verification. This was the initially proposed solution53. However, this solution lacks flexibility54 for several reasons: (1) projects may utilize different hash functions; (2) some projects might employ the
Another alternative is to perform BLS12-381 operations off-chain. In this scenario, applications utilizing the BLS curve will no longer maintain trustlessness.
In the future, there might be support for working with various curves beyond just BLS12-381. In Ethereum, prior to EIP-253720, there was a proposal, EIP-196255, to introduce pairing-friendly elliptic curves in a versatile format, accommodating not only BLS curves but numerous others as well. However, this proposal wasn't adopted due to its extensive scope and complexity. Implementing every conceivable curve might not be practical, but it remains a potential extension worth considering.
Another potential extension could involve supporting hash_to_field
or hash_to_curve
operations56. Enabling their support would optimize gas usage for encoding messages into elements on the curve, which could be beneficial to BLS signatures. However, implementing the hash_to_field operation requires supporting multiple hashing algorithms simultaneously and doesn't demand a significant amount of gas for implementation within the contract. Therefore, these functions exceed the scope of this proposal.
Additionally, a potential expansion might encompass supporting not only affine coordinates but also other coordinate systems, such as homogeneous or Jacobian projective coordinates.
- Projects currently utilizing BN254 will have the capability to transition to the BLS12-381 curve, thereby enhancing their security.
- Trustless cross-chain interactions with blockchains employing BLS12-381 in protocols (like Ethereum 2.0) will become feasible.
- There emerges a dependency on a library that supports operations with BLS12-381 curves.
- We'll have to continually maintain operations with BLS12-381 curves, even if vulnerabilities are discovered, and it becomes unsafe to use these curves.
There are no backward compatibility questions.
The previous NEP for supporting BLS signature based on BLS12-38153
Footnotes
-
BLS 2002 https://www.researchgate.net/publication/2894224_Constructing_Elliptic_Curves_with_Prescribed_Embedding_Degrees ↩
-
BLS12-381 for the Rest of Us: https://hackmd.io/@benjaminion/bls12-381 ↩ ↩2 ↩3
-
Paper with BLS12-381: https://eprint.iacr.org/2019/403.pdf ↩
-
Intro into zkSNARKs: https://media.consensys.net/introduction-to-zksnarks-with-examples-3283b554fc3b ↩
-
BN2005: https://eprint.iacr.org/2005/133 ↩
-
BN254 for the Rest of Us: https://hackmd.io/@jpw/bn254 ↩
-
NEP-98 for BN254 host functions on NEAR: /~https://github.com/near/NEPs/issues/98 ↩ ↩2
-
Zeropool project: https://zeropool.network/ ↩ ↩2
-
Some analytics of different curve security: https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-02.html#name-for-100-bits-of-security ↩ ↩2
-
Specification of pairing friendly curves, the security level for BLS12-381: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-09#section-4.2.1 ↩
-
ZCash protocol: https://zips.z.cash/protocol/protocol.pdf ↩ ↩2
-
Ethereum 2 specification: /~https://github.com/ethereum/consensus-specs/blob/master/specs/phase0/beacon-chain.md ↩ ↩2 ↩3
-
Dfinity: https://internetcomputer.org/docs/current/references/ic-interface-spec#certificate ↩
-
Tezos: https://wiki.tezosagora.org/learn/futuredevelopments/layer2#zkchannels ↩ ↩2
-
Filecoin: https://spec.filecoin.io/ ↩ ↩2
-
Specification of pairing friendly curves with a list of applications in the table: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-09#name-adoption-status-of-pairing- ↩
-
Article about Rainbow Bridge https://near.org/blog/eth-near-rainbow-bridge ↩
-
hash_to_field specification: https://datatracker.ietf.org/doc/html/rfc9380#name-hash_to_field-implementatio ↩
-
Implementation of BLS-signature based on these host functions: /~https://github.com/olga24912/bls-signature-verificaion-poc/blob/main/src/lib.rs ↩
-
EIP-2537 Precompiles for Ethereum for BLS12-381: https://eips.ethereum.org/EIPS/eip-2537 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
Precompiles on Aurora: https://doc.aurora.dev/dev-reference/precompiles/ ↩
-
draft-irtf-cfrg-pairing-friendly-curves-11 https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11#name-bls-curves-for-the-128-bit- ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9
-
ZCash Transfer from bn254 to bls12-381: https://electriccoin.co/blog/new-snark-curve/ ↩ ↩2 ↩3
-
RFC 9380 Hashing to Elliptic Curves specification: https://www.rfc-editor.org/rfc/rfc9380 ↩
-
map_to_curve and clear_cofactor functions: https://datatracker.ietf.org/doc/html/rfc9380#name-encoding-byte-strings-to-el ↩
-
Specification of parameters for BLS12-381 G1: https://datatracker.ietf.org/doc/html/rfc9380#name-bls12-381-g1 ↩
-
Specification of parameters for BLS12-381 G2: https://datatracker.ietf.org/doc/html/rfc9380#name-bls12-381-g2 ↩
-
Zkcrypto points encoding: /~https://github.com/zkcrypto/pairing/blob/0.14.0/src/bls12_381/README.md ↩
-
BLS12-381 Milagro: /~https://github.com/sigp/incubator-milagro-crypto-rust/tree/057d238936c0cbbe3a59dfae6f2405db1090f474 ↩ ↩2
-
Bench vectors from EIP2537: https://eips.ethereum.org/assets/eip-2537/bench_vectors ↩
-
Metter Labs tests for EIP2537: /~https://github.com/matter-labs/eip1962/tree/master/src/test/test_vectors/eip2537 ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Tests from Go Ethereum implementation: /~https://github.com/ethereum/go-ethereum/tree/master/core/vm/testdata/precompiles ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Pippenger Algorithm: /~https://github.com/wborgeaud/python-pippenger/blob/master/pippenger.pdf ↩ ↩2
-
double-and-add algorithm: https://en.wikipedia.org/wiki/Exponentiation_by_squaring ↩
-
BLST EIP-2537 adaptation: /~https://github.com/sean-sn/blst_eip2537 ↩
-
EIP-1962 implementation matter labs Rust: /~https://github.com/matter-labs/eip1962 ↩
-
zCash origin rust implementation: /~https://github.com/zcash/zcash/tree/master/src/rust/src ↩
-
MCL library: /~https://github.com/herumi/bls ↩
-
filecoin/bls-signature: /~https://github.com/filecoin-project/bls-signatures ↩
-
zkCrypto: /~https://github.com/zkcrypto/bls12_381, /~https://github.com/zkcrypto/pairing ↩
-
EIP-2537 with links: /~https://github.com/matter-labs-forks/EIPs/blob/bls12_381/EIPS/eip-2537.md ↩
-
Pairing-friendly curves specification, crypto libs: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-09#name-cryptographic-libraries ↩
-
Comparing different libs for pairing-friendly curves: https://hackmd.io/@gnark/eccbench ↩ ↩2
-
BLS12-381 code bases for ETH2.0 client Chia library C++: /~https://github.com/Chia-Network/bls-signatures ↩
-
Adjoint Lib: /~https://github.com/sdiehl/pairing ↩
-
Ethereum Go implementation for EIP-2537: /~https://github.com/ethereum/go-ethereum/tree/master/core/vm/testdata/precompiles ↩
-
Noble JS implementation: /~https://github.com/paulmillr/noble-bls12-381 ↩
-
EIP-1962 implementation matter labs Go: /~https://github.com/kilic/eip2537, ↩
-
EIP-1962 implementation matter labs C++: /~https://github.com/matter-labs-archive/eip1962_cpp ↩
-
Audit for BLST library: https://research.nccgroup.com/wp-content/uploads/2021/01/NCC_Group_EthereumFoundation_ETHF002_Report_2021-01-20_v1.0.pdf ↩
-
Draft PR for BLS12-381 operations in nearcore: /~https://github.com/near/nearcore/pull/9317 ↩
-
NEP-446 proposal for BLS-signature verification: /~https://github.com/nearprotocol/neps/pull/446 ↩ ↩2
-
Drawbacks of NEP-446: /~https://github.com/near/NEPs/pull/446#pullrequestreview-1314601508 ↩
-
EIP-1962 EC arithmetic and pairings with runtime definitions: https://eips.ethereum.org/EIPS/eip-1962 ↩
-
hash_to_curve and hash_to_field function: https://datatracker.ietf.org/doc/html/rfc9380#name-hash_to_field-implementatio ↩