Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Precompiled contract for pairing check. #212

Merged
merged 22 commits into from
Dec 7, 2017
Merged
Changes from 1 commit
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
130 changes: 130 additions & 0 deletions EIPS/pairings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
## Preamble
<pre>
EIP: to be assigned
Title: Precompiled contracts for optimal ate pairing check
on the elliptic curve alt_bn128
Author: Vitalik Buterin &lt;vitalik@ethereum.org&gt;, Christian Reitwiessner &lt;chris@ethereum.org&gt;
Type: Standard Track
Category(*only required for Standard Track): Core
Status: Draft
Created: 2017-02-06
</pre>

## Simple Summary

Precompiled contracts for elliptic curve pairing operations are required in order to perform zkSNARK verification within the block gas limit.

## Abstract

This EIP suggests to add precompiled contracts for a pairing function on a specific pairing-friendly elliptic curve. This can in turn be combined with https://github.com/ethereum/EIPs/issues/196 to verify zkSNARKs in Ethereum smart contracts. The general benefit of zkSNARKs for Ethereum is that it will increase the privacy for users (because of the Zero-Knowledge property) and might also be a scalability solution (because of the succinctness and efficient verifiability property).

## Motivation

Current smart contract executions on Ethereum are fully transparent, which makes them unsuitable for several use-cases that involve private information like the location, identity or history of past transactions. The technology of zkSNARKs could be a solution to this problem. While the Ethereum Virtual Machine can make use of zkSNARKs in theory, they are currently too expensive
to fit the block gas limit. Because of that, this EIP proposes to specify certain parameters for some elementary primitives that enable zkSNARKs so that they can be implemented more efficiently and the gas cost be reduced.

Note that fixing these parameters will in no way limit the use-cases for zkSNARKs, it will even allow for incorporating some advances in zkSNARK research without the need for a further hard fork.

Pairing functions can be used to perform a limited form of multiplicatively homomorphic operations, which are necessary for current zkSNARKs. This precompile can be used to run such computations within the block gas limit. This precompiled contract only specifies a certain check, and not an evaluation of a pairing function. The reason is that the codomain of a pairing function is a rather complex field which could provide encoding problems and all known uses of pairing function in zkSNARKs only require the specified check.

## Specification

Add a precompiled contracts for a bilinear function on groups on the elliptic curve "alt_bn128". We will define the precompiled contract in terms of a discrete logarithm. The discrete logarithm is of course assumed to be hard to compute, but we will give an equivalent specification that makes use of elliptic curve pairing functions which can be efficiently computed below.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some libraries, I saw alt_bn128 is an alternative implementation of bn128. I got an impression they are for the same curve.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this is the case, probably better call it just bn128, because alt_bn128 is just implementation-specific label for the same curve in one particular library (libsnark, https://github.com/scipr-lab/libsnark)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NikVolf the curve might be the same, but the generators used seem to be different:
https://github.com/scipr-lab/libsnark/blob/master/src/algebra/curves/bn128/bn128_init.cpp#L166 - https://github.com/scipr-lab/libsnark/blob/master/src/algebra/curves/alt_bn128/alt_bn128_init.cpp#L208 (but it might just be a different encoding)
In that case, we should perhaps move this wording closer to the specification of the generators.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are the permalinks.

alt_bn128 generator: https://github.com/scipr-lab/libff/blob/a44f482e18b8ac04d034c193bd9d7df7817ad73f/libff/algebra/curves/alt_bn128/alt_bn128_init.cpp#L208-L211

bn128 generator: https://github.com/scipr-lab/libff/blob/a44f482e18b8ac04d034c193bd9d7df7817ad73f/libff/algebra/curves/bn128/bn128_init.cpp#L166-L169

The numbers can be copy/pasted and they're both valid G2 points on the bn128 curve, so they are different generators (with the same encoding).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also note that currently the cpp-ethereum implementation is not actually using the alt_bn128 optimization, but the bn128 generator points (if I remember correctly) ethereum/aleth#4450


Address: 0x8

For a cyclic group `G` (written additively) of prime order q let `log_P: G -> F_q` be the discrete logarithm on this group with respect to a generator `P`, i.e. `log_P(x)` is the integer `n` such that `n * P = x`.

The precompiled contract is defined as follows, where the two groups `G_1` and `G_2` and their generators `P_1` and `P_2` are defined below:

```
Input: (a1, b1, a2, b2, ..., ak, bk) from (G_1 x G_2)^k
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's worth clarifying if k = 0 is allowed (usually I don't say this, but now it's about protocol consensus).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say of course it is, but it probably makes sense to be explicit about that.

Output: If the length of the input is incorrect or any of the inputs are not elements of
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a distinct break from the pattern of an infinitely zero-extended input. Why not prepend the number of elements?

the respective group or are not encoded correctly, the call fails.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mention explicitly that we have to check that the second elements are in the group G_2 (which is a subgroup of the full elliptic curve). In order to do that, verify that the order of the element is q=21888242871839275222246405745257275088548364400416034343698204186575808495617 (the prime group order of G_2).

TODO: Check that G_2 is the only subgroup of the elliptic curve with that order.

Copy link
Member

@pirapira pirapira Feb 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zcash does something slightly different for the purpose zcash-hackworks/bn@ef95df6

The Zcash implementation adds an element of G_2 at the end.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I edited my post above because zcash does something else for input checking.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The article by Barreto and Naehrig "Pairing-Friendly Elliptic Curves of Prime Order" mentions that the order of the curve twist is n(2p-n) (the proof is left to the reader, so it would be nice to find such a reader) where n is the order of the field and p the order of the original curve. Since G_2 has the same order p and both p and n are primes, it suffices to verify the order of the point is p as long as p does not divide 2p-n, which is the case with our numbers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that the order of the curve twist is n(2p-n) (the proof is left to the reader, so it would be nice to find such a reader)

Consider a transformation that takes (x, y) into (x / cbrt(xi / 3), y / sqrt(xi / 3)). If one puts in an (x, y) pair that is on the original curve, with order n (ie. x**3 - y**2 = 3), into this transformation, then one should get out a value that satisfies x**3 - y**2 = 3/xi. However, xi / 3 is a quadratic nonresidue (though it is a cubic residue), so any x value that provides two points on the original curve would provide no points when transformed in this way, and because nonresidue * nonresidue = residue, any x value that provides no points on the original curve would provide two points when transformed. There are 2p possible points on the original curve (taking a possible point as a pair (x, parity of y), of which n turn out to be actual points; hence, the transformation gives us 2p - n points (though no proof yet that they are all linearly dependent).

I haven't gotten any further yet but this seems like the right path.

Otherwise, return one if
log_P1(a1) * log_P2(b1) + ... + log_P1(ak) * log_P2(bk) = 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On which ring is this equation evaluated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log_P is defined to map into F_q and that is the ring / field where this expression is evaluated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is q common both to G_1 and G_2?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, the answer is there above.

and zero else.
```

### Definition of the groups

The groups `G_1` and `G_1` are cyclic groups on the elliptic curve `alt_bn128` defined by the curve equation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it's G_1 and G_2

`Y^2 = X^3 + 3`.

The group `G_1` is a cyclic group of prime order on the above curve over the field `F_p` with `p = 21888242871839275222246405745257275088696311157297823662689037894645226208583` with generator `P1 = (1, 2)`.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than specifying the groups using giant magic numbers, might be worth specifying them in terms of the BN parameter from which they are derived. See e.g. https://github.com/cdetrio/py_ecc/blob/4d7096a313b7cbff0f0103c548160c8098b4b0f4/py_ecc/optimized_bn128/parameters.py#L4-L8

Copy link
Member

@pirapira pirapira Nov 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, we need to specify how to identify a group given BN parameters.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A giant magic number obviously specifies a unique mathematical object. A more indirect description must be accompanied by a proof that there is a unique thing that satisfies the indirect description.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The BN parameter 4965661367192848881 can be used to derive the field modulus: field_modulus = 36*(BN_PARAM_U**4) + 36*(BN_PARAM_U**3) + 24*(BN_PARAM_U**2) + 6*BN_PARAM_U + 1. I suggested including it because its a neat formula that took me a while to track down, but perhaps other readers don't care.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cdetrio please describe how that number uniquely identifies a subgroup on the curve. Is that a shorter way to identify the group?

The group `G_2` is a cyclic group of prime order in the same elliptic curve over a different field `F_p^2 = F_p[X] / (X^2 + 1)` (p is the same as above) with generator
```
P2 = (
11559732032986387107991004021392285783925812861821192530917403151452391805634 * i +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should stick to X instead of i. An element of F_p^2 would look like a polynomial of X. (Of course i is an intuitive choice because i * i + 1 = 0.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, it should be consistent.

10857046999023057135944570762232829481370756359578518086990519993285655852781,
4082367875863433681332203403145435568316851327593401208105741076214120093531 * i +
8495653923123431417604973247489272438418190587263600148770280649306958101930
)
```
Copy link
Member

@pirapira pirapira Feb 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let G_3 be the cyclic subgroup generated by P3, where P3 is very similar to P2 but uses -i instead of i. Is it the case that G_3 has the same order as G_2? If that's the case, the current input checking method checks <G_2, G_3> I guess.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't it be that P3 is just another element of the same cyclic group?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The question is the other way around, why is it the case that P3 is just another element of the same cyclic group?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I guess Lagrange's theorem might be useful.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mentioning this generator constant is superfluous. It is not needed to implement the precompiles. Here is a branch of the pyethereum elliptic pairing dependency that does not use this constant at all, but passes every test case: ethereum/py_ecc#3

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the alternative definitions of G_1 and G_2?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's an attempt at G_2 using only the constants in the py_ecc branch:

The group with order curve_order on the curve y**2 * z - x**3 == b2 * z**3 where b2 = FQ2([3, 0]) / FQ2([9, 1]) and curve_order = 36*(BN_PARAM_U**4) + 36*(BN_PARAM_U**3) + 18*(BN_PARAM_U**2) + 6*BN_PARAM_U + 1 and BN_PARAM_U = 4965661367192848881.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cdetrio we would need to add a proof that there is only one group of that order.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that already a TODO from @chriseth?

TODO: Check that G_2 is the only subgroup of the elliptic curve with that order.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cdetrio the current specification with the generator specifies a unique existence without this TODO. Your approach requires solving this TODO.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cdetrio In addition, with your approach, you need to prove that a subgroup of that order exists.



### Encoding

Elements of `F_p` are encoded as 32 byte big-endian numbers. An encoding value of `p` or larger is invalid.

Elements `a * i + b` of `F_p^2` are encoded as two elements of `F_p`, `(a, b)`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, the text is not very specific about how k is determined. I think it makes sense to mention:

  • pairs are encoded as concatenation of bytes, and/or
  • F_p^2 is encoded into 64 bytes
  • k is the length of the input divided by 192
  • when the input length is not divisible by 192, the input is invalid.


Elliptic curve points are encoded as a Jacobian pair `(X, Y)` where the point at infinity is encoded as `(0, 0)`.

Note that the number `k` is derived from the input length.

The length of the returned data is always exactly 32 bytes and encoded as a 32 byte big-endian number.

### Gas costs

To be determined.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three or four clients have already implemented this, so cross-benchmark should be possible now, to determine the gas costs.


## Rationale

The specific curve `alt_bn128` was chosen because it is particularly well-suited for zkSNARKs, or, more specifically their verification building block of pairing functions. Furthermore, by choosing this curve, we can use synergy effects with ZCash and re-use some of their components and artifacts.

The feature of adding curve and field parameters to the inputs was considered but ultimately rejected since it complicates the specification: The gas costs are much harder to determine and it would be possible to call the contracts on something which is not an actual elliptic curve or does not admit an efficient pairing implementation.

A non-compact point encoding was chosen since it still allows to perform some operations in the smart contract itself (inclusion of the full y coordinate) and two encoded points can be compared for equality (no third projective coordinate).

The encoding of field elements in `F_p^2` was chosen in this order to be in line with the big endian encoding of the elements themselves.

## Backwards Compatibility

As with the introduction of any precompiled contract, contracts that already use the given addresses will change their semantics. Because of that, the addresses are taken from the "reserved range" below 256.

## Test Cases

To be written.

## Implementation

The precompiled contract can be implemented using elliptic curve pairing functions, more specifically, an optimal ate pairing on the alt_bn128 curve, which can be implemented efficiently. In order to see that, first note that a pairing function `e: G_1 x G_2 -> G_T` fulfills the following properties (`G_1` and `G_2` are written additively, `G_T` is written multiplicatively):

(1) `e(m * P1, n * P2) = e(P1, P2)^(m * n)`
(2) `e` is non-degenerate

Now observe that
```
log_P1(a1) * log_P2(b1) + ... + log_P1(ak) * log_P2(bk) = 0
```
if and only if
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This "if and only if" is broken as pointed out by @bbuenz .

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now it's fine.

```
e(P1, P2)^(log_P1(a1) * log_P2(b1) + ... + log_P1(ak) * log_P2(bk)) = e(P1, P2)
```

Furthermore, the left hand side of this equation is equal to
```
e(log_P1(a1) * P1, log_P2(b1) * P2) * ... * e(log_P1(ak) * P1, log_P2(bk) * P2)
= e(a1, b1) * ... * e(ak, bk)
```

And thus, the precompiled contract can be implemented by verifying that
`e(a1, b1) * ... * e(ak, bk) = e(P1, P2)`

Implementations are available here:

- [libsnark](https://github.com/scipr-lab/libsnark/blob/master/src/algebra/curves/alt_bn128/alt_bn128_g1.hpp) (C++)
- [bn](https://github.com/zcash/bn/blob/master/src/groups/mod.rs) (Rust)
- [Python](https://github.com/ethereum/research/blob/master/zksnark/bn128_pairing.py)