diff --git a/src/arithmetic/fields.rs b/src/arithmetic/fields.rs index 10f3102a..7c8a788b 100644 --- a/src/arithmetic/fields.rs +++ b/src/arithmetic/fields.rs @@ -19,6 +19,9 @@ const_assert!(size_of::() >= 4); pub trait FieldExt: ff::PrimeField + From + Ord + ConstantTimeEq + Group { + /// Modulus of the field written as a string for display purposes + const MODULUS: &'static str; + /// Generator of the $2^S$ multiplicative subgroup const ROOT_OF_UNITY: Self; diff --git a/src/pasta/curves.rs b/src/pasta/curves.rs index 01ef683d..23d0fcbd 100644 --- a/src/pasta/curves.rs +++ b/src/pasta/curves.rs @@ -29,13 +29,23 @@ macro_rules! new_curve_impl { /// Represents a point in the affine coordinate space (or the point at /// infinity). - #[derive(Copy, Clone, Debug)] + #[derive(Copy, Clone)] pub struct $name_affine { x: $base, y: $base, infinity: Choice, } + impl std::fmt::Debug for $name_affine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + if self.infinity.into() { + write!(f, "Infinity") + } else { + write!(f, "({:?}, {:?})", self.x, self.y) + } + } + } + impl Curve for $name { type Affine = $name_affine; type Scalar = $scalar; diff --git a/src/pasta/fields/fp.rs b/src/pasta/fields/fp.rs index 4e876cf0..737789cb 100644 --- a/src/pasta/fields/fp.rs +++ b/src/pasta/fields/fp.rs @@ -605,6 +605,8 @@ lazy_static! { } impl FieldExt for Fp { + const MODULUS: &'static str = + "0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001"; const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; const ROOT_OF_UNITY_INV: Self = Fp::from_raw([ 0xf0b87c7db2ce91f6, diff --git a/src/pasta/fields/fq.rs b/src/pasta/fields/fq.rs index 61deaa13..56e73c72 100644 --- a/src/pasta/fields/fq.rs +++ b/src/pasta/fields/fq.rs @@ -605,6 +605,8 @@ lazy_static! { } impl FieldExt for Fq { + const MODULUS: &'static str = + "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001"; const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; const ROOT_OF_UNITY_INV: Self = Fq::from_raw([ 0x57eecda0a84b6836, diff --git a/src/plonk.rs b/src/plonk.rs index 598d683e..80a05bcf 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -5,11 +5,14 @@ //! [halo]: https://eprint.iacr.org/2019/1021 //! [plonk]: https://eprint.iacr.org/2019/953 -use crate::arithmetic::CurveAffine; +use blake2b_simd::Params as Blake2bParams; + +use crate::arithmetic::{CurveAffine, FieldExt}; use crate::poly::{ - commitment::Params, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, + commitment::Params, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, + PinnedEvaluationDomain, Polynomial, }; -use crate::transcript::ChallengeScalar; +use crate::transcript::{ChallengeScalar, Transcript}; mod circuit; mod keygen; @@ -74,8 +77,50 @@ impl VerifyingKey { cs, }) } + + /// Hashes a verification key into a transcript. + pub fn hash_into>(&self, transcript: &mut T) -> io::Result<()> { + let mut hasher = Blake2bParams::new() + .hash_length(64) + .personal(b"Halo2-Verify-Key") + .to_state(); + + let s = format!("{:?}", self.pinned()); + + hasher.update(&(s.len() as u64).to_le_bytes()); + hasher.update(s.as_bytes()); + + // Hash in final Blake2bState + transcript.common_scalar(C::Scalar::from_bytes_wide(hasher.finalize().as_array()))?; + + Ok(()) + } + + /// Obtains a pinned representation of this verification key that contains + /// the minimal information necessary to reconstruct the verification key. + pub fn pinned(&self) -> PinnedVerificationKey<'_, C> { + PinnedVerificationKey { + base_modulus: C::Base::MODULUS, + scalar_modulus: C::Scalar::MODULUS, + domain: self.domain.pinned(), + fixed_commitments: &self.fixed_commitments, + permutations: &self.permutations, + cs: self.cs.pinned(), + } + } } +/// Minimal representation of a verification key that can be used to identify +/// its active contents. +#[derive(Debug)] +pub struct PinnedVerificationKey<'a, C: CurveAffine> { + base_modulus: &'static str, + scalar_modulus: &'static str, + domain: PinnedEvaluationDomain<'a, C::Scalar>, + cs: PinnedConstraintSystem<'a, C::Scalar>, + fixed_commitments: &'a Vec, + permutations: &'a Vec>, +} /// This is a proving key which allows for the creation of proofs for a /// particular circuit. #[derive(Debug)] @@ -504,7 +549,7 @@ fn test_proving() { } } - let a = Fp::rand(); + let a = Fp::from_u64(2834758237) * Fp::ZETA; let a_squared = a * &a; let instance = Fp::one() + Fp::one(); let lookup_table = vec![instance, a, a, Fp::zero()]; @@ -596,4 +641,334 @@ fn test_proving() { assert!(msm.eval()); } } + + // Check that the verification key has not changed unexpectedly + { + assert_eq!( + format!("{:#?}", pk.vk.pinned()), + r#####"PinnedVerificationKey { + base_modulus: "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001", + scalar_modulus: "0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001", + domain: PinnedEvaluationDomain { + k: 5, + extended_k: 7, + omega: 0x0cc3380dc616f2e1daf29ad1560833ed3baea3393eceb7bc8fa36376929b78cc, + }, + cs: PinnedConstraintSystem { + num_fixed_columns: 8, + num_advice_columns: 5, + num_instance_columns: 1, + gates: [ + Sum( + Sum( + Sum( + Sum( + Product( + Advice( + 0, + ), + Fixed( + 3, + ), + ), + Product( + Advice( + 1, + ), + Fixed( + 4, + ), + ), + ), + Product( + Product( + Advice( + 0, + ), + Advice( + 1, + ), + ), + Fixed( + 6, + ), + ), + ), + Scaled( + Product( + Advice( + 2, + ), + Fixed( + 5, + ), + ), + 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000000, + ), + ), + Product( + Fixed( + 2, + ), + Product( + Advice( + 3, + ), + Advice( + 4, + ), + ), + ), + ), + Product( + Fixed( + 7, + ), + Sum( + Advice( + 0, + ), + Scaled( + Instance( + 0, + ), + 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000000, + ), + ), + ), + ], + advice_queries: [ + ( + Column { + index: 1, + column_type: Advice, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 2, + column_type: Advice, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 3, + column_type: Advice, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 4, + column_type: Advice, + }, + Rotation( + 1, + ), + ), + ( + Column { + index: 0, + column_type: Advice, + }, + Rotation( + -1, + ), + ), + ], + instance_queries: [ + ( + Column { + index: 0, + column_type: Instance, + }, + Rotation( + 0, + ), + ), + ], + fixed_queries: [ + ( + Column { + index: 6, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 7, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 0, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 2, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 3, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 4, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 1, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 5, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ], + permutations: [ + Argument { + columns: [ + Column { + index: 1, + column_type: Advice, + }, + Column { + index: 2, + column_type: Advice, + }, + Column { + index: 3, + column_type: Advice, + }, + ], + }, + Argument { + columns: [ + Column { + index: 1, + column_type: Advice, + }, + Column { + index: 2, + column_type: Advice, + }, + Column { + index: 3, + column_type: Advice, + }, + ], + }, + ], + lookups: [ + Argument { + input_columns: [ + Column { + index: 1, + column_type: Advice, + }, + ], + table_columns: [ + Column { + index: 6, + column_type: Fixed, + }, + ], + }, + Argument { + input_columns: [ + Column { + index: 1, + column_type: Advice, + }, + Column { + index: 2, + column_type: Advice, + }, + ], + table_columns: [ + Column { + index: 6, + column_type: Fixed, + }, + Column { + index: 7, + column_type: Fixed, + }, + ], + }, + ], + }, + fixed_commitments: [ + (0x3710f15f98bf0a7421343fdf390b9519506c67431a5c78678fbcc4db815c8547, 0x0a3c77f30ab2a2741b21cd45326c87dc7f7050b1a5e8c181a534b9c84a09cb02), + (0x115235d6bd2467772db857d66e7f44837cd38bb6ac0c7b412c2997dd47cbbc3c, 0x0f339c20c40e10bed476699e2ddb88256092447582b250f329b0cbf6c3f66f17), + (0x2a9ba245dcce53752e1de999b45c975472bb33b58aed8bcebdfcd185627895f0, 0x1252bf596b7acd19625f68845749f6672b99e71238cfabe91ca545103168d0f0), + (0x2a9ba245dcce53752e1de999b45c975472bb33b58aed8bcebdfcd185627895f0, 0x1252bf596b7acd19625f68845749f6672b99e71238cfabe91ca545103168d0f0), + (0x241d6d9c2060ce821d4b05ff2f9566c3947541f3d14a9aabcdb96c19158e8bc7, 0x39582cc6bdb1a4a88e89c050ad6db0ade34f45ec5791b07de6e694e9627ca66a), + (0x1ee805e20232ba31eeae1fa345bd88ac7df81dc43ffb3967a218ea4defc9d17d, 0x2838c83c064d44e87e5c8d05a234ad24d2d4a502a370acb514b430f516c0f0bf), + (0x37ead9904c760201ec4734ef398f0bdb5fe5a5e6e9db19c85e6b5483bdeb0a0b, 0x1dc08c38ed713b14f7a21a891a83b52160a3ffb0dccfbd70db7c7eb235dd193e), + (0x2dc3d20553691216c988ecbb596c4bda329f27d50bd8a7c2fb0d84b423da3cb4, 0x025b40e800020458e15e3a57268562e6c08c6971d71262bd67c72437cfc60b4c), + ], + permutations: [ + VerifyingKey { + commitments: [ + (0x289f468bca3471a3d240169ec65047d0c4bb5e1135e81822523c74b596139fed, 0x1a585c821c71fb49c883859f1389bcae45f17593ddb5f9fee1781b27129e1b06), + (0x096ef96a7725c636e7ca645dfe539694cf2a988da1ca50a468320f419d008054, 0x1ac0b48a254f8e2311081f81aa4a8ff39e37e40aa4c003325f1ac1219d231818), + (0x254c9f0088599aba37607cfd1700c653ef3ec21bfd60b98c3d725678540bc4df, 0x134d9818929589052f3cd80d50140b851db26231b2b784a6b2528a64805598dc), + ], + }, + VerifyingKey { + commitments: [ + (0x289f468bca3471a3d240169ec65047d0c4bb5e1135e81822523c74b596139fed, 0x1a585c821c71fb49c883859f1389bcae45f17593ddb5f9fee1781b27129e1b06), + (0x096ef96a7725c636e7ca645dfe539694cf2a988da1ca50a468320f419d008054, 0x1ac0b48a254f8e2311081f81aa4a8ff39e37e40aa4c003325f1ac1219d231818), + (0x254c9f0088599aba37607cfd1700c653ef3ec21bfd60b98c3d725678540bc4df, 0x134d9818929589052f3cd80d50140b851db26231b2b784a6b2528a64805598dc), + ], + }, + ], +}"##### + ); + } } diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index e8dfcfcc..bb664847 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -10,7 +10,7 @@ use super::{lookup, permutation, Error}; use crate::poly::Rotation; /// A column type -pub trait ColumnType: 'static + Sized {} +pub trait ColumnType: 'static + Sized + std::fmt::Debug {} /// A column with an index and type #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] @@ -380,6 +380,30 @@ pub struct ConstraintSystem { pub(crate) lookups: Vec, } +/// Represents the minimal parameters that determine a `ConstraintSystem`. +#[derive(Debug)] +pub struct PinnedConstraintSystem<'a, F: Field> { + num_fixed_columns: &'a usize, + num_advice_columns: &'a usize, + num_instance_columns: &'a usize, + gates: PinnedGates<'a, F>, + advice_queries: &'a Vec<(Column, Rotation)>, + instance_queries: &'a Vec<(Column, Rotation)>, + fixed_queries: &'a Vec<(Column, Rotation)>, + permutations: &'a Vec, + lookups: &'a Vec, +} + +struct PinnedGates<'a, F: Field>(&'a Vec<(&'static str, Expression)>); + +impl<'a, F: Field> std::fmt::Debug for PinnedGates<'a, F> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + f.debug_list() + .entries(self.0.iter().map(|(_, expr)| expr)) + .finish() + } +} + impl Default for ConstraintSystem { fn default() -> ConstraintSystem { ConstraintSystem { @@ -397,7 +421,24 @@ impl Default for ConstraintSystem { } impl ConstraintSystem { - /// Add a permutation argument for some columns + /// Obtain a pinned version of this constraint system; a structure with the + /// minimal parameters needed to determine the rest of the constraint + /// system. + pub fn pinned(&self) -> PinnedConstraintSystem<'_, F> { + PinnedConstraintSystem { + num_fixed_columns: &self.num_fixed_columns, + num_advice_columns: &self.num_advice_columns, + num_instance_columns: &self.num_instance_columns, + gates: PinnedGates(&self.gates), + fixed_queries: &self.fixed_queries, + advice_queries: &self.advice_queries, + instance_queries: &self.instance_queries, + permutations: &self.permutations, + lookups: &self.lookups, + } + } + + /// Add a permutation argument for some advice columns pub fn permutation(&mut self, columns: &[Column]) -> usize { let index = self.permutations.len(); diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 038b12d5..15ec08b0 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -30,6 +30,11 @@ pub fn create_proof, ConcreteCircuit: Circ } } + // Hash verification key into transcript + pk.vk + .hash_into(transcript) + .map_err(|_| Error::TranscriptError)?; + let domain = &pk.vk.domain; let mut meta = ConstraintSystem::default(); let config = ConcreteCircuit::configure(&mut meta); diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 6d1f7219..f1f09d2e 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -29,6 +29,10 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead>( let num_proofs = instance_commitments.len(); + // Hash verification key into transcript + vk.hash_into(transcript) + .map_err(|_| Error::TranscriptError)?; + for instance_commitments in instance_commitments.iter() { // Hash the instance (external) commitments into the transcript for commitment in *instance_commitments { diff --git a/src/poly/domain.rs b/src/poly/domain.rs index e8cc41ee..c6bd2ced 100644 --- a/src/poly/domain.rs +++ b/src/poly/domain.rs @@ -376,4 +376,23 @@ impl EvaluationDomain { pub fn get_quotient_poly_degree(&self) -> usize { self.quotient_poly_degree as usize } + + /// Obtain a pinned version of this evaluation domain; a structure with the + /// minimal parameters needed to determine the rest of the evaluation + /// domain. + pub fn pinned(&self) -> PinnedEvaluationDomain<'_, G> { + PinnedEvaluationDomain { + k: &self.k, + extended_k: &self.extended_k, + omega: &self.omega, + } + } +} + +/// Represents the minimal parameters that determine an `EvaluationDomain`. +#[derive(Debug)] +pub struct PinnedEvaluationDomain<'a, G: Group> { + k: &'a u32, + extended_k: &'a u32, + omega: &'a G::Scalar, }