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

Feat/plonk prover #64

Merged
merged 46 commits into from
Mar 30, 2021
Merged

Feat/plonk prover #64

merged 46 commits into from
Mar 30, 2021

Conversation

ThomasPiellard
Copy link
Collaborator

@ThomasPiellard ThomasPiellard commented Mar 19, 2021

This PR contains a first version of the plonk prover, instantiated with a dummy polynomial commitment scheme. There are no custom gates.

The exposed API for using PLONK is the same as Groth16: plonk.Setup(...), plonk.Prove(...), plonk.Verify(...).

Note 1: currently the verifier is not succint. Indeed the public polynomials corresponding to a circuit are not committed, and the verifier has to evaluate them at a random point, and this operation is not succint.

Note 2: Fiat Shamir is not implemented, so the numerous challenges are hard coded at the moment. It's the next step to add Fiat Shamir.

API

The workflow for writing a circuit is the same as Groth16. Things starts to differ at the circuit compilation stage.

Compilation

  • sparseR1cs, err := frontend.Compile(curve.ID, backend.PLONK, circuit.Circuit) -> sparseR1cs is now a list of constraints of the form ql*l+qr*r+qm*l*r+qo*o+qk=0, where ql,qr,qm,qo,qk are coefficients corresponding to the circuit, and l,r,o are the wires.

Setup

Once a circuit is compiled, the list of coefficients (ql,qr,qm,qo,qk) needs to be converted to polynomials, and the permutation leaving l || r || o invariant (corresponding to the gates wiring) needs to be computed. Domains for FFTs and associated precomputed values are computed (there are 2 domains). Finally a polynomial commitment scheme is chosen.

Those information are public (since they correspond to a circuit that a prover and a verifier share) and are stored in a struct called PublicRaw (the raw means that the polynomials are not committed).

Generation of public data:
publicData, err := Setup(sparseR1cs, polynomialCommitmentScheme, witness)

Currently only a dummy polynomial commitment scheme is implemented in crypto/ (it is not necessary for validating the plonk prover and plonk verifier).

The witness is necessary in the setup to add special constraints encoding the public input, namely the constraints l-witness[i]=0 are prepend to the constraint system. The permutation takes in account those constraints (which are not encoded in the circuit after compilation), so they cannot be tampered. This operation is described in the comments of the Setup function which refer to "placeholders".

Prove

proof, err := Prove(sparseR1cs, publicData, witness) --> it returns a proof object, containing commitments to the solution l,r,o, as well as the permutation accumulating polynomial Z, and the quotient polynomial H.

The order of operations is such that the number of FFT calls is minimized.

Verify

err = Verify(proof, publicData, witness) --> verifies the openings of l,r,o,h,z at a challenge (on which the prover has no control), and verifies the final polynomial equality evaluated at the challenge.

Note: the opening of l from the prover is incomplete, and the verifier needs to complete it by adding the public witness, just as in Groth16. This mecanism ensures that the verifies is convinced that the proof is associated to the right public inputs, exactly like in Groth16.

Status

  • All tests circuits used for Groth16 pass for PLONK
  • The verifier is not optimized, some operations need some speed ups (for instance using batch inversion at several places)
  • No real polynomial commitment scheme is implemented ATM
  • Fiat Shamir is not implemented, so all the challenges are hardcoded
  • There are no breaking API change

Copy link
Collaborator

@gbotrel gbotrel left a comment

Choose a reason for hiding this comment

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

💯 awesome.
Added few minor remarks --
will probably be refactored a bit once we have KZG commitment and various PLONK instantiations.


// WriteTo mock impementation
func (md *MockDigest) WriteTo(w io.Writer) (n int64, err error) {
return 0, nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

return errNotImplemented or panic


// ReadFrom mock impementation
func (md *MockDigest) ReadFrom(r io.Reader) (n int64, err error) {
return 0, nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

return errNotImplemented or panic


// WriteTo panics
func (s *Scheme) WriteTo(w io.Writer) (n int64, err error) {
return 0, nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

return errNotImplemented or panic


// Degree returns the degree of the polynomial, which is the length of Data.
func (p Poly) Degree() uint64 {
res := uint64(len(p) - 1)
Copy link
Collaborator

Choose a reason for hiding this comment

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

if we have a unitialized Polynomial, (ie len(p) == 0) this return a large degree. Maybe return 0 if len(p) == 0? or panic, if we never hit that.

// Eval evaluates p at v
func (p Poly) Eval(v interface{}) interface{} {
var res, _v fr.Element
_v.Set(v.(*fr.Element))
Copy link
Collaborator

Choose a reason for hiding this comment

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

since you don't modify v, you may try to keep it as an address, to avoid unneeded copies

var res, _v fr.Element
_v.Set(v.(*fr.Element))
s := len(p)
res.Set(&p[s-1])
Copy link
Collaborator

Choose a reason for hiding this comment

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

same as above, this panics if len(p) == 0.

}
return lro
// TODO panic if wire is already instantiated
Copy link
Collaborator

Choose a reason for hiding this comment

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

check if O was already instantiated

if coset != 0 {
if decimation == DIF {
Copy link
Collaborator

Choose a reason for hiding this comment

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

as discussed, this significantly slows down groth16.Prove

@gbotrel gbotrel merged commit 9565bbf into develop Mar 30, 2021
@gbotrel gbotrel deleted the feat/plonk_prover branch March 30, 2021 22:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants