-
Notifications
You must be signed in to change notification settings - Fork 67
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 plonky3 support #1158
base: main
Are you sure you want to change the base?
Add plonky3 support #1158
Conversation
55b0f06
to
f8656e2
Compare
c934ab6
to
d7ac075
Compare
Required for #1158
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.
checked all interfaces but not the plonky3 crate yet
pipeline/src/test_util.rs
Outdated
.with_prover_inputs(inputs) | ||
.with_backend(powdr_backend::BackendType::Plonky3, None); | ||
|
||
// Generate a proof with the setup and verification key generated on the fly |
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.
// Generate a proof with the setup and verification key generated on the fly | |
// Generate a proof with the verification key generated on the fly |
I guess setup doesn't apply here?
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.
I updated the description of the PR to explain why setup does apply here.
@@ -63,11 +62,11 @@ pub struct Artifacts<T: FieldElement> { | |||
/// An analyzed .pil file, with all dependencies imported, potentially from other files. | |||
analyzed_pil: Option<Analyzed<T>>, | |||
/// An optimized .pil file. | |||
optimized_pil: Option<Rc<Analyzed<T>>>, | |||
optimized_pil: Option<Arc<Analyzed<T>>>, |
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?
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 general because of plonky3 trait bounds, but maybe the change was not required on this struct, will check.
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.
Ok it is, because from Pipeline
we create WitnessCallback
, which is included in PowdrCircuit
, which implements BaseAir
, which requires Sync
, which isn't implemented on Rc
. So we need Arc
everywhere.
@@ -49,15 +48,15 @@ impl<T, F> QueryCallback<T> for F where F: Fn(&str) -> Result<Option<T>, String> | |||
|
|||
#[derive(Clone)] | |||
pub struct WitgenCallback<T> { | |||
analyzed: Rc<Analyzed<T>>, | |||
fixed_col_values: Rc<Vec<(String, Vec<T>)>>, | |||
analyzed: Arc<Analyzed<T>>, |
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?
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.
See above
pil: &'a Analyzed<T>, | ||
fixed: &'a [(String, Vec<T>)], | ||
_output_dir: Option<&'a Path>, | ||
setup: Option<&mut dyn io::Read>, |
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.
what is the setup here?
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.
removed
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.
Nice!!
plonky3/src/prover/mod.rs
Outdated
|
||
use self::types::*; | ||
|
||
/// In the context of plonky3, "setup" generation is the process of instantiating Poseidon with randomly sampled constants |
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.
I see
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.
changed to static
plonky3/src/prover/mod.rs
Outdated
/// The constants which uniquely determine the prover, loosely interpreted as a setup | ||
setup: Constants, | ||
/// The constants which uniquely determine the prover, loosely interpreted as a verification key | ||
vkey: Option<Constants>, |
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.
Is this the same as above? Shouldn't the vkey depend on the circuit as well?
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.
good call, the verifier I'm using somehow depends on the air still, hmm
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.
Ok I think it's fine as is, when you call verify
the prover has access to the circuit
#[cfg(test)] | ||
mod tests { | ||
use powdr_number::GoldilocksField; | ||
use powdr_pipeline::Pipeline; |
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.
Not sure we should have Pipeline as a dependency here, even as a dev-dep. Maybe better to keep these tests together with the others?
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.
Would this be fixed by moving plonky3 into backend?
Maybe @georgwiese can take a look at the circuit builder too? |
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.
Really cool! I got a few questions & comments.
plonky3/src/prover/mod.rs
Outdated
} | ||
|
||
impl<'a, T: FieldElement> Plonky3Prover<'a, T> { | ||
pub fn prove_ast( |
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.
What does ast
stand for?
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.
Good question 😆 I was sure I copied this from halo2. Changing to prove
plonky3/src/circuit_builder.rs
Outdated
pub fn cast_to_goldilocks<T: FieldElement>(v: T) -> Val { | ||
Val::from_canonical_u64(v.to_integer().try_into_u64().unwrap()) | ||
} |
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.
Can we write it in a way that it would fail for any non-Goldilocks field? Now it would fail for BN254 (because try_into_u64()
fails), but I think it also fail for other fields that fit into u64
that we might have in the future.
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
plonky3/src/circuit_builder.rs
Outdated
/// The analyzed PIL | ||
analyzed: &'a Analyzed<T>, | ||
/// The number of committed polynomials, computed from `analyzed` and cached | ||
commitment_count: usize, |
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 field is redundant, it should equalanalyzed.commitment_count()
, right?
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.
Correct. I don't really like it but that method iterates through all definitions in the program and is needed to convert each reference. Seemed like a bit much for a number which never changes, but I didn't measure. I could alternatively pass it around in the functions so it's not in the state here.
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.
Or even remove it completely, and eventually if it's an issue it should be cached from within Analyzed
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.
removed
plonky3/src/circuit_builder.rs
Outdated
PolynomialType::Committed => r.poly_id.id as usize, | ||
PolynomialType::Constant => self.commitment_count + r.poly_id.id as usize, |
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.
Using the Poly ID assumes that they are consecutive. I think that should be the case, but could you add an assertion?
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.
I added a check that no poly_id can exceed the total count of polynomials of each kind. It does not prove that all declared polynomials are referenced somewhere, so it could technically be non-consecutive, but it makes sure there is no overlap in the plonky3 ids, which I think is what you want?
plonky3/src/circuit_builder.rs
Outdated
fn to_plonky3_expr<AB: AirBuilder<F = Val>>( | ||
&self, | ||
e: &AlgebraicExpression<T>, | ||
builder: &AB, |
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.
Maybe just receive the row pair / matrix?
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, although it requires more type annotations
let left = | ||
self.to_plonky3_expr(identity.left.selector.as_ref().unwrap(), builder); | ||
|
||
builder.assert_zero(left); |
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.
Do you know why Plonky3 has a built-in is_first_row
? I guess it saves a fixed column, because it is just the first Lagrange polynomial can be evaluated efficiently by the verifier?
We could detect when we're using a fixed column of the form [1] + [0]*
and use the built-in. But if it only saves 1 commitment during setup + evaluation proof, it might be just a slight advantage and not worth the hassle now.
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.
I remember Miden also has special treatment of the first row, so you could be right. I think this can be a separate issue
plonky3/src/circuit_builder.rs
Outdated
builder.assert_zero(left); | ||
} | ||
IdentityKind::Plookup => unimplemented!("Plonky3 does not support plookup"), | ||
IdentityKind::Permutation => unimplemented!("Plonky does not support permutations"), |
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.
IdentityKind::Permutation => unimplemented!("Plonky does not support permutations"), | |
IdentityKind::Permutation => unimplemented!("Plonky3 does not support permutations"), |
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
plonky3/src/circuit_builder.rs
Outdated
self.publics | ||
.iter() | ||
.map(|(col_name, i)| cast_to_goldilocks(witness.get(col_name).unwrap()[*i])) | ||
.collect() |
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.
Do you actually constrain them somewhere? If not, I think we should assert that the publics are empty for now.
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.
Good point, I did not implement that logic in eval
. Removing as it can be a separate PR.
//! The concrete parameters used in the prover | ||
//! Inspired from [this example](https://github.com/Plonky3/Plonky3/blob/6a1b0710fdf85136d0fdd645b92933615867740a/keccak-air/examples/prove_goldilocks_keccak.rs#L57) |
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.
OK, this looks like something a cryptographer should have a say on. I guess for now it doesn't really matter, except if it has performance implications?
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.
Agreed
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.
How do fixed columns work? The comment says "Since plonky3 does not have fixed columns", but it needs to right? Otherwise, we're not proving the right statement?
I think we need the equivalent of this code somewhere to commit to fixed columns during setup.
self.commitment_count + self.constant_count | ||
} | ||
|
||
fn preprocessed_trace(&self) -> Option<RowMajorMatrix<Val>> { |
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.
I don't know why this is actually in the BaseAir
trait (doesn't seem to be called by Plonky3?), but from the name I think they mean this to return fixed columns only.
In SP1, they actually don't implement this function (they panic when it's called) and instead implement this trait, which has both generate_preprocessed_trace()
and generate_trace()
, which return fixed and committed columns.
plonky3 backend integration. It uses Poseidon with a random array of constants, which acts as a setup. I am not sure this is required, maybe using a fixed constant array is fine, in which case a lot of logic around setup and verification key disappear.