#parameters #zk-snarks #mpc #public #circuit #performing

phase2

Library for performing MPCs for creating zk-SNARK public parameters

5 unstable releases

Uses old Rust 2015

0.2.2 Apr 13, 2018
0.2.1 Apr 8, 2018
0.2.0 Apr 8, 2018
0.1.0 Apr 6, 2018
0.0.1 Apr 4, 2018

#32 in #mpc

MIT/Apache

45KB
877 lines

phase2 Crates.io

This library is still under development.

Documentation

Security Warnings

This library does not make any guarantees about constant-time operations, memory access patterns, or resistance to side-channel attacks.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.


lib.rs:

zk-SNARK MPCs, made easy.

Make your circuit

Grab the bellman and pairing crates. Bellman provides a trait called Circuit, which you must implement for your computation.

Here's a silly example: proving you know the cube root of a field element.

extern crate pairing;
extern crate bellman;

use pairing::{Engine, Field};
use bellman::{
    Circuit,
    ConstraintSystem,
    SynthesisError,
};

struct CubeRoot<E: Engine> {
    cube_root: Option<E::Fr>
}

impl<E: Engine> Circuit<E> for CubeRoot<E> {
    fn synthesize<CS: ConstraintSystem<E>>(
        self,
        cs: &mut CS
    ) -> Result<(), SynthesisError>
    {
        // Witness the cube root
        let root = cs.alloc(|| "root", || {
            self.cube_root.ok_or(SynthesisError::AssignmentMissing)
        })?;

        // Witness the square of the cube root
        let square = cs.alloc(|| "square", || {
            self.cube_root
                .ok_or(SynthesisError::AssignmentMissing)
                .map(|mut root| {root.square(); root })
        })?;

        // Enforce that `square` is root^2
        cs.enforce(
            || "squaring",
            |lc| lc + root,
            |lc| lc + root,
            |lc| lc + square 
        );

        // Witness the cube, as a public input
        let cube = cs.alloc_input(|| "cube", || {
            self.cube_root
                .ok_or(SynthesisError::AssignmentMissing)
                .map(|root| {
                    let mut tmp = root;
                    tmp.square();
                    tmp.mul_assign(&root);
                    tmp
                })
        })?;

        // Enforce that `cube` is root^3
        // i.e. that `cube` is `root` * `square`
        cs.enforce(
            || "cubing",
            |lc| lc + root,
            |lc| lc + square,
            |lc| lc + cube
        );

        Ok(())
    }
}

Create some proofs

Now that we have CubeRoot<E> implementing Circuit, let's create some parameters and make some proofs.

extern crate rand; 

use pairing::bls12_381::{Bls12, Fr};
use bellman::groth16::{
    generate_random_parameters,
    create_random_proof,
    prepare_verifying_key,
    verify_proof
};
use rand::{OsRng, Rand};

let rng = &mut OsRng::new();

// Create public parameters for our circuit
let params = {
    let circuit = CubeRoot::<Bls12> {
        cube_root: None
    };

    generate_random_parameters::<Bls12, _, _>(
        circuit,
        rng
    ).unwrap()
};

// Prepare the verifying key for verification
let pvk = prepare_verifying_key(&params.vk);

// Let's start making proofs!
for _ in 0..50 {
    // Verifier picks a cube in the field.
    // Let's just make a random one.
    let root = Fr::rand(rng);
    let mut cube = root;
    cube.square();
    cube.mul_assign(&root);

    // Prover gets the cube, figures out the cube
    // root, and makes the proof:
    let proof = create_random_proof(
        CubeRoot::<Bls12> {
            cube_root: Some(root)
        }, &params, rng
    ).unwrap();

    // Verifier checks the proof against the cube
    assert!(verify_proof(&pvk, &proof, &[cube]).unwrap());
}

Creating parameters

Notice in the previous example that we created our zk-SNARK parameters by calling generate_random_parameters. However, if you wanted you could have called generate_parameters with some secret numbers you chose, and kept them for yourself. Given those numbers, you can create false proofs.

In order to convince others you didn't, a multi-party computation (MPC) can be used. The MPC has the property that only one participant needs to be honest for the parameters to be secure. This crate (phase2) is about creating parameters securely using such an MPC.

Let's start by using phase2 to create some base parameters for our circuit:

extern crate phase2;

let mut params = phase2::MPCParameters::new(CubeRoot {
    cube_root: None
}).unwrap();

The first time you try this, it will try to read a file like phase1radix2m2 from the current directory. You need to grab that from the Powers of Tau.

These parameters are not safe to use; false proofs can be created for them. Let's contribute some randomness to these parameters.

// Contribute randomness to the parameters. Remember this hash,
// it's how we know our contribution is in the parameters!
let hash = params.contribute(rng);

These parameters are now secure to use, so long as you weren't malicious. That may not be convincing to others, so let them contribute randomness too! params can be serialized and sent elsewhere, where they can do the same thing and send new parameters back to you. Only one person needs to be honest for the final parameters to be secure.

Once you're done setting up the parameters, you can verify the parameters:

let contributions = params.verify(CubeRoot {
    cube_root: None
}).expect("parameters should be valid!");

// We need to check the `contributions` to see if our `hash`
// is in it (see above, when we first contributed)
assert!(phase2::contains_contribution(&contributions, &hash));

Great, now if you're happy, grab the Groth16 Parameters with params.params(), so that you can interact with the bellman APIs just as before.

Dependencies

~1.5MB
~22K SLoC