How can I verify a proof which may have more than 2 steps inside its query_round_proofs
AndrewLeeCHCH opened this issue ยท 12 comments
After running the e2e test successfully, I try to verify plonky2-sha256 proof through Circom way. Then, I add the following code to the plonky2-sha256 project. It compiles successfully and faces fatal error since the steps of query round proof is more than 2.
Is there a way to reduce the proof's query round step to pass this assertion?
let proof = data.prove(pw).unwrap(); /// sha256 proof
let conf = generate_verifier_config(&proof)?; /// Generate verifier config for sha256 proof, it failed
let (circom_constants, circom_gates) = generate_circom_verifier(&conf, &data.common, &data.verifier_only)?;
Fatal error position
plonky2-circom/src/verifier.rs
Line 192 in ae53283
Did you use standard_recursion_config in Plonky2 circuit? It should use only 2 steps.
You can always recursively prove your proof with this config.
Hi Saideng,
Thank you for the response. Unfortunately, I did use the standard recursion config. To avoid anything being messed up with my code change, I did a quick test with the plonky2-sha256 sample. I added one line for logging. It turns out the length of steps is 4.
Here is code snippet.
let mut hasher = Sha256::new();
hasher.update(msg);
let hash = hasher.finalize();
// println!("Hash: {:#04X}", hash);
let msg_bits = array_to_bits(msg);
let len = msg.len() * 8;
println!("block count: {}", (len + 65 + 511) / 512);
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
let mut builder = CircuitBuilder::<F, D>::new(CircuitConfig::standard_recursion_config());
let targets = make_circuits(&mut builder, len as u64);
let mut pw = PartialWitness::new();
for i in 0..len {
pw.set_bool_target(targets.message[i], msg_bits[i]);
}
let expected_res = array_to_bits(hash.as_slice());
for i in 0..expected_res.len() {
if expected_res[i] {
builder.assert_one(targets.digest[i].target);
} else {
builder.assert_zero(targets.digest[i].target);
}
}
println!(
"Constructing inner proof with {} gates",
builder.num_gates()
);
let data = builder.build::<C>();
let timing = TimingTree::new("prove", Level::Debug);
let proof = data.prove(pw).unwrap();
println!("proof steps length {}", proof.proof.opening_proof.query_round_proofs[0].steps.len()); // Print steps length
timing.print();
let timing = TimingTree::new("verify", Level::Debug);
let res = data.verify(proof);
timing.print();
res
}
Here is the console log.
block count: 45
Constructing inner proof with 261980 gates
[INFO plonky2::plonk::circuit_builder] Degree before blinding & padding: 262019
[INFO plonky2::plonk::circuit_builder] Degree after blinding & padding: 262144
[DEBUG plonky2::plonk::circuit_builder] Building circuit took 43.592686s
proof steps length 4
[DEBUG plonky2::util::timing] 66.4551s to prove
[DEBUG plonky2::util::timing] 0.0172s to verify
I suggest you recursively prove it in a new proof. like this one: https://github.com/polymerdao/plonky2-ed25519/blob/main/src/main.rs#L71
It works as expected, thank you
After I replaced e2e testing constants.circom
, gates.circom
, conf.json
and proof.json
with my own generation files, plonky2.circom failed to compile. Do I miss any needed modifications for circom compilation?
****COMPILING CIRCUIT****
error[T3001]: Out of bounds exception
โโ "/home/user/zk-benchmark/plonky2-circom/circom/circuits/poseidon.circom":305:5
โ
305 โ cPoseidon[0].capacity[0] <== capacity[0];
โ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found here
โ
= call trace:
->VerifyPlonky2Proof
->HashNoPad_GL
previous errors were found
Do you have any public inputs in your proof?
I don't think so
Here is the full sample code
pub fn prove_sha256(msg: &[u8]) -> Result<()> {
let mut hasher = Sha256::new();
hasher.update(msg);
let hash = hasher.finalize();
// println!("Hash: {:#04X}", hash);
let msg_bits = array_to_bits(msg);
let len = msg.len() * 8;
println!("block count: {}", (len + 65 + 511) / 512);
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_recursion_config();
// config.fri_config.rate_bits = 3;
println!("{:?}", config);
let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::new();
let expected_res = array_to_bits(hash.as_slice());
let mut targets = vec![];
for _ in 0..2 {
let curr = targets.len();
targets.push(make_circuits(&mut builder, len as u64));
for i in 0..len {
pw.set_bool_target(targets[curr].message[i], msg_bits[i]);
}
for i in 0..expected_res.len() {
if expected_res[i] {
builder.assert_one(targets[curr].digest[i].target);
} else {
builder.assert_zero(targets[curr].digest[i].target);
}
}
}
println!(
"Constructing inner proof with {} gates",
builder.num_gates()
);
let data = builder.build::<C>();
let timing = TimingTree::new("prove", Level::Debug);
let sha256proof = data.prove(pw).unwrap();
timing.print();
println!("proof steps length {}", sha256proof.proof.opening_proof.query_round_proofs[0].steps.len());
let config = CircuitConfig::standard_recursion_config();
let proof = recursive_proof::<F, C, C, D>(&(sha256proof, data.verifier_only, data.common), None, &config, None)?;
println!("middle proof steps {:?}", proof.0.proof.opening_proof.query_round_proofs[0].steps.len());
let conf = generate_verifier_config(&proof.0)?;
let (circom_constants, circom_gates) = generate_circom_verifier(&conf, &proof.2, &proof.1)?;
let mut circom_file = File::create("./circom/circuits/constants.circom")?;
circom_file.write_all(circom_constants.as_bytes())?;
circom_file = File::create("./circom/circuits/gates.circom")?;
circom_file.write_all(circom_gates.as_bytes())?;
let proof_json = generate_proof_base64(&proof.0, &conf)?;
if !Path::new("./circom/test/data").is_dir() {
std::fs::create_dir("./circom/test/data")?;
}
let mut proof_file = File::create("./circom/test/data/proof.json")?;
proof_file.write_all(proof_json.as_bytes())?;
let mut conf_file = File::create("./circom/test/data/conf.json")?;
conf_file.write_all(serde_json::to_string(&conf)?.as_ref())?;
// let timing = TimingTree::new("verify", Level::Debug);
// let res = data.verify(proof);
// timing.print();
// res
Ok(())
}
Currently the circuits require some public inputs. Try to add some public inputs in your code.
builder.register_public_inputs()
Thanks a lot. I missed some information inside the recursive proving logic. Right now, it works fine. One more thing to confirm is whether I can use some lower ptau to generate circuit's corresponding zkey.
I haven't done this step. I think it needs 2**25 powers.
I haven't done this step. I think it needs 2**25 powers.
Exactly, 2**25 is the minimum requirement for this circuit