Skip to content

Commit 9bbdfc5

Browse files
authored
Full flow example (#90)
* expose params & structs for external usage * add full_flow example, move examples into 'examples' dir
1 parent 97df224 commit 9bbdfc5

File tree

16 files changed

+270
-26
lines changed

16 files changed

+270
-26
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ jobs:
7979
steps:
8080
- uses: actions/checkout@v2
8181
- uses: actions-rs/toolchain@v1
82+
- name: Download solc
83+
run: |
84+
curl -sSfL https://github.com/ethereum/solidity/releases/download/v0.8.4/solc-static-linux -o /usr/local/bin/solc
85+
chmod +x /usr/local/bin/solc
8286
- name: Run examples tests
8387
run: cargo test --examples
8488
- name: Run examples

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ folding-schemes/src/frontend/circom/test_folder/cubic_circuit_js/
77

88
# generated contracts at test time
99
solidity-verifiers/generated
10+
examples/*.sol
11+
examples/*.calldata
12+
examples/*.inputs
File renamed without changes.

examples/full_flow.rs

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
#![allow(non_snake_case)]
2+
#![allow(non_camel_case_types)]
3+
#![allow(clippy::upper_case_acronyms)]
4+
///
5+
/// This example performs the full flow:
6+
/// - define the circuit to be folded
7+
/// - fold the circuit with Nova+CycleFold's IVC
8+
/// - generate a DeciderEthCircuit final proof
9+
/// - generate the Solidity contract that verifies the proof
10+
/// - verify the proof in the EVM
11+
///
12+
use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as G1};
13+
use ark_crypto_primitives::snark::SNARK;
14+
use ark_ff::PrimeField;
15+
use ark_groth16::VerifyingKey as G16VerifierKey;
16+
use ark_groth16::{Groth16, ProvingKey};
17+
use ark_grumpkin::{constraints::GVar as GVar2, Projective as G2};
18+
use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey;
19+
use ark_r1cs_std::alloc::AllocVar;
20+
use ark_r1cs_std::fields::fp::FpVar;
21+
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
22+
use ark_std::Zero;
23+
use std::marker::PhantomData;
24+
use std::time::Instant;
25+
26+
use folding_schemes::{
27+
commitment::{
28+
kzg::{ProverKey as KZGProverKey, KZG},
29+
pedersen::Pedersen,
30+
CommitmentScheme,
31+
},
32+
folding::nova::{
33+
decider_eth::{prepare_calldata, Decider as DeciderEth},
34+
decider_eth_circuit::DeciderEthCircuit,
35+
get_cs_params_len, Nova, ProverParams,
36+
},
37+
frontend::FCircuit,
38+
transcript::poseidon::poseidon_test_config,
39+
Decider, Error, FoldingScheme,
40+
};
41+
use solidity_verifiers::{
42+
evm::{compile_solidity, Evm},
43+
utils::get_function_selector_for_nova_cyclefold_verifier,
44+
verifiers::nova_cyclefold::get_decider_template_for_cyclefold_decider,
45+
NovaCycleFoldVerifierKey,
46+
};
47+
48+
/// Test circuit to be folded
49+
#[derive(Clone, Copy, Debug)]
50+
pub struct CubicFCircuit<F: PrimeField> {
51+
_f: PhantomData<F>,
52+
}
53+
impl<F: PrimeField> FCircuit<F> for CubicFCircuit<F> {
54+
type Params = ();
55+
fn new(_params: Self::Params) -> Self {
56+
Self { _f: PhantomData }
57+
}
58+
fn state_len(&self) -> usize {
59+
1
60+
}
61+
fn step_native(&self, _i: usize, z_i: Vec<F>) -> Result<Vec<F>, Error> {
62+
Ok(vec![z_i[0] * z_i[0] * z_i[0] + z_i[0] + F::from(5_u32)])
63+
}
64+
fn generate_step_constraints(
65+
&self,
66+
cs: ConstraintSystemRef<F>,
67+
_i: usize,
68+
z_i: Vec<FpVar<F>>,
69+
) -> Result<Vec<FpVar<F>>, SynthesisError> {
70+
let five = FpVar::<F>::new_constant(cs.clone(), F::from(5u32))?;
71+
let z_i = z_i[0].clone();
72+
73+
Ok(vec![&z_i * &z_i * &z_i + &z_i + &five])
74+
}
75+
}
76+
77+
#[allow(clippy::type_complexity)]
78+
fn init_test_prover_params<FC: FCircuit<Fr, Params = ()>>() -> (
79+
ProverParams<G1, G2, KZG<'static, Bn254>, Pedersen<G2>>,
80+
KZGVerifierKey<Bn254>,
81+
) {
82+
let mut rng = ark_std::test_rng();
83+
let poseidon_config = poseidon_test_config::<Fr>();
84+
let f_circuit = FC::new(());
85+
let (cs_len, cf_cs_len) =
86+
get_cs_params_len::<G1, GVar, G2, GVar2, FC>(&poseidon_config, f_circuit).unwrap();
87+
let (kzg_pk, kzg_vk): (KZGProverKey<G1>, KZGVerifierKey<Bn254>) =
88+
KZG::<Bn254>::setup(&mut rng, cs_len).unwrap();
89+
let (cf_pedersen_params, _) = Pedersen::<G2>::setup(&mut rng, cf_cs_len).unwrap();
90+
let fs_prover_params = ProverParams::<G1, G2, KZG<Bn254>, Pedersen<G2>> {
91+
poseidon_config: poseidon_config.clone(),
92+
cs_params: kzg_pk.clone(),
93+
cf_cs_params: cf_pedersen_params,
94+
};
95+
(fs_prover_params, kzg_vk)
96+
}
97+
/// Initializes Nova parameters and DeciderEth parameters. Only for test purposes.
98+
#[allow(clippy::type_complexity)]
99+
fn init_params<FC: FCircuit<Fr, Params = ()>>() -> (
100+
ProverParams<G1, G2, KZG<'static, Bn254>, Pedersen<G2>>,
101+
KZGVerifierKey<Bn254>,
102+
ProvingKey<Bn254>,
103+
G16VerifierKey<Bn254>,
104+
) {
105+
let mut rng = rand::rngs::OsRng;
106+
let start = Instant::now();
107+
let (fs_prover_params, kzg_vk) = init_test_prover_params::<FC>();
108+
println!("generated Nova folding params: {:?}", start.elapsed());
109+
let f_circuit = FC::new(());
110+
111+
pub type NOVA<FC> = Nova<G1, GVar, G2, GVar2, FC, KZG<'static, Bn254>, Pedersen<G2>>;
112+
let z_0 = vec![Fr::zero(); f_circuit.state_len()];
113+
let nova = NOVA::init(&fs_prover_params, f_circuit, z_0.clone()).unwrap();
114+
115+
let decider_circuit =
116+
DeciderEthCircuit::<G1, GVar, G2, GVar2, KZG<Bn254>, Pedersen<G2>>::from_nova::<FC>(
117+
nova.clone(),
118+
)
119+
.unwrap();
120+
let start = Instant::now();
121+
let (g16_pk, g16_vk) =
122+
Groth16::<Bn254>::circuit_specific_setup(decider_circuit.clone(), &mut rng).unwrap();
123+
println!(
124+
"generated G16 (Decider circuit) params: {:?}",
125+
start.elapsed()
126+
);
127+
(fs_prover_params, kzg_vk, g16_pk, g16_vk)
128+
}
129+
130+
fn main() {
131+
let n_steps = 10;
132+
// set the initial state
133+
let z_0 = vec![Fr::from(3_u32)];
134+
135+
let (fs_prover_params, kzg_vk, g16_pk, g16_vk) = init_params::<CubicFCircuit<Fr>>();
136+
137+
pub type NOVA = Nova<G1, GVar, G2, GVar2, CubicFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<G2>>;
138+
pub type DECIDERETH_FCircuit = DeciderEth<
139+
G1,
140+
GVar,
141+
G2,
142+
GVar2,
143+
CubicFCircuit<Fr>,
144+
KZG<'static, Bn254>,
145+
Pedersen<G2>,
146+
Groth16<Bn254>,
147+
NOVA,
148+
>;
149+
let f_circuit = CubicFCircuit::<Fr>::new(());
150+
151+
// initialize the folding scheme engine, in our case we use Nova
152+
let mut nova = NOVA::init(&fs_prover_params, f_circuit, z_0).unwrap();
153+
// run n steps of the folding iteration
154+
for i in 0..n_steps {
155+
let start = Instant::now();
156+
nova.prove_step().unwrap();
157+
println!("Nova::prove_step {}: {:?}", i, start.elapsed());
158+
}
159+
160+
let rng = rand::rngs::OsRng;
161+
let start = Instant::now();
162+
let proof = DECIDERETH_FCircuit::prove(
163+
(g16_pk, fs_prover_params.cs_params.clone()),
164+
rng,
165+
nova.clone(),
166+
)
167+
.unwrap();
168+
println!("generated Decider proof: {:?}", start.elapsed());
169+
170+
let verified = DECIDERETH_FCircuit::verify(
171+
(g16_vk.clone(), kzg_vk.clone()),
172+
nova.i,
173+
nova.z_0.clone(),
174+
nova.z_i.clone(),
175+
&nova.U_i,
176+
&nova.u_i,
177+
&proof,
178+
)
179+
.unwrap();
180+
assert!(verified);
181+
println!("Decider proof verification: {}", verified);
182+
183+
// Now, let's generate the Solidity code that verifies this Decider final proof
184+
let function_selector =
185+
get_function_selector_for_nova_cyclefold_verifier(nova.z_0.len() * 2 + 1);
186+
187+
let calldata: Vec<u8> = prepare_calldata(
188+
function_selector,
189+
nova.i,
190+
nova.z_0,
191+
nova.z_i,
192+
&nova.U_i,
193+
&nova.u_i,
194+
proof,
195+
)
196+
.unwrap();
197+
198+
// prepare the setup params for the solidity verifier
199+
let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from((g16_vk, kzg_vk, f_circuit.state_len()));
200+
201+
// generate the solidity code
202+
let decider_solidity_code = get_decider_template_for_cyclefold_decider(nova_cyclefold_vk);
203+
204+
// verify the proof against the solidity code in the EVM
205+
let nova_cyclefold_verifier_bytecode = compile_solidity(&decider_solidity_code, "NovaDecider");
206+
let mut evm = Evm::default();
207+
let verifier_address = evm.create(nova_cyclefold_verifier_bytecode);
208+
let (_, output) = evm.call(verifier_address, calldata.clone());
209+
assert_eq!(*output.last().unwrap(), 1);
210+
211+
// save smart contract and the calldata
212+
println!("storing nova-verifier.sol and the calldata into files");
213+
use std::fs;
214+
fs::write(
215+
"./examples/nova-verifier.sol",
216+
decider_solidity_code.clone(),
217+
)
218+
.unwrap();
219+
fs::write("./examples/solidity-calldata.calldata", calldata.clone()).unwrap();
220+
let s = solidity_verifiers::utils::get_formatted_calldata(calldata.clone());
221+
fs::write("./examples/solidity-calldata.inputs", s.join(",\n")).expect("");
222+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use folding_schemes::folding::nova::{get_r1cs, ProverParams, VerifierParams};
1111
use folding_schemes::frontend::FCircuit;
1212
use folding_schemes::transcript::poseidon::poseidon_test_config;
1313

14-
// This method computes the Prover & Verifier parameters for the example.
14+
// This method computes the Nova's Prover & Verifier parameters for the example.
1515
// Warning: this method is only for testing purposes. For a real world use case those parameters
1616
// should be generated carefully (both the PoseidonConfig and the PedersenParams).
1717
#[allow(clippy::type_complexity)]

folding-schemes/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,16 @@ parallel = [
4747
"ark-crypto-primitives/parallel",
4848
"ark-r1cs-std/parallel",
4949
]
50+
51+
52+
[[example]]
53+
name = "sha256"
54+
path = "../examples/sha256.rs"
55+
56+
[[example]]
57+
name = "multi_inputs"
58+
path = "../examples/multi_inputs.rs"
59+
60+
[[example]]
61+
name = "external_inputs"
62+
path = "../examples/external_inputs.rs"

folding-schemes/src/frontend/circom/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ impl<F: PrimeField> FCircuit<F> for CircomFCircuit<F> {
8383
let circom_circuit = CircomCircuit {
8484
r1cs,
8585
witness: witness.clone(),
86-
inputs_already_computed: true,
86+
inputs_already_allocated: true,
8787
};
8888

8989
// Generates the constraints for the circom_circuit.

folding-schemes/src/frontend/circom/utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ mod tests {
140140
let circom_circuit = CircomCircuit {
141141
r1cs,
142142
witness,
143-
inputs_already_computed: false,
143+
inputs_already_allocated: false,
144144
};
145145

146146
circom_circuit.generate_constraints(cs.clone()).unwrap();

0 commit comments

Comments
 (0)