From f7f5fb4e9326e1b77c683c6e84fe8fea4e72605c Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Wed, 22 Feb 2023 16:33:14 -0500 Subject: [PATCH 1/7] Change display for GoldilocksField --- field/src/goldilocks_field.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/field/src/goldilocks_field.rs b/field/src/goldilocks_field.rs index 8c8d8bc3..e10d0c74 100644 --- a/field/src/goldilocks_field.rs +++ b/field/src/goldilocks_field.rs @@ -46,13 +46,13 @@ impl Hash for GoldilocksField { impl Display for GoldilocksField { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - Display::fmt(&self.0, f) + Display::fmt(&self.to_canonical_u64(), f) } } impl Debug for GoldilocksField { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - Debug::fmt(&self.0, f) + Debug::fmt(&self.to_canonical_u64(), f) } } From f71139d934238c6d0d0e928055ca53bbd9c21e99 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 21 Feb 2023 21:12:03 -0500 Subject: [PATCH 2/7] Add serialisation support for gates, generators, and various structs Co-authored-by: Sebastien La Duca --- evm/src/fixed_recursive_verifier.rs | 316 ++++- evm/src/permutation.rs | 25 + evm/src/proof.rs | 56 + evm/src/recursive_verifier.rs | 38 + evm/tests/empty_txn_list.rs | 37 +- plonky2/examples/bench_recursion.rs | 14 + plonky2/examples/square_root.rs | 22 +- plonky2/src/fri/oracle.rs | 1 + plonky2/src/fri/proof.rs | 8 +- plonky2/src/gadgets/arithmetic.rs | 24 +- plonky2/src/gadgets/arithmetic_extension.rs | 26 +- plonky2/src/gadgets/polynomial.rs | 2 +- plonky2/src/gadgets/range_check.rs | 29 +- plonky2/src/gadgets/split_base.rs | 20 +- plonky2/src/gadgets/split_join.rs | 45 +- plonky2/src/gates/arithmetic_base.rs | 48 +- plonky2/src/gates/arithmetic_extension.rs | 48 +- plonky2/src/gates/base_sum.rs | 34 +- plonky2/src/gates/constant.rs | 15 +- plonky2/src/gates/coset_interpolation.rs | 49 +- plonky2/src/gates/exponentiation.rs | 38 +- plonky2/src/gates/gate.rs | 25 +- plonky2/src/gates/multiplication_extension.rs | 41 +- plonky2/src/gates/noop.rs | 14 +- plonky2/src/gates/poseidon.rs | 36 +- plonky2/src/gates/poseidon_mds.rs | 33 +- plonky2/src/gates/public_input.rs | 14 +- plonky2/src/gates/random_access.rs | 48 +- plonky2/src/gates/reducing.rs | 42 +- plonky2/src/gates/reducing_extension.rs | 42 +- plonky2/src/hash/hash_types.rs | 4 +- plonky2/src/hash/merkle_proofs.rs | 2 +- plonky2/src/hash/merkle_tree.rs | 2 +- plonky2/src/iop/ext_target.rs | 6 + plonky2/src/iop/generator.rs | 152 +- plonky2/src/iop/target.rs | 8 +- plonky2/src/plonk/circuit_builder.rs | 12 +- plonky2/src/plonk/circuit_data.rs | 91 +- plonky2/src/plonk/proof.rs | 8 +- plonky2/src/recursion/cyclic_recursion.rs | 18 + plonky2/src/recursion/dummy_circuit.rs | 114 +- plonky2/src/util/gate_serialization.rs | 101 ++ plonky2/src/util/generator_serialization.rs | 156 ++ plonky2/src/util/mod.rs | 4 + plonky2/src/util/serialization.rs | 1259 ++++++++++++++++- 45 files changed, 2974 insertions(+), 153 deletions(-) create mode 100644 plonky2/src/util/gate_serialization.rs create mode 100644 plonky2/src/util/generator_serialization.rs diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 2dcd1824..8b8cf284 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -1,3 +1,4 @@ +use core::mem::{self, MaybeUninit}; use std::collections::BTreeMap; use std::ops::Range; @@ -19,6 +20,9 @@ use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use plonky2::recursion::cyclic_recursion::check_cyclic_proof_verifier_data; use plonky2::recursion::dummy_circuit::cyclic_base_proof; +use plonky2::util::gate_serialization::GateSerializer; +use plonky2::util::generator_serialization::WitnessGeneratorSerializer; +use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; use plonky2::util::timing::TimingTree; use plonky2_util::log2_ceil; @@ -47,6 +51,7 @@ const THRESHOLD_DEGREE_BITS: usize = 13; /// `degree_bits`, this contains a chain of recursive circuits for shrinking that STARK from /// `degree_bits` to a constant `THRESHOLD_DEGREE_BITS`. It also contains a special root circuit /// for combining each STARK's shrunk wrapper proof into a single proof. +#[derive(Eq, PartialEq, Debug)] pub struct AllRecursiveCircuits where F: RichField + Extendable, @@ -64,12 +69,13 @@ where /// Data for the EVM root circuit, which is used to combine each STARK's shrunk wrapper proof /// into a single proof. +#[derive(Eq, PartialEq, Debug)] pub struct RootCircuitData where F: RichField + Extendable, C: GenericConfig, { - circuit: CircuitData, + pub circuit: CircuitData, proof_with_pis: [ProofWithPublicInputsTarget; NUM_TABLES], /// For each table, various inner circuits may be used depending on the initial table size. /// This target holds the index of the circuit (within `final_circuits()`) that was used. @@ -79,37 +85,182 @@ where cyclic_vk: VerifierCircuitTarget, } +impl RootCircuitData +where + F: RichField + Extendable, + C: GenericConfig, +{ + pub fn to_buffer( + &self, + buffer: &mut Vec, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult<()> { + buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?; + for proof in &self.proof_with_pis { + buffer.write_target_proof_with_public_inputs(proof)?; + } + for index in self.index_verifier_data { + buffer.write_target(index)?; + } + buffer.write_target_verifier_circuit(&self.cyclic_vk)?; + Ok(()) + } + + pub fn from_buffer( + buffer: &mut Buffer, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult { + let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; + let mut proof_with_pis = Vec::with_capacity(NUM_TABLES); + for _ in 0..NUM_TABLES { + proof_with_pis.push(buffer.read_target_proof_with_public_inputs()?); + } + let mut index_verifier_data = Vec::with_capacity(NUM_TABLES); + for _ in 0..NUM_TABLES { + index_verifier_data.push(buffer.read_target()?); + } + let cyclic_vk = buffer.read_target_verifier_circuit()?; + + Ok(Self { + circuit, + proof_with_pis: proof_with_pis.try_into().unwrap(), + index_verifier_data: index_verifier_data.try_into().unwrap(), + cyclic_vk, + }) + } +} + /// Data for the aggregation circuit, which is used to compress two proofs into one. Each inner /// proof can be either an EVM root proof or another aggregation proof. +#[derive(Eq, PartialEq, Debug)] pub struct AggregationCircuitData where F: RichField + Extendable, C: GenericConfig, { - circuit: CircuitData, + pub circuit: CircuitData, lhs: AggregationChildTarget, rhs: AggregationChildTarget, cyclic_vk: VerifierCircuitTarget, } +impl AggregationCircuitData +where + F: RichField + Extendable, + C: GenericConfig, +{ + pub fn to_buffer( + &self, + buffer: &mut Vec, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult<()> { + buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?; + buffer.write_target_verifier_circuit(&self.cyclic_vk)?; + self.lhs.to_buffer(buffer)?; + self.rhs.to_buffer(buffer)?; + Ok(()) + } + + pub fn from_buffer( + buffer: &mut Buffer, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult { + let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; + let cyclic_vk = buffer.read_target_verifier_circuit()?; + let lhs = AggregationChildTarget::from_buffer(buffer)?; + let rhs = AggregationChildTarget::from_buffer(buffer)?; + Ok(Self { + circuit, + lhs, + rhs, + cyclic_vk, + }) + } +} + +#[derive(Eq, PartialEq, Debug)] pub struct AggregationChildTarget { is_agg: BoolTarget, agg_proof: ProofWithPublicInputsTarget, evm_proof: ProofWithPublicInputsTarget, } +impl AggregationChildTarget { + pub fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { + buffer.write_target_bool(self.is_agg)?; + buffer.write_target_proof_with_public_inputs(&self.agg_proof)?; + buffer.write_target_proof_with_public_inputs(&self.evm_proof)?; + Ok(()) + } + + pub fn from_buffer(buffer: &mut Buffer) -> IoResult { + let is_agg = buffer.read_target_bool()?; + let agg_proof = buffer.read_target_proof_with_public_inputs()?; + let evm_proof = buffer.read_target_proof_with_public_inputs()?; + Ok(Self { + is_agg, + agg_proof, + evm_proof, + }) + } +} + +#[derive(Eq, PartialEq, Debug)] pub struct BlockCircuitData where F: RichField + Extendable, C: GenericConfig, { - circuit: CircuitData, + pub circuit: CircuitData, has_parent_block: BoolTarget, parent_block_proof: ProofWithPublicInputsTarget, agg_root_proof: ProofWithPublicInputsTarget, cyclic_vk: VerifierCircuitTarget, } +impl BlockCircuitData +where + F: RichField + Extendable, + C: GenericConfig, +{ + pub fn to_buffer( + &self, + buffer: &mut Vec, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult<()> { + buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?; + buffer.write_target_bool(self.has_parent_block)?; + buffer.write_target_proof_with_public_inputs(&self.parent_block_proof)?; + buffer.write_target_proof_with_public_inputs(&self.agg_root_proof)?; + buffer.write_target_verifier_circuit(&self.cyclic_vk)?; + Ok(()) + } + + pub fn from_buffer( + buffer: &mut Buffer, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult { + let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; + let has_parent_block = buffer.read_target_bool()?; + let parent_block_proof = buffer.read_target_proof_with_public_inputs()?; + let agg_root_proof = buffer.read_target_proof_with_public_inputs()?; + let cyclic_vk = buffer.read_target_verifier_circuit()?; + Ok(Self { + circuit, + has_parent_block, + parent_block_proof, + agg_root_proof, + cyclic_vk, + }) + } +} + impl AllRecursiveCircuits where F: RichField + Extendable, @@ -124,6 +275,67 @@ where [(); C::HCO::WIDTH]:, [(); C::HCI::WIDTH]:, { + pub fn to_bytes( + &self, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult> { + // TODO: would be better to initialize it dynamically based on the supported max degree. + let mut buffer = Vec::with_capacity(1 << 34); + self.root + .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; + self.aggregation + .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; + self.block + .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; + for table in &self.by_table { + table.to_buffer(&mut buffer, gate_serializer, generator_serializer)?; + } + Ok(buffer) + } + + pub fn from_bytes( + bytes: &[u8], + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult { + let mut buffer = Buffer::new(bytes.to_vec()); + let root = + RootCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; + let aggregation = AggregationCircuitData::from_buffer( + &mut buffer, + gate_serializer, + generator_serializer, + )?; + let block = + BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; + + // Tricky use of MaybeUninit to remove the need for implementing Debug + // for all underlying types, necessary to convert a by_table Vec to an array. + let by_table = { + let mut by_table: [MaybeUninit>; NUM_TABLES] = + unsafe { MaybeUninit::uninit().assume_init() }; + for table in &mut by_table[..] { + let value = RecursiveCircuitsForTable::from_buffer( + &mut buffer, + gate_serializer, + generator_serializer, + )?; + *table = MaybeUninit::new(value); + } + unsafe { + mem::transmute::<_, [RecursiveCircuitsForTable; NUM_TABLES]>(by_table) + } + }; + + Ok(Self { + root, + aggregation, + block, + by_table, + }) + } + /// Preprocess all recursive circuits used by the system. pub fn new( all_stark: &AllStark, @@ -484,7 +696,8 @@ where } } -struct RecursiveCircuitsForTable +#[derive(Eq, PartialEq, Debug)] +pub struct RecursiveCircuitsForTable where F: RichField + Extendable, C: GenericConfig, @@ -504,6 +717,39 @@ where [(); C::HCO::WIDTH]:, [(); C::HCI::WIDTH]:, { + pub fn to_buffer( + &self, + buffer: &mut Vec, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult<()> { + buffer.write_usize(self.by_stark_size.len())?; + for (&size, table) in &self.by_stark_size { + buffer.write_usize(size)?; + table.to_buffer(buffer, gate_serializer, generator_serializer)?; + } + Ok(()) + } + + pub fn from_buffer( + buffer: &mut Buffer, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult { + let length = buffer.read_usize()?; + let mut by_stark_size = BTreeMap::new(); + for _ in 0..length { + let key = buffer.read_usize()?; + let table = RecursiveCircuitsForTableSize::from_buffer( + buffer, + gate_serializer, + generator_serializer, + )?; + by_stark_size.insert(key, table); + } + Ok(Self { by_stark_size }) + } + fn new>( table: Table, stark: &S, @@ -549,6 +795,7 @@ where /// A chain of shrinking wrapper circuits, ending with a final circuit with `degree_bits` /// `THRESHOLD_DEGREE_BITS`. +#[derive(Eq, PartialEq, Debug)] struct RecursiveCircuitsForTableSize where F: RichField + Extendable, @@ -568,6 +815,67 @@ where [(); C::HCO::WIDTH]:, [(); C::HCI::WIDTH]:, { + pub fn to_buffer( + &self, + buffer: &mut Vec, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult<()> { + buffer.write_usize(self.shrinking_wrappers.len())?; + if !self.shrinking_wrappers.is_empty() { + buffer.write_common_circuit_data( + &self.shrinking_wrappers[0].circuit.common, + gate_serializer, + )?; + } + for wrapper in &self.shrinking_wrappers { + buffer.write_prover_only_circuit_data( + &wrapper.circuit.prover_only, + generator_serializer, + )?; + buffer.write_verifier_only_circuit_data(&wrapper.circuit.verifier_only)?; + buffer.write_target_proof_with_public_inputs(&wrapper.proof_with_pis_target)?; + } + self.initial_wrapper + .to_buffer(buffer, gate_serializer, generator_serializer)?; + Ok(()) + } + + pub fn from_buffer( + buffer: &mut Buffer, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult { + let length = buffer.read_usize()?; + let mut shrinking_wrappers = Vec::with_capacity(length); + if length != 0 { + let common = buffer.read_common_circuit_data(gate_serializer)?; + + for _ in 0..length { + let prover_only = + buffer.read_prover_only_circuit_data(generator_serializer, &common)?; + let verifier_only = buffer.read_verifier_only_circuit_data()?; + let proof_with_pis_target = buffer.read_target_proof_with_public_inputs()?; + shrinking_wrappers.push(PlonkWrapperCircuit { + circuit: CircuitData { + common: common.clone(), + prover_only, + verifier_only, + }, + proof_with_pis_target, + }) + } + }; + + let initial_wrapper = + StarkWrapperCircuit::from_buffer(buffer, gate_serializer, generator_serializer)?; + + Ok(Self { + initial_wrapper, + shrinking_wrappers, + }) + } + fn new>( table: Table, stark: &S, diff --git a/evm/src/permutation.rs b/evm/src/permutation.rs index 7dc21871..72078c93 100644 --- a/evm/src/permutation.rs +++ b/evm/src/permutation.rs @@ -17,6 +17,7 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, Hasher}; use plonky2::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit}; use plonky2::util::reducing::{ReducingFactor, ReducingFactorTarget}; +use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; use plonky2_maybe_rayon::*; use crate::config::StarkConfig; @@ -89,6 +90,30 @@ pub(crate) struct GrandProductChallengeSet { pub(crate) challenges: Vec>, } +impl GrandProductChallengeSet { + pub fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { + buffer.write_usize(self.challenges.len())?; + for challenge in &self.challenges { + buffer.write_target(challenge.beta)?; + buffer.write_target(challenge.gamma)?; + } + Ok(()) + } + + pub fn from_buffer(buffer: &mut Buffer) -> IoResult { + let length = buffer.read_usize()?; + let mut challenges = Vec::with_capacity(length); + for _ in 0..length { + challenges.push(GrandProductChallenge { + beta: buffer.read_target()?, + gamma: buffer.read_target()?, + }); + } + + Ok(GrandProductChallengeSet { challenges }) + } +} + /// Compute all Z polynomials (for permutation arguments). pub(crate) fn compute_permutation_z_polys( stark: &S, diff --git a/evm/src/proof.rs b/evm/src/proof.rs index b243c9be..0fefcc0c 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -12,6 +12,7 @@ use plonky2::hash::merkle_tree::MerkleCap; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; use plonky2::plonk::config::GenericConfig; +use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; use plonky2_maybe_rayon::*; use serde::{Deserialize, Serialize}; @@ -148,6 +149,7 @@ impl, C: GenericConfig, const D: usize> S } } +#[derive(Eq, PartialEq, Debug)] pub struct StarkProofTarget { pub trace_cap: MerkleCapTarget, pub permutation_ctl_zs_cap: MerkleCapTarget, @@ -157,6 +159,31 @@ pub struct StarkProofTarget { } impl StarkProofTarget { + pub fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { + buffer.write_target_merkle_cap(&self.trace_cap)?; + buffer.write_target_merkle_cap(&self.permutation_ctl_zs_cap)?; + buffer.write_target_merkle_cap(&self.quotient_polys_cap)?; + buffer.write_target_fri_proof(&self.opening_proof)?; + self.openings.to_buffer(buffer)?; + Ok(()) + } + + pub fn from_buffer(buffer: &mut Buffer) -> IoResult { + let trace_cap = buffer.read_target_merkle_cap()?; + let permutation_ctl_zs_cap = buffer.read_target_merkle_cap()?; + let quotient_polys_cap = buffer.read_target_merkle_cap()?; + let opening_proof = buffer.read_target_fri_proof()?; + let openings = StarkOpeningSetTarget::from_buffer(buffer)?; + + Ok(Self { + trace_cap, + permutation_ctl_zs_cap, + quotient_polys_cap, + openings, + opening_proof, + }) + } + /// Recover the length of the trace from a STARK proof and a STARK config. pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize { let initial_merkle_proof = &self.opening_proof.query_round_proofs[0] @@ -276,6 +303,7 @@ impl, const D: usize> StarkOpeningSet { } } +#[derive(Eq, PartialEq, Debug)] pub struct StarkOpeningSetTarget { pub local_values: Vec>, pub next_values: Vec>, @@ -286,6 +314,34 @@ pub struct StarkOpeningSetTarget { } impl StarkOpeningSetTarget { + pub fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { + buffer.write_target_ext_vec(&self.local_values)?; + buffer.write_target_ext_vec(&self.next_values)?; + buffer.write_target_ext_vec(&self.permutation_ctl_zs)?; + buffer.write_target_ext_vec(&self.permutation_ctl_zs_next)?; + buffer.write_target_vec(&self.ctl_zs_last)?; + buffer.write_target_ext_vec(&self.quotient_polys)?; + Ok(()) + } + + pub fn from_buffer(buffer: &mut Buffer) -> IoResult { + let local_values = buffer.read_target_ext_vec::()?; + let next_values = buffer.read_target_ext_vec::()?; + let permutation_ctl_zs = buffer.read_target_ext_vec::()?; + let permutation_ctl_zs_next = buffer.read_target_ext_vec::()?; + let ctl_zs_last = buffer.read_target_vec()?; + let quotient_polys = buffer.read_target_ext_vec::()?; + + Ok(Self { + local_values, + next_values, + permutation_ctl_zs, + permutation_ctl_zs_next, + ctl_zs_last, + quotient_polys, + }) + } + pub(crate) fn to_fri_openings(&self, zero: Target) -> FriOpeningsTarget { let zeta_batch = FriOpeningBatchTarget { values: self diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 14e31341..5f4c5538 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -18,7 +18,10 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, VerifierCircuitData}; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; +use plonky2::util::gate_serialization::GateSerializer; +use plonky2::util::generator_serialization::WitnessGeneratorSerializer; use plonky2::util::reducing::ReducingFactorTarget; +use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; use plonky2::with_context; use plonky2_util::log2_ceil; @@ -157,6 +160,7 @@ impl, C: GenericConfig, const D: usize> } /// Represents a circuit which recursively verifies a STARK proof. +#[derive(Eq, PartialEq, Debug)] pub(crate) struct StarkWrapperCircuit where F: RichField + Extendable, @@ -178,6 +182,39 @@ where [(); C::HCO::WIDTH]:, [(); C::HCI::WIDTH]:, { + pub fn to_buffer( + &self, + buffer: &mut Vec, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult<()> { + buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?; + buffer.write_target_vec(&self.init_challenger_state_target)?; + buffer.write_target(self.zero_target)?; + self.stark_proof_target.to_buffer(buffer)?; + self.ctl_challenges_target.to_buffer(buffer)?; + Ok(()) + } + + pub fn from_buffer( + buffer: &mut Buffer, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult { + let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; + let init_challenger_state_target = buffer.read_target_vec()?; + let zero_target = buffer.read_target()?; + let stark_proof_target = StarkProofTarget::from_buffer(buffer)?; + let ctl_challenges_target = GrandProductChallengeSet::from_buffer(buffer)?; + Ok(Self { + circuit, + stark_proof_target, + ctl_challenges_target, + init_challenger_state_target: init_challenger_state_target.try_into().unwrap(), + zero_target, + }) + } + pub(crate) fn prove( &self, proof_with_metadata: &StarkProofWithMetadata, @@ -212,6 +249,7 @@ where } /// Represents a circuit which recursively verifies a PLONK proof. +#[derive(Eq, PartialEq, Debug)] pub(crate) struct PlonkWrapperCircuit where F: RichField + Extendable, diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 9a4eb5e0..fae2f656 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -1,13 +1,17 @@ #![allow(clippy::upper_case_acronyms)] use std::collections::HashMap; +use std::marker::PhantomData; use std::time::Duration; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; use keccak_hash::keccak; +use log::info; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::PoseidonGoldilocksConfig; +use plonky2::util::gate_serialization::default::DefaultGateSerializer; +use plonky2::util::generator_serialization::default::DefaultGeneratorSerializer; use plonky2::util::timing::TimingTree; use plonky2_evm::all_stark::AllStark; use plonky2_evm::config::StarkConfig; @@ -92,8 +96,39 @@ fn test_empty_txn_list() -> anyhow::Result<()> { verify_proof(&all_stark, proof, &config)?; - let all_circuits = AllRecursiveCircuits::::new(&all_stark, 9..19, &config); + let all_circuits = AllRecursiveCircuits::::new(&all_stark, 9..18, &config); + + { + let gate_serializer = DefaultGateSerializer; + let generator_serializer = DefaultGeneratorSerializer { + _phantom: PhantomData::, + }; + + let timing = TimingTree::new("serialize AllRecursiveCircuits", log::Level::Info); + let all_circuits_bytes = all_circuits + .to_bytes(&gate_serializer, &generator_serializer) + .map_err(|_| anyhow::Error::msg("AllRecursiveCircuits serialization failed."))?; + timing.filter(Duration::from_millis(100)).print(); + info!( + "AllRecursiveCircuits length: {} bytes", + all_circuits_bytes.len() + ); + + let timing = TimingTree::new("deserialize AllRecursiveCircuits", log::Level::Info); + let all_circuits_from_bytes = AllRecursiveCircuits::::from_bytes( + &all_circuits_bytes, + &gate_serializer, + &generator_serializer, + ) + .map_err(|_| anyhow::Error::msg("AllRecursiveCircuits deserialization failed."))?; + timing.filter(Duration::from_millis(100)).print(); + + assert_eq!(all_circuits, all_circuits_from_bytes); + } + + let mut timing = TimingTree::new("prove", log::Level::Info); let root_proof = all_circuits.prove_root(&all_stark, &config, inputs, &mut timing)?; + timing.filter(Duration::from_millis(100)).print(); all_circuits.verify_root(root_proof.clone())?; let agg_proof = all_circuits.prove_aggregation(false, &root_proof, false, &root_proof)?; diff --git a/plonky2/examples/bench_recursion.rs b/plonky2/examples/bench_recursion.rs index fd5c9518..c5635a1d 100644 --- a/plonky2/examples/bench_recursion.rs +++ b/plonky2/examples/bench_recursion.rs @@ -22,6 +22,7 @@ use plonky2::plonk::circuit_data::{CircuitConfig, CommonCircuitData, VerifierOnl use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig}; use plonky2::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs}; use plonky2::plonk::prover::prove; +use plonky2::util::gate_serialization::default::DefaultGateSerializer; use plonky2::util::timing::TimingTree; use plonky2_field::extension::Extendable; use plonky2_maybe_rayon::rayon; @@ -181,6 +182,19 @@ where CompressedProofWithPublicInputs::from_bytes(compressed_proof_bytes, cd)?; assert_eq!(compressed_proof, compressed_proof_from_bytes); + let gate_serializer = DefaultGateSerializer; + let common_data_bytes = cd + .to_bytes(&gate_serializer) + .map_err(|_| anyhow::Error::msg("CommonCircuitData serialization failed."))?; + info!( + "Common circuit data length: {} bytes", + common_data_bytes.len() + ); + let common_data_from_bytes = + CommonCircuitData::::from_bytes(common_data_bytes, &gate_serializer) + .map_err(|_| anyhow::Error::msg("CommonCircuitData deserialization failed."))?; + assert_eq!(cd, &common_data_from_bytes); + Ok(()) } diff --git a/plonky2/examples/square_root.rs b/plonky2/examples/square_root.rs index 9c253b69..7cf294c6 100644 --- a/plonky2/examples/square_root.rs +++ b/plonky2/examples/square_root.rs @@ -11,11 +11,12 @@ use plonky2::iop::witness::{PartialWitness, PartitionWitness, Witness, WitnessWr use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_data::CircuitConfig; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; +use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; use plonky2_field::extension::Extendable; /// A generator used by the prover to calculate the square root (`x`) of a given value /// (`x_squared`), outside of the circuit, in order to supply it as an additional public input. -#[derive(Debug)] +#[derive(Debug, Default)] struct SquareRootGenerator, const D: usize> { x: Target, x_squared: Target, @@ -25,6 +26,10 @@ struct SquareRootGenerator, const D: usize> { impl, const D: usize> SimpleGenerator for SquareRootGenerator { + fn id(&self) -> String { + "SquareRootGenerator".to_string() + } + fn dependencies(&self) -> Vec { vec![self.x_squared] } @@ -37,6 +42,21 @@ impl, const D: usize> SimpleGenerator out_buffer.set_target(self.x, x); } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_target(self.x)?; + dst.write_target(self.x_squared) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let x = src.read_target()?; + let x_squared = src.read_target()?; + Ok(Self { + x, + x_squared, + _phantom: PhantomData, + }) + } } /// An example of using Plonky2 to prove a statement of the form diff --git a/plonky2/src/fri/oracle.rs b/plonky2/src/fri/oracle.rs index 7855167a..bb0421fe 100644 --- a/plonky2/src/fri/oracle.rs +++ b/plonky2/src/fri/oracle.rs @@ -27,6 +27,7 @@ use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place, transp pub const SALT_SIZE: usize = 4; /// Represents a FRI oracle, i.e. a batch of polynomials which have been Merklized. +#[derive(Eq, PartialEq, Debug)] pub struct PolynomialBatch, C: GenericConfig, const D: usize> { pub polynomials: Vec>, diff --git a/plonky2/src/fri/proof.rs b/plonky2/src/fri/proof.rs index cf1a60d1..326001fd 100644 --- a/plonky2/src/fri/proof.rs +++ b/plonky2/src/fri/proof.rs @@ -33,7 +33,7 @@ pub struct FriQueryStep< pub merkle_proof: MerkleProof, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct FriQueryStepTarget { pub evals: Vec>, pub merkle_proof: MerkleProofTarget, @@ -58,7 +58,7 @@ impl> FriInitialTreeProof, MerkleProofTarget)>, } @@ -92,7 +92,7 @@ pub struct FriQueryRound< pub steps: Vec>, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct FriQueryRoundTarget { pub initial_trees_proof: FriInitialTreeProofTarget, pub steps: Vec>, @@ -129,7 +129,7 @@ pub struct FriProof, HC: HashConfig, H: Hasher { pub commit_phase_merkle_caps: Vec, pub query_round_proofs: Vec>, diff --git a/plonky2/src/gadgets/arithmetic.rs b/plonky2/src/gadgets/arithmetic.rs index 6bff79d1..9a89e5a3 100644 --- a/plonky2/src/gadgets/arithmetic.rs +++ b/plonky2/src/gadgets/arithmetic.rs @@ -11,6 +11,7 @@ use crate::iop::generator::{GeneratedValues, SimpleGenerator}; use crate::iop::target::{BoolTarget, Target}; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; impl, const D: usize> CircuitBuilder { /// Computes `-x`. @@ -370,8 +371,8 @@ impl, const D: usize> CircuitBuilder { } } -#[derive(Debug)] -struct EqualityGenerator { +#[derive(Debug, Default)] +pub(crate) struct EqualityGenerator { x: Target, y: Target, equal: BoolTarget, @@ -379,6 +380,10 @@ struct EqualityGenerator { } impl SimpleGenerator for EqualityGenerator { + fn id(&self) -> String { + "EqualityGenerator".to_string() + } + fn dependencies(&self) -> Vec { vec![self.x, self.y] } @@ -392,6 +397,21 @@ impl SimpleGenerator for EqualityGenerator { out_buffer.set_bool_target(self.equal, x == y); out_buffer.set_target(self.inv, inv); } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_target(self.x)?; + dst.write_target(self.y)?; + dst.write_target_bool(self.equal)?; + dst.write_target(self.inv) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let x = src.read_target()?; + let y = src.read_target()?; + let equal = src.read_target_bool()?; + let inv = src.read_target()?; + Ok(Self { x, y, equal, inv }) + } } /// Represents a base arithmetic operation in the circuit. Used to memoize results. diff --git a/plonky2/src/gadgets/arithmetic_extension.rs b/plonky2/src/gadgets/arithmetic_extension.rs index 6fcab718..69a5668c 100644 --- a/plonky2/src/gadgets/arithmetic_extension.rs +++ b/plonky2/src/gadgets/arithmetic_extension.rs @@ -13,6 +13,7 @@ use crate::iop::target::Target; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::util::bits_u64; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; impl, const D: usize> CircuitBuilder { pub fn arithmetic_extension( @@ -493,8 +494,8 @@ impl, const D: usize> CircuitBuilder { } } -#[derive(Debug)] -struct QuotientGeneratorExtension { +#[derive(Debug, Default)] +pub(crate) struct QuotientGeneratorExtension { numerator: ExtensionTarget, denominator: ExtensionTarget, quotient: ExtensionTarget, @@ -503,6 +504,10 @@ struct QuotientGeneratorExtension { impl, const D: usize> SimpleGenerator for QuotientGeneratorExtension { + fn id(&self) -> String { + "QuotientGeneratorExtension".to_string() + } + fn dependencies(&self) -> Vec { let mut deps = self.numerator.to_target_array().to_vec(); deps.extend(self.denominator.to_target_array()); @@ -515,6 +520,23 @@ impl, const D: usize> SimpleGenerator let quotient = num / dem; out_buffer.set_extension_target(self.quotient, quotient) } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_target_ext(self.numerator)?; + dst.write_target_ext(self.denominator)?; + dst.write_target_ext(self.quotient) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let numerator = src.read_target_ext()?; + let denominator = src.read_target_ext()?; + let quotient = src.read_target_ext()?; + Ok(Self { + numerator, + denominator, + quotient, + }) + } } /// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`. diff --git a/plonky2/src/gadgets/polynomial.rs b/plonky2/src/gadgets/polynomial.rs index 80beb62f..d43d99c2 100644 --- a/plonky2/src/gadgets/polynomial.rs +++ b/plonky2/src/gadgets/polynomial.rs @@ -7,7 +7,7 @@ use crate::iop::target::Target; use crate::plonk::circuit_builder::CircuitBuilder; use crate::util::reducing::ReducingFactorTarget; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct PolynomialCoeffsExtTarget(pub Vec>); impl PolynomialCoeffsExtTarget { diff --git a/plonky2/src/gadgets/range_check.rs b/plonky2/src/gadgets/range_check.rs index 4c22009d..144ae5dc 100644 --- a/plonky2/src/gadgets/range_check.rs +++ b/plonky2/src/gadgets/range_check.rs @@ -7,6 +7,7 @@ use crate::iop::generator::{GeneratedValues, SimpleGenerator}; use crate::iop::target::{BoolTarget, Target}; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; impl, const D: usize> CircuitBuilder { /// Checks that `x < 2^n_log` using a `BaseSumGate`. @@ -51,8 +52,8 @@ impl, const D: usize> CircuitBuilder { } } -#[derive(Debug)] -struct LowHighGenerator { +#[derive(Debug, Default)] +pub(crate) struct LowHighGenerator { integer: Target, n_log: usize, low: Target, @@ -60,6 +61,10 @@ struct LowHighGenerator { } impl SimpleGenerator for LowHighGenerator { + fn id(&self) -> String { + "LowHighGenerator".to_string() + } + fn dependencies(&self) -> Vec { vec![self.integer] } @@ -72,4 +77,24 @@ impl SimpleGenerator for LowHighGenerator { out_buffer.set_target(self.low, F::from_canonical_u64(low)); out_buffer.set_target(self.high, F::from_canonical_u64(high)); } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_target(self.integer)?; + dst.write_usize(self.n_log)?; + dst.write_target(self.low)?; + dst.write_target(self.high) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let integer = src.read_target()?; + let n_log = src.read_usize()?; + let low = src.read_target()?; + let high = src.read_target()?; + Ok(Self { + integer, + n_log, + low, + high, + }) + } } diff --git a/plonky2/src/gadgets/split_base.rs b/plonky2/src/gadgets/split_base.rs index dd0edf5d..ace3284d 100644 --- a/plonky2/src/gadgets/split_base.rs +++ b/plonky2/src/gadgets/split_base.rs @@ -13,6 +13,7 @@ use crate::iop::target::{BoolTarget, Target}; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::util::log_floor; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; impl, const D: usize> CircuitBuilder { /// Split the given element into a list of targets, where each one represents a @@ -79,13 +80,17 @@ impl, const D: usize> CircuitBuilder { } } -#[derive(Debug)] -struct BaseSumGenerator { +#[derive(Debug, Default)] +pub(crate) struct BaseSumGenerator { row: usize, limbs: Vec, } impl SimpleGenerator for BaseSumGenerator { + fn id(&self) -> String { + "BaseSumGenerator".to_string() + } + fn dependencies(&self) -> Vec { self.limbs.iter().map(|b| b.target).collect() } @@ -102,6 +107,17 @@ impl SimpleGenerator for BaseSumGenerator { out_buffer.set_target(Target::wire(self.row, BaseSumGate::::WIRE_SUM), sum); } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.row)?; + dst.write_target_bool_vec(&self.limbs) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let row = src.read_usize()?; + let limbs = src.read_target_bool_vec()?; + Ok(Self { row, limbs }) + } } #[cfg(test)] diff --git a/plonky2/src/gadgets/split_join.rs b/plonky2/src/gadgets/split_join.rs index 56e469b9..4e39a2be 100644 --- a/plonky2/src/gadgets/split_join.rs +++ b/plonky2/src/gadgets/split_join.rs @@ -9,6 +9,7 @@ use crate::iop::target::{BoolTarget, Target}; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::util::ceil_div_usize; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; impl, const D: usize> CircuitBuilder { /// Split the given integer into a list of wires, where each one represents a @@ -55,13 +56,17 @@ impl, const D: usize> CircuitBuilder { } } -#[derive(Debug)] -struct SplitGenerator { +#[derive(Debug, Default)] +pub(crate) struct SplitGenerator { integer: Target, bits: Vec, } impl SimpleGenerator for SplitGenerator { + fn id(&self) -> String { + "SplitGenerator".to_string() + } + fn dependencies(&self) -> Vec { vec![self.integer] } @@ -80,16 +85,31 @@ impl SimpleGenerator for SplitGenerator { "Integer too large to fit in given number of bits" ); } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_target(self.integer)?; + dst.write_target_vec(&self.bits) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let integer = src.read_target()?; + let bits = src.read_target_vec()?; + Ok(Self { integer, bits }) + } } -#[derive(Debug)] -struct WireSplitGenerator { +#[derive(Debug, Default)] +pub(crate) struct WireSplitGenerator { integer: Target, gates: Vec, num_limbs: usize, } impl SimpleGenerator for WireSplitGenerator { + fn id(&self) -> String { + "WireSplitGenerator".to_string() + } + fn dependencies(&self) -> Vec { vec![self.integer] } @@ -120,4 +140,21 @@ impl SimpleGenerator for WireSplitGenerator { self.gates.len() ); } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_target(self.integer)?; + dst.write_usize_vec(&self.gates)?; + dst.write_usize(self.num_limbs) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let integer = src.read_target()?; + let gates = src.read_usize_vec()?; + let num_limbs = src.read_usize()?; + Ok(Self { + integer, + gates, + num_limbs, + }) + } } diff --git a/plonky2/src/gates/arithmetic_base.rs b/plonky2/src/gates/arithmetic_base.rs index 13b4a2c5..aa9f2d86 100644 --- a/plonky2/src/gates/arithmetic_base.rs +++ b/plonky2/src/gates/arithmetic_base.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::format; use alloc::string::String; use alloc::vec::Vec; @@ -10,7 +9,7 @@ use crate::gates::packed_util::PackedEvaluableBase; use crate::gates::util::StridedConstraintConsumer; use crate::hash::hash_types::RichField; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef}; use crate::iop::target::Target; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; @@ -19,6 +18,7 @@ use crate::plonk::vars::{ EvaluationTargets, EvaluationVars, EvaluationVarsBase, EvaluationVarsBaseBatch, EvaluationVarsBasePacked, }; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// A gate which can perform a weighted multiply-add, i.e. `result = c0 x y + c1 z`. If the config /// supports enough routed wires, it can support several such operations in one gate. @@ -60,6 +60,15 @@ impl, const D: usize> Gate for ArithmeticGate format!("{self:?}") } + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.num_ops) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let num_ops = src.read_usize()?; + Ok(Self { num_ops }) + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let const_0 = vars.local_constants[0]; let const_1 = vars.local_constants[1]; @@ -117,10 +126,10 @@ impl, const D: usize> Gate for ArithmeticGate constraints } - fn generators(&self, row: usize, local_constants: &[F]) -> Vec>> { + fn generators(&self, row: usize, local_constants: &[F]) -> Vec> { (0..self.num_ops) .map(|i| { - let g: Box> = Box::new( + WitnessGeneratorRef::new( ArithmeticBaseGenerator { row, const_0: local_constants[0], @@ -128,8 +137,7 @@ impl, const D: usize> Gate for ArithmeticGate i, } .adapter(), - ); - g + ) }) .collect() } @@ -172,8 +180,8 @@ impl, const D: usize> PackedEvaluableBase for } } -#[derive(Clone, Debug)] -struct ArithmeticBaseGenerator, const D: usize> { +#[derive(Clone, Debug, Default)] +pub(crate) struct ArithmeticBaseGenerator, const D: usize> { row: usize, const_0: F, const_1: F, @@ -183,6 +191,10 @@ struct ArithmeticBaseGenerator, const D: usize> { impl, const D: usize> SimpleGenerator for ArithmeticBaseGenerator { + fn id(&self) -> String { + "ArithmeticBaseGenerator".to_string() + } + fn dependencies(&self) -> Vec { [ ArithmeticGate::wire_ith_multiplicand_0(self.i), @@ -208,6 +220,26 @@ impl, const D: usize> SimpleGenerator out_buffer.set_target(output_target, computed_output) } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.row)?; + dst.write_field(self.const_0)?; + dst.write_field(self.const_1)?; + dst.write_usize(self.i) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let row = src.read_usize()?; + let const_0 = src.read_field()?; + let const_1 = src.read_field()?; + let i = src.read_usize()?; + Ok(Self { + row, + const_0, + const_1, + i, + }) + } } #[cfg(test)] diff --git a/plonky2/src/gates/arithmetic_extension.rs b/plonky2/src/gates/arithmetic_extension.rs index 4632dbd4..36274dfd 100644 --- a/plonky2/src/gates/arithmetic_extension.rs +++ b/plonky2/src/gates/arithmetic_extension.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::format; use alloc::string::String; use alloc::vec::Vec; @@ -9,12 +8,13 @@ use crate::gates::gate::Gate; use crate::gates::util::StridedConstraintConsumer; use crate::hash::hash_types::RichField; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef}; use crate::iop::target::Target; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// A gate which can perform a weighted multiply-add, i.e. `result = c0 x y + c1 z`. If the config /// supports enough routed wires, it can support several such operations in one gate. @@ -56,6 +56,15 @@ impl, const D: usize> Gate for ArithmeticExte format!("{self:?}") } + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.num_ops) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let num_ops = src.read_usize()?; + Ok(Self { num_ops }) + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let const_0 = vars.local_constants[0]; let const_1 = vars.local_constants[1]; @@ -122,10 +131,10 @@ impl, const D: usize> Gate for ArithmeticExte constraints } - fn generators(&self, row: usize, local_constants: &[F]) -> Vec>> { + fn generators(&self, row: usize, local_constants: &[F]) -> Vec> { (0..self.num_ops) .map(|i| { - let g: Box> = Box::new( + WitnessGeneratorRef::new( ArithmeticExtensionGenerator { row, const_0: local_constants[0], @@ -133,8 +142,7 @@ impl, const D: usize> Gate for ArithmeticExte i, } .adapter(), - ); - g + ) }) .collect() } @@ -156,8 +164,8 @@ impl, const D: usize> Gate for ArithmeticExte } } -#[derive(Clone, Debug)] -struct ArithmeticExtensionGenerator, const D: usize> { +#[derive(Clone, Debug, Default)] +pub(crate) struct ArithmeticExtensionGenerator, const D: usize> { row: usize, const_0: F, const_1: F, @@ -167,6 +175,10 @@ struct ArithmeticExtensionGenerator, const D: usize impl, const D: usize> SimpleGenerator for ArithmeticExtensionGenerator { + fn id(&self) -> String { + "ArithmeticExtensionGenerator".to_string() + } + fn dependencies(&self) -> Vec { ArithmeticExtensionGate::::wires_ith_multiplicand_0(self.i) .chain(ArithmeticExtensionGate::::wires_ith_multiplicand_1( @@ -201,6 +213,26 @@ impl, const D: usize> SimpleGenerator out_buffer.set_extension_target(output_target, computed_output) } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.row)?; + dst.write_field(self.const_0)?; + dst.write_field(self.const_1)?; + dst.write_usize(self.i) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let row = src.read_usize()?; + let const_0 = src.read_field()?; + let const_1 = src.read_field()?; + let i = src.read_usize()?; + Ok(Self { + row, + const_0, + const_1, + i, + }) + } } #[cfg(test)] diff --git a/plonky2/src/gates/base_sum.rs b/plonky2/src/gates/base_sum.rs index 5883d71c..fee50c66 100644 --- a/plonky2/src/gates/base_sum.rs +++ b/plonky2/src/gates/base_sum.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use alloc::{format, vec}; @@ -12,7 +11,7 @@ use crate::gates::packed_util::PackedEvaluableBase; use crate::gates::util::StridedConstraintConsumer; use crate::hash::hash_types::RichField; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef}; use crate::iop::target::Target; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; @@ -23,6 +22,7 @@ use crate::plonk::vars::{ EvaluationVarsBasePacked, }; use crate::util::log_floor; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// A gate which can decompose a number into base B little-endian limbs. #[derive(Copy, Clone, Debug)] @@ -55,6 +55,15 @@ impl, const D: usize, const B: usize> Gate fo format!("{self:?} + Base: {B}") } + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.num_limbs) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let num_limbs = src.read_usize()?; + Ok(Self { num_limbs }) + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let sum = vars.local_wires[Self::WIRE_SUM]; let limbs = vars.local_wires[self.limbs()].to_vec(); @@ -109,12 +118,12 @@ impl, const D: usize, const B: usize> Gate fo constraints } - fn generators(&self, row: usize, _local_constants: &[F]) -> Vec>> { + fn generators(&self, row: usize, _local_constants: &[F]) -> Vec> { let gen = BaseSplitGenerator:: { row, num_limbs: self.num_limbs, }; - vec![Box::new(gen.adapter())] + vec![WitnessGeneratorRef::new(gen.adapter())] } // 1 for the sum then `num_limbs` for the limbs. @@ -160,13 +169,17 @@ impl, const D: usize, const B: usize> PackedEvaluab } } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct BaseSplitGenerator { row: usize, num_limbs: usize, } impl SimpleGenerator for BaseSplitGenerator { + fn id(&self) -> String { + "BaseSplitGenerator".to_string() + } + fn dependencies(&self) -> Vec { vec![Target::wire(self.row, BaseSumGate::::WIRE_SUM)] } @@ -195,6 +208,17 @@ impl SimpleGenerator for BaseSplitGenerator out_buffer.set_target(b, b_value); } } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.row)?; + dst.write_usize(self.num_limbs) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let row = src.read_usize()?; + let num_limbs = src.read_usize()?; + Ok(Self { row, num_limbs }) + } } #[cfg(test)] diff --git a/plonky2/src/gates/constant.rs b/plonky2/src/gates/constant.rs index bf365b04..f794c904 100644 --- a/plonky2/src/gates/constant.rs +++ b/plonky2/src/gates/constant.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use alloc::{format, vec}; @@ -10,12 +9,13 @@ use crate::gates::packed_util::PackedEvaluableBase; use crate::gates::util::StridedConstraintConsumer; use crate::hash::hash_types::RichField; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::WitnessGenerator; +use crate::iop::generator::WitnessGeneratorRef; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::vars::{ EvaluationTargets, EvaluationVars, EvaluationVarsBase, EvaluationVarsBaseBatch, EvaluationVarsBasePacked, }; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// A gate which takes a single constant parameter and outputs that value. #[derive(Copy, Clone, Debug)] @@ -40,6 +40,15 @@ impl, const D: usize> Gate for ConstantGate { format!("{self:?}") } + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.num_consts) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let num_consts = src.read_usize()?; + Ok(Self { num_consts }) + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { (0..self.num_consts) .map(|i| { @@ -75,7 +84,7 @@ impl, const D: usize> Gate for ConstantGate { .collect() } - fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec>> { + fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec> { vec![] } diff --git a/plonky2/src/gates/coset_interpolation.rs b/plonky2/src/gates/coset_interpolation.rs index da94d1c0..6ec0eae5 100644 --- a/plonky2/src/gates/coset_interpolation.rs +++ b/plonky2/src/gates/coset_interpolation.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use alloc::{format, vec}; @@ -13,12 +12,13 @@ use crate::gates::gate::Gate; use crate::gates::util::StridedConstraintConsumer; use crate::hash::hash_types::RichField; use crate::iop::ext_target::{ExtensionAlgebraTarget, ExtensionTarget}; -use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef}; use crate::iop::target::Target; use crate::iop::wire::Wire; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// One of the instantiations of `InterpolationGate`: allows constraints of variable /// degree, up to `1<, const D: usize> { pub subgroup_bits: usize, pub degree: usize, @@ -168,6 +168,26 @@ impl, const D: usize> Gate for CosetInterpola format!("{self:?}") } + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.subgroup_bits)?; + dst.write_usize(self.degree)?; + dst.write_usize(self.barycentric_weights.len())?; + dst.write_field_vec(&self.barycentric_weights) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let subgroup_bits = src.read_usize()?; + let degree = src.read_usize()?; + let length = src.read_usize()?; + let barycentric_weights: Vec = src.read_field_vec(length)?; + Ok(Self { + subgroup_bits, + degree, + barycentric_weights, + _phantom: PhantomData, + }) + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let mut constraints = Vec::with_capacity(self.num_constraints()); @@ -342,9 +362,9 @@ impl, const D: usize> Gate for CosetInterpola constraints } - fn generators(&self, row: usize, _local_constants: &[F]) -> Vec>> { + fn generators(&self, row: usize, _local_constants: &[F]) -> Vec> { let gen = InterpolationGenerator::::new(row, self.clone()); - vec![Box::new(gen.adapter())] + vec![WitnessGeneratorRef::new(gen.adapter())] } fn num_wires(&self) -> usize { @@ -366,8 +386,8 @@ impl, const D: usize> Gate for CosetInterpola } } -#[derive(Debug)] -struct InterpolationGenerator, const D: usize> { +#[derive(Debug, Default)] +pub(crate) struct InterpolationGenerator, const D: usize> { row: usize, gate: CosetInterpolationGate, interpolation_domain: Vec, @@ -389,6 +409,10 @@ impl, const D: usize> InterpolationGenerator impl, const D: usize> SimpleGenerator for InterpolationGenerator { + fn id(&self) -> String { + "InterpolationGenerator".to_string() + } + fn dependencies(&self) -> Vec { let local_target = |column| { Target::Wire(Wire { @@ -471,6 +495,17 @@ impl, const D: usize> SimpleGenerator let evaluation_value_wires = self.gate.wires_evaluation_value().map(local_wire); out_buffer.set_ext_wires(evaluation_value_wires, computed_eval); } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.row)?; + self.gate.serialize(dst) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let row = src.read_usize()?; + let gate = CosetInterpolationGate::deserialize(src)?; + Ok(Self::new(row, gate)) + } } /// Interpolate the polynomial defined by its values on an arbitrary domain at the given point `x`. diff --git a/plonky2/src/gates/exponentiation.rs b/plonky2/src/gates/exponentiation.rs index 218f77e8..64c685dc 100644 --- a/plonky2/src/gates/exponentiation.rs +++ b/plonky2/src/gates/exponentiation.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use alloc::{format, vec}; @@ -13,7 +12,7 @@ use crate::gates::packed_util::PackedEvaluableBase; use crate::gates::util::StridedConstraintConsumer; use crate::hash::hash_types::RichField; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef}; use crate::iop::target::Target; use crate::iop::wire::Wire; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; @@ -23,9 +22,10 @@ use crate::plonk::vars::{ EvaluationTargets, EvaluationVars, EvaluationVarsBase, EvaluationVarsBaseBatch, EvaluationVarsBasePacked, }; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// A gate for raising a value to a power. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct ExponentiationGate, const D: usize> { pub num_power_bits: usize, pub _phantom: PhantomData, @@ -76,6 +76,15 @@ impl, const D: usize> Gate for Exponentiation format!("{self:?}") } + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.num_power_bits) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let num_power_bits = src.read_usize()?; + Ok(Self::new(num_power_bits)) + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let base = vars.local_wires[self.wire_base()]; @@ -164,12 +173,12 @@ impl, const D: usize> Gate for Exponentiation constraints } - fn generators(&self, row: usize, _local_constants: &[F]) -> Vec>> { + fn generators(&self, row: usize, _local_constants: &[F]) -> Vec> { let gen = ExponentiationGenerator:: { row, gate: self.clone(), }; - vec![Box::new(gen.adapter())] + vec![WitnessGeneratorRef::new(gen.adapter())] } fn num_wires(&self) -> usize { @@ -228,8 +237,8 @@ impl, const D: usize> PackedEvaluableBase } } -#[derive(Debug)] -struct ExponentiationGenerator, const D: usize> { +#[derive(Debug, Default)] +pub(crate) struct ExponentiationGenerator, const D: usize> { row: usize, gate: ExponentiationGate, } @@ -237,6 +246,10 @@ struct ExponentiationGenerator, const D: usize> { impl, const D: usize> SimpleGenerator for ExponentiationGenerator { + fn id(&self) -> String { + "ExponentiationGenerator".to_string() + } + fn dependencies(&self) -> Vec { let local_target = |column| Target::wire(self.row, column); @@ -281,6 +294,17 @@ impl, const D: usize> SimpleGenerator let output_wire = local_wire(self.gate.wire_output()); out_buffer.set_wire(output_wire, intermediate_values[num_power_bits - 1]); } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.row)?; + self.gate.serialize(dst) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let row = src.read_usize()?; + let gate = ExponentiationGate::deserialize(src)?; + Ok(Self { row, gate }) + } } #[cfg(test)] diff --git a/plonky2/src/gates/gate.rs b/plonky2/src/gates/gate.rs index 34950b76..9821106c 100644 --- a/plonky2/src/gates/gate.rs +++ b/plonky2/src/gates/gate.rs @@ -1,8 +1,8 @@ -use alloc::boxed::Box; use alloc::string::String; use alloc::sync::Arc; use alloc::vec; use alloc::vec::Vec; +use core::any::Any; use core::fmt::{Debug, Error, Formatter}; use core::hash::{Hash, Hasher}; use core::ops::Range; @@ -16,16 +16,23 @@ use crate::gates::selectors::UNUSED_SELECTOR; use crate::gates::util::StridedConstraintConsumer; use crate::hash::hash_types::RichField; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::WitnessGenerator; +use crate::iop::generator::WitnessGeneratorRef; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::vars::{ EvaluationTargets, EvaluationVars, EvaluationVarsBase, EvaluationVarsBaseBatch, }; +use crate::util::serialization::{Buffer, IoResult}; /// A custom gate. pub trait Gate, const D: usize>: 'static + Send + Sync { fn id(&self) -> String; + fn serialize(&self, dst: &mut Vec) -> IoResult<()>; + + fn deserialize(src: &mut Buffer) -> IoResult + where + Self: Sized; + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec; /// Like `eval_unfiltered`, but specialized for points in the base field. @@ -162,7 +169,7 @@ pub trait Gate, const D: usize>: 'static + Send + S /// The generators used to populate the witness. /// Note: This should return exactly 1 generator per operation in the gate. - fn generators(&self, row: usize, local_constants: &[F]) -> Vec>>; + fn generators(&self, row: usize, local_constants: &[F]) -> Vec>; /// The number of wires used by this gate. fn num_wires(&self) -> usize; @@ -191,9 +198,19 @@ pub trait Gate, const D: usize>: 'static + Send + S } } +pub trait AnyGate, const D: usize>: Gate { + fn as_any(&self) -> &dyn Any; +} + +impl, F: RichField + Extendable, const D: usize> AnyGate for T { + fn as_any(&self) -> &dyn Any { + self + } +} + /// A wrapper around an `Rc` which implements `PartialEq`, `Eq` and `Hash` based on gate IDs. #[derive(Clone)] -pub struct GateRef, const D: usize>(pub(crate) Arc>); +pub struct GateRef, const D: usize>(pub(crate) Arc>); impl, const D: usize> GateRef { pub fn new>(gate: G) -> GateRef { diff --git a/plonky2/src/gates/multiplication_extension.rs b/plonky2/src/gates/multiplication_extension.rs index 1f900441..c82c30a1 100644 --- a/plonky2/src/gates/multiplication_extension.rs +++ b/plonky2/src/gates/multiplication_extension.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::format; use alloc::string::String; use alloc::vec::Vec; @@ -9,12 +8,13 @@ use crate::gates::gate::Gate; use crate::gates::util::StridedConstraintConsumer; use crate::hash::hash_types::RichField; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef}; use crate::iop::target::Target; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// A gate which can perform a weighted multiplication, i.e. `result = c0 x y`. If the config /// supports enough routed wires, it can support several such operations in one gate. @@ -53,6 +53,15 @@ impl, const D: usize> Gate for MulExtensionGa format!("{self:?}") } + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.num_ops) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let num_ops = src.read_usize()?; + Ok(Self { num_ops }) + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let const_0 = vars.local_constants[0]; @@ -110,18 +119,17 @@ impl, const D: usize> Gate for MulExtensionGa constraints } - fn generators(&self, row: usize, local_constants: &[F]) -> Vec>> { + fn generators(&self, row: usize, local_constants: &[F]) -> Vec> { (0..self.num_ops) .map(|i| { - let g: Box> = Box::new( + WitnessGeneratorRef::new( MulExtensionGenerator { row, const_0: local_constants[0], i, } .adapter(), - ); - g + ) }) .collect() } @@ -143,8 +151,8 @@ impl, const D: usize> Gate for MulExtensionGa } } -#[derive(Clone, Debug)] -struct MulExtensionGenerator, const D: usize> { +#[derive(Clone, Debug, Default)] +pub(crate) struct MulExtensionGenerator, const D: usize> { row: usize, const_0: F, i: usize, @@ -153,6 +161,10 @@ struct MulExtensionGenerator, const D: usize> { impl, const D: usize> SimpleGenerator for MulExtensionGenerator { + fn id(&self) -> String { + "MulExtensionGenerator".to_string() + } + fn dependencies(&self) -> Vec { MulExtensionGate::::wires_ith_multiplicand_0(self.i) .chain(MulExtensionGate::::wires_ith_multiplicand_1(self.i)) @@ -178,6 +190,19 @@ impl, const D: usize> SimpleGenerator out_buffer.set_extension_target(output_target, computed_output) } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.row)?; + dst.write_field(self.const_0)?; + dst.write_usize(self.i) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let row = src.read_usize()?; + let const_0 = src.read_field()?; + let i = src.read_usize()?; + Ok(Self { row, const_0, i }) + } } #[cfg(test)] diff --git a/plonky2/src/gates/noop.rs b/plonky2/src/gates/noop.rs index f6f9853a..cfcfb912 100644 --- a/plonky2/src/gates/noop.rs +++ b/plonky2/src/gates/noop.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; @@ -6,9 +5,10 @@ use crate::field::extension::Extendable; use crate::gates::gate::Gate; use crate::hash::hash_types::RichField; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::WitnessGenerator; +use crate::iop::generator::WitnessGeneratorRef; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBaseBatch}; +use crate::util::serialization::{Buffer, IoResult}; /// A gate which does nothing. pub struct NoopGate; @@ -18,6 +18,14 @@ impl, const D: usize> Gate for NoopGate { "NoopGate".into() } + fn serialize(&self, _dst: &mut Vec) -> IoResult<()> { + Ok(()) + } + + fn deserialize(_src: &mut Buffer) -> IoResult { + Ok(Self) + } + fn eval_unfiltered(&self, _vars: EvaluationVars) -> Vec { Vec::new() } @@ -34,7 +42,7 @@ impl, const D: usize> Gate for NoopGate { Vec::new() } - fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec>> { + fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec> { Vec::new() } diff --git a/plonky2/src/gates/poseidon.rs b/plonky2/src/gates/poseidon.rs index 80184797..af82a608 100644 --- a/plonky2/src/gates/poseidon.rs +++ b/plonky2/src/gates/poseidon.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use alloc::{format, vec}; @@ -13,12 +12,13 @@ use crate::hash::hash_types::RichField; use crate::hash::poseidon; use crate::hash::poseidon::{Poseidon, SPONGE_WIDTH}; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef}; use crate::iop::target::Target; use crate::iop::wire::Wire; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// Evaluates a full Poseidon permutation with 12 state elements. /// @@ -99,6 +99,14 @@ impl, const D: usize> Gate for PoseidonGate") } + fn serialize(&self, _dst: &mut Vec) -> IoResult<()> { + Ok(()) + } + + fn deserialize(_src: &mut Buffer) -> IoResult { + Ok(PoseidonGate::new()) + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let mut constraints = Vec::with_capacity(self.num_constraints()); @@ -372,12 +380,12 @@ impl, const D: usize> Gate for PoseidonGate Vec>> { + fn generators(&self, row: usize, _local_constants: &[F]) -> Vec> { let gen = PoseidonGenerator:: { row, _phantom: PhantomData, }; - vec![Box::new(gen.adapter())] + vec![WitnessGeneratorRef::new(gen.adapter())] } fn num_wires(&self) -> usize { @@ -401,8 +409,8 @@ impl, const D: usize> Gate for PoseidonGate + Poseidon, const D: usize> { +#[derive(Debug, Default)] +pub(crate) struct PoseidonGenerator + Poseidon, const D: usize> { row: usize, _phantom: PhantomData, } @@ -410,6 +418,10 @@ struct PoseidonGenerator + Poseidon, const D: usize impl + Poseidon, const D: usize> SimpleGenerator for PoseidonGenerator { + fn id(&self) -> String { + "PoseidonGenerator".to_string() + } + fn dependencies(&self) -> Vec { (0..SPONGE_WIDTH) .map(|i| PoseidonGate::::wire_input(i)) @@ -499,6 +511,18 @@ impl + Poseidon, const D: usize> SimpleGenerator out_buffer.set_wire(local_wire(PoseidonGate::::wire_output(i)), state[i]); } } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.row) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let row = src.read_usize()?; + Ok(Self { + row, + _phantom: PhantomData, + }) + } } #[cfg(test)] diff --git a/plonky2/src/gates/poseidon_mds.rs b/plonky2/src/gates/poseidon_mds.rs index cddd0f9e..409d0325 100644 --- a/plonky2/src/gates/poseidon_mds.rs +++ b/plonky2/src/gates/poseidon_mds.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use alloc::{format, vec}; @@ -13,11 +12,12 @@ use crate::gates::util::StridedConstraintConsumer; use crate::hash::hash_types::RichField; use crate::hash::poseidon::{Poseidon, SPONGE_WIDTH}; use crate::iop::ext_target::{ExtensionAlgebraTarget, ExtensionTarget}; -use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef}; use crate::iop::target::Target; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// Poseidon MDS Gate #[derive(Debug, Default)] @@ -118,6 +118,14 @@ impl + Poseidon, const D: usize> Gate for Pos format!("{self:?}") } + fn serialize(&self, _dst: &mut Vec) -> IoResult<()> { + Ok(()) + } + + fn deserialize(_src: &mut Buffer) -> IoResult { + Ok(PoseidonMdsGate::new()) + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let inputs: [_; SPONGE_WIDTH] = (0..SPONGE_WIDTH) .map(|i| vars.get_local_ext_algebra(Self::wires_input(i))) @@ -179,9 +187,9 @@ impl + Poseidon, const D: usize> Gate for Pos .collect() } - fn generators(&self, row: usize, _local_constants: &[F]) -> Vec>> { + fn generators(&self, row: usize, _local_constants: &[F]) -> Vec> { let gen = PoseidonMdsGenerator:: { row }; - vec![Box::new(gen.adapter())] + vec![WitnessGeneratorRef::new(gen.adapter())] } fn num_wires(&self) -> usize { @@ -201,14 +209,18 @@ impl + Poseidon, const D: usize> Gate for Pos } } -#[derive(Clone, Debug)] -struct PoseidonMdsGenerator { +#[derive(Clone, Debug, Default)] +pub(crate) struct PoseidonMdsGenerator { row: usize, } impl + Poseidon, const D: usize> SimpleGenerator for PoseidonMdsGenerator { + fn id(&self) -> String { + "PoseidonMdsGenerator".to_string() + } + fn dependencies(&self) -> Vec { (0..SPONGE_WIDTH) .flat_map(|i| { @@ -237,6 +249,15 @@ impl + Poseidon, const D: usize> SimpleGenerator ); } } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.row) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let row = src.read_usize()?; + Ok(Self { row }) + } } #[cfg(test)] diff --git a/plonky2/src/gates/public_input.rs b/plonky2/src/gates/public_input.rs index 10c42f00..2c3e2428 100644 --- a/plonky2/src/gates/public_input.rs +++ b/plonky2/src/gates/public_input.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use core::ops::Range; @@ -10,12 +9,13 @@ use crate::gates::packed_util::PackedEvaluableBase; use crate::gates::util::StridedConstraintConsumer; use crate::hash::hash_types::RichField; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::WitnessGenerator; +use crate::iop::generator::WitnessGeneratorRef; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::vars::{ EvaluationTargets, EvaluationVars, EvaluationVarsBase, EvaluationVarsBaseBatch, EvaluationVarsBasePacked, }; +use crate::util::serialization::{Buffer, IoResult}; /// A gate whose first four wires will be equal to a hash of public inputs. pub struct PublicInputGate; @@ -31,6 +31,14 @@ impl, const D: usize> Gate for PublicInputGat "PublicInputGate".into() } + fn serialize(&self, _dst: &mut Vec) -> IoResult<()> { + Ok(()) + } + + fn deserialize(_src: &mut Buffer) -> IoResult { + Ok(Self) + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { Self::wires_public_inputs_hash() .zip(vars.public_inputs_hash.elements) @@ -64,7 +72,7 @@ impl, const D: usize> Gate for PublicInputGat .collect() } - fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec>> { + fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec> { Vec::new() } diff --git a/plonky2/src/gates/random_access.rs b/plonky2/src/gates/random_access.rs index 80874505..6d520786 100644 --- a/plonky2/src/gates/random_access.rs +++ b/plonky2/src/gates/random_access.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use alloc::{format, vec}; @@ -14,7 +13,7 @@ use crate::gates::packed_util::PackedEvaluableBase; use crate::gates::util::StridedConstraintConsumer; use crate::hash::hash_types::RichField; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef}; use crate::iop::target::Target; use crate::iop::wire::Wire; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; @@ -24,9 +23,10 @@ use crate::plonk::vars::{ EvaluationTargets, EvaluationVars, EvaluationVarsBase, EvaluationVarsBaseBatch, EvaluationVarsBasePacked, }; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// A gate for checking that a particular element of a list matches a given value. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Default)] pub struct RandomAccessGate, const D: usize> { /// Number of bits in the index (log2 of the list size). pub bits: usize, @@ -122,6 +122,20 @@ impl, const D: usize> Gate for RandomAccessGa format!("{self:?}") } + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.bits)?; + dst.write_usize(self.num_copies)?; + dst.write_usize(self.num_extra_constants)?; + Ok(()) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let bits = src.read_usize()?; + let num_copies = src.read_usize()?; + let num_extra_constants = src.read_usize()?; + Ok(Self::new(num_copies, bits, num_extra_constants)) + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let mut constraints = Vec::with_capacity(self.num_constraints()); @@ -238,18 +252,17 @@ impl, const D: usize> Gate for RandomAccessGa constraints } - fn generators(&self, row: usize, _local_constants: &[F]) -> Vec>> { + fn generators(&self, row: usize, _local_constants: &[F]) -> Vec> { (0..self.num_copies) .map(|copy| { - let g: Box> = Box::new( + WitnessGeneratorRef::new( RandomAccessGenerator { row, gate: *self, copy, } .adapter(), - ); - g + ) }) .collect() } @@ -325,8 +338,8 @@ impl, const D: usize> PackedEvaluableBase } } -#[derive(Debug)] -struct RandomAccessGenerator, const D: usize> { +#[derive(Debug, Default)] +pub(crate) struct RandomAccessGenerator, const D: usize> { row: usize, gate: RandomAccessGate, copy: usize, @@ -335,6 +348,10 @@ struct RandomAccessGenerator, const D: usize> { impl, const D: usize> SimpleGenerator for RandomAccessGenerator { + fn id(&self) -> String { + "RandomAccessGenerator".to_string() + } + fn dependencies(&self) -> Vec { let local_target = |column| Target::wire(self.row, column); @@ -376,6 +393,19 @@ impl, const D: usize> SimpleGenerator set_local_wire(self.gate.wire_bit(i, copy), bit); } } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.row)?; + dst.write_usize(self.copy)?; + self.gate.serialize(dst) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let row = src.read_usize()?; + let copy = src.read_usize()?; + let gate = RandomAccessGate::::deserialize(src)?; + Ok(Self { row, gate, copy }) + } } #[cfg(test)] diff --git a/plonky2/src/gates/reducing.rs b/plonky2/src/gates/reducing.rs index 9bdadce8..43549210 100644 --- a/plonky2/src/gates/reducing.rs +++ b/plonky2/src/gates/reducing.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use alloc::{format, vec}; @@ -9,14 +8,15 @@ use crate::gates::gate::Gate; use crate::gates::util::StridedConstraintConsumer; use crate::hash::hash_types::RichField; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef}; use crate::iop::target::Target; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// Computes `sum alpha^i c_i` for a vector `c_i` of `num_coeffs` elements of the base field. -#[derive(Debug, Clone)] +#[derive(Debug, Default, Clone)] pub struct ReducingGate { pub num_coeffs: usize, } @@ -60,6 +60,19 @@ impl, const D: usize> Gate for ReducingGate) -> IoResult<()> { + dst.write_usize(self.num_coeffs)?; + Ok(()) + } + + fn deserialize(src: &mut Buffer) -> IoResult + where + Self: Sized, + { + let num_coeffs = src.read_usize()?; + Ok(Self::new(num_coeffs)) + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let alpha = vars.get_local_ext_algebra(Self::wires_alpha()); let old_acc = vars.get_local_ext_algebra(Self::wires_old_acc()); @@ -137,8 +150,8 @@ impl, const D: usize> Gate for ReducingGate Vec>> { - vec![Box::new( + fn generators(&self, row: usize, _local_constants: &[F]) -> Vec> { + vec![WitnessGeneratorRef::new( ReducingGenerator { row, gate: self.clone(), @@ -164,13 +177,17 @@ impl, const D: usize> Gate for ReducingGate { +#[derive(Debug, Default)] +pub(crate) struct ReducingGenerator { row: usize, gate: ReducingGate, } impl, const D: usize> SimpleGenerator for ReducingGenerator { + fn id(&self) -> String { + "ReducingGenerator".to_string() + } + fn dependencies(&self) -> Vec { ReducingGate::::wires_alpha() .chain(ReducingGate::::wires_old_acc()) @@ -207,6 +224,17 @@ impl, const D: usize> SimpleGenerator for Reduci } out_buffer.set_extension_target(output, acc); } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.row)?; + as Gate>::serialize(&self.gate, dst) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let row = src.read_usize()?; + let gate = as Gate>::deserialize(src)?; + Ok(Self { row, gate }) + } } #[cfg(test)] diff --git a/plonky2/src/gates/reducing_extension.rs b/plonky2/src/gates/reducing_extension.rs index 0ad48bb0..ce6a9725 100644 --- a/plonky2/src/gates/reducing_extension.rs +++ b/plonky2/src/gates/reducing_extension.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use alloc::{format, vec}; @@ -9,14 +8,15 @@ use crate::gates::gate::Gate; use crate::gates::util::StridedConstraintConsumer; use crate::hash::hash_types::RichField; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef}; use crate::iop::target::Target; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// Computes `sum alpha^i c_i` for a vector `c_i` of `num_coeffs` elements of the extension field. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct ReducingExtensionGate { pub num_coeffs: usize, } @@ -63,6 +63,19 @@ impl, const D: usize> Gate for ReducingExtens format!("{self:?}") } + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.num_coeffs)?; + Ok(()) + } + + fn deserialize(src: &mut Buffer) -> IoResult + where + Self: Sized, + { + let num_coeffs = src.read_usize()?; + Ok(Self::new(num_coeffs)) + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let alpha = vars.get_local_ext_algebra(Self::wires_alpha()); let old_acc = vars.get_local_ext_algebra(Self::wires_old_acc()); @@ -137,8 +150,8 @@ impl, const D: usize> Gate for ReducingExtens .collect() } - fn generators(&self, row: usize, _local_constants: &[F]) -> Vec>> { - vec![Box::new( + fn generators(&self, row: usize, _local_constants: &[F]) -> Vec> { + vec![WitnessGeneratorRef::new( ReducingGenerator { row, gate: self.clone(), @@ -164,13 +177,17 @@ impl, const D: usize> Gate for ReducingExtens } } -#[derive(Debug)] -struct ReducingGenerator { +#[derive(Debug, Default)] +pub(crate) struct ReducingGenerator { row: usize, gate: ReducingExtensionGate, } impl, const D: usize> SimpleGenerator for ReducingGenerator { + fn id(&self) -> String { + "ReducingExtensionGenerator".to_string() + } + fn dependencies(&self) -> Vec { ReducingExtensionGate::::wires_alpha() .chain(ReducingExtensionGate::::wires_old_acc()) @@ -201,6 +218,17 @@ impl, const D: usize> SimpleGenerator for Reduci acc = computed_acc; } } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.row)?; + as Gate>::serialize(&self.gate, dst) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let row = src.read_usize()?; + let gate = as Gate>::deserialize(src)?; + Ok(Self { row, gate }) + } } #[cfg(test)] diff --git a/plonky2/src/hash/hash_types.rs b/plonky2/src/hash/hash_types.rs index c725c45c..c16092a8 100644 --- a/plonky2/src/hash/hash_types.rs +++ b/plonky2/src/hash/hash_types.rs @@ -110,7 +110,7 @@ impl Default for HashOut { } /// Represents a ~256 bit hash output. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct HashOutTarget { pub elements: [Target; 4], } @@ -148,7 +148,7 @@ impl TryFrom<&[Target]> for HashOutTarget { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct MerkleCapTarget(pub Vec); /// Hash consisting of a byte array. diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index 3d523312..240e6cd2 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -31,7 +31,7 @@ impl> MerkleProof { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct MerkleProofTarget { /// The Merkle digest of each sibling subtree, staying from the bottommost layer. pub siblings: Vec, diff --git a/plonky2/src/hash/merkle_tree.rs b/plonky2/src/hash/merkle_tree.rs index f49d9f19..67fe7968 100644 --- a/plonky2/src/hash/merkle_tree.rs +++ b/plonky2/src/hash/merkle_tree.rs @@ -36,7 +36,7 @@ impl> MerkleCap { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct MerkleTree> { /// The data in the leaves of the Merkle tree. pub leaves: Vec>, diff --git a/plonky2/src/iop/ext_target.rs b/plonky2/src/iop/ext_target.rs index 08bdcba0..21eb3e55 100644 --- a/plonky2/src/iop/ext_target.rs +++ b/plonky2/src/iop/ext_target.rs @@ -12,6 +12,12 @@ use crate::plonk::circuit_builder::CircuitBuilder; #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct ExtensionTarget(pub [Target; D]); +impl Default for ExtensionTarget { + fn default() -> Self { + Self([Target::default(); D]) + } +} + impl ExtensionTarget { pub fn to_target_array(&self) -> [Target; D] { self.0 diff --git a/plonky2/src/iop/generator.rs b/plonky2/src/iop/generator.rs index a65d1748..dbe7f475 100644 --- a/plonky2/src/iop/generator.rs +++ b/plonky2/src/iop/generator.rs @@ -1,5 +1,6 @@ use alloc::vec; use alloc::vec::Vec; +use core::any::Any; use core::fmt::Debug; use core::marker::PhantomData; @@ -12,6 +13,7 @@ use crate::iop::wire::Wire; use crate::iop::witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_data::{CommonCircuitData, ProverOnlyCircuitData}; use crate::plonk::config::GenericConfig; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// Given a `PartitionWitness` that has only inputs set, populates the rest of the witness using the /// given set of generators. @@ -58,7 +60,7 @@ pub(crate) fn generate_partial_witness< continue; } - let finished = generators[generator_idx].run(&witness, &mut buffer); + let finished = generators[generator_idx].0.run(&witness, &mut buffer); if finished { generator_is_expired[generator_idx] = true; remaining_generators -= 1; @@ -98,6 +100,8 @@ pub(crate) fn generate_partial_witness< /// A generator participates in the generation of the witness. pub trait WitnessGenerator: 'static + Send + Sync + Debug { + fn id(&self) -> String; + /// Targets to be "watched" by this generator. Whenever a target in the watch list is populated, /// the generator will be queued to run. fn watch_list(&self) -> Vec; @@ -106,6 +110,52 @@ pub trait WitnessGenerator: 'static + Send + Sync + Debug { /// flag is true, the generator will never be run again, otherwise it will be queued for another /// run next time a target in its watch list is populated. fn run(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) -> bool; + + fn serialize(&self, dst: &mut Vec) -> IoResult<()>; + + fn deserialize(src: &mut Buffer) -> IoResult + where + Self: Sized; +} + +pub trait AnyWitnessGenerator: WitnessGenerator { + fn as_any(&self) -> &dyn Any; +} + +impl, F: Field> AnyWitnessGenerator for T { + fn as_any(&self) -> &dyn Any { + self + } +} + +/// A wrapper around an `Box`. +pub struct WitnessGeneratorRef(pub(crate) Box>); + +impl WitnessGeneratorRef { + pub fn new>(generator: G) -> WitnessGeneratorRef { + WitnessGeneratorRef(Box::new(generator)) + } +} + +impl PartialEq for WitnessGeneratorRef { + fn eq(&self, other: &Self) -> bool { + let mut buf1 = Vec::new(); + let mut buf2 = Vec::new(); + self.0.serialize(&mut buf1).unwrap(); + other.0.serialize(&mut buf2).unwrap(); + + buf1 == buf2 + } +} + +impl Eq for WitnessGeneratorRef {} + +impl Debug for WitnessGeneratorRef { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut buf = Vec::new(); + self.0.serialize(&mut buf).unwrap(); + write!(f, "{:?}", buf) + } } /// Values generated by a generator invocation. @@ -158,6 +208,8 @@ impl GeneratedValues { /// A generator which runs once after a list of dependencies is present in the witness. pub trait SimpleGenerator: 'static + Send + Sync + Debug { + fn id(&self) -> String; + fn dependencies(&self) -> Vec; fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues); @@ -171,6 +223,12 @@ pub trait SimpleGenerator: 'static + Send + Sync + Debug { _phantom: PhantomData, } } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()>; + + fn deserialize(src: &mut Buffer) -> IoResult + where + Self: Sized; } #[derive(Debug)] @@ -180,6 +238,10 @@ pub struct SimpleGeneratorAdapter + ?Sized> { } impl> WitnessGenerator for SimpleGeneratorAdapter { + fn id(&self) -> String { + self.inner.id() + } + fn watch_list(&self) -> Vec { self.inner.dependencies() } @@ -192,16 +254,31 @@ impl> WitnessGenerator for SimpleGeneratorAd false } } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + self.inner.serialize(dst) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + Ok(Self { + inner: SG::deserialize(src)?, + _phantom: PhantomData, + }) + } } /// A generator which copies one wire to another. -#[derive(Debug)] +#[derive(Debug, Default)] pub(crate) struct CopyGenerator { pub(crate) src: Target, pub(crate) dst: Target, } impl SimpleGenerator for CopyGenerator { + fn id(&self) -> String { + "CopyGenerator".to_string() + } + fn dependencies(&self) -> Vec { vec![self.src] } @@ -210,15 +287,30 @@ impl SimpleGenerator for CopyGenerator { let value = witness.get_target(self.src); out_buffer.set_target(self.dst, value); } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_target(self.src)?; + dst.write_target(self.dst) + } + + fn deserialize(source: &mut Buffer) -> IoResult { + let src = source.read_target()?; + let dst = source.read_target()?; + Ok(Self { src, dst }) + } } /// A generator for including a random value -#[derive(Debug)] +#[derive(Debug, Default)] pub(crate) struct RandomValueGenerator { pub(crate) target: Target, } impl SimpleGenerator for RandomValueGenerator { + fn id(&self) -> String { + "RandomValueGenerator".to_string() + } + fn dependencies(&self) -> Vec { Vec::new() } @@ -227,16 +319,29 @@ impl SimpleGenerator for RandomValueGenerator { let random_value = F::rand(); out_buffer.set_target(self.target, random_value); } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_target(self.target) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let target = src.read_target()?; + Ok(Self { target }) + } } /// A generator for testing if a value equals zero -#[derive(Debug)] +#[derive(Debug, Default)] pub(crate) struct NonzeroTestGenerator { pub(crate) to_test: Target, pub(crate) dummy: Target, } impl SimpleGenerator for NonzeroTestGenerator { + fn id(&self) -> String { + "NonzeroTestGenerator".to_string() + } + fn dependencies(&self) -> Vec { vec![self.to_test] } @@ -252,10 +357,21 @@ impl SimpleGenerator for NonzeroTestGenerator { out_buffer.set_target(self.dummy, dummy_value); } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_target(self.to_test)?; + dst.write_target(self.dummy) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let to_test = src.read_target()?; + let dummy = src.read_target()?; + Ok(Self { to_test, dummy }) + } } /// Generator used to fill an extra constant. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub(crate) struct ConstantGenerator { pub row: usize, pub constant_index: usize, @@ -269,7 +385,11 @@ impl ConstantGenerator { } } -impl SimpleGenerator for ConstantGenerator { +impl SimpleGenerator for ConstantGenerator { + fn id(&self) -> String { + "ConstantGenerator".to_string() + } + fn dependencies(&self) -> Vec { vec![] } @@ -277,4 +397,24 @@ impl SimpleGenerator for ConstantGenerator { fn run_once(&self, _witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { out_buffer.set_target(Target::wire(self.row, self.wire_index), self.constant); } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_usize(self.row)?; + dst.write_usize(self.constant_index)?; + dst.write_usize(self.wire_index)?; + dst.write_field(self.constant) + } + + fn deserialize(src: &mut Buffer) -> IoResult { + let row = src.read_usize()?; + let constant_index = src.read_usize()?; + let wire_index = src.read_usize()?; + let constant = src.read_field()?; + Ok(Self { + row, + constant_index, + wire_index, + constant, + }) + } } diff --git a/plonky2/src/iop/target.rs b/plonky2/src/iop/target.rs index 15be6943..9f1ab96b 100644 --- a/plonky2/src/iop/target.rs +++ b/plonky2/src/iop/target.rs @@ -17,6 +17,12 @@ pub enum Target { }, } +impl Default for Target { + fn default() -> Self { + Self::VirtualTarget { index: 0 } + } +} + impl Target { pub fn wire(row: usize, column: usize) -> Self { Self::Wire(Wire { row, column }) @@ -49,7 +55,7 @@ impl Target { } /// A `Target` which has already been constrained such that it can only be 0 or 1. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] #[allow(clippy::manual_non_exhaustive)] pub struct BoolTarget { pub target: Target, diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 15c01ab0..851e23a8 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::collections::BTreeMap; use alloc::vec; use alloc::vec::Vec; @@ -33,7 +32,7 @@ use crate::hash::merkle_proofs::MerkleProofTarget; use crate::hash::merkle_tree::MerkleCap; use crate::iop::ext_target::ExtensionTarget; use crate::iop::generator::{ - ConstantGenerator, CopyGenerator, RandomValueGenerator, SimpleGenerator, WitnessGenerator, + ConstantGenerator, CopyGenerator, RandomValueGenerator, SimpleGenerator, WitnessGeneratorRef, }; use crate::iop::target::{BoolTarget, Target}; use crate::iop::wire::Wire; @@ -77,7 +76,7 @@ pub struct CircuitBuilder, const D: usize> { context_log: ContextTree, /// Generators used to generate the witness. - generators: Vec>>, + generators: Vec>, constants_to_targets: HashMap, targets_to_constants: HashMap, @@ -367,12 +366,13 @@ impl, const D: usize> CircuitBuilder { self.connect(x, one); } - pub fn add_generators(&mut self, generators: Vec>>) { + pub fn add_generators(&mut self, generators: Vec>) { self.generators.extend(generators); } pub fn add_simple_generator>(&mut self, generator: G) { - self.generators.push(Box::new(generator.adapter())); + self.generators + .push(WitnessGeneratorRef::new(generator.adapter())); } /// Returns a routable target with a value of 0. @@ -865,7 +865,7 @@ impl, const D: usize> CircuitBuilder { // Index generator indices by their watched targets. let mut generator_indices_by_watches = BTreeMap::new(); for (i, generator) in self.generators.iter().enumerate() { - for watch in generator.watch_list() { + for watch in generator.0.watch_list() { let watch_index = forest.target_index(watch); let watch_rep_index = forest.parents[watch_index]; generator_indices_by_watches diff --git a/plonky2/src/plonk/circuit_data.rs b/plonky2/src/plonk/circuit_data.rs index 2346b9db..7bbf8971 100644 --- a/plonky2/src/plonk/circuit_data.rs +++ b/plonky2/src/plonk/circuit_data.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use alloc::collections::BTreeMap; use alloc::vec; use alloc::vec::Vec; @@ -22,7 +21,7 @@ use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField}; use crate::hash::hashing::HashConfig; use crate::hash::merkle_tree::MerkleCap; use crate::iop::ext_target::ExtensionTarget; -use crate::iop::generator::WitnessGenerator; +use crate::iop::generator::WitnessGeneratorRef; use crate::iop::target::Target; use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; @@ -31,6 +30,9 @@ use crate::plonk::plonk_common::PlonkOracle; use crate::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs}; use crate::plonk::prover::prove; use crate::plonk::verifier::verify; +use crate::util::gate_serialization::GateSerializer; +use crate::util::generator_serialization::WitnessGeneratorSerializer; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; use crate::util::timing::TimingTree; #[derive(Clone, Debug, Eq, PartialEq)] @@ -107,6 +109,7 @@ impl CircuitConfig { } /// Circuit data required by the prover or the verifier. +#[derive(Eq, PartialEq, Debug)] pub struct CircuitData, C: GenericConfig, const D: usize> { pub prover_only: ProverOnlyCircuitData, pub verifier_only: VerifierOnlyCircuitData, @@ -116,6 +119,25 @@ pub struct CircuitData, C: GenericConfig, impl, C: GenericConfig, const D: usize> CircuitData { + pub fn to_bytes( + &self, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult> { + let mut buffer = Vec::new(); + buffer.write_circuit_data(self, gate_serializer, generator_serializer)?; + Ok(buffer) + } + + pub fn from_bytes( + bytes: &[u8], + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult { + let mut buffer = Buffer::new(bytes.to_vec()); + buffer.read_circuit_data(gate_serializer, generator_serializer) + } + pub fn prove(&self, inputs: PartialWitness) -> Result> where [(); C::HCO::WIDTH]:, @@ -214,6 +236,25 @@ pub struct ProverCircuitData< impl, C: GenericConfig, const D: usize> ProverCircuitData { + pub fn to_bytes( + &self, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult> { + let mut buffer = Vec::new(); + buffer.write_prover_circuit_data(self, gate_serializer, generator_serializer)?; + Ok(buffer) + } + + pub fn from_bytes( + bytes: &[u8], + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult { + let mut buffer = Buffer::new(bytes.to_vec()); + buffer.read_prover_circuit_data(gate_serializer, generator_serializer) + } + pub fn prove(&self, inputs: PartialWitness) -> Result> where [(); C::HCO::WIDTH]:, @@ -242,6 +283,20 @@ pub struct VerifierCircuitData< impl, C: GenericConfig, const D: usize> VerifierCircuitData { + pub fn to_bytes(&self, gate_serializer: &dyn GateSerializer) -> IoResult> { + let mut buffer = Vec::new(); + buffer.write_verifier_circuit_data(self, gate_serializer)?; + Ok(buffer) + } + + pub fn from_bytes( + bytes: Vec, + gate_serializer: &dyn GateSerializer, + ) -> IoResult { + let mut buffer = Buffer::new(bytes); + buffer.read_verifier_circuit_data(gate_serializer) + } + pub fn verify(&self, proof_with_pis: ProofWithPublicInputs) -> Result<()> where [(); C::HCO::WIDTH]:, @@ -263,12 +318,13 @@ impl, C: GenericConfig, const D: usize> } /// Circuit data required by the prover, but not the verifier. +#[derive(Eq, PartialEq, Debug)] pub struct ProverOnlyCircuitData< F: RichField + Extendable, C: GenericConfig, const D: usize, > { - pub generators: Vec>>, + pub generators: Vec>, /// Generator indices (within the `Vec` above), indexed by the representative of each target /// they watch. pub generator_indices_by_watches: BTreeMap>, @@ -300,6 +356,19 @@ pub struct VerifierOnlyCircuitData, const D: usize> { pub circuit_digest: <>::Hasher as Hasher>::Hash, } +impl, const D: usize> VerifierOnlyCircuitData { + pub fn to_bytes(&self) -> IoResult> { + let mut buffer = Vec::new(); + buffer.write_verifier_only_circuit_data(self)?; + Ok(buffer) + } + + pub fn from_bytes(bytes: Vec) -> IoResult { + let mut buffer = Buffer::new(bytes); + buffer.read_verifier_only_circuit_data() + } +} + /// Circuit data required by both the prover and the verifier. #[derive(Debug, Clone, Eq, PartialEq)] pub struct CommonCircuitData, const D: usize> { @@ -332,6 +401,20 @@ pub struct CommonCircuitData, const D: usize> { } impl, const D: usize> CommonCircuitData { + pub fn to_bytes(&self, gate_serializer: &dyn GateSerializer) -> IoResult> { + let mut buffer = Vec::new(); + buffer.write_common_circuit_data(self, gate_serializer)?; + Ok(buffer) + } + + pub fn from_bytes( + bytes: Vec, + gate_serializer: &dyn GateSerializer, + ) -> IoResult { + let mut buffer = Buffer::new(bytes); + buffer.read_common_circuit_data(gate_serializer) + } + pub const fn degree_bits(&self) -> usize { self.fri_params.degree_bits } @@ -503,7 +586,7 @@ impl, const D: usize> CommonCircuitData { /// is intentionally missing certain fields, such as `CircuitConfig`, because we support only a /// limited form of dynamic inner circuits. We can't practically make things like the wire count /// dynamic, at least not without setting a maximum wire count and paying for the worst case. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct VerifierCircuitTarget { /// A commitment to each constant polynomial and each permutation polynomial. pub constants_sigmas_cap: MerkleCapTarget, diff --git a/plonky2/src/plonk/proof.rs b/plonky2/src/plonk/proof.rs index 6332252f..90a7ea86 100644 --- a/plonky2/src/plonk/proof.rs +++ b/plonky2/src/plonk/proof.rs @@ -41,7 +41,7 @@ pub struct Proof, C: GenericConfig, const pub opening_proof: FriProof, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct ProofTarget { pub wires_cap: MerkleCapTarget, pub plonk_zs_partial_products_cap: MerkleCapTarget, @@ -305,13 +305,13 @@ pub(crate) struct FriInferredElements, const D: usi pub Vec, ); -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct ProofWithPublicInputsTarget { pub proof: ProofTarget, pub public_inputs: Vec, } -#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)] /// The purported values of each polynomial at a single point. pub struct OpeningSet, const D: usize> { pub constants: Vec, @@ -377,7 +377,7 @@ impl, const D: usize> OpeningSet { } /// The purported values of each polynomial at a single point. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct OpeningSetTarget { pub constants: Vec>, pub plonk_sigmas: Vec>, diff --git a/plonky2/src/recursion/cyclic_recursion.rs b/plonky2/src/recursion/cyclic_recursion.rs index c973ba60..7b584e4d 100644 --- a/plonky2/src/recursion/cyclic_recursion.rs +++ b/plonky2/src/recursion/cyclic_recursion.rs @@ -13,6 +13,7 @@ use crate::plonk::circuit_data::{ }; use crate::plonk::config::{AlgebraicHasher, GenericConfig}; use crate::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; impl, const D: usize> VerifierOnlyCircuitData { fn from_slice(slice: &[C::F], common_data: &CommonCircuitData) -> Result @@ -41,6 +42,23 @@ impl, const D: usize> VerifierOnlyCircuitData { } impl VerifierCircuitTarget { + pub fn to_bytes(&self) -> IoResult> { + let mut buffer = Vec::new(); + buffer.write_target_merkle_cap(&self.constants_sigmas_cap)?; + buffer.write_target_hash(&self.circuit_digest)?; + Ok(buffer) + } + + pub fn from_bytes(bytes: Vec) -> IoResult { + let mut buffer = Buffer::new(bytes); + let constants_sigmas_cap = buffer.read_target_merkle_cap()?; + let circuit_digest = buffer.read_target_hash()?; + Ok(Self { + constants_sigmas_cap, + circuit_digest, + }) + } + fn from_slice, const D: usize>( slice: &[Target], common_data: &CommonCircuitData, diff --git a/plonky2/src/recursion/dummy_circuit.rs b/plonky2/src/recursion/dummy_circuit.rs index b0536b9c..b052cdd5 100644 --- a/plonky2/src/recursion/dummy_circuit.rs +++ b/plonky2/src/recursion/dummy_circuit.rs @@ -3,11 +3,15 @@ use alloc::vec::Vec; use hashbrown::HashMap; use plonky2_field::extension::Extendable; +use plonky2_field::polynomial::PolynomialCoeffs; use plonky2_util::ceil_div_usize; +use crate::fri::proof::{FriProof, FriProofTarget}; +use crate::gadgets::polynomial::PolynomialCoeffsExtTarget; use crate::gates::noop::NoopGate; -use crate::hash::hash_types::RichField; +use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField}; use crate::hash::hashing::HashConfig; +use crate::hash::merkle_tree::MerkleCap; use crate::iop::generator::{GeneratedValues, SimpleGenerator}; use crate::iop::target::Target; use crate::iop::witness::{PartialWitness, PartitionWitness, WitnessWrite}; @@ -15,8 +19,12 @@ use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::{ CircuitData, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData, }; -use crate::plonk::config::{AlgebraicHasher, GenericConfig}; -use crate::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; +use crate::plonk::config::{AlgebraicHasher, GenericConfig, GenericHashOut, Hasher}; +use crate::plonk::proof::{ + OpeningSet, OpeningSetTarget, Proof, ProofTarget, ProofWithPublicInputs, + ProofWithPublicInputsTarget, +}; +use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// Creates a dummy proof which is suitable for use as a base proof in a cyclic recursion tree. /// Such a base proof will not actually be verified, so most of its data is arbitrary. However, its @@ -157,12 +165,101 @@ where pub(crate) verifier_data: VerifierOnlyCircuitData, } +impl Default for DummyProofGenerator +where + F: RichField + Extendable, + C: GenericConfig, +{ + fn default() -> Self { + let proof_with_pis_target = ProofWithPublicInputsTarget { + proof: ProofTarget { + wires_cap: MerkleCapTarget(vec![]), + plonk_zs_partial_products_cap: MerkleCapTarget(vec![]), + quotient_polys_cap: MerkleCapTarget(vec![]), + openings: OpeningSetTarget::default(), + opening_proof: FriProofTarget { + commit_phase_merkle_caps: vec![], + query_round_proofs: vec![], + final_poly: PolynomialCoeffsExtTarget(vec![]), + pow_witness: Target::default(), + }, + }, + public_inputs: vec![], + }; + + let proof_with_pis = ProofWithPublicInputs { + proof: Proof { + wires_cap: MerkleCap(vec![]), + plonk_zs_partial_products_cap: MerkleCap(vec![]), + quotient_polys_cap: MerkleCap(vec![]), + openings: OpeningSet::default(), + opening_proof: FriProof { + commit_phase_merkle_caps: vec![], + query_round_proofs: vec![], + final_poly: PolynomialCoeffs { coeffs: vec![] }, + pow_witness: F::ZERO, + }, + }, + public_inputs: vec![], + }; + + let verifier_data_target = VerifierCircuitTarget { + constants_sigmas_cap: MerkleCapTarget(vec![]), + circuit_digest: HashOutTarget { + elements: [Target::default(); 4], + }, + }; + + let verifier_data = VerifierOnlyCircuitData { + constants_sigmas_cap: MerkleCap(vec![]), + circuit_digest: + <>::Hasher as Hasher>::Hash::from_bytes( + &vec![0; <>::Hasher as Hasher>::HASH_SIZE], + ), + }; + + Self { + proof_with_pis_target, + proof_with_pis, + verifier_data_target, + verifier_data, + } + } +} + +impl DummyProofGenerator +where + F: RichField + Extendable, + C: GenericConfig + 'static, + C::Hasher: AlgebraicHasher, +{ + pub fn deserialize_with_circuit_data( + src: &mut Buffer, + cd: &CommonCircuitData, + ) -> IoResult { + let proof_with_pis_target = src.read_target_proof_with_public_inputs()?; + let proof_with_pis = src.read_proof_with_public_inputs(cd)?; + let verifier_data_target = src.read_target_verifier_circuit()?; + let verifier_data = src.read_verifier_only_circuit_data()?; + Ok(Self { + proof_with_pis_target, + proof_with_pis, + verifier_data_target, + verifier_data, + }) + } +} + impl SimpleGenerator for DummyProofGenerator where F: RichField + Extendable, C: GenericConfig + 'static, C::Hasher: AlgebraicHasher, { + fn id(&self) -> String { + "DummyProofGenerator".to_string() + } + fn dependencies(&self) -> Vec { vec![] } @@ -171,4 +268,15 @@ where out_buffer.set_proof_with_pis_target(&self.proof_with_pis_target, &self.proof_with_pis); out_buffer.set_verifier_data_target(&self.verifier_data_target, &self.verifier_data); } + + fn serialize(&self, dst: &mut Vec) -> IoResult<()> { + dst.write_target_proof_with_public_inputs(&self.proof_with_pis_target)?; + dst.write_proof_with_public_inputs(&self.proof_with_pis)?; + dst.write_target_verifier_circuit(&self.verifier_data_target)?; + dst.write_verifier_only_circuit_data(&self.verifier_data) + } + + fn deserialize(_src: &mut Buffer) -> IoResult { + panic!() + } } diff --git a/plonky2/src/util/gate_serialization.rs b/plonky2/src/util/gate_serialization.rs new file mode 100644 index 00000000..3df74049 --- /dev/null +++ b/plonky2/src/util/gate_serialization.rs @@ -0,0 +1,101 @@ +use plonky2_field::extension::Extendable; + +use crate::gates::gate::GateRef; +use crate::hash::hash_types::RichField; +use crate::util::serialization::{Buffer, IoResult}; + +pub trait GateSerializer, const D: usize> { + fn read_gate(&self, buf: &mut Buffer) -> IoResult>; + fn write_gate(&self, buf: &mut Vec, gate: &GateRef) -> IoResult<()>; +} + +#[macro_export] +macro_rules! read_gate_impl { + ($buf:expr, $tag:expr, $($gate_types:ty),+) => {{ + let tag = $tag; + let buf = $buf; + let mut i = 0..; + $(if tag == i.next().unwrap() { + let gate = <$gate_types as $crate::gates::gate::Gate>::deserialize(buf)?; + Ok($crate::gates::gate::GateRef::::new(gate)) + } else)* + { + Err($crate::util::serialization::IoError) + } + }} +} + +#[macro_export] +macro_rules! get_gate_tag_impl { + ($gate:expr, $($gate_types:ty),+) => {{ + let gate_any = $gate.0.as_any(); + let mut i = 0..; + $(if let (tag, true) = (i.next().unwrap(), gate_any.is::<$gate_types>()) { + Ok(tag) + } else)* + { + log::log!(log::Level::Error, "attempted to serialize gate with id `{}` which is unsupported by this gate serializer", $gate.0.id()); + Err($crate::util::serialization::IoError) + } + }}; +} + +#[macro_export] +macro_rules! impl_gate_serializer { + ($target:ty, $($gate_types:ty),+) => { + fn read_gate(&self, buf: &mut $crate::util::serialization::Buffer) -> $crate::util::serialization::IoResult<$crate::gates::gate::GateRef> { + let tag = $crate::util::serialization::Read::read_u32(buf)?; + read_gate_impl!(buf, tag, $($gate_types),+) + } + + fn write_gate(&self, buf: &mut Vec, gate: &$crate::gates::gate::GateRef) -> $crate::util::serialization::IoResult<()> { + let tag = get_gate_tag_impl!(gate, $($gate_types),+)?; + + $crate::util::serialization::Write::write_u32(buf, tag)?; + gate.0.serialize(buf)?; + Ok(()) + } + }; +} + +pub mod default { + use plonky2_field::extension::Extendable; + + use crate::gates::arithmetic_base::ArithmeticGate; + use crate::gates::arithmetic_extension::ArithmeticExtensionGate; + use crate::gates::base_sum::BaseSumGate; + use crate::gates::constant::ConstantGate; + use crate::gates::coset_interpolation::CosetInterpolationGate; + use crate::gates::exponentiation::ExponentiationGate; + use crate::gates::multiplication_extension::MulExtensionGate; + use crate::gates::noop::NoopGate; + use crate::gates::poseidon::PoseidonGate; + use crate::gates::poseidon_mds::PoseidonMdsGate; + use crate::gates::public_input::PublicInputGate; + use crate::gates::random_access::RandomAccessGate; + use crate::gates::reducing::ReducingGate; + use crate::gates::reducing_extension::ReducingExtensionGate; + use crate::hash::hash_types::RichField; + use crate::util::gate_serialization::GateSerializer; + + pub struct DefaultGateSerializer; + impl, const D: usize> GateSerializer for DefaultGateSerializer { + impl_gate_serializer! { + DefaultGateSerializer, + ArithmeticGate, + ArithmeticExtensionGate, + BaseSumGate<2>, + ConstantGate, + CosetInterpolationGate, + ExponentiationGate, + MulExtensionGate, + NoopGate, + PoseidonMdsGate, + PoseidonGate, + PublicInputGate, + RandomAccessGate, + ReducingExtensionGate, + ReducingGate + } + } +} diff --git a/plonky2/src/util/generator_serialization.rs b/plonky2/src/util/generator_serialization.rs new file mode 100644 index 00000000..4020e993 --- /dev/null +++ b/plonky2/src/util/generator_serialization.rs @@ -0,0 +1,156 @@ +//! A module to help with WitnessGeneratorRef serialization + +use plonky2_field::extension::Extendable; + +use crate::hash::hash_types::RichField; +use crate::iop::generator::WitnessGeneratorRef; +use crate::plonk::circuit_data::CommonCircuitData; +use crate::util::serialization::{Buffer, IoResult}; + +pub trait WitnessGeneratorSerializer, const D: usize> { + fn read_generator( + &self, + buf: &mut Buffer, + common: &CommonCircuitData, + ) -> IoResult>; + fn write_generator( + &self, + buf: &mut Vec, + generator: &WitnessGeneratorRef, + ) -> IoResult<()>; +} + +#[macro_export] +macro_rules! read_generator_impl { + ($buf:expr, $tag:expr, $common:expr, $($generator_types:ty),+) => {{ + let tag = $tag; + let buf = $buf; + let mut i = 0..; + + if tag == 0 { + let generator: $crate::recursion::dummy_circuit::DummyProofGenerator = + $crate::recursion::dummy_circuit::DummyProofGenerator::deserialize_with_circuit_data(buf, $common)?; + return Ok($crate::iop::generator::WitnessGeneratorRef::::new( + $crate::iop::generator::SimpleGenerator::::adapter(generator), + )); + } + + $(if tag == i.next().unwrap() { + let generator = + <$generator_types as $crate::iop::generator::SimpleGenerator>::deserialize(buf)?; + Ok($crate::iop::generator::WitnessGeneratorRef::::new( + $crate::iop::generator::SimpleGenerator::::adapter(generator), + )) + } else)* + { + Err($crate::util::serialization::IoError) + } + }}; +} + +#[macro_export] +macro_rules! get_generator_tag_impl { + ($generator:expr, $($generator_types:ty),+) => {{ + let mut i = 0..; + $(if let (tag, true) = (i.next().unwrap(), $generator.0.id() == $crate::iop::generator::SimpleGenerator::::id(&<$generator_types>::default())) { + Ok(tag) + } else)* + { + log::log!(log::Level::Error, "attempted to serialize generator with id {} which is unsupported by this generator serializer", $generator.0.id()); + Err($crate::util::serialization::IoError) + } + }}; +} + +#[macro_export] +macro_rules! impl_generator_serializer { + ($target:ty, $($generator_types:ty),+) => { + fn read_generator( + &self, + buf: &mut $crate::util::serialization::Buffer, + common: &$crate::plonk::circuit_data::CommonCircuitData, + ) -> $crate::util::serialization::IoResult<$crate::iop::generator::WitnessGeneratorRef> { + let tag = $crate::util::serialization::Read::read_u32(buf)?; + read_generator_impl!(buf, tag, common, $($generator_types),+) + } + + fn write_generator( + &self, + buf: &mut Vec, + generator: &$crate::iop::generator::WitnessGeneratorRef, + ) -> $crate::util::serialization::IoResult<()> { + let tag = get_generator_tag_impl!(generator, $($generator_types),+)?; + + $crate::util::serialization::Write::write_u32(buf, tag)?; + generator.0.serialize(buf)?; + Ok(()) + } + }; +} + +pub mod default { + use core::marker::PhantomData; + + use plonky2_field::extension::Extendable; + + use crate::gadgets::arithmetic::EqualityGenerator; + use crate::gadgets::arithmetic_extension::QuotientGeneratorExtension; + use crate::gadgets::range_check::LowHighGenerator; + use crate::gadgets::split_base::BaseSumGenerator; + use crate::gadgets::split_join::{SplitGenerator, WireSplitGenerator}; + use crate::gates::arithmetic_base::ArithmeticBaseGenerator; + use crate::gates::arithmetic_extension::ArithmeticExtensionGenerator; + use crate::gates::base_sum::BaseSplitGenerator; + use crate::gates::coset_interpolation::InterpolationGenerator; + use crate::gates::exponentiation::ExponentiationGenerator; + use crate::gates::multiplication_extension::MulExtensionGenerator; + use crate::gates::poseidon::PoseidonGenerator; + use crate::gates::poseidon_mds::PoseidonMdsGenerator; + use crate::gates::random_access::RandomAccessGenerator; + use crate::gates::reducing::ReducingGenerator; + use crate::gates::reducing_extension::ReducingGenerator as ReducingExtensionGenerator; + use crate::hash::hash_types::RichField; + use crate::iop::generator::{ + ConstantGenerator, CopyGenerator, NonzeroTestGenerator, RandomValueGenerator, + }; + use crate::plonk::config::{AlgebraicHasher, GenericConfig}; + use crate::recursion::dummy_circuit::DummyProofGenerator; + use crate::util::generator_serialization::WitnessGeneratorSerializer; + + pub struct DefaultGeneratorSerializer, const D: usize> { + pub _phantom: PhantomData, + } + + impl WitnessGeneratorSerializer for DefaultGeneratorSerializer + where + F: RichField + Extendable, + C: GenericConfig + 'static, + C::Hasher: AlgebraicHasher, + { + impl_generator_serializer! { + DefaultGateSerializer, + DummyProofGenerator, + ArithmeticBaseGenerator, + ArithmeticExtensionGenerator, + BaseSplitGenerator<2>, + BaseSumGenerator<2>, + ConstantGenerator, + CopyGenerator, + EqualityGenerator, + ExponentiationGenerator, + InterpolationGenerator, + LowHighGenerator, + MulExtensionGenerator, + NonzeroTestGenerator, + PoseidonGenerator, + PoseidonMdsGenerator, + QuotientGeneratorExtension, + RandomAccessGenerator, + RandomValueGenerator, + ReducingGenerator, + ReducingExtensionGenerator, + SplitGenerator, + WireSplitGenerator + } + } +} diff --git a/plonky2/src/util/mod.rs b/plonky2/src/util/mod.rs index 19f3cb74..be4fab4d 100644 --- a/plonky2/src/util/mod.rs +++ b/plonky2/src/util/mod.rs @@ -10,6 +10,10 @@ use crate::field::types::Field; pub(crate) mod context_tree; pub(crate) mod partial_products; +#[macro_use] +pub mod gate_serialization; +#[macro_use] +pub mod generator_serialization; pub mod reducing; pub mod serialization; pub mod strided_view; diff --git a/plonky2/src/util/serialization.rs b/plonky2/src/util/serialization.rs index 5ef93835..488cbd61 100644 --- a/plonky2/src/util/serialization.rs +++ b/plonky2/src/util/serialization.rs @@ -1,27 +1,45 @@ +use alloc::collections::BTreeMap; use alloc::vec; use alloc::vec::Vec; use core::convert::Infallible; use core::fmt::{Debug, Display, Formatter}; use core::mem::size_of; +use core::ops::Range; use hashbrown::HashMap; +use super::gate_serialization::GateSerializer; +use super::generator_serialization::WitnessGeneratorSerializer; use crate::field::extension::{Extendable, FieldExtension}; use crate::field::polynomial::PolynomialCoeffs; use crate::field::types::{Field64, PrimeField64}; +use crate::fri::oracle::PolynomialBatch; use crate::fri::proof::{ - CompressedFriProof, CompressedFriQueryRounds, FriInitialTreeProof, FriProof, FriQueryRound, - FriQueryStep, + CompressedFriProof, CompressedFriQueryRounds, FriInitialTreeProof, FriInitialTreeProofTarget, + FriProof, FriProofTarget, FriQueryRound, FriQueryRoundTarget, FriQueryStep, FriQueryStepTarget, }; -use crate::hash::hash_types::RichField; +use crate::fri::reduction_strategies::FriReductionStrategy; +use crate::fri::{FriConfig, FriParams}; +use crate::gadgets::polynomial::PolynomialCoeffsExtTarget; +use crate::gates::gate::GateRef; +use crate::gates::selectors::SelectorsInfo; +use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField}; use crate::hash::hashing::HashConfig; -use crate::hash::merkle_proofs::MerkleProof; -use crate::hash::merkle_tree::MerkleCap; -use crate::plonk::circuit_data::CommonCircuitData; +use crate::hash::merkle_proofs::{MerkleProof, MerkleProofTarget}; +use crate::hash::merkle_tree::{MerkleCap, MerkleTree}; +use crate::iop::ext_target::ExtensionTarget; +use crate::iop::generator::WitnessGeneratorRef; +use crate::iop::target::{BoolTarget, Target}; +use crate::iop::wire::Wire; +use crate::plonk::circuit_data::{ + CircuitConfig, CircuitData, CommonCircuitData, ProverCircuitData, ProverOnlyCircuitData, + VerifierCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData, +}; use crate::plonk::config::{GenericConfig, GenericHashOut, Hasher}; use crate::plonk::plonk_common::salt_size; use crate::plonk::proof::{ - CompressedProof, CompressedProofWithPublicInputs, OpeningSet, Proof, ProofWithPublicInputs, + CompressedProof, CompressedProofWithPublicInputs, OpeningSet, OpeningSetTarget, Proof, + ProofTarget, ProofWithPublicInputs, ProofWithPublicInputsTarget, }; /// A no_std compatible variant of `std::io::Error` @@ -53,6 +71,32 @@ pub trait Read { /// Reads exactly the length of `bytes` from `self` and writes it to `bytes`. fn read_exact(&mut self, bytes: &mut [u8]) -> IoResult<()>; + /// Reads a `bool` value from `self`. + #[inline] + fn read_bool(&mut self) -> IoResult { + let i = self.read_u8()?; + match i { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(IoError), + } + } + + /// Reads a `BoolTarget` value from `self`. + #[inline] + fn read_target_bool(&mut self) -> IoResult { + Ok(BoolTarget::new_unsafe(self.read_target()?)) + } + + /// Reads a vector of `BoolTarget` from `self`. + #[inline] + fn read_target_bool_vec(&mut self) -> IoResult> { + let length = self.read_usize()?; + (0..length) + .map(|_| self.read_target_bool()) + .collect::, _>>() + } + /// Reads a `u8` value from `self`. #[inline] fn read_u8(&mut self) -> IoResult { @@ -69,6 +113,26 @@ pub trait Read { Ok(u32::from_le_bytes(buf)) } + /// Reads a `usize` value from `self`. + #[inline] + fn read_usize(&mut self) -> IoResult { + let mut buf = [0; std::mem::size_of::()]; + self.read_exact(&mut buf)?; + Ok(u64::from_le_bytes(buf) as usize) + } + + /// Reads a vector of `usize` value from `self`. + #[inline] + fn read_usize_vec(&mut self) -> IoResult> { + let len = self.read_usize()?; + let mut res = Vec::with_capacity(len); + for _ in 0..len { + res.push(self.read_usize()?); + } + + Ok(res) + } + /// Reads a element from the field `F` with size less than `2^64` from `self.` #[inline] fn read_field(&mut self) -> IoResult @@ -118,6 +182,49 @@ pub trait Read { (0..length).map(|_| self.read_field_ext::()).collect() } + /// Reads a Target from `self.` + #[inline] + fn read_target(&mut self) -> IoResult { + let is_wire = self.read_bool()?; + if is_wire { + let row = self.read_usize()?; + let column = self.read_usize()?; + Ok(Target::wire(row, column)) + } else { + let index = self.read_usize()?; + Ok(Target::VirtualTarget { index }) + } + } + + /// Reads an ExtensionTarget from `self`. + #[inline] + fn read_target_ext(&mut self) -> IoResult> { + let mut res = [Target::wire(0, 0); D]; + for r in res.iter_mut() { + *r = self.read_target()?; + } + + Ok(ExtensionTarget(res)) + } + + /// Reads a vector of Target from `self`. + #[inline] + fn read_target_vec(&mut self) -> IoResult> { + let length = self.read_usize()?; + (0..length) + .map(|_| self.read_target()) + .collect::, _>>() + } + + /// Reads a vector of ExtensionTarget from `self`. + #[inline] + fn read_target_ext_vec(&mut self) -> IoResult>> { + let length = self.read_usize()?; + (0..length) + .map(|_| self.read_target_ext::()) + .collect::, _>>() + } + /// Reads a hash value from `self`. #[inline] fn read_hash(&mut self) -> IoResult @@ -131,6 +238,30 @@ pub trait Read { Ok(H::Hash::from_bytes(&buf)) } + /// Reads a HashOutTarget value from `self`. + #[inline] + fn read_target_hash(&mut self) -> IoResult { + let mut elements = [Target::wire(0, 0); 4]; + for e in elements.iter_mut() { + *e = self.read_target()?; + } + + Ok(HashOutTarget { elements }) + } + + /// Reads a vector of Hash from `self`. + #[inline] + fn read_hash_vec(&mut self, length: usize) -> IoResult> + where + F: RichField, + HC: HashConfig, + H: Hasher, + { + (0..length) + .map(|_| self.read_hash::()) + .collect::, _>>() + } + /// Reads a value of type [`MerkleCap`] from `self` with the given `cap_height`. #[inline] fn read_merkle_cap(&mut self, cap_height: usize) -> IoResult> @@ -147,6 +278,43 @@ pub trait Read { )) } + /// Reads a value of type [`MerkleCapTarget`] from `self`. + #[inline] + fn read_target_merkle_cap(&mut self) -> IoResult { + let length = self.read_usize()?; + Ok(MerkleCapTarget( + (0..length) + .map(|_| self.read_target_hash()) + .collect::, _>>()?, + )) + } + + /// Reads a value of type [`MerkleTree`] from `self`. + #[inline] + fn read_merkle_tree(&mut self) -> IoResult> + where + F: RichField, + HC: HashConfig, + H: Hasher, + { + let leaves_len = self.read_usize()?; + let mut leaves = Vec::with_capacity(leaves_len); + for _ in 0..leaves_len { + let leaf_len = self.read_usize()?; + leaves.push(self.read_field_vec(leaf_len)?); + } + + let digests_len = self.read_usize()?; + let digests = self.read_hash_vec::(digests_len)?; + let cap_height = self.read_usize()?; + let cap = self.read_merkle_cap::(cap_height)?; + Ok(MerkleTree { + leaves, + digests, + cap, + }) + } + /// Reads a value of type [`OpeningSet`] from `self` with the given `common_data`. #[inline] fn read_opening_set( @@ -179,6 +347,28 @@ pub trait Read { }) } + /// Reads a value of type [`OpeningSetTarget`] from `self`. + #[inline] + fn read_target_opening_set(&mut self) -> IoResult> { + let constants = self.read_target_ext_vec::()?; + let plonk_sigmas = self.read_target_ext_vec::()?; + let wires = self.read_target_ext_vec::()?; + let plonk_zs = self.read_target_ext_vec::()?; + let plonk_zs_next = self.read_target_ext_vec::()?; + let partial_products = self.read_target_ext_vec::()?; + let quotient_polys = self.read_target_ext_vec::()?; + + Ok(OpeningSetTarget { + constants, + plonk_sigmas, + wires, + plonk_zs, + plonk_zs_next, + partial_products, + quotient_polys, + }) + } + /// Reads a value of type [`MerkleProof`] from `self`. #[inline] fn read_merkle_proof(&mut self) -> IoResult> @@ -195,6 +385,17 @@ pub trait Read { }) } + /// Reads a value of type [`MerkleProofTarget`] from `self`. + #[inline] + fn read_target_merkle_proof(&mut self) -> IoResult { + let length = self.read_u8()?; + Ok(MerkleProofTarget { + siblings: (0..length) + .map(|_| self.read_target_hash()) + .collect::>()?, + }) + } + /// Reads a value of type [`FriInitialTreeProof`] from `self` with the given `common_data`. #[inline] fn read_fri_initial_proof( @@ -232,6 +433,19 @@ pub trait Read { Ok(FriInitialTreeProof { evals_proofs }) } + /// Reads a value of type [`FriInitialTreeProofTarget`] from `self`. + #[inline] + fn read_target_fri_initial_proof(&mut self) -> IoResult { + let len = self.read_usize()?; + let mut evals_proofs = Vec::with_capacity(len); + + for _ in 0..len { + evals_proofs.push((self.read_target_vec()?, self.read_target_merkle_proof()?)); + } + + Ok(FriInitialTreeProofTarget { evals_proofs }) + } + /// Reads a value of type [`FriQueryStep`] from `self` with the given `arity` and `compressed` /// flag. #[inline] @@ -252,6 +466,17 @@ pub trait Read { }) } + /// Reads a value of type [`FriQueryStepTarget`] from `self`. + #[inline] + fn read_target_fri_query_step(&mut self) -> IoResult> { + let evals = self.read_target_ext_vec::()?; + let merkle_proof = self.read_target_merkle_proof()?; + Ok(FriQueryStepTarget { + evals, + merkle_proof, + }) + } + /// Reads a vector of [`FriQueryRound`]s from `self` with `common_data`. #[inline] #[allow(clippy::type_complexity)] @@ -281,6 +506,27 @@ pub trait Read { Ok(fqrs) } + /// Reads a vector of [`FriQueryRoundTarget`]s from `self`. + #[inline] + fn read_target_fri_query_rounds( + &mut self, + ) -> IoResult>> { + let num_query_rounds = self.read_usize()?; + let mut fqrs = Vec::with_capacity(num_query_rounds); + for _ in 0..num_query_rounds { + let initial_trees_proof = self.read_target_fri_initial_proof()?; + let num_steps = self.read_usize()?; + let steps = (0..num_steps) + .map(|_| self.read_target_fri_query_step::()) + .collect::, _>>()?; + fqrs.push(FriQueryRoundTarget { + initial_trees_proof, + steps, + }) + } + Ok(fqrs) + } + /// Reads a value of type [`FriProof`] from `self` with `common_data`. #[inline] fn read_fri_proof( @@ -308,6 +554,348 @@ pub trait Read { }) } + /// Reads a value of type [`FriProofTarget`] from `self`. + #[inline] + fn read_target_fri_proof(&mut self) -> IoResult> { + let length = self.read_usize()?; + let commit_phase_merkle_caps = (0..length) + .map(|_| self.read_target_merkle_cap()) + .collect::, _>>()?; + let query_round_proofs = self.read_target_fri_query_rounds::()?; + let final_poly = PolynomialCoeffsExtTarget(self.read_target_ext_vec::()?); + let pow_witness = self.read_target()?; + + Ok(FriProofTarget { + commit_phase_merkle_caps, + query_round_proofs, + final_poly, + pow_witness, + }) + } + + fn read_fri_reduction_strategy(&mut self) -> IoResult { + let variant = self.read_u8()?; + match variant { + 0 => { + let arities = self.read_usize_vec()?; + Ok(FriReductionStrategy::Fixed(arities)) + } + 1 => { + let arity_bits = self.read_usize()?; + let final_poly_bits = self.read_usize()?; + + Ok(FriReductionStrategy::ConstantArityBits( + arity_bits, + final_poly_bits, + )) + } + 2 => { + let is_some = self.read_u8()?; + match is_some { + 0 => Ok(FriReductionStrategy::MinSize(None)), + 1 => { + let max = self.read_usize()?; + Ok(FriReductionStrategy::MinSize(Some(max))) + } + _ => Err(IoError), + } + } + _ => Err(IoError), + } + } + + fn read_fri_config(&mut self) -> IoResult { + let rate_bits = self.read_usize()?; + let cap_height = self.read_usize()?; + let num_query_rounds = self.read_usize()?; + let proof_of_work_bits = self.read_u32()?; + let reduction_strategy = self.read_fri_reduction_strategy()?; + + Ok(FriConfig { + rate_bits, + cap_height, + num_query_rounds, + proof_of_work_bits, + reduction_strategy, + }) + } + + fn read_circuit_config(&mut self) -> IoResult { + let num_wires = self.read_usize()?; + let num_routed_wires = self.read_usize()?; + let num_constants = self.read_usize()?; + let security_bits = self.read_usize()?; + let num_challenges = self.read_usize()?; + let max_quotient_degree_factor = self.read_usize()?; + let use_base_arithmetic_gate = self.read_bool()?; + let zero_knowledge = self.read_bool()?; + let fri_config = self.read_fri_config()?; + + Ok(CircuitConfig { + num_wires, + num_routed_wires, + num_constants, + security_bits, + num_challenges, + max_quotient_degree_factor, + use_base_arithmetic_gate, + zero_knowledge, + fri_config, + }) + } + + fn read_fri_params(&mut self) -> IoResult { + let config = self.read_fri_config()?; + let reduction_arity_bits = self.read_usize_vec()?; + let degree_bits = self.read_usize()?; + let hiding = self.read_bool()?; + + Ok(FriParams { + config, + reduction_arity_bits, + degree_bits, + hiding, + }) + } + + fn read_gate, const D: usize>( + &mut self, + gate_serializer: &dyn GateSerializer, + ) -> IoResult>; + + fn read_generator, const D: usize>( + &mut self, + generator_serializer: &dyn WitnessGeneratorSerializer, + common_data: &CommonCircuitData, + ) -> IoResult>; + + fn read_selectors_info(&mut self) -> IoResult { + let selector_indices = self.read_usize_vec()?; + let groups_len = self.read_usize()?; + let mut groups = Vec::with_capacity(groups_len); + for _ in 0..groups_len { + let start = self.read_usize()?; + let end = self.read_usize()?; + groups.push(Range { start, end }); + } + + Ok(SelectorsInfo { + selector_indices, + groups, + }) + } + + fn read_polynomial_batch< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + >( + &mut self, + ) -> IoResult> { + let poly_len = self.read_usize()?; + let mut polynomials = Vec::with_capacity(poly_len); + for _ in 0..poly_len { + let plen = self.read_usize()?; + polynomials.push(PolynomialCoeffs::new(self.read_field_vec(plen)?)); + } + + let merkle_tree = self.read_merkle_tree()?; + let degree_log = self.read_usize()?; + let rate_bits = self.read_usize()?; + let blinding = self.read_bool()?; + + Ok(PolynomialBatch { + polynomials, + merkle_tree, + degree_log, + rate_bits, + blinding, + }) + } + + fn read_common_circuit_data, const D: usize>( + &mut self, + gate_serializer: &dyn GateSerializer, + ) -> IoResult> { + let config = self.read_circuit_config()?; + let fri_params = self.read_fri_params()?; + + let gates_len = self.read_usize()?; + let mut gates = Vec::with_capacity(gates_len); + for _ in 0..gates_len { + let gate = self.read_gate::(gate_serializer)?; + gates.push(gate); + } + + let selectors_info = self.read_selectors_info()?; + let quotient_degree_factor = self.read_usize()?; + let num_gate_constraints = self.read_usize()?; + let num_constants = self.read_usize()?; + let num_public_inputs = self.read_usize()?; + + let k_is_len = self.read_usize()?; + let k_is = self.read_field_vec(k_is_len)?; + + let num_partial_products = self.read_usize()?; + + Ok(CommonCircuitData { + config, + fri_params, + gates, + selectors_info, + quotient_degree_factor, + num_gate_constraints, + num_constants, + num_public_inputs, + k_is, + num_partial_products, + }) + } + + fn read_circuit_data< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + >( + &mut self, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult> { + let common = self.read_common_circuit_data(gate_serializer)?; + let prover_only = self.read_prover_only_circuit_data(generator_serializer, &common)?; + let verifier_only = self.read_verifier_only_circuit_data()?; + Ok(CircuitData { + prover_only, + verifier_only, + common, + }) + } + + fn read_prover_only_circuit_data< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + >( + &mut self, + generator_serializer: &dyn WitnessGeneratorSerializer, + cd: &CommonCircuitData, + ) -> IoResult> { + let gen_len = self.read_usize()?; + let mut generators = Vec::with_capacity(gen_len); + for _ in 0..gen_len { + generators.push(self.read_generator(generator_serializer, cd)?); + } + let map_len = self.read_usize()?; + let mut generator_indices_by_watches = BTreeMap::new(); + for _ in 0..map_len { + let k = self.read_usize()?; + generator_indices_by_watches.insert(k, self.read_usize_vec()?); + } + + let constants_sigmas_commitment = self.read_polynomial_batch()?; + let sigmas_len = self.read_usize()?; + let mut sigmas = Vec::with_capacity(sigmas_len); + for _ in 0..sigmas_len { + let sigma_len = self.read_usize()?; + sigmas.push(self.read_field_vec(sigma_len)?); + } + + let subgroup_len = self.read_usize()?; + let subgroup = self.read_field_vec(subgroup_len)?; + + let public_inputs = self.read_target_vec()?; + + let representative_map = self.read_usize_vec()?; + + let is_some = self.read_bool()?; + let fft_root_table = match is_some { + true => { + let table_len = self.read_usize()?; + let mut table = Vec::with_capacity(table_len); + for _ in 0..table_len { + let len = self.read_usize()?; + table.push(self.read_field_vec(len)?); + } + Some(table) + } + false => None, + }; + + let circuit_digest = + self.read_hash::>::HCO, >::Hasher>()?; + + Ok(ProverOnlyCircuitData { + generators, + generator_indices_by_watches, + constants_sigmas_commitment, + sigmas, + subgroup, + public_inputs, + representative_map, + fft_root_table, + circuit_digest, + }) + } + + fn read_prover_circuit_data< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + >( + &mut self, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult> { + let common = self.read_common_circuit_data(gate_serializer)?; + let prover_only = self.read_prover_only_circuit_data(generator_serializer, &common)?; + Ok(ProverCircuitData { + prover_only, + common, + }) + } + + fn read_verifier_only_circuit_data< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + >( + &mut self, + ) -> IoResult> { + let height = self.read_usize()?; + let constants_sigmas_cap = self.read_merkle_cap(height)?; + let circuit_digest = + self.read_hash::>::HCO, >::Hasher>()?; + Ok(VerifierOnlyCircuitData { + constants_sigmas_cap, + circuit_digest, + }) + } + + fn read_verifier_circuit_data< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + >( + &mut self, + gate_serializer: &dyn GateSerializer, + ) -> IoResult> { + let verifier_only = self.read_verifier_only_circuit_data()?; + let common = self.read_common_circuit_data(gate_serializer)?; + Ok(VerifierCircuitData { + verifier_only, + common, + }) + } + + fn read_target_verifier_circuit(&mut self) -> IoResult { + let constants_sigmas_cap = self.read_target_merkle_cap()?; + let circuit_digest = self.read_target_hash()?; + Ok(VerifierCircuitTarget { + constants_sigmas_cap, + circuit_digest, + }) + } + /// Reads a value of type [`Proof`] from `self` with `common_data`. #[inline] fn read_proof( @@ -333,6 +921,23 @@ pub trait Read { }) } + /// Reads a value of type [`ProofTarget`] from `self`. + #[inline] + fn read_target_proof(&mut self) -> IoResult> { + let wires_cap = self.read_target_merkle_cap()?; + let plonk_zs_partial_products_cap = self.read_target_merkle_cap()?; + let quotient_polys_cap = self.read_target_merkle_cap()?; + let openings = self.read_target_opening_set::()?; + let opening_proof = self.read_target_fri_proof::()?; + Ok(ProofTarget { + wires_cap, + plonk_zs_partial_products_cap, + quotient_polys_cap, + openings, + opening_proof, + }) + } + /// Reads a value of type [`ProofWithPublicInputs`] from `self` with `common_data`. #[inline] fn read_proof_with_public_inputs( @@ -345,13 +950,27 @@ pub trait Read { C: GenericConfig, { let proof = self.read_proof(common_data)?; - let public_inputs = self.read_field_vec(self.remaining() / size_of::())?; + let pi_len = self.read_usize()?; + let public_inputs = self.read_field_vec(pi_len)?; Ok(ProofWithPublicInputs { proof, public_inputs, }) } + /// Reads a value of type [`ProofWithPublicInputsTarget`] from `self`. + #[inline] + fn read_target_proof_with_public_inputs( + &mut self, + ) -> IoResult> { + let proof = self.read_target_proof()?; + let public_inputs = self.read_target_vec()?; + Ok(ProofWithPublicInputsTarget { + proof, + public_inputs, + }) + } + /// Reads a value of type [`CompressedFriQueryRounds`] from `self` with `common_data`. #[inline] fn read_compressed_fri_query_rounds( @@ -480,6 +1099,29 @@ pub trait Write { /// Writes all `bytes` to `self`. fn write_all(&mut self, bytes: &[u8]) -> IoResult<()>; + /// Writes a bool `x` to `self`. + #[inline] + fn write_bool(&mut self, x: bool) -> IoResult<()> { + self.write_u8(u8::from(x)) + } + + /// Writes a target bool `x` to `self`. + #[inline] + fn write_target_bool(&mut self, x: BoolTarget) -> IoResult<()> { + self.write_target(x.target) + } + + /// Writes a vector of BoolTarget `v` to `self.` + #[inline] + fn write_target_bool_vec(&mut self, v: &[BoolTarget]) -> IoResult<()> { + self.write_usize(v.len())?; + for &elem in v.iter() { + self.write_target_bool(elem)?; + } + + Ok(()) + } + /// Writes a byte `x` to `self`. #[inline] fn write_u8(&mut self, x: u8) -> IoResult<()> { @@ -492,6 +1134,23 @@ pub trait Write { self.write_all(&x.to_le_bytes()) } + /// Writes a word `x` to `self.` + #[inline] + fn write_usize(&mut self, x: usize) -> IoResult<()> { + self.write_all(&(x as u64).to_le_bytes()) + } + + /// Writes a vector of words `v` to `self.` + #[inline] + fn write_usize_vec(&mut self, v: &[usize]) -> IoResult<()> { + self.write_usize(v.len())?; + for &elem in v.iter() { + self.write_usize(elem)?; + } + + Ok(()) + } + /// Writes an element `x` from the field `F` to `self`. #[inline] fn write_field(&mut self, x: F) -> IoResult<()> @@ -537,6 +1196,56 @@ pub trait Write { Ok(()) } + /// Writes a Target `x` to `self.` + #[inline] + fn write_target(&mut self, x: Target) -> IoResult<()> { + match x { + Target::Wire(Wire { row, column }) => { + self.write_bool(true)?; + self.write_usize(row)?; + self.write_usize(column)?; + } + Target::VirtualTarget { index } => { + self.write_bool(false)?; + self.write_usize(index)?; + } + }; + + Ok(()) + } + + /// Writes an ExtensionTarget `x` to `self.` + #[inline] + fn write_target_ext(&mut self, x: ExtensionTarget) -> IoResult<()> { + for &elem in x.0.iter() { + self.write_target(elem)?; + } + + Ok(()) + } + + /// Writes a vector of Target `v` to `self.` + #[inline] + fn write_target_vec(&mut self, v: &[Target]) -> IoResult<()> { + self.write_usize(v.len())?; + for &elem in v.iter() { + self.write_target(elem)?; + } + + Ok(()) + } + + /// Writes a vector of ExtensionTarget `v` to `self.` + #[inline] + fn write_target_ext_vec(&mut self, v: &[ExtensionTarget]) -> IoResult<()> { + self.write_usize(v.len())?; + for &elem in v.iter() { + self.write_target_ext(elem)?; + } + + Ok(()) + } + /// Writes a hash `h` to `self`. #[inline] fn write_hash(&mut self, h: H::Hash) -> IoResult<()> @@ -548,6 +1257,32 @@ pub trait Write { self.write_all(&h.to_bytes()) } + /// Writes a HashOutTarget `h` to `self`. + #[inline] + fn write_target_hash(&mut self, h: &HashOutTarget) -> IoResult<()> { + for r in h.elements.iter() { + self.write_target(*r)?; + } + + Ok(()) + } + + /// Writes a vector of Hash `v` to `self.` + #[inline] + fn write_hash_vec(&mut self, v: &[H::Hash]) -> IoResult<()> + where + F: RichField, + HC: HashConfig, + H: Hasher, + { + self.write_usize(v.len())?; + for &elem in v.iter() { + self.write_hash::(elem)?; + } + + Ok(()) + } + /// Writes `cap`, a value of type [`MerkleCap`], to `self`. #[inline] fn write_merkle_cap(&mut self, cap: &MerkleCap) -> IoResult<()> @@ -562,6 +1297,36 @@ pub trait Write { Ok(()) } + /// Writes `cap`, a value of type [`MerkleCapTarget`], to `self`. + #[inline] + fn write_target_merkle_cap(&mut self, cap: &MerkleCapTarget) -> IoResult<()> { + self.write_usize(cap.0.len())?; + for a in &cap.0 { + self.write_target_hash(a)?; + } + Ok(()) + } + + /// Writes `tree`, a value of type [`MerkleTree`], to `self`. + #[inline] + fn write_merkle_tree(&mut self, tree: &MerkleTree) -> IoResult<()> + where + F: RichField, + HC: HashConfig, + H: Hasher, + { + self.write_usize(tree.leaves.len())?; + for i in 0..tree.leaves.len() { + self.write_usize(tree.leaves[i].len())?; + self.write_field_vec(&tree.leaves[i])?; + } + self.write_hash_vec::(&tree.digests)?; + self.write_usize(tree.cap.height())?; + self.write_merkle_cap(&tree.cap)?; + + Ok(()) + } + /// Writes a value `os` of type [`OpeningSet`] to `self.` #[inline] fn write_opening_set(&mut self, os: &OpeningSet) -> IoResult<()> @@ -577,6 +1342,21 @@ pub trait Write { self.write_field_ext_vec::(&os.quotient_polys) } + /// Writes a value `os` of type [`OpeningSet`] to `self.` + #[inline] + fn write_target_opening_set( + &mut self, + os: &OpeningSetTarget, + ) -> IoResult<()> { + self.write_target_ext_vec::(&os.constants)?; + self.write_target_ext_vec::(&os.plonk_sigmas)?; + self.write_target_ext_vec::(&os.wires)?; + self.write_target_ext_vec::(&os.plonk_zs)?; + self.write_target_ext_vec::(&os.plonk_zs_next)?; + self.write_target_ext_vec::(&os.partial_products)?; + self.write_target_ext_vec::(&os.quotient_polys) + } + /// Writes a value `p` of type [`MerkleProof`] to `self.` #[inline] fn write_merkle_proof(&mut self, p: &MerkleProof) -> IoResult<()> @@ -597,6 +1377,21 @@ pub trait Write { Ok(()) } + /// Writes a value `pt` of type [`MerkleProofTarget`] to `self.` + #[inline] + fn write_target_merkle_proof(&mut self, pt: &MerkleProofTarget) -> IoResult<()> { + let length = pt.siblings.len(); + self.write_u8( + length + .try_into() + .expect("Merkle proof length must fit in u8."), + )?; + for h in &pt.siblings { + self.write_target_hash(h)?; + } + Ok(()) + } + /// Writes a value `fitp` of type [`FriInitialTreeProof`] to `self.` #[inline] fn write_fri_initial_proof( @@ -614,6 +1409,20 @@ pub trait Write { Ok(()) } + /// Writes a value `fitpt` of type [`FriInitialTreeProofTarget`] to `self.` + #[inline] + fn write_target_fri_initial_proof( + &mut self, + fitpt: &FriInitialTreeProofTarget, + ) -> IoResult<()> { + self.write_usize(fitpt.evals_proofs.len())?; + for (v, p) in &fitpt.evals_proofs { + self.write_target_vec(v)?; + self.write_target_merkle_proof(p)?; + } + Ok(()) + } + /// Writes a value `fqs` of type [`FriQueryStep`] to `self.` #[inline] fn write_fri_query_step( @@ -628,6 +1437,16 @@ pub trait Write { self.write_merkle_proof(&fqs.merkle_proof) } + /// Writes a value `fqst` of type [`FriQueryStepTarget`] to `self.` + #[inline] + fn write_target_fri_query_step( + &mut self, + fqst: &FriQueryStepTarget, + ) -> IoResult<()> { + self.write_target_ext_vec(&fqst.evals)?; + self.write_target_merkle_proof(&fqst.merkle_proof) + } + /// Writes a value `fqrs` of type [`FriQueryRound`] to `self.` #[inline] fn write_fri_query_rounds( @@ -647,7 +1466,24 @@ pub trait Write { Ok(()) } - /// Writes a value `fq` of type [`FriProof`] to `self.` + /// Writes a value `fqrst` of type [`FriQueryRoundTarget`] to `self.` + #[inline] + fn write_target_fri_query_rounds( + &mut self, + fqrst: &[FriQueryRoundTarget], + ) -> IoResult<()> { + self.write_usize(fqrst.len())?; + for fqr in fqrst { + self.write_target_fri_initial_proof(&fqr.initial_trees_proof)?; + self.write_usize(fqr.steps.len())?; + for fqs in &fqr.steps { + self.write_target_fri_query_step::(fqs)?; + } + } + Ok(()) + } + + /// Writes a value `fp` of type [`FriProof`] to `self.` #[inline] fn write_fri_proof( &mut self, @@ -665,6 +1501,339 @@ pub trait Write { self.write_field(fp.pow_witness) } + /// Writes a value `fpt` of type [`FriProofTarget`] to `self.` + #[inline] + fn write_target_fri_proof(&mut self, fpt: &FriProofTarget) -> IoResult<()> { + self.write_usize(fpt.commit_phase_merkle_caps.len())?; + for cap in &fpt.commit_phase_merkle_caps { + self.write_target_merkle_cap(cap)?; + } + self.write_target_fri_query_rounds::(&fpt.query_round_proofs)?; + self.write_target_ext_vec::(&fpt.final_poly.0)?; + self.write_target(fpt.pow_witness) + } + + fn write_fri_reduction_strategy( + &mut self, + reduction_strategy: &FriReductionStrategy, + ) -> IoResult<()> { + match reduction_strategy { + FriReductionStrategy::Fixed(seq) => { + self.write_u8(0)?; + self.write_usize_vec(seq.as_slice())?; + + Ok(()) + } + FriReductionStrategy::ConstantArityBits(arity_bits, final_poly_bits) => { + self.write_u8(1)?; + self.write_usize(*arity_bits)?; + self.write_usize(*final_poly_bits)?; + + Ok(()) + } + FriReductionStrategy::MinSize(max) => { + self.write_u8(2)?; + if let Some(max) = max { + self.write_u8(1)?; + self.write_usize(*max)?; + } else { + self.write_u8(0)?; + } + + Ok(()) + } + } + } + + fn write_fri_config(&mut self, config: &FriConfig) -> IoResult<()> { + let FriConfig { + rate_bits, + cap_height, + num_query_rounds, + proof_of_work_bits, + reduction_strategy, + } = &config; + + self.write_usize(*rate_bits)?; + self.write_usize(*cap_height)?; + self.write_usize(*num_query_rounds)?; + self.write_u32(*proof_of_work_bits)?; + self.write_fri_reduction_strategy(reduction_strategy)?; + + Ok(()) + } + + fn write_fri_params(&mut self, fri_params: &FriParams) -> IoResult<()> { + let FriParams { + config, + reduction_arity_bits, + degree_bits, + hiding, + } = fri_params; + + self.write_fri_config(config)?; + self.write_usize_vec(reduction_arity_bits.as_slice())?; + self.write_usize(*degree_bits)?; + self.write_bool(*hiding)?; + + Ok(()) + } + + fn write_circuit_config(&mut self, config: &CircuitConfig) -> IoResult<()> { + let CircuitConfig { + num_wires, + num_routed_wires, + num_constants, + security_bits, + num_challenges, + max_quotient_degree_factor, + use_base_arithmetic_gate, + zero_knowledge, + fri_config, + } = config; + + self.write_usize(*num_wires)?; + self.write_usize(*num_routed_wires)?; + self.write_usize(*num_constants)?; + self.write_usize(*security_bits)?; + self.write_usize(*num_challenges)?; + self.write_usize(*max_quotient_degree_factor)?; + self.write_bool(*use_base_arithmetic_gate)?; + self.write_bool(*zero_knowledge)?; + self.write_fri_config(fri_config)?; + + Ok(()) + } + + fn write_gate, const D: usize>( + &mut self, + gate: &GateRef, + gate_serializer: &dyn GateSerializer, + ) -> IoResult<()>; + + fn write_generator, const D: usize>( + &mut self, + generator: &WitnessGeneratorRef, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult<()>; + + fn write_selectors_info(&mut self, selectors_info: &SelectorsInfo) -> IoResult<()> { + let SelectorsInfo { + selector_indices, + groups, + } = selectors_info; + + self.write_usize_vec(selector_indices.as_slice())?; + self.write_usize(groups.len())?; + for group in groups.iter() { + self.write_usize(group.start)?; + self.write_usize(group.end)?; + } + Ok(()) + } + + fn write_polynomial_batch< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + >( + &mut self, + poly_batch: &PolynomialBatch, + ) -> IoResult<()> { + self.write_usize(poly_batch.polynomials.len())?; + for i in 0..poly_batch.polynomials.len() { + self.write_usize(poly_batch.polynomials[i].coeffs.len())?; + self.write_field_vec(&poly_batch.polynomials[i].coeffs)?; + } + self.write_merkle_tree(&poly_batch.merkle_tree)?; + self.write_usize(poly_batch.degree_log)?; + self.write_usize(poly_batch.rate_bits)?; + self.write_bool(poly_batch.blinding)?; + + Ok(()) + } + + fn write_common_circuit_data, const D: usize>( + &mut self, + common_data: &CommonCircuitData, + gate_serializer: &dyn GateSerializer, + ) -> IoResult<()> { + let CommonCircuitData { + config, + fri_params, + gates, + selectors_info, + quotient_degree_factor, + num_gate_constraints, + num_constants, + num_public_inputs, + k_is, + num_partial_products, + } = common_data; + + self.write_circuit_config(config)?; + self.write_fri_params(fri_params)?; + + self.write_usize(gates.len())?; + for gate in gates.iter() { + self.write_gate::(gate, gate_serializer)?; + } + + self.write_selectors_info(selectors_info)?; + self.write_usize(*quotient_degree_factor)?; + self.write_usize(*num_gate_constraints)?; + self.write_usize(*num_constants)?; + self.write_usize(*num_public_inputs)?; + + self.write_usize(k_is.len())?; + self.write_field_vec(k_is.as_slice())?; + + self.write_usize(*num_partial_products)?; + + Ok(()) + } + + fn write_circuit_data< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + >( + &mut self, + circuit_data: &CircuitData, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult<()> { + self.write_common_circuit_data(&circuit_data.common, gate_serializer)?; + self.write_prover_only_circuit_data(&circuit_data.prover_only, generator_serializer)?; + self.write_verifier_only_circuit_data(&circuit_data.verifier_only) + } + + fn write_prover_only_circuit_data< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + >( + &mut self, + prover_only_circuit_data: &ProverOnlyCircuitData, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult<()> { + let ProverOnlyCircuitData { + generators, + generator_indices_by_watches, + constants_sigmas_commitment, + sigmas, + subgroup, + public_inputs, + representative_map, + fft_root_table, + circuit_digest, + } = prover_only_circuit_data; + + self.write_usize(generators.len())?; + for generator in generators.iter() { + self.write_generator::(generator, generator_serializer)?; + } + + self.write_usize(generator_indices_by_watches.len())?; + for (k, v) in generator_indices_by_watches { + self.write_usize(*k)?; + self.write_usize_vec(v)?; + } + + self.write_polynomial_batch(constants_sigmas_commitment)?; + self.write_usize(sigmas.len())?; + for i in 0..sigmas.len() { + self.write_usize(sigmas[i].len())?; + self.write_field_vec(&sigmas[i])?; + } + self.write_usize(subgroup.len())?; + self.write_field_vec(subgroup)?; + self.write_target_vec(public_inputs)?; + self.write_usize_vec(representative_map)?; + + match fft_root_table { + Some(table) => { + self.write_bool(true)?; + self.write_usize(table.len())?; + for i in 0..table.len() { + self.write_usize(table[i].len())?; + self.write_field_vec(&table[i])?; + } + } + None => self.write_bool(false)?, + } + + self.write_hash::>::HCO, >::Hasher>( + *circuit_digest, + )?; + + Ok(()) + } + + fn write_prover_circuit_data< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + >( + &mut self, + prover_circuit_data: &ProverCircuitData, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult<()> { + self.write_common_circuit_data(&prover_circuit_data.common, gate_serializer)?; + self.write_prover_only_circuit_data(&prover_circuit_data.prover_only, generator_serializer) + } + + fn write_verifier_only_circuit_data< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + >( + &mut self, + verifier_only_circuit_data: &VerifierOnlyCircuitData, + ) -> IoResult<()> { + let VerifierOnlyCircuitData { + constants_sigmas_cap, + circuit_digest, + } = verifier_only_circuit_data; + + self.write_usize(constants_sigmas_cap.height())?; + self.write_merkle_cap(constants_sigmas_cap)?; + self.write_hash::>::HCO, >::Hasher>( + *circuit_digest, + )?; + + Ok(()) + } + + fn write_verifier_circuit_data< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + >( + &mut self, + verifier_circuit_data: &VerifierCircuitData, + gate_serializer: &dyn GateSerializer, + ) -> IoResult<()> { + self.write_verifier_only_circuit_data(&verifier_circuit_data.verifier_only)?; + self.write_common_circuit_data(&verifier_circuit_data.common, gate_serializer) + } + + fn write_target_verifier_circuit( + &mut self, + verifier_circuit: &VerifierCircuitTarget, + ) -> IoResult<()> { + let VerifierCircuitTarget { + constants_sigmas_cap, + circuit_digest, + } = verifier_circuit; + + self.write_target_merkle_cap(constants_sigmas_cap)?; + self.write_target_hash(circuit_digest)?; + + Ok(()) + } + /// Writes a value `proof` of type [`Proof`] to `self.` #[inline] fn write_proof(&mut self, proof: &Proof) -> IoResult<()> @@ -679,6 +1848,16 @@ pub trait Write { self.write_fri_proof::(&proof.opening_proof) } + /// Writes a value `proof` of type [`Proof`] to `self.` + #[inline] + fn write_target_proof(&mut self, proof: &ProofTarget) -> IoResult<()> { + self.write_target_merkle_cap(&proof.wires_cap)?; + self.write_target_merkle_cap(&proof.plonk_zs_partial_products_cap)?; + self.write_target_merkle_cap(&proof.quotient_polys_cap)?; + self.write_target_opening_set(&proof.openings)?; + self.write_target_fri_proof::(&proof.opening_proof) + } + /// Writes a value `proof_with_pis` of type [`ProofWithPublicInputs`] to `self.` #[inline] fn write_proof_with_public_inputs( @@ -694,9 +1873,24 @@ pub trait Write { public_inputs, } = proof_with_pis; self.write_proof(proof)?; + self.write_usize(public_inputs.len())?; self.write_field_vec(public_inputs) } + /// Writes a value `proof_with_pis` of type [`ProofWithPublicInputsTarget`] to `self.` + #[inline] + fn write_target_proof_with_public_inputs( + &mut self, + proof_with_pis: &ProofWithPublicInputsTarget, + ) -> IoResult<()> { + let ProofWithPublicInputsTarget { + proof, + public_inputs, + } = proof_with_pis; + self.write_target_proof(proof)?; + self.write_target_vec(public_inputs) + } + /// Writes a value `cfqrs` of type [`CompressedFriQueryRounds`] to `self.` #[inline] fn write_compressed_fri_query_rounds( @@ -787,6 +1981,22 @@ impl Write for Vec { self.extend_from_slice(bytes); Ok(()) } + + fn write_gate, const D: usize>( + &mut self, + gate: &GateRef, + gate_serializer: &dyn GateSerializer, + ) -> IoResult<()> { + gate_serializer.write_gate(self, gate) + } + + fn write_generator, const D: usize>( + &mut self, + generator: &WitnessGeneratorRef, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult<()> { + generator_serializer.write_generator(self, generator) + } } /// Buffer @@ -805,10 +2015,22 @@ impl Buffer { Self { bytes, pos: 0 } } + /// Returns the inner position. + #[inline] + pub fn pos(&self) -> usize { + self.pos + } + /// Returns the inner buffer. #[inline] - pub fn bytes(self) -> Vec { - self.bytes + pub fn bytes(&self) -> Vec { + self.bytes.clone() + } + + /// Returns the inner unread buffer. + #[inline] + pub fn unread_bytes(&self) -> Vec { + self.bytes[self.pos..].to_vec() } } @@ -832,4 +2054,19 @@ impl Read for Buffer { Ok(()) } } + + fn read_gate, const D: usize>( + &mut self, + gate_serializer: &dyn GateSerializer, + ) -> IoResult> { + gate_serializer.read_gate(self) + } + + fn read_generator, const D: usize>( + &mut self, + generator_serializer: &dyn WitnessGeneratorSerializer, + common_data: &CommonCircuitData, + ) -> IoResult> { + generator_serializer.read_generator(self, common_data) + } } From 0e465c1ccf6f1415e89d7d827da8fca4dbc32b52 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Sun, 2 Apr 2023 11:35:54 -0400 Subject: [PATCH 3/7] Customize range specification for AllRecursiveCircuits --- evm/src/fixed_recursive_verifier.rs | 12 ++++++------ evm/tests/empty_txn_list.rs | 6 +++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 8b8cf284..d85f42c4 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -339,41 +339,41 @@ where /// Preprocess all recursive circuits used by the system. pub fn new( all_stark: &AllStark, - degree_bits_range: Range, + degree_bits_ranges: &[Range; NUM_TABLES], stark_config: &StarkConfig, ) -> Self { let cpu = RecursiveCircuitsForTable::new( Table::Cpu, &all_stark.cpu_stark, - degree_bits_range.clone(), + degree_bits_ranges[0].clone(), &all_stark.cross_table_lookups, stark_config, ); let keccak = RecursiveCircuitsForTable::new( Table::Keccak, &all_stark.keccak_stark, - degree_bits_range.clone(), + degree_bits_ranges[1].clone(), &all_stark.cross_table_lookups, stark_config, ); let keccak_sponge = RecursiveCircuitsForTable::new( Table::KeccakSponge, &all_stark.keccak_sponge_stark, - degree_bits_range.clone(), + degree_bits_ranges[2].clone(), &all_stark.cross_table_lookups, stark_config, ); let logic = RecursiveCircuitsForTable::new( Table::Logic, &all_stark.logic_stark, - degree_bits_range.clone(), + degree_bits_ranges[3].clone(), &all_stark.cross_table_lookups, stark_config, ); let memory = RecursiveCircuitsForTable::new( Table::Memory, &all_stark.memory_stark, - degree_bits_range, + degree_bits_ranges[4].clone(), &all_stark.cross_table_lookups, stark_config, ); diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index fae2f656..66367ac6 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -96,7 +96,11 @@ fn test_empty_txn_list() -> anyhow::Result<()> { verify_proof(&all_stark, proof, &config)?; - let all_circuits = AllRecursiveCircuits::::new(&all_stark, 9..18, &config); + let all_circuits = AllRecursiveCircuits::::new( + &all_stark, + &[9..15, 9..15, 9..10, 9..12, 9..18], // Minimal ranges to prove an empty list + &config, + ); { let gate_serializer = DefaultGateSerializer; From 5de5bfb5e496b5f9c29608ca036d14691fa5a69f Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 4 Apr 2023 15:23:19 -0400 Subject: [PATCH 4/7] Move serialization files into dedicated module --- evm/src/fixed_recursive_verifier.rs | 6 +++--- evm/src/recursive_verifier.rs | 6 +++--- evm/tests/empty_txn_list.rs | 3 +-- plonky2/examples/bench_recursion.rs | 2 +- plonky2/src/plonk/circuit_data.rs | 6 +++--- plonky2/src/util/mod.rs | 5 ----- .../util/{ => serialization}/gate_serialization.rs | 6 +++++- .../{ => serialization}/generator_serialization.rs | 4 ++-- .../util/{serialization.rs => serialization/mod.rs} | 12 ++++++++++-- 9 files changed, 28 insertions(+), 22 deletions(-) rename plonky2/src/util/{ => serialization}/gate_serialization.rs (92%) rename plonky2/src/util/{ => serialization}/generator_serialization.rs (98%) rename plonky2/src/util/{serialization.rs => serialization/mod.rs} (99%) diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index d85f42c4..2e6f01aa 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -20,9 +20,9 @@ use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use plonky2::recursion::cyclic_recursion::check_cyclic_proof_verifier_data; use plonky2::recursion::dummy_circuit::cyclic_base_proof; -use plonky2::util::gate_serialization::GateSerializer; -use plonky2::util::generator_serialization::WitnessGeneratorSerializer; -use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; +use plonky2::util::serialization::{ + Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write, +}; use plonky2::util::timing::TimingTree; use plonky2_util::log2_ceil; diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 5f4c5538..9da9281a 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -18,10 +18,10 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, VerifierCircuitData}; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; -use plonky2::util::gate_serialization::GateSerializer; -use plonky2::util::generator_serialization::WitnessGeneratorSerializer; use plonky2::util::reducing::ReducingFactorTarget; -use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; +use plonky2::util::serialization::{ + Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write, +}; use plonky2::with_context; use plonky2_util::log2_ceil; diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 66367ac6..ec2d999b 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -10,8 +10,7 @@ use keccak_hash::keccak; use log::info; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::PoseidonGoldilocksConfig; -use plonky2::util::gate_serialization::default::DefaultGateSerializer; -use plonky2::util::generator_serialization::default::DefaultGeneratorSerializer; +use plonky2::util::serialization::{DefaultGateSerializer, DefaultGeneratorSerializer}; use plonky2::util::timing::TimingTree; use plonky2_evm::all_stark::AllStark; use plonky2_evm::config::StarkConfig; diff --git a/plonky2/examples/bench_recursion.rs b/plonky2/examples/bench_recursion.rs index c5635a1d..a6fe9c37 100644 --- a/plonky2/examples/bench_recursion.rs +++ b/plonky2/examples/bench_recursion.rs @@ -22,7 +22,7 @@ use plonky2::plonk::circuit_data::{CircuitConfig, CommonCircuitData, VerifierOnl use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig}; use plonky2::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs}; use plonky2::plonk::prover::prove; -use plonky2::util::gate_serialization::default::DefaultGateSerializer; +use plonky2::util::serialization::DefaultGateSerializer; use plonky2::util::timing::TimingTree; use plonky2_field::extension::Extendable; use plonky2_maybe_rayon::rayon; diff --git a/plonky2/src/plonk/circuit_data.rs b/plonky2/src/plonk/circuit_data.rs index 7bbf8971..fa9af818 100644 --- a/plonky2/src/plonk/circuit_data.rs +++ b/plonky2/src/plonk/circuit_data.rs @@ -30,9 +30,9 @@ use crate::plonk::plonk_common::PlonkOracle; use crate::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs}; use crate::plonk::prover::prove; use crate::plonk::verifier::verify; -use crate::util::gate_serialization::GateSerializer; -use crate::util::generator_serialization::WitnessGeneratorSerializer; -use crate::util::serialization::{Buffer, IoResult, Read, Write}; +use crate::util::serialization::{ + Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write, +}; use crate::util::timing::TimingTree; #[derive(Clone, Debug, Eq, PartialEq)] diff --git a/plonky2/src/util/mod.rs b/plonky2/src/util/mod.rs index be4fab4d..dfc32fd8 100644 --- a/plonky2/src/util/mod.rs +++ b/plonky2/src/util/mod.rs @@ -9,11 +9,6 @@ use crate::field::types::Field; pub(crate) mod context_tree; pub(crate) mod partial_products; - -#[macro_use] -pub mod gate_serialization; -#[macro_use] -pub mod generator_serialization; pub mod reducing; pub mod serialization; pub mod strided_view; diff --git a/plonky2/src/util/gate_serialization.rs b/plonky2/src/util/serialization/gate_serialization.rs similarity index 92% rename from plonky2/src/util/gate_serialization.rs rename to plonky2/src/util/serialization/gate_serialization.rs index 3df74049..351fa76b 100644 --- a/plonky2/src/util/gate_serialization.rs +++ b/plonky2/src/util/serialization/gate_serialization.rs @@ -41,6 +41,10 @@ macro_rules! get_gate_tag_impl { } #[macro_export] +/// Macro implementing the `GateSerializer` trait. +/// To serialize a list of gates used for a circuit, +/// this macro should be called with a struct on which to implement +/// this as first argument, followed by all the targeted gates. macro_rules! impl_gate_serializer { ($target:ty, $($gate_types:ty),+) => { fn read_gate(&self, buf: &mut $crate::util::serialization::Buffer) -> $crate::util::serialization::IoResult<$crate::gates::gate::GateRef> { @@ -76,7 +80,7 @@ pub mod default { use crate::gates::reducing::ReducingGate; use crate::gates::reducing_extension::ReducingExtensionGate; use crate::hash::hash_types::RichField; - use crate::util::gate_serialization::GateSerializer; + use crate::util::serialization::GateSerializer; pub struct DefaultGateSerializer; impl, const D: usize> GateSerializer for DefaultGateSerializer { diff --git a/plonky2/src/util/generator_serialization.rs b/plonky2/src/util/serialization/generator_serialization.rs similarity index 98% rename from plonky2/src/util/generator_serialization.rs rename to plonky2/src/util/serialization/generator_serialization.rs index 4020e993..6566a897 100644 --- a/plonky2/src/util/generator_serialization.rs +++ b/plonky2/src/util/serialization/generator_serialization.rs @@ -115,7 +115,7 @@ pub mod default { }; use crate::plonk::config::{AlgebraicHasher, GenericConfig}; use crate::recursion::dummy_circuit::DummyProofGenerator; - use crate::util::generator_serialization::WitnessGeneratorSerializer; + use crate::util::serialization::WitnessGeneratorSerializer; pub struct DefaultGeneratorSerializer, const D: usize> { pub _phantom: PhantomData, @@ -128,7 +128,7 @@ pub mod default { C::Hasher: AlgebraicHasher, { impl_generator_serializer! { - DefaultGateSerializer, + DefaultGeneratorSerializer, DummyProofGenerator, ArithmeticBaseGenerator, ArithmeticExtensionGenerator, diff --git a/plonky2/src/util/serialization.rs b/plonky2/src/util/serialization/mod.rs similarity index 99% rename from plonky2/src/util/serialization.rs rename to plonky2/src/util/serialization/mod.rs index 488cbd61..5a92afb5 100644 --- a/plonky2/src/util/serialization.rs +++ b/plonky2/src/util/serialization/mod.rs @@ -1,3 +1,9 @@ +#[macro_use] +pub mod generator_serialization; + +#[macro_use] +pub mod gate_serialization; + use alloc::collections::BTreeMap; use alloc::vec; use alloc::vec::Vec; @@ -6,10 +12,12 @@ use core::fmt::{Debug, Display, Formatter}; use core::mem::size_of; use core::ops::Range; +pub use gate_serialization::default::DefaultGateSerializer; +pub use gate_serialization::GateSerializer; +pub use generator_serialization::default::DefaultGeneratorSerializer; +pub use generator_serialization::WitnessGeneratorSerializer; use hashbrown::HashMap; -use super::gate_serialization::GateSerializer; -use super::generator_serialization::WitnessGeneratorSerializer; use crate::field::extension::{Extendable, FieldExtension}; use crate::field::polynomial::PolynomialCoeffs; use crate::field::types::{Field64, PrimeField64}; From bf02a3e87dd9cfbceaaa429c1c7f4200787ba90a Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Wed, 12 Apr 2023 12:34:47 -0400 Subject: [PATCH 5/7] Make generators public --- plonky2/src/gadgets/arithmetic.rs | 2 +- plonky2/src/gadgets/arithmetic_extension.rs | 2 +- plonky2/src/gadgets/mod.rs | 2 +- plonky2/src/gadgets/range_check.rs | 2 +- plonky2/src/gadgets/split_base.rs | 2 +- plonky2/src/gadgets/split_join.rs | 4 ++-- plonky2/src/gates/arithmetic_base.rs | 2 +- plonky2/src/gates/arithmetic_extension.rs | 2 +- plonky2/src/gates/coset_interpolation.rs | 2 +- plonky2/src/gates/exponentiation.rs | 2 +- plonky2/src/gates/multiplication_extension.rs | 2 +- plonky2/src/gates/poseidon.rs | 2 +- plonky2/src/gates/poseidon_mds.rs | 2 +- plonky2/src/gates/random_access.rs | 2 +- plonky2/src/gates/reducing.rs | 2 +- plonky2/src/gates/reducing_extension.rs | 2 +- plonky2/src/iop/generator.rs | 10 +++++----- plonky2/src/recursion/dummy_circuit.rs | 2 +- .../src/util/serialization/generator_serialization.rs | 8 ++++++++ 19 files changed, 31 insertions(+), 23 deletions(-) diff --git a/plonky2/src/gadgets/arithmetic.rs b/plonky2/src/gadgets/arithmetic.rs index 9a89e5a3..ed9e7c4a 100644 --- a/plonky2/src/gadgets/arithmetic.rs +++ b/plonky2/src/gadgets/arithmetic.rs @@ -372,7 +372,7 @@ impl, const D: usize> CircuitBuilder { } #[derive(Debug, Default)] -pub(crate) struct EqualityGenerator { +pub struct EqualityGenerator { x: Target, y: Target, equal: BoolTarget, diff --git a/plonky2/src/gadgets/arithmetic_extension.rs b/plonky2/src/gadgets/arithmetic_extension.rs index 69a5668c..ccb84a40 100644 --- a/plonky2/src/gadgets/arithmetic_extension.rs +++ b/plonky2/src/gadgets/arithmetic_extension.rs @@ -495,7 +495,7 @@ impl, const D: usize> CircuitBuilder { } #[derive(Debug, Default)] -pub(crate) struct QuotientGeneratorExtension { +pub struct QuotientGeneratorExtension { numerator: ExtensionTarget, denominator: ExtensionTarget, quotient: ExtensionTarget, diff --git a/plonky2/src/gadgets/mod.rs b/plonky2/src/gadgets/mod.rs index 6309eb3d..bac94475 100644 --- a/plonky2/src/gadgets/mod.rs +++ b/plonky2/src/gadgets/mod.rs @@ -7,4 +7,4 @@ pub mod random_access; pub mod range_check; pub mod select; pub mod split_base; -pub(crate) mod split_join; +pub mod split_join; diff --git a/plonky2/src/gadgets/range_check.rs b/plonky2/src/gadgets/range_check.rs index 144ae5dc..bdd1942c 100644 --- a/plonky2/src/gadgets/range_check.rs +++ b/plonky2/src/gadgets/range_check.rs @@ -53,7 +53,7 @@ impl, const D: usize> CircuitBuilder { } #[derive(Debug, Default)] -pub(crate) struct LowHighGenerator { +pub struct LowHighGenerator { integer: Target, n_log: usize, low: Target, diff --git a/plonky2/src/gadgets/split_base.rs b/plonky2/src/gadgets/split_base.rs index ace3284d..c2919315 100644 --- a/plonky2/src/gadgets/split_base.rs +++ b/plonky2/src/gadgets/split_base.rs @@ -81,7 +81,7 @@ impl, const D: usize> CircuitBuilder { } #[derive(Debug, Default)] -pub(crate) struct BaseSumGenerator { +pub struct BaseSumGenerator { row: usize, limbs: Vec, } diff --git a/plonky2/src/gadgets/split_join.rs b/plonky2/src/gadgets/split_join.rs index 4e39a2be..e3eda241 100644 --- a/plonky2/src/gadgets/split_join.rs +++ b/plonky2/src/gadgets/split_join.rs @@ -57,7 +57,7 @@ impl, const D: usize> CircuitBuilder { } #[derive(Debug, Default)] -pub(crate) struct SplitGenerator { +pub struct SplitGenerator { integer: Target, bits: Vec, } @@ -99,7 +99,7 @@ impl SimpleGenerator for SplitGenerator { } #[derive(Debug, Default)] -pub(crate) struct WireSplitGenerator { +pub struct WireSplitGenerator { integer: Target, gates: Vec, num_limbs: usize, diff --git a/plonky2/src/gates/arithmetic_base.rs b/plonky2/src/gates/arithmetic_base.rs index aa9f2d86..7ed25328 100644 --- a/plonky2/src/gates/arithmetic_base.rs +++ b/plonky2/src/gates/arithmetic_base.rs @@ -181,7 +181,7 @@ impl, const D: usize> PackedEvaluableBase for } #[derive(Clone, Debug, Default)] -pub(crate) struct ArithmeticBaseGenerator, const D: usize> { +pub struct ArithmeticBaseGenerator, const D: usize> { row: usize, const_0: F, const_1: F, diff --git a/plonky2/src/gates/arithmetic_extension.rs b/plonky2/src/gates/arithmetic_extension.rs index 36274dfd..ebe7a650 100644 --- a/plonky2/src/gates/arithmetic_extension.rs +++ b/plonky2/src/gates/arithmetic_extension.rs @@ -165,7 +165,7 @@ impl, const D: usize> Gate for ArithmeticExte } #[derive(Clone, Debug, Default)] -pub(crate) struct ArithmeticExtensionGenerator, const D: usize> { +pub struct ArithmeticExtensionGenerator, const D: usize> { row: usize, const_0: F, const_1: F, diff --git a/plonky2/src/gates/coset_interpolation.rs b/plonky2/src/gates/coset_interpolation.rs index 6ec0eae5..ff6d9918 100644 --- a/plonky2/src/gates/coset_interpolation.rs +++ b/plonky2/src/gates/coset_interpolation.rs @@ -387,7 +387,7 @@ impl, const D: usize> Gate for CosetInterpola } #[derive(Debug, Default)] -pub(crate) struct InterpolationGenerator, const D: usize> { +pub struct InterpolationGenerator, const D: usize> { row: usize, gate: CosetInterpolationGate, interpolation_domain: Vec, diff --git a/plonky2/src/gates/exponentiation.rs b/plonky2/src/gates/exponentiation.rs index 64c685dc..eefc7ba7 100644 --- a/plonky2/src/gates/exponentiation.rs +++ b/plonky2/src/gates/exponentiation.rs @@ -238,7 +238,7 @@ impl, const D: usize> PackedEvaluableBase } #[derive(Debug, Default)] -pub(crate) struct ExponentiationGenerator, const D: usize> { +pub struct ExponentiationGenerator, const D: usize> { row: usize, gate: ExponentiationGate, } diff --git a/plonky2/src/gates/multiplication_extension.rs b/plonky2/src/gates/multiplication_extension.rs index c82c30a1..7e1c1924 100644 --- a/plonky2/src/gates/multiplication_extension.rs +++ b/plonky2/src/gates/multiplication_extension.rs @@ -152,7 +152,7 @@ impl, const D: usize> Gate for MulExtensionGa } #[derive(Clone, Debug, Default)] -pub(crate) struct MulExtensionGenerator, const D: usize> { +pub struct MulExtensionGenerator, const D: usize> { row: usize, const_0: F, i: usize, diff --git a/plonky2/src/gates/poseidon.rs b/plonky2/src/gates/poseidon.rs index af82a608..caab7faf 100644 --- a/plonky2/src/gates/poseidon.rs +++ b/plonky2/src/gates/poseidon.rs @@ -410,7 +410,7 @@ impl, const D: usize> Gate for PoseidonGate + Poseidon, const D: usize> { +pub struct PoseidonGenerator + Poseidon, const D: usize> { row: usize, _phantom: PhantomData, } diff --git a/plonky2/src/gates/poseidon_mds.rs b/plonky2/src/gates/poseidon_mds.rs index 409d0325..a3b4c927 100644 --- a/plonky2/src/gates/poseidon_mds.rs +++ b/plonky2/src/gates/poseidon_mds.rs @@ -210,7 +210,7 @@ impl + Poseidon, const D: usize> Gate for Pos } #[derive(Clone, Debug, Default)] -pub(crate) struct PoseidonMdsGenerator { +pub struct PoseidonMdsGenerator { row: usize, } diff --git a/plonky2/src/gates/random_access.rs b/plonky2/src/gates/random_access.rs index 6d520786..7f651bab 100644 --- a/plonky2/src/gates/random_access.rs +++ b/plonky2/src/gates/random_access.rs @@ -339,7 +339,7 @@ impl, const D: usize> PackedEvaluableBase } #[derive(Debug, Default)] -pub(crate) struct RandomAccessGenerator, const D: usize> { +pub struct RandomAccessGenerator, const D: usize> { row: usize, gate: RandomAccessGate, copy: usize, diff --git a/plonky2/src/gates/reducing.rs b/plonky2/src/gates/reducing.rs index 43549210..b9bd025a 100644 --- a/plonky2/src/gates/reducing.rs +++ b/plonky2/src/gates/reducing.rs @@ -178,7 +178,7 @@ impl, const D: usize> Gate for ReducingGate { +pub struct ReducingGenerator { row: usize, gate: ReducingGate, } diff --git a/plonky2/src/gates/reducing_extension.rs b/plonky2/src/gates/reducing_extension.rs index ce6a9725..11541ec2 100644 --- a/plonky2/src/gates/reducing_extension.rs +++ b/plonky2/src/gates/reducing_extension.rs @@ -178,7 +178,7 @@ impl, const D: usize> Gate for ReducingExtens } #[derive(Debug, Default)] -pub(crate) struct ReducingGenerator { +pub struct ReducingGenerator { row: usize, gate: ReducingExtensionGate, } diff --git a/plonky2/src/iop/generator.rs b/plonky2/src/iop/generator.rs index dbe7f475..165b5ada 100644 --- a/plonky2/src/iop/generator.rs +++ b/plonky2/src/iop/generator.rs @@ -129,7 +129,7 @@ impl, F: Field> AnyWitnessGenerator for T { } /// A wrapper around an `Box`. -pub struct WitnessGeneratorRef(pub(crate) Box>); +pub struct WitnessGeneratorRef(pub Box>); impl WitnessGeneratorRef { pub fn new>(generator: G) -> WitnessGeneratorRef { @@ -269,7 +269,7 @@ impl> WitnessGenerator for SimpleGeneratorAd /// A generator which copies one wire to another. #[derive(Debug, Default)] -pub(crate) struct CopyGenerator { +pub struct CopyGenerator { pub(crate) src: Target, pub(crate) dst: Target, } @@ -302,7 +302,7 @@ impl SimpleGenerator for CopyGenerator { /// A generator for including a random value #[derive(Debug, Default)] -pub(crate) struct RandomValueGenerator { +pub struct RandomValueGenerator { pub(crate) target: Target, } @@ -332,7 +332,7 @@ impl SimpleGenerator for RandomValueGenerator { /// A generator for testing if a value equals zero #[derive(Debug, Default)] -pub(crate) struct NonzeroTestGenerator { +pub struct NonzeroTestGenerator { pub(crate) to_test: Target, pub(crate) dummy: Target, } @@ -372,7 +372,7 @@ impl SimpleGenerator for NonzeroTestGenerator { /// Generator used to fill an extra constant. #[derive(Debug, Clone, Default)] -pub(crate) struct ConstantGenerator { +pub struct ConstantGenerator { pub row: usize, pub constant_index: usize, pub wire_index: usize, diff --git a/plonky2/src/recursion/dummy_circuit.rs b/plonky2/src/recursion/dummy_circuit.rs index b052cdd5..79bfff15 100644 --- a/plonky2/src/recursion/dummy_circuit.rs +++ b/plonky2/src/recursion/dummy_circuit.rs @@ -154,7 +154,7 @@ impl, const D: usize> CircuitBuilder { } #[derive(Debug)] -pub(crate) struct DummyProofGenerator +pub struct DummyProofGenerator where F: RichField + Extendable, C: GenericConfig, diff --git a/plonky2/src/util/serialization/generator_serialization.rs b/plonky2/src/util/serialization/generator_serialization.rs index 6566a897..5fab57e1 100644 --- a/plonky2/src/util/serialization/generator_serialization.rs +++ b/plonky2/src/util/serialization/generator_serialization.rs @@ -63,6 +63,14 @@ macro_rules! get_generator_tag_impl { } #[macro_export] +/// Macro implementing the `WitnessGeneratorSerializer` trait. +/// To serialize a list of generators used for a circuit, +/// this macro should be called with a struct on which to implement +/// this as first argument, followed by all the targeted generators. +/// +/// ***NOTE:*** If you need to include `DummyProofGenerator`, you **MUST** +/// place it at the *beginning* of the generators list, right after +/// the serializer struct. macro_rules! impl_generator_serializer { ($target:ty, $($generator_types:ty),+) => { fn read_generator( From 2a9c5cfd328409c37ebaee94886f7e8929d2c64e Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Wed, 12 Apr 2023 12:35:09 -0400 Subject: [PATCH 6/7] Add serialization check in square_root example --- plonky2/examples/square_root.rs | 60 ++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/plonky2/examples/square_root.rs b/plonky2/examples/square_root.rs index 7cf294c6..6ac9eddd 100644 --- a/plonky2/examples/square_root.rs +++ b/plonky2/examples/square_root.rs @@ -4,14 +4,23 @@ use core::marker::PhantomData; use anyhow::Result; use plonky2::field::types::{PrimeField, Sample}; +use plonky2::gates::arithmetic_base::ArithmeticBaseGenerator; +use plonky2::gates::poseidon::PoseidonGenerator; +use plonky2::gates::poseidon_mds::PoseidonMdsGenerator; use plonky2::hash::hash_types::RichField; -use plonky2::iop::generator::{GeneratedValues, SimpleGenerator}; +use plonky2::iop::generator::{ + ConstantGenerator, GeneratedValues, RandomValueGenerator, SimpleGenerator, +}; use plonky2::iop::target::Target; use plonky2::iop::witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite}; use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::circuit_data::CircuitConfig; -use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; -use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; +use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; +use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig}; +use plonky2::recursion::dummy_circuit::DummyProofGenerator; +use plonky2::util::serialization::{ + Buffer, DefaultGateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write, +}; +use plonky2::{get_generator_tag_impl, impl_generator_serializer, read_generator_impl}; use plonky2_field::extension::Extendable; /// A generator used by the prover to calculate the square root (`x`) of a given value @@ -59,6 +68,28 @@ impl, const D: usize> SimpleGenerator } } +pub struct CustomGeneratorSerializer, const D: usize> { + pub _phantom: PhantomData, +} + +impl WitnessGeneratorSerializer for CustomGeneratorSerializer +where + F: RichField + Extendable, + C: GenericConfig + 'static, + C::Hasher: AlgebraicHasher, +{ + impl_generator_serializer! { + CustomGeneratorSerializer, + DummyProofGenerator, + ArithmeticBaseGenerator, + ConstantGenerator, + PoseidonGenerator, + PoseidonMdsGenerator, + RandomValueGenerator, + SquareRootGenerator + } +} + /// An example of using Plonky2 to prove a statement of the form /// "I know the square root of this field element." fn main() -> Result<()> { @@ -99,5 +130,26 @@ fn main() -> Result<()> { let x_squared_actual = proof.public_inputs[0]; println!("Field element (square): {x_squared_actual}"); + // Test serialization + { + let gate_serializer = DefaultGateSerializer; + let generator_serializer = CustomGeneratorSerializer { + _phantom: PhantomData::, + }; + + let data_bytes = data + .to_bytes(&gate_serializer, &generator_serializer) + .map_err(|_| anyhow::Error::msg("CircuitData serialization failed."))?; + + let data_from_bytes = CircuitData::::from_bytes( + &data_bytes, + &gate_serializer, + &generator_serializer, + ) + .map_err(|_| anyhow::Error::msg("CircuitData deserialization failed."))?; + + assert_eq!(data, data_from_bytes); + } + data.verify(proof) } From 723f197d0e954a72de2eb355a62b9f6a484356f9 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Thu, 20 Apr 2023 08:36:54 +0200 Subject: [PATCH 7/7] Cleanup --- plonky2/src/gates/gate.rs | 3 ++- plonky2/src/iop/generator.rs | 29 ++++++----------------------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/plonky2/src/gates/gate.rs b/plonky2/src/gates/gate.rs index 9821106c..43c1179a 100644 --- a/plonky2/src/gates/gate.rs +++ b/plonky2/src/gates/gate.rs @@ -198,6 +198,7 @@ pub trait Gate, const D: usize>: 'static + Send + S } } +/// A wrapper trait over a `Gate`, to allow for gate serialization. pub trait AnyGate, const D: usize>: Gate { fn as_any(&self) -> &dyn Any; } @@ -208,7 +209,7 @@ impl, F: RichField + Extendable, const D: usize> AnyGate } } -/// A wrapper around an `Rc` which implements `PartialEq`, `Eq` and `Hash` based on gate IDs. +/// A wrapper around an `Arc` which implements `PartialEq`, `Eq` and `Hash` based on gate IDs. #[derive(Clone)] pub struct GateRef, const D: usize>(pub(crate) Arc>); diff --git a/plonky2/src/iop/generator.rs b/plonky2/src/iop/generator.rs index 165b5ada..16a23e26 100644 --- a/plonky2/src/iop/generator.rs +++ b/plonky2/src/iop/generator.rs @@ -1,6 +1,5 @@ use alloc::vec; use alloc::vec::Vec; -use core::any::Any; use core::fmt::Debug; use core::marker::PhantomData; @@ -118,33 +117,19 @@ pub trait WitnessGenerator: 'static + Send + Sync + Debug { Self: Sized; } -pub trait AnyWitnessGenerator: WitnessGenerator { - fn as_any(&self) -> &dyn Any; -} - -impl, F: Field> AnyWitnessGenerator for T { - fn as_any(&self) -> &dyn Any { - self - } -} - -/// A wrapper around an `Box`. -pub struct WitnessGeneratorRef(pub Box>); +/// A wrapper around an `Box` which implements `PartialEq` +/// and `Eq` based on generator IDs. +pub struct WitnessGeneratorRef(pub Box>); impl WitnessGeneratorRef { - pub fn new>(generator: G) -> WitnessGeneratorRef { + pub fn new>(generator: G) -> WitnessGeneratorRef { WitnessGeneratorRef(Box::new(generator)) } } impl PartialEq for WitnessGeneratorRef { fn eq(&self, other: &Self) -> bool { - let mut buf1 = Vec::new(); - let mut buf2 = Vec::new(); - self.0.serialize(&mut buf1).unwrap(); - other.0.serialize(&mut buf2).unwrap(); - - buf1 == buf2 + self.0.id() == other.0.id() } } @@ -152,9 +137,7 @@ impl Eq for WitnessGeneratorRef {} impl Debug for WitnessGeneratorRef { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut buf = Vec::new(); - self.0.serialize(&mut buf).unwrap(); - write!(f, "{:?}", buf) + write!(f, "{}", self.0.id()) } }