2 stable releases
1.0.1 | Dec 9, 2020 |
---|---|
1.0.0 | Dec 1, 2020 |
#45 in #mpc
1MB
10K
SLoC
Filecoin Phase2
Library and binary to run the 2nd phase of the trusted setup for the circuits.
License
MIT or Apache 2.0
lib.rs
:
zk-SNARK MPCs, made easy.
Make your circuit
Grab the bellperson
crate. 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.
use fff::Field;
use bellperson::{
Circuit,
ConstraintSystem,
SynthesisError,
bls::Engine,
};
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.
use bellperson::bls::{Bls12, Fr};
use bellperson::groth16::{
generate_random_parameters,
create_random_proof,
prepare_verifying_key,
verify_proof
};
use rand::rngs::OsRng;
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(¶ms.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)
}, ¶ms, 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 (filecoin-phase2
) is about creating parameters
securely using such an MPC.
Let's start by using filecoin-phase2
to create some base parameters
for our circuit:
let mut params = crate::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!(crate::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
~22–38MB
~486K SLoC