-
Notifications
You must be signed in to change notification settings - Fork 15
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
Add num_instances to transcript #648
base: master
Are you sure you want to change the base?
Conversation
ceno_zkvm/src/scheme/prover.rs
Outdated
@@ -150,6 +150,7 @@ impl<E: ExtensionField, PCS: PolynomialCommitmentScheme<E>> ZKVMProver<E, PCS> { | |||
for lk_s in cs.lk_expressions_namespace_map.iter() { | |||
tracing::debug!("opcode circuit {}: {}", circuit_name, lk_s); | |||
} | |||
transcript.append_field_element(&E::BaseField::from(num_instances as u64)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming it is line 131 above that does the skipping of empty circuits:
if witness.is_empty() {
continue;
Then to say "there is an empty circuit" the num_instances
(potentially 0) should be written before that if
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the comment. Now this problem is addressed in the expected way, right after forking the transcripts and including zeros.
ceno_zkvm/src/scheme/verifier.rs
Outdated
@@ -148,6 +149,7 @@ impl<E: ExtensionField, PCS: PolynomialCommitmentScheme<E>> ZKVMVerifier<E, PCS> | |||
.circuit_vks | |||
.get(&name) | |||
.ok_or(ZKVMError::VKNotFound(name.clone()))?; | |||
transcript.append_field_element(&E::BaseField::from(num_instances as u64)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this loop there will be only non-empty circuits. To catch all circuits the self.vk
should be involved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The verifier now is basically doing the same as the prover, using the information included in the proofs, mimicking the same updates in the corresponding transcripts.
ceno_zkvm/src/scheme/verifier.rs
Outdated
@@ -117,7 +117,7 @@ impl<E: ExtensionField, PCS: PolynomialCommitmentScheme<E>> ZKVMVerifier<E, PCS> | |||
} | |||
} | |||
|
|||
for (name, (_, proof)) in vm_proof.opcode_proofs.iter() { | |||
for (name, (i, proof)) in vm_proof.opcode_proofs.iter() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the change here? You don't use the variable i
, do you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
tracing.folded
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably want to put the name of this file into the .gitignore
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
ceno_zkvm/src/scheme/verifier.rs
Outdated
.vk | ||
.circuit_vks | ||
.iter() // Sorted by key. | ||
.zip_eq(transcripts.iter_mut().enumerate()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably less confusing to do the enumerate once on the outside of the zip:
diff --git a/ceno_zkvm/src/scheme/verifier.rs b/ceno_zkvm/src/scheme/verifier.rs
index 52c11a2c..d6b3e41a 100644
--- a/ceno_zkvm/src/scheme/verifier.rs
+++ b/ceno_zkvm/src/scheme/verifier.rs
@@ -4,7 +4,7 @@ use ark_std::iterable::Iterable;
use ceno_emul::WORD_SIZE;
use ff_ext::ExtensionField;
-use itertools::{Itertools, interleave, izip};
+use itertools::{Itertools, enumerate, interleave, izip};
use mpcs::PolynomialCommitmentScheme;
use multilinear_extensions::{
mle::{IntoMLE, MultilinearExtension},
@@ -142,12 +142,8 @@ impl<E: ExtensionField, PCS: PolynomialCommitmentScheme<E>> ZKVMVerifier<E, PCS>
// For each opcode, include the num_instances
// into its corresponding fork of the transcript.
- for ((name, _), (i, transcript)) in self
- .vk
- .circuit_vks
- .iter() // Sorted by key.
- .zip_eq(transcripts.iter_mut().enumerate())
- {
+ // Note: circuits are sorted by key.
+ for (i, ((name, _), transcript)) in enumerate(izip!(self.vk.circuit_vks, transcripts)) {
// get num_instances from opcode proof
let opcode_result = vm_proof.opcode_proofs.get(name);
let num_instances = opcode_result.map(|(_, p)| p.num_instances).unwrap_or(0);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enumeration is not needed just for updating the transcripts, so I removed it.
Just a remark: without "iter_mut()" the compiler asks to clone the transcripts, which is not what we want, right? So maybe I am missing something from your suggestion here. Any way, since the enumeration was removed, this discussion may be useless now. But I appreciate your comment, thank you.
ceno_zkvm/src/scheme/prover.rs
Outdated
@@ -92,6 +92,7 @@ impl<E: ExtensionField, PCS: PolynomialCommitmentScheme<E>> ZKVMProver<E, PCS> { | |||
// commit to opcode circuits first and then commit to table circuits, sorted by name | |||
for (circuit_name, witness) in witnesses.into_iter_sorted() { | |||
let num_instances = witness.num_instances(); | |||
//transcript.append_field_element(&E::BaseField::from(num_instances as u64)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this from the final PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
// TODO: add an enum for circuit type either in constraint_system or vk | ||
let cs = pk.get_cs(); | ||
let is_opcode_circuit = cs.lk_table_expressions.is_empty() | ||
&& cs.r_table_expressions.is_empty() | ||
&& cs.w_table_expressions.is_empty(); | ||
if is_opcode_circuit { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the table circuits may also be skipped. Is there a blocker that requires this if
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although it is a little less obvious how the num_instances
of tables is communicated. Look at the verifier variable expected_rounds
. Maybe we can do the table case somehow in another PR.
// get num_instances from opcode proof | ||
let opcode_result = vm_proof.opcode_proofs.get(name); | ||
let num_instances = opcode_result.map(|(_, p)| p.num_instances).unwrap_or(0); | ||
if opcode_result.is_some() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is supposed to be the same as if is_opcode_circuit
in the prover side, but I’m not sure that’s the case. Looking at vm_proof.opcode_proofs.insert
which ultimately becomes opcode_result.is_some()
, not the same as is_opcode_circuit
?
let opcode_result = vm_proof.opcode_proofs.get(name); | ||
let num_instances = opcode_result.map(|(_, p)| p.num_instances).unwrap_or(0); | ||
if opcode_result.is_some() { | ||
transcript.append_field_element(&E::BaseField::from(num_instances as u64)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would protect against more potential attacks to do this before the alpha, beta read_challenge
above. It’s before the transcript fork, so just a sequence of append to the root transcript.
Each opcode has its own transcript after forking. This PR includes num_instances for each opcode as the first element of each transcript (after forking). This way it is not possible to fold instances arbitrarily.