Merge pull request #981 from toposware/serialization

Serialization support for all circuit components
This commit is contained in:
Daniel Lubarov 2023-04-22 08:15:30 -07:00 committed by GitHub
commit ea82d68084
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 3871 additions and 996 deletions

View File

@ -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::serialization::{
Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, 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<F, C, const D: usize>
where
F: RichField + Extendable<D>,
@ -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<F, C, const D: usize>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
circuit: CircuitData<F, C, D>,
pub circuit: CircuitData<F, C, D>,
proof_with_pis: [ProofWithPublicInputsTarget<D>; 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<F, C, const D: usize> RootCircuitData<F, C, D>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
pub fn to_buffer(
&self,
buffer: &mut Vec<u8>,
gate_serializer: &dyn GateSerializer<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> 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<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> IoResult<Self> {
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<F, C, const D: usize>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
circuit: CircuitData<F, C, D>,
pub circuit: CircuitData<F, C, D>,
lhs: AggregationChildTarget<D>,
rhs: AggregationChildTarget<D>,
cyclic_vk: VerifierCircuitTarget,
}
impl<F, C, const D: usize> AggregationCircuitData<F, C, D>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
pub fn to_buffer(
&self,
buffer: &mut Vec<u8>,
gate_serializer: &dyn GateSerializer<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> 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<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> IoResult<Self> {
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<const D: usize> {
is_agg: BoolTarget,
agg_proof: ProofWithPublicInputsTarget<D>,
evm_proof: ProofWithPublicInputsTarget<D>,
}
impl<const D: usize> AggregationChildTarget<D> {
pub fn to_buffer(&self, buffer: &mut Vec<u8>) -> 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<Self> {
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<F, C, const D: usize>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
circuit: CircuitData<F, C, D>,
pub circuit: CircuitData<F, C, D>,
has_parent_block: BoolTarget,
parent_block_proof: ProofWithPublicInputsTarget<D>,
agg_root_proof: ProofWithPublicInputsTarget<D>,
cyclic_vk: VerifierCircuitTarget,
}
impl<F, C, const D: usize> BlockCircuitData<F, C, D>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
pub fn to_buffer(
&self,
buffer: &mut Vec<u8>,
gate_serializer: &dyn GateSerializer<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> 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<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> IoResult<Self> {
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<F, C, const D: usize> AllRecursiveCircuits<F, C, D>
where
F: RichField + Extendable<D>,
@ -124,44 +275,105 @@ where
[(); C::HCO::WIDTH]:,
[(); C::HCI::WIDTH]:,
{
pub fn to_bytes(
&self,
gate_serializer: &dyn GateSerializer<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> IoResult<Vec<u8>> {
// 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<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> IoResult<Self> {
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<RecursiveCircuitsForTable<F, C, D>>; 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<F, C, D>; 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<F, D>,
degree_bits_range: Range<usize>,
degree_bits_ranges: &[Range<usize>; 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,
);
@ -484,7 +696,8 @@ where
}
}
struct RecursiveCircuitsForTable<F, C, const D: usize>
#[derive(Eq, PartialEq, Debug)]
pub struct RecursiveCircuitsForTable<F, C, const D: usize>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
@ -504,6 +717,39 @@ where
[(); C::HCO::WIDTH]:,
[(); C::HCI::WIDTH]:,
{
pub fn to_buffer(
&self,
buffer: &mut Vec<u8>,
gate_serializer: &dyn GateSerializer<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> 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<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> IoResult<Self> {
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<S: Stark<F, D>>(
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<F, C, const D: usize>
where
F: RichField + Extendable<D>,
@ -568,6 +815,67 @@ where
[(); C::HCO::WIDTH]:,
[(); C::HCI::WIDTH]:,
{
pub fn to_buffer(
&self,
buffer: &mut Vec<u8>,
gate_serializer: &dyn GateSerializer<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> 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<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> IoResult<Self> {
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<S: Stark<F, D>>(
table: Table,
stark: &S,

View File

@ -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<T: Copy + Eq + PartialEq + Debug> {
pub(crate) challenges: Vec<GrandProductChallenge<T>>,
}
impl GrandProductChallengeSet<Target> {
pub fn to_buffer(&self, buffer: &mut Vec<u8>) -> 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<Self> {
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<F, S, const D: usize>(
stark: &S,

View File

@ -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<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> S
}
}
#[derive(Eq, PartialEq, Debug)]
pub struct StarkProofTarget<const D: usize> {
pub trace_cap: MerkleCapTarget,
pub permutation_ctl_zs_cap: MerkleCapTarget,
@ -157,6 +159,31 @@ pub struct StarkProofTarget<const D: usize> {
}
impl<const D: usize> StarkProofTarget<D> {
pub fn to_buffer(&self, buffer: &mut Vec<u8>) -> 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<Self> {
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<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
}
}
#[derive(Eq, PartialEq, Debug)]
pub struct StarkOpeningSetTarget<const D: usize> {
pub local_values: Vec<ExtensionTarget<D>>,
pub next_values: Vec<ExtensionTarget<D>>,
@ -286,6 +314,34 @@ pub struct StarkOpeningSetTarget<const D: usize> {
}
impl<const D: usize> StarkOpeningSetTarget<D> {
pub fn to_buffer(&self, buffer: &mut Vec<u8>) -> 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<Self> {
let local_values = buffer.read_target_ext_vec::<D>()?;
let next_values = buffer.read_target_ext_vec::<D>()?;
let permutation_ctl_zs = buffer.read_target_ext_vec::<D>()?;
let permutation_ctl_zs_next = buffer.read_target_ext_vec::<D>()?;
let ctl_zs_last = buffer.read_target_vec()?;
let quotient_polys = buffer.read_target_ext_vec::<D>()?;
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<D> {
let zeta_batch = FriOpeningBatchTarget {
values: self

View File

@ -19,6 +19,9 @@ use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, VerifierCircuitDa
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2::util::reducing::ReducingFactorTarget;
use plonky2::util::serialization::{
Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write,
};
use plonky2::with_context;
use plonky2_util::log2_ceil;
@ -157,6 +160,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
}
/// Represents a circuit which recursively verifies a STARK proof.
#[derive(Eq, PartialEq, Debug)]
pub(crate) struct StarkWrapperCircuit<F, C, const D: usize>
where
F: RichField + Extendable<D>,
@ -178,6 +182,39 @@ where
[(); C::HCO::WIDTH]:,
[(); C::HCI::WIDTH]:,
{
pub fn to_buffer(
&self,
buffer: &mut Vec<u8>,
gate_serializer: &dyn GateSerializer<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> 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<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> IoResult<Self> {
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<F, C, D>,
@ -212,6 +249,7 @@ where
}
/// Represents a circuit which recursively verifies a PLONK proof.
#[derive(Eq, PartialEq, Debug)]
pub(crate) struct PlonkWrapperCircuit<F, C, const D: usize>
where
F: RichField + Extendable<D>,

View File

@ -1,13 +1,16 @@
#![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::serialization::{DefaultGateSerializer, DefaultGeneratorSerializer};
use plonky2::util::timing::TimingTree;
use plonky2_evm::all_stark::AllStark;
use plonky2_evm::config::StarkConfig;
@ -92,8 +95,43 @@ fn test_empty_txn_list() -> anyhow::Result<()> {
verify_proof(&all_stark, proof, &config)?;
let all_circuits = AllRecursiveCircuits::<F, C, D>::new(&all_stark, 9..19, &config);
let all_circuits = AllRecursiveCircuits::<F, C, D>::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;
let generator_serializer = DefaultGeneratorSerializer {
_phantom: PhantomData::<C>,
};
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::<F, C, D>::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)?;

View File

@ -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)
}
}

View File

@ -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::serialization::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::<F, D>::from_bytes(common_data_bytes, &gate_serializer)
.map_err(|_| anyhow::Error::msg("CommonCircuitData deserialization failed."))?;
assert_eq!(cd, &common_data_from_bytes);
Ok(())
}

View File

@ -4,18 +4,28 @@ 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::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
/// (`x_squared`), outside of the circuit, in order to supply it as an additional public input.
#[derive(Debug)]
#[derive(Debug, Default)]
struct SquareRootGenerator<F: RichField + Extendable<D>, const D: usize> {
x: Target,
x_squared: Target,
@ -25,6 +35,10 @@ struct SquareRootGenerator<F: RichField + Extendable<D>, const D: usize> {
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
for SquareRootGenerator<F, D>
{
fn id(&self) -> String {
"SquareRootGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
vec![self.x_squared]
}
@ -37,6 +51,43 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
out_buffer.set_target(self.x, x);
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_target(self.x)?;
dst.write_target(self.x_squared)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let x = src.read_target()?;
let x_squared = src.read_target()?;
Ok(Self {
x,
x_squared,
_phantom: PhantomData,
})
}
}
pub struct CustomGeneratorSerializer<C: GenericConfig<D>, const D: usize> {
pub _phantom: PhantomData<C>,
}
impl<F, C, const D: usize> WitnessGeneratorSerializer<F, D> for CustomGeneratorSerializer<C, D>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F> + 'static,
C::Hasher: AlgebraicHasher<F, C::HCO>,
{
impl_generator_serializer! {
CustomGeneratorSerializer,
DummyProofGenerator<F, C, D>,
ArithmeticBaseGenerator<F, D>,
ConstantGenerator<F>,
PoseidonGenerator<F, D>,
PoseidonMdsGenerator<D>,
RandomValueGenerator,
SquareRootGenerator<F, D>
}
}
/// An example of using Plonky2 to prove a statement of the form
@ -79,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::<C>,
};
let data_bytes = data
.to_bytes(&gate_serializer, &generator_serializer)
.map_err(|_| anyhow::Error::msg("CircuitData serialization failed."))?;
let data_from_bytes = CircuitData::<F, C, D>::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)
}

View File

@ -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<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
{
pub polynomials: Vec<PolynomialCoeffs<F>>,

View File

@ -33,7 +33,7 @@ pub struct FriQueryStep<
pub merkle_proof: MerkleProof<F, HC, H>,
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FriQueryStepTarget<const D: usize> {
pub evals: Vec<ExtensionTarget<D>>,
pub merkle_proof: MerkleProofTarget,
@ -58,7 +58,7 @@ impl<F: RichField, HC: HashConfig, H: Hasher<F, HC>> FriInitialTreeProof<F, HC,
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FriInitialTreeProofTarget {
pub evals_proofs: Vec<(Vec<Target>, MerkleProofTarget)>,
}
@ -92,7 +92,7 @@ pub struct FriQueryRound<
pub steps: Vec<FriQueryStep<F, HC, H, D>>,
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FriQueryRoundTarget<const D: usize> {
pub initial_trees_proof: FriInitialTreeProofTarget,
pub steps: Vec<FriQueryStepTarget<D>>,
@ -129,7 +129,7 @@ pub struct FriProof<F: RichField + Extendable<D>, HC: HashConfig, H: Hasher<F, H
pub pow_witness: F,
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FriProofTarget<const D: usize> {
pub commit_phase_merkle_caps: Vec<MerkleCapTarget>,
pub query_round_proofs: Vec<FriQueryRoundTarget<D>>,

View File

@ -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<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Computes `-x`.
@ -370,8 +371,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
#[derive(Debug)]
struct EqualityGenerator {
#[derive(Debug, Default)]
pub struct EqualityGenerator {
x: Target,
y: Target,
equal: BoolTarget,
@ -379,6 +380,10 @@ struct EqualityGenerator {
}
impl<F: RichField> SimpleGenerator<F> for EqualityGenerator {
fn id(&self) -> String {
"EqualityGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
vec![self.x, self.y]
}
@ -392,6 +397,21 @@ impl<F: RichField> SimpleGenerator<F> for EqualityGenerator {
out_buffer.set_bool_target(self.equal, x == y);
out_buffer.set_target(self.inv, inv);
}
fn serialize(&self, dst: &mut Vec<u8>) -> 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<Self> {
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.

View File

@ -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<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn arithmetic_extension(
@ -493,8 +494,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
#[derive(Debug)]
struct QuotientGeneratorExtension<const D: usize> {
#[derive(Debug, Default)]
pub struct QuotientGeneratorExtension<const D: usize> {
numerator: ExtensionTarget<D>,
denominator: ExtensionTarget<D>,
quotient: ExtensionTarget<D>,
@ -503,6 +504,10 @@ struct QuotientGeneratorExtension<const D: usize> {
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
for QuotientGeneratorExtension<D>
{
fn id(&self) -> String {
"QuotientGeneratorExtension".to_string()
}
fn dependencies(&self) -> Vec<Target> {
let mut deps = self.numerator.to_target_array().to_vec();
deps.extend(self.denominator.to_target_array());
@ -515,6 +520,23 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
let quotient = num / dem;
out_buffer.set_extension_target(self.quotient, quotient)
}
fn serialize(&self, dst: &mut Vec<u8>) -> 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<Self> {
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, ...`.

View File

@ -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;

View File

@ -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<const D: usize>(pub Vec<ExtensionTarget<D>>);
impl<const D: usize> PolynomialCoeffsExtTarget<D> {

View File

@ -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<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Checks that `x < 2^n_log` using a `BaseSumGate`.
@ -51,8 +52,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
#[derive(Debug)]
struct LowHighGenerator {
#[derive(Debug, Default)]
pub struct LowHighGenerator {
integer: Target,
n_log: usize,
low: Target,
@ -60,6 +61,10 @@ struct LowHighGenerator {
}
impl<F: RichField> SimpleGenerator<F> for LowHighGenerator {
fn id(&self) -> String {
"LowHighGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
vec![self.integer]
}
@ -72,4 +77,24 @@ impl<F: RichField> SimpleGenerator<F> 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<u8>) -> 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<Self> {
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,
})
}
}

View File

@ -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<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Split the given element into a list of targets, where each one represents a
@ -79,13 +80,17 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
#[derive(Debug)]
struct BaseSumGenerator<const B: usize> {
#[derive(Debug, Default)]
pub struct BaseSumGenerator<const B: usize> {
row: usize,
limbs: Vec<BoolTarget>,
}
impl<F: Field, const B: usize> SimpleGenerator<F> for BaseSumGenerator<B> {
fn id(&self) -> String {
"BaseSumGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
self.limbs.iter().map(|b| b.target).collect()
}
@ -102,6 +107,17 @@ impl<F: Field, const B: usize> SimpleGenerator<F> for BaseSumGenerator<B> {
out_buffer.set_target(Target::wire(self.row, BaseSumGate::<B>::WIRE_SUM), sum);
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.row)?;
dst.write_target_bool_vec(&self.limbs)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let row = src.read_usize()?;
let limbs = src.read_target_bool_vec()?;
Ok(Self { row, limbs })
}
}
#[cfg(test)]

View File

@ -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<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Split the given integer into a list of wires, where each one represents a
@ -55,13 +56,17 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
#[derive(Debug)]
struct SplitGenerator {
#[derive(Debug, Default)]
pub struct SplitGenerator {
integer: Target,
bits: Vec<Target>,
}
impl<F: RichField> SimpleGenerator<F> for SplitGenerator {
fn id(&self) -> String {
"SplitGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
vec![self.integer]
}
@ -80,16 +85,31 @@ impl<F: RichField> SimpleGenerator<F> for SplitGenerator {
"Integer too large to fit in given number of bits"
);
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_target(self.integer)?;
dst.write_target_vec(&self.bits)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let integer = src.read_target()?;
let bits = src.read_target_vec()?;
Ok(Self { integer, bits })
}
}
#[derive(Debug)]
struct WireSplitGenerator {
#[derive(Debug, Default)]
pub struct WireSplitGenerator {
integer: Target,
gates: Vec<usize>,
num_limbs: usize,
}
impl<F: RichField> SimpleGenerator<F> for WireSplitGenerator {
fn id(&self) -> String {
"WireSplitGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
vec![self.integer]
}
@ -120,4 +140,21 @@ impl<F: RichField> SimpleGenerator<F> for WireSplitGenerator {
self.gates.len()
);
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_target(self.integer)?;
dst.write_usize_vec(&self.gates)?;
dst.write_usize(self.num_limbs)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let integer = src.read_target()?;
let gates = src.read_usize_vec()?;
let num_limbs = src.read_usize()?;
Ok(Self {
integer,
gates,
num_limbs,
})
}
}

View File

@ -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<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticGate
format!("{self:?}")
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.num_ops)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let num_ops = src.read_usize()?;
Ok(Self { num_ops })
}
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let const_0 = vars.local_constants[0];
let const_1 = vars.local_constants[1];
@ -117,10 +126,10 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticGate
constraints
}
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
(0..self.num_ops)
.map(|i| {
let g: Box<dyn WitnessGenerator<F>> = Box::new(
WitnessGeneratorRef::new(
ArithmeticBaseGenerator {
row,
const_0: local_constants[0],
@ -128,8 +137,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticGate
i,
}
.adapter(),
);
g
)
})
.collect()
}
@ -172,8 +180,8 @@ impl<F: RichField + Extendable<D>, const D: usize> PackedEvaluableBase<F, D> for
}
}
#[derive(Clone, Debug)]
struct ArithmeticBaseGenerator<F: RichField + Extendable<D>, const D: usize> {
#[derive(Clone, Debug, Default)]
pub struct ArithmeticBaseGenerator<F: RichField + Extendable<D>, const D: usize> {
row: usize,
const_0: F,
const_1: F,
@ -183,6 +191,10 @@ struct ArithmeticBaseGenerator<F: RichField + Extendable<D>, const D: usize> {
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
for ArithmeticBaseGenerator<F, D>
{
fn id(&self) -> String {
"ArithmeticBaseGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
[
ArithmeticGate::wire_ith_multiplicand_0(self.i),
@ -208,6 +220,26 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
out_buffer.set_target(output_target, computed_output)
}
fn serialize(&self, dst: &mut Vec<u8>) -> 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<Self> {
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)]

View File

@ -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<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticExte
format!("{self:?}")
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.num_ops)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let num_ops = src.read_usize()?;
Ok(Self { num_ops })
}
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let const_0 = vars.local_constants[0];
let const_1 = vars.local_constants[1];
@ -122,10 +131,10 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticExte
constraints
}
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
(0..self.num_ops)
.map(|i| {
let g: Box<dyn WitnessGenerator<F>> = Box::new(
WitnessGeneratorRef::new(
ArithmeticExtensionGenerator {
row,
const_0: local_constants[0],
@ -133,8 +142,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticExte
i,
}
.adapter(),
);
g
)
})
.collect()
}
@ -156,8 +164,8 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticExte
}
}
#[derive(Clone, Debug)]
struct ArithmeticExtensionGenerator<F: RichField + Extendable<D>, const D: usize> {
#[derive(Clone, Debug, Default)]
pub struct ArithmeticExtensionGenerator<F: RichField + Extendable<D>, const D: usize> {
row: usize,
const_0: F,
const_1: F,
@ -167,6 +175,10 @@ struct ArithmeticExtensionGenerator<F: RichField + Extendable<D>, const D: usize
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
for ArithmeticExtensionGenerator<F, D>
{
fn id(&self) -> String {
"ArithmeticExtensionGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
ArithmeticExtensionGate::<D>::wires_ith_multiplicand_0(self.i)
.chain(ArithmeticExtensionGate::<D>::wires_ith_multiplicand_1(
@ -201,6 +213,26 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
out_buffer.set_extension_target(output_target, computed_output)
}
fn serialize(&self, dst: &mut Vec<u8>) -> 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<Self> {
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)]

View File

@ -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<F: RichField + Extendable<D>, const D: usize, const B: usize> Gate<F, D> fo
format!("{self:?} + Base: {B}")
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.num_limbs)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let num_limbs = src.read_usize()?;
Ok(Self { num_limbs })
}
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let sum = vars.local_wires[Self::WIRE_SUM];
let limbs = vars.local_wires[self.limbs()].to_vec();
@ -109,12 +118,12 @@ impl<F: RichField + Extendable<D>, const D: usize, const B: usize> Gate<F, D> fo
constraints
}
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
let gen = BaseSplitGenerator::<B> {
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<F: RichField + Extendable<D>, const D: usize, const B: usize> PackedEvaluab
}
}
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct BaseSplitGenerator<const B: usize> {
row: usize,
num_limbs: usize,
}
impl<F: RichField, const B: usize> SimpleGenerator<F> for BaseSplitGenerator<B> {
fn id(&self) -> String {
"BaseSplitGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
vec![Target::wire(self.row, BaseSumGate::<B>::WIRE_SUM)]
}
@ -195,6 +208,17 @@ impl<F: RichField, const B: usize> SimpleGenerator<F> for BaseSplitGenerator<B>
out_buffer.set_target(b, b_value);
}
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.row)?;
dst.write_usize(self.num_limbs)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let row = src.read_usize()?;
let num_limbs = src.read_usize()?;
Ok(Self { row, num_limbs })
}
}
#[cfg(test)]

View File

@ -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<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ConstantGate {
format!("{self:?}")
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.num_consts)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let num_consts = src.read_usize()?;
Ok(Self { num_consts })
}
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
(0..self.num_consts)
.map(|i| {
@ -75,7 +84,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ConstantGate {
.collect()
}
fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
vec![]
}

View File

@ -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<<subgroup_bits`.
@ -45,7 +45,7 @@ use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
///
/// Then e[N] is the final interpolated value. The non-routed wires hold every (d - 1)'th
/// intermediate value of p and e, starting at p[d] and e[d], where d is the gate degree.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct CosetInterpolationGate<F: RichField + Extendable<D>, const D: usize> {
pub subgroup_bits: usize,
pub degree: usize,
@ -168,6 +168,26 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for CosetInterpola
format!("{self:?}<D={D}>")
}
fn serialize(&self, dst: &mut Vec<u8>) -> 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<Self> {
let subgroup_bits = src.read_usize()?;
let degree = src.read_usize()?;
let length = src.read_usize()?;
let barycentric_weights: Vec<F> = src.read_field_vec(length)?;
Ok(Self {
subgroup_bits,
degree,
barycentric_weights,
_phantom: PhantomData,
})
}
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let mut constraints = Vec::with_capacity(self.num_constraints());
@ -342,9 +362,9 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for CosetInterpola
constraints
}
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
let gen = InterpolationGenerator::<F, D>::new(row, self.clone());
vec![Box::new(gen.adapter())]
vec![WitnessGeneratorRef::new(gen.adapter())]
}
fn num_wires(&self) -> usize {
@ -366,8 +386,8 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for CosetInterpola
}
}
#[derive(Debug)]
struct InterpolationGenerator<F: RichField + Extendable<D>, const D: usize> {
#[derive(Debug, Default)]
pub struct InterpolationGenerator<F: RichField + Extendable<D>, const D: usize> {
row: usize,
gate: CosetInterpolationGate<F, D>,
interpolation_domain: Vec<F>,
@ -389,6 +409,10 @@ impl<F: RichField + Extendable<D>, const D: usize> InterpolationGenerator<F, D>
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
for InterpolationGenerator<F, D>
{
fn id(&self) -> String {
"InterpolationGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
let local_target = |column| {
Target::Wire(Wire {
@ -471,6 +495,17 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
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<u8>) -> IoResult<()> {
dst.write_usize(self.row)?;
self.gate.serialize(dst)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
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`.

View File

@ -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<F: RichField + Extendable<D>, const D: usize> {
pub num_power_bits: usize,
pub _phantom: PhantomData<F>,
@ -76,6 +76,15 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for Exponentiation
format!("{self:?}<D={D}>")
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.num_power_bits)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let num_power_bits = src.read_usize()?;
Ok(Self::new(num_power_bits))
}
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let base = vars.local_wires[self.wire_base()];
@ -164,12 +173,12 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for Exponentiation
constraints
}
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
let gen = ExponentiationGenerator::<F, D> {
row,
gate: self.clone(),
};
vec![Box::new(gen.adapter())]
vec![WitnessGeneratorRef::new(gen.adapter())]
}
fn num_wires(&self) -> usize {
@ -228,8 +237,8 @@ impl<F: RichField + Extendable<D>, const D: usize> PackedEvaluableBase<F, D>
}
}
#[derive(Debug)]
struct ExponentiationGenerator<F: RichField + Extendable<D>, const D: usize> {
#[derive(Debug, Default)]
pub struct ExponentiationGenerator<F: RichField + Extendable<D>, const D: usize> {
row: usize,
gate: ExponentiationGate<F, D>,
}
@ -237,6 +246,10 @@ struct ExponentiationGenerator<F: RichField + Extendable<D>, const D: usize> {
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
for ExponentiationGenerator<F, D>
{
fn id(&self) -> String {
"ExponentiationGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
let local_target = |column| Target::wire(self.row, column);
@ -281,6 +294,17 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
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<u8>) -> IoResult<()> {
dst.write_usize(self.row)?;
self.gate.serialize(dst)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let row = src.read_usize()?;
let gate = ExponentiationGate::deserialize(src)?;
Ok(Self { row, gate })
}
}
#[cfg(test)]

View File

@ -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<F: RichField + Extendable<D>, const D: usize>: 'static + Send + Sync {
fn id(&self) -> String;
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()>;
fn deserialize(src: &mut Buffer) -> IoResult<Self>
where
Self: Sized;
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension>;
/// Like `eval_unfiltered`, but specialized for points in the base field.
@ -162,7 +169,7 @@ pub trait Gate<F: RichField + Extendable<D>, 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<Box<dyn WitnessGenerator<F>>>;
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>>;
/// The number of wires used by this gate.
fn num_wires(&self) -> usize;
@ -191,9 +198,20 @@ pub trait Gate<F: RichField + Extendable<D>, const D: usize>: 'static + Send + S
}
}
/// A wrapper around an `Rc<Gate>` which implements `PartialEq`, `Eq` and `Hash` based on gate IDs.
/// A wrapper trait over a `Gate`, to allow for gate serialization.
pub trait AnyGate<F: RichField + Extendable<D>, const D: usize>: Gate<F, D> {
fn as_any(&self) -> &dyn Any;
}
impl<T: Gate<F, D>, F: RichField + Extendable<D>, const D: usize> AnyGate<F, D> for T {
fn as_any(&self) -> &dyn Any {
self
}
}
/// A wrapper around an `Arc<AnyGate>` which implements `PartialEq`, `Eq` and `Hash` based on gate IDs.
#[derive(Clone)]
pub struct GateRef<F: RichField + Extendable<D>, const D: usize>(pub(crate) Arc<dyn Gate<F, D>>);
pub struct GateRef<F: RichField + Extendable<D>, const D: usize>(pub(crate) Arc<dyn AnyGate<F, D>>);
impl<F: RichField + Extendable<D>, const D: usize> GateRef<F, D> {
pub fn new<G: Gate<F, D>>(gate: G) -> GateRef<F, D> {

View File

@ -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<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for MulExtensionGa
format!("{self:?}")
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.num_ops)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let num_ops = src.read_usize()?;
Ok(Self { num_ops })
}
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let const_0 = vars.local_constants[0];
@ -110,18 +119,17 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for MulExtensionGa
constraints
}
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
(0..self.num_ops)
.map(|i| {
let g: Box<dyn WitnessGenerator<F>> = Box::new(
WitnessGeneratorRef::new(
MulExtensionGenerator {
row,
const_0: local_constants[0],
i,
}
.adapter(),
);
g
)
})
.collect()
}
@ -143,8 +151,8 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for MulExtensionGa
}
}
#[derive(Clone, Debug)]
struct MulExtensionGenerator<F: RichField + Extendable<D>, const D: usize> {
#[derive(Clone, Debug, Default)]
pub struct MulExtensionGenerator<F: RichField + Extendable<D>, const D: usize> {
row: usize,
const_0: F,
i: usize,
@ -153,6 +161,10 @@ struct MulExtensionGenerator<F: RichField + Extendable<D>, const D: usize> {
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
for MulExtensionGenerator<F, D>
{
fn id(&self) -> String {
"MulExtensionGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
MulExtensionGate::<D>::wires_ith_multiplicand_0(self.i)
.chain(MulExtensionGate::<D>::wires_ith_multiplicand_1(self.i))
@ -178,6 +190,19 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
out_buffer.set_extension_target(output_target, computed_output)
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.row)?;
dst.write_field(self.const_0)?;
dst.write_usize(self.i)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let row = src.read_usize()?;
let const_0 = src.read_field()?;
let i = src.read_usize()?;
Ok(Self { row, const_0, i })
}
}
#[cfg(test)]

View File

@ -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<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for NoopGate {
"NoopGate".into()
}
fn serialize(&self, _dst: &mut Vec<u8>) -> IoResult<()> {
Ok(())
}
fn deserialize(_src: &mut Buffer) -> IoResult<Self> {
Ok(Self)
}
fn eval_unfiltered(&self, _vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
Vec::new()
}
@ -34,7 +42,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for NoopGate {
Vec::new()
}
fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
Vec::new()
}

View File

@ -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<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for PoseidonGate<F
format!("{self:?}<WIDTH={SPONGE_WIDTH}>")
}
fn serialize(&self, _dst: &mut Vec<u8>) -> IoResult<()> {
Ok(())
}
fn deserialize(_src: &mut Buffer) -> IoResult<Self> {
Ok(PoseidonGate::new())
}
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let mut constraints = Vec::with_capacity(self.num_constraints());
@ -372,12 +380,12 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for PoseidonGate<F
constraints
}
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
let gen = PoseidonGenerator::<F, D> {
row,
_phantom: PhantomData,
};
vec![Box::new(gen.adapter())]
vec![WitnessGeneratorRef::new(gen.adapter())]
}
fn num_wires(&self) -> usize {
@ -401,8 +409,8 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for PoseidonGate<F
}
}
#[derive(Debug)]
struct PoseidonGenerator<F: RichField + Extendable<D> + Poseidon, const D: usize> {
#[derive(Debug, Default)]
pub struct PoseidonGenerator<F: RichField + Extendable<D> + Poseidon, const D: usize> {
row: usize,
_phantom: PhantomData<F>,
}
@ -410,6 +418,10 @@ struct PoseidonGenerator<F: RichField + Extendable<D> + Poseidon, const D: usize
impl<F: RichField + Extendable<D> + Poseidon, const D: usize> SimpleGenerator<F>
for PoseidonGenerator<F, D>
{
fn id(&self) -> String {
"PoseidonGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
(0..SPONGE_WIDTH)
.map(|i| PoseidonGate::<F, D>::wire_input(i))
@ -499,6 +511,18 @@ impl<F: RichField + Extendable<D> + Poseidon, const D: usize> SimpleGenerator<F>
out_buffer.set_wire(local_wire(PoseidonGate::<F, D>::wire_output(i)), state[i]);
}
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.row)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let row = src.read_usize()?;
Ok(Self {
row,
_phantom: PhantomData,
})
}
}
#[cfg(test)]

View File

@ -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<F: RichField + Extendable<D> + Poseidon, const D: usize> Gate<F, D> for Pos
format!("{self:?}<WIDTH={SPONGE_WIDTH}>")
}
fn serialize(&self, _dst: &mut Vec<u8>) -> IoResult<()> {
Ok(())
}
fn deserialize(_src: &mut Buffer) -> IoResult<Self> {
Ok(PoseidonMdsGate::new())
}
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let inputs: [_; SPONGE_WIDTH] = (0..SPONGE_WIDTH)
.map(|i| vars.get_local_ext_algebra(Self::wires_input(i)))
@ -179,9 +187,9 @@ impl<F: RichField + Extendable<D> + Poseidon, const D: usize> Gate<F, D> for Pos
.collect()
}
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
let gen = PoseidonMdsGenerator::<D> { row };
vec![Box::new(gen.adapter())]
vec![WitnessGeneratorRef::new(gen.adapter())]
}
fn num_wires(&self) -> usize {
@ -201,14 +209,18 @@ impl<F: RichField + Extendable<D> + Poseidon, const D: usize> Gate<F, D> for Pos
}
}
#[derive(Clone, Debug)]
struct PoseidonMdsGenerator<const D: usize> {
#[derive(Clone, Debug, Default)]
pub struct PoseidonMdsGenerator<const D: usize> {
row: usize,
}
impl<F: RichField + Extendable<D> + Poseidon, const D: usize> SimpleGenerator<F>
for PoseidonMdsGenerator<D>
{
fn id(&self) -> String {
"PoseidonMdsGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
(0..SPONGE_WIDTH)
.flat_map(|i| {
@ -237,6 +249,15 @@ impl<F: RichField + Extendable<D> + Poseidon, const D: usize> SimpleGenerator<F>
);
}
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.row)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let row = src.read_usize()?;
Ok(Self { row })
}
}
#[cfg(test)]

View File

@ -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<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for PublicInputGat
"PublicInputGate".into()
}
fn serialize(&self, _dst: &mut Vec<u8>) -> IoResult<()> {
Ok(())
}
fn deserialize(_src: &mut Buffer) -> IoResult<Self> {
Ok(Self)
}
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
Self::wires_public_inputs_hash()
.zip(vars.public_inputs_hash.elements)
@ -64,7 +72,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for PublicInputGat
.collect()
}
fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
Vec::new()
}

View File

@ -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<F: RichField + Extendable<D>, const D: usize> {
/// Number of bits in the index (log2 of the list size).
pub bits: usize,
@ -122,6 +122,20 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for RandomAccessGa
format!("{self:?}<D={D}>")
}
fn serialize(&self, dst: &mut Vec<u8>) -> 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<Self> {
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<F, D>) -> Vec<F::Extension> {
let mut constraints = Vec::with_capacity(self.num_constraints());
@ -238,18 +252,17 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for RandomAccessGa
constraints
}
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
(0..self.num_copies)
.map(|copy| {
let g: Box<dyn WitnessGenerator<F>> = Box::new(
WitnessGeneratorRef::new(
RandomAccessGenerator {
row,
gate: *self,
copy,
}
.adapter(),
);
g
)
})
.collect()
}
@ -325,8 +338,8 @@ impl<F: RichField + Extendable<D>, const D: usize> PackedEvaluableBase<F, D>
}
}
#[derive(Debug)]
struct RandomAccessGenerator<F: RichField + Extendable<D>, const D: usize> {
#[derive(Debug, Default)]
pub struct RandomAccessGenerator<F: RichField + Extendable<D>, const D: usize> {
row: usize,
gate: RandomAccessGate<F, D>,
copy: usize,
@ -335,6 +348,10 @@ struct RandomAccessGenerator<F: RichField + Extendable<D>, const D: usize> {
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
for RandomAccessGenerator<F, D>
{
fn id(&self) -> String {
"RandomAccessGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
let local_target = |column| Target::wire(self.row, column);
@ -376,6 +393,19 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
set_local_wire(self.gate.wire_bit(i, copy), bit);
}
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.row)?;
dst.write_usize(self.copy)?;
self.gate.serialize(dst)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let row = src.read_usize()?;
let copy = src.read_usize()?;
let gate = RandomAccessGate::<F, D>::deserialize(src)?;
Ok(Self { row, gate, copy })
}
}
#[cfg(test)]

View File

@ -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<const D: usize> {
pub num_coeffs: usize,
}
@ -60,6 +60,19 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ReducingGate<D
format!("{self:?}")
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.num_coeffs)?;
Ok(())
}
fn deserialize(src: &mut Buffer) -> IoResult<Self>
where
Self: Sized,
{
let num_coeffs = src.read_usize()?;
Ok(Self::new(num_coeffs))
}
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
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<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ReducingGate<D
.collect()
}
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
vec![Box::new(
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
vec![WitnessGeneratorRef::new(
ReducingGenerator {
row,
gate: self.clone(),
@ -164,13 +177,17 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ReducingGate<D
}
}
#[derive(Debug)]
struct ReducingGenerator<const D: usize> {
#[derive(Debug, Default)]
pub struct ReducingGenerator<const D: usize> {
row: usize,
gate: ReducingGate<D>,
}
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F> for ReducingGenerator<D> {
fn id(&self) -> String {
"ReducingGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
ReducingGate::<D>::wires_alpha()
.chain(ReducingGate::<D>::wires_old_acc())
@ -207,6 +224,17 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F> for Reduci
}
out_buffer.set_extension_target(output, acc);
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.row)?;
<ReducingGate<D> as Gate<F, D>>::serialize(&self.gate, dst)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let row = src.read_usize()?;
let gate = <ReducingGate<D> as Gate<F, D>>::deserialize(src)?;
Ok(Self { row, gate })
}
}
#[cfg(test)]

View File

@ -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<const D: usize> {
pub num_coeffs: usize,
}
@ -63,6 +63,19 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ReducingExtens
format!("{self:?}")
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.num_coeffs)?;
Ok(())
}
fn deserialize(src: &mut Buffer) -> IoResult<Self>
where
Self: Sized,
{
let num_coeffs = src.read_usize()?;
Ok(Self::new(num_coeffs))
}
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
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<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ReducingExtens
.collect()
}
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
vec![Box::new(
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
vec![WitnessGeneratorRef::new(
ReducingGenerator {
row,
gate: self.clone(),
@ -164,13 +177,17 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ReducingExtens
}
}
#[derive(Debug)]
struct ReducingGenerator<const D: usize> {
#[derive(Debug, Default)]
pub struct ReducingGenerator<const D: usize> {
row: usize,
gate: ReducingExtensionGate<D>,
}
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F> for ReducingGenerator<D> {
fn id(&self) -> String {
"ReducingExtensionGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
ReducingExtensionGate::<D>::wires_alpha()
.chain(ReducingExtensionGate::<D>::wires_old_acc())
@ -201,6 +218,17 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F> for Reduci
acc = computed_acc;
}
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_usize(self.row)?;
<ReducingExtensionGate<D> as Gate<F, D>>::serialize(&self.gate, dst)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let row = src.read_usize()?;
let gate = <ReducingExtensionGate<D> as Gate<F, D>>::deserialize(src)?;
Ok(Self { row, gate })
}
}
#[cfg(test)]

View File

@ -110,7 +110,7 @@ impl<F: Field> Default for HashOut<F> {
}
/// 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<HashOutTarget>);
/// Hash consisting of a byte array.

View File

@ -31,7 +31,7 @@ impl<F: RichField, HC: HashConfig, H: Hasher<F, HC>> MerkleProof<F, HC, H> {
}
}
#[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<HashOutTarget>,

View File

@ -36,7 +36,7 @@ impl<F: RichField, HC: HashConfig, H: Hasher<F, HC>> MerkleCap<F, HC, H> {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MerkleTree<F: RichField, HC: HashConfig, H: Hasher<F, HC>> {
/// The data in the leaves of the Merkle tree.
pub leaves: Vec<Vec<F>>,

View File

@ -12,6 +12,12 @@ use crate::plonk::circuit_builder::CircuitBuilder;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ExtensionTarget<const D: usize>(pub [Target; D]);
impl<const D: usize> Default for ExtensionTarget<D> {
fn default() -> Self {
Self([Target::default(); D])
}
}
impl<const D: usize> ExtensionTarget<D> {
pub fn to_target_array(&self) -> [Target; D] {
self.0

View File

@ -12,6 +12,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 +59,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 +99,8 @@ pub(crate) fn generate_partial_witness<
/// A generator participates in the generation of the witness.
pub trait WitnessGenerator<F: Field>: '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<Target>;
@ -106,6 +109,36 @@ pub trait WitnessGenerator<F: Field>: '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<F>, out_buffer: &mut GeneratedValues<F>) -> bool;
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()>;
fn deserialize(src: &mut Buffer) -> IoResult<Self>
where
Self: Sized;
}
/// A wrapper around an `Box<WitnessGenerator>` which implements `PartialEq`
/// and `Eq` based on generator IDs.
pub struct WitnessGeneratorRef<F: Field>(pub Box<dyn WitnessGenerator<F>>);
impl<F: Field> WitnessGeneratorRef<F> {
pub fn new<G: WitnessGenerator<F>>(generator: G) -> WitnessGeneratorRef<F> {
WitnessGeneratorRef(Box::new(generator))
}
}
impl<F: Field> PartialEq for WitnessGeneratorRef<F> {
fn eq(&self, other: &Self) -> bool {
self.0.id() == other.0.id()
}
}
impl<F: Field> Eq for WitnessGeneratorRef<F> {}
impl<F: Field> Debug for WitnessGeneratorRef<F> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0.id())
}
}
/// Values generated by a generator invocation.
@ -158,6 +191,8 @@ impl<F: Field> GeneratedValues<F> {
/// A generator which runs once after a list of dependencies is present in the witness.
pub trait SimpleGenerator<F: Field>: 'static + Send + Sync + Debug {
fn id(&self) -> String;
fn dependencies(&self) -> Vec<Target>;
fn run_once(&self, witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>);
@ -171,6 +206,12 @@ pub trait SimpleGenerator<F: Field>: 'static + Send + Sync + Debug {
_phantom: PhantomData,
}
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()>;
fn deserialize(src: &mut Buffer) -> IoResult<Self>
where
Self: Sized;
}
#[derive(Debug)]
@ -180,6 +221,10 @@ pub struct SimpleGeneratorAdapter<F: Field, SG: SimpleGenerator<F> + ?Sized> {
}
impl<F: Field, SG: SimpleGenerator<F>> WitnessGenerator<F> for SimpleGeneratorAdapter<F, SG> {
fn id(&self) -> String {
self.inner.id()
}
fn watch_list(&self) -> Vec<Target> {
self.inner.dependencies()
}
@ -192,16 +237,31 @@ impl<F: Field, SG: SimpleGenerator<F>> WitnessGenerator<F> for SimpleGeneratorAd
false
}
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
self.inner.serialize(dst)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
Ok(Self {
inner: SG::deserialize(src)?,
_phantom: PhantomData,
})
}
}
/// A generator which copies one wire to another.
#[derive(Debug)]
pub(crate) struct CopyGenerator {
#[derive(Debug, Default)]
pub struct CopyGenerator {
pub(crate) src: Target,
pub(crate) dst: Target,
}
impl<F: Field> SimpleGenerator<F> for CopyGenerator {
fn id(&self) -> String {
"CopyGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
vec![self.src]
}
@ -210,15 +270,30 @@ impl<F: Field> SimpleGenerator<F> for CopyGenerator {
let value = witness.get_target(self.src);
out_buffer.set_target(self.dst, value);
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_target(self.src)?;
dst.write_target(self.dst)
}
fn deserialize(source: &mut Buffer) -> IoResult<Self> {
let src = source.read_target()?;
let dst = source.read_target()?;
Ok(Self { src, dst })
}
}
/// A generator for including a random value
#[derive(Debug)]
pub(crate) struct RandomValueGenerator {
#[derive(Debug, Default)]
pub struct RandomValueGenerator {
pub(crate) target: Target,
}
impl<F: Field> SimpleGenerator<F> for RandomValueGenerator {
fn id(&self) -> String {
"RandomValueGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
Vec::new()
}
@ -227,16 +302,29 @@ impl<F: Field> SimpleGenerator<F> for RandomValueGenerator {
let random_value = F::rand();
out_buffer.set_target(self.target, random_value);
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_target(self.target)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
let target = src.read_target()?;
Ok(Self { target })
}
}
/// A generator for testing if a value equals zero
#[derive(Debug)]
pub(crate) struct NonzeroTestGenerator {
#[derive(Debug, Default)]
pub struct NonzeroTestGenerator {
pub(crate) to_test: Target,
pub(crate) dummy: Target,
}
impl<F: Field> SimpleGenerator<F> for NonzeroTestGenerator {
fn id(&self) -> String {
"NonzeroTestGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
vec![self.to_test]
}
@ -252,11 +340,22 @@ impl<F: Field> SimpleGenerator<F> for NonzeroTestGenerator {
out_buffer.set_target(self.dummy, dummy_value);
}
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
dst.write_target(self.to_test)?;
dst.write_target(self.dummy)
}
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
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)]
pub(crate) struct ConstantGenerator<F: Field> {
#[derive(Debug, Clone, Default)]
pub struct ConstantGenerator<F: Field> {
pub row: usize,
pub constant_index: usize,
pub wire_index: usize,
@ -269,7 +368,11 @@ impl<F: Field> ConstantGenerator<F> {
}
}
impl<F: Field> SimpleGenerator<F> for ConstantGenerator<F> {
impl<F: RichField> SimpleGenerator<F> for ConstantGenerator<F> {
fn id(&self) -> String {
"ConstantGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
vec![]
}
@ -277,4 +380,24 @@ impl<F: Field> SimpleGenerator<F> for ConstantGenerator<F> {
fn run_once(&self, _witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>) {
out_buffer.set_target(Target::wire(self.row, self.wire_index), self.constant);
}
fn serialize(&self, dst: &mut Vec<u8>) -> 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<Self> {
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,
})
}
}

View File

@ -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,

View File

@ -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<F: RichField + Extendable<D>, const D: usize> {
context_log: ContextTree,
/// Generators used to generate the witness.
generators: Vec<Box<dyn WitnessGenerator<F>>>,
generators: Vec<WitnessGeneratorRef<F>>,
constants_to_targets: HashMap<F, Target>,
targets_to_constants: HashMap<Target, F>,
@ -367,12 +366,13 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.connect(x, one);
}
pub fn add_generators(&mut self, generators: Vec<Box<dyn WitnessGenerator<F>>>) {
pub fn add_generators(&mut self, generators: Vec<WitnessGeneratorRef<F>>) {
self.generators.extend(generators);
}
pub fn add_simple_generator<G: SimpleGenerator<F>>(&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<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
// 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

View File

@ -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::serialization::{
Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, 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<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
pub prover_only: ProverOnlyCircuitData<F, C, D>,
pub verifier_only: VerifierOnlyCircuitData<C, D>,
@ -116,6 +119,25 @@ pub struct CircuitData<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>,
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
CircuitData<F, C, D>
{
pub fn to_bytes(
&self,
gate_serializer: &dyn GateSerializer<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> IoResult<Vec<u8>> {
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<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> IoResult<Self> {
let mut buffer = Buffer::new(bytes.to_vec());
buffer.read_circuit_data(gate_serializer, generator_serializer)
}
pub fn prove(&self, inputs: PartialWitness<F>) -> Result<ProofWithPublicInputs<F, C, D>>
where
[(); C::HCO::WIDTH]:,
@ -214,6 +236,25 @@ pub struct ProverCircuitData<
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
ProverCircuitData<F, C, D>
{
pub fn to_bytes(
&self,
gate_serializer: &dyn GateSerializer<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> IoResult<Vec<u8>> {
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<F, D>,
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
) -> IoResult<Self> {
let mut buffer = Buffer::new(bytes.to_vec());
buffer.read_prover_circuit_data(gate_serializer, generator_serializer)
}
pub fn prove(&self, inputs: PartialWitness<F>) -> Result<ProofWithPublicInputs<F, C, D>>
where
[(); C::HCO::WIDTH]:,
@ -242,6 +283,20 @@ pub struct VerifierCircuitData<
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
VerifierCircuitData<F, C, D>
{
pub fn to_bytes(&self, gate_serializer: &dyn GateSerializer<F, D>) -> IoResult<Vec<u8>> {
let mut buffer = Vec::new();
buffer.write_verifier_circuit_data(self, gate_serializer)?;
Ok(buffer)
}
pub fn from_bytes(
bytes: Vec<u8>,
gate_serializer: &dyn GateSerializer<F, D>,
) -> IoResult<Self> {
let mut buffer = Buffer::new(bytes);
buffer.read_verifier_circuit_data(gate_serializer)
}
pub fn verify(&self, proof_with_pis: ProofWithPublicInputs<F, C, D>) -> Result<()>
where
[(); C::HCO::WIDTH]:,
@ -263,12 +318,13 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
}
/// Circuit data required by the prover, but not the verifier.
#[derive(Eq, PartialEq, Debug)]
pub struct ProverOnlyCircuitData<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
> {
pub generators: Vec<Box<dyn WitnessGenerator<F>>>,
pub generators: Vec<WitnessGeneratorRef<F>>,
/// Generator indices (within the `Vec` above), indexed by the representative of each target
/// they watch.
pub generator_indices_by_watches: BTreeMap<usize, Vec<usize>>,
@ -300,6 +356,19 @@ pub struct VerifierOnlyCircuitData<C: GenericConfig<D>, const D: usize> {
pub circuit_digest: <<C as GenericConfig<D>>::Hasher as Hasher<C::F, C::HCO>>::Hash,
}
impl<C: GenericConfig<D>, const D: usize> VerifierOnlyCircuitData<C, D> {
pub fn to_bytes(&self) -> IoResult<Vec<u8>> {
let mut buffer = Vec::new();
buffer.write_verifier_only_circuit_data(self)?;
Ok(buffer)
}
pub fn from_bytes(bytes: Vec<u8>) -> IoResult<Self> {
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<F: RichField + Extendable<D>, const D: usize> {
@ -332,6 +401,20 @@ pub struct CommonCircuitData<F: RichField + Extendable<D>, const D: usize> {
}
impl<F: RichField + Extendable<D>, const D: usize> CommonCircuitData<F, D> {
pub fn to_bytes(&self, gate_serializer: &dyn GateSerializer<F, D>) -> IoResult<Vec<u8>> {
let mut buffer = Vec::new();
buffer.write_common_circuit_data(self, gate_serializer)?;
Ok(buffer)
}
pub fn from_bytes(
bytes: Vec<u8>,
gate_serializer: &dyn GateSerializer<F, D>,
) -> IoResult<Self> {
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<F: RichField + Extendable<D>, const D: usize> CommonCircuitData<F, D> {
/// 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,

View File

@ -41,7 +41,7 @@ pub struct Proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const
pub opening_proof: FriProof<F, C::HCO, C::Hasher, D>,
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ProofTarget<const D: usize> {
pub wires_cap: MerkleCapTarget,
pub plonk_zs_partial_products_cap: MerkleCapTarget,
@ -305,13 +305,13 @@ pub(crate) struct FriInferredElements<F: RichField + Extendable<D>, const D: usi
pub Vec<F::Extension>,
);
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ProofWithPublicInputsTarget<const D: usize> {
pub proof: ProofTarget<D>,
pub public_inputs: Vec<Target>,
}
#[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<F: RichField + Extendable<D>, const D: usize> {
pub constants: Vec<F::Extension>,
@ -377,7 +377,7 @@ impl<F: RichField + Extendable<D>, const D: usize> OpeningSet<F, D> {
}
/// The purported values of each polynomial at a single point.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct OpeningSetTarget<const D: usize> {
pub constants: Vec<ExtensionTarget<D>>,
pub plonk_sigmas: Vec<ExtensionTarget<D>>,

View File

@ -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<C: GenericConfig<D>, const D: usize> VerifierOnlyCircuitData<C, D> {
fn from_slice(slice: &[C::F], common_data: &CommonCircuitData<C::F, D>) -> Result<Self>
@ -41,6 +42,23 @@ impl<C: GenericConfig<D>, const D: usize> VerifierOnlyCircuitData<C, D> {
}
impl VerifierCircuitTarget {
pub fn to_bytes(&self) -> IoResult<Vec<u8>> {
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<u8>) -> IoResult<Self> {
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<F: RichField + Extendable<D>, const D: usize>(
slice: &[Target],
common_data: &CommonCircuitData<F, D>,

View File

@ -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
@ -146,7 +154,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
#[derive(Debug)]
pub(crate) struct DummyProofGenerator<F, C, const D: usize>
pub struct DummyProofGenerator<F, C, const D: usize>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
@ -157,12 +165,101 @@ where
pub(crate) verifier_data: VerifierOnlyCircuitData<C, D>,
}
impl<F, C, const D: usize> Default for DummyProofGenerator<F, C, D>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
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:
<<C as GenericConfig<D>>::Hasher as Hasher<C::F, C::HCO>>::Hash::from_bytes(
&vec![0; <<C as GenericConfig<D>>::Hasher as Hasher<C::F, C::HCO>>::HASH_SIZE],
),
};
Self {
proof_with_pis_target,
proof_with_pis,
verifier_data_target,
verifier_data,
}
}
}
impl<F, C, const D: usize> DummyProofGenerator<F, C, D>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F> + 'static,
C::Hasher: AlgebraicHasher<F, C::HCO>,
{
pub fn deserialize_with_circuit_data(
src: &mut Buffer,
cd: &CommonCircuitData<F, D>,
) -> IoResult<Self> {
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<F, C, const D: usize> SimpleGenerator<F> for DummyProofGenerator<F, C, D>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F> + 'static,
C::Hasher: AlgebraicHasher<F, C::HCO>,
{
fn id(&self) -> String {
"DummyProofGenerator".to_string()
}
fn dependencies(&self) -> Vec<Target> {
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<u8>) -> 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<Self> {
panic!()
}
}

View File

@ -9,7 +9,6 @@ use crate::field::types::Field;
pub(crate) mod context_tree;
pub(crate) mod partial_products;
pub mod reducing;
pub mod serialization;
pub mod strided_view;

View File

@ -1,835 +0,0 @@
use alloc::vec;
use alloc::vec::Vec;
use core::convert::Infallible;
use core::fmt::{Debug, Display, Formatter};
use core::mem::size_of;
use hashbrown::HashMap;
use crate::field::extension::{Extendable, FieldExtension};
use crate::field::polynomial::PolynomialCoeffs;
use crate::field::types::{Field64, PrimeField64};
use crate::fri::proof::{
CompressedFriProof, CompressedFriQueryRounds, FriInitialTreeProof, FriProof, FriQueryRound,
FriQueryStep,
};
use crate::hash::hash_types::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::plonk::config::{GenericConfig, GenericHashOut, Hasher};
use crate::plonk::plonk_common::salt_size;
use crate::plonk::proof::{
CompressedProof, CompressedProofWithPublicInputs, OpeningSet, Proof, ProofWithPublicInputs,
};
/// A no_std compatible variant of `std::io::Error`
#[derive(Debug)]
pub struct IoError;
impl Display for IoError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
Debug::fmt(self, f)
}
}
/// A no_std compatible variant of `std::io::Result`
pub type IoResult<T> = Result<T, IoError>;
/// A `Read` which is able to report how many bytes are remaining.
pub trait Remaining: Read {
/// Returns the number of bytes remaining in the buffer.
fn remaining(&self) -> usize;
/// Returns whether zero bytes are remaining.
fn is_empty(&self) -> bool {
self.remaining() == 0
}
}
/// Similar to `std::io::Read`, but works with no_std.
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 `u8` value from `self`.
#[inline]
fn read_u8(&mut self) -> IoResult<u8> {
let mut buf = [0; size_of::<u8>()];
self.read_exact(&mut buf)?;
Ok(buf[0])
}
/// Reads a `u32` value from `self`.
#[inline]
fn read_u32(&mut self) -> IoResult<u32> {
let mut buf = [0; size_of::<u32>()];
self.read_exact(&mut buf)?;
Ok(u32::from_le_bytes(buf))
}
/// Reads a element from the field `F` with size less than `2^64` from `self.`
#[inline]
fn read_field<F>(&mut self) -> IoResult<F>
where
F: Field64,
{
let mut buf = [0; size_of::<u64>()];
self.read_exact(&mut buf)?;
Ok(F::from_canonical_u64(u64::from_le_bytes(buf)))
}
/// Reads a vector of elements from the field `F` from `self`.
#[inline]
fn read_field_vec<F>(&mut self, length: usize) -> IoResult<Vec<F>>
where
F: Field64,
{
(0..length)
.map(|_| self.read_field())
.collect::<Result<Vec<_>, _>>()
}
/// Reads an element from the field extension of `F` from `self.`
#[inline]
fn read_field_ext<F, const D: usize>(&mut self) -> IoResult<F::Extension>
where
F: Field64 + Extendable<D>,
{
let mut arr = [F::ZERO; D];
for a in arr.iter_mut() {
*a = self.read_field()?;
}
Ok(<F::Extension as FieldExtension<D>>::from_basefield_array(
arr,
))
}
/// Reads a vector of elements from the field extension of `F` from `self`.
#[inline]
fn read_field_ext_vec<F, const D: usize>(
&mut self,
length: usize,
) -> IoResult<Vec<F::Extension>>
where
F: RichField + Extendable<D>,
{
(0..length).map(|_| self.read_field_ext::<F, D>()).collect()
}
/// Reads a hash value from `self`.
#[inline]
fn read_hash<F, HC, H>(&mut self) -> IoResult<H::Hash>
where
F: RichField,
HC: HashConfig,
H: Hasher<F, HC>,
{
let mut buf = vec![0; H::HASH_SIZE];
self.read_exact(&mut buf)?;
Ok(H::Hash::from_bytes(&buf))
}
/// Reads a value of type [`MerkleCap`] from `self` with the given `cap_height`.
#[inline]
fn read_merkle_cap<F, HC, H>(&mut self, cap_height: usize) -> IoResult<MerkleCap<F, HC, H>>
where
F: RichField,
HC: HashConfig,
H: Hasher<F, HC>,
{
let cap_length = 1 << cap_height;
Ok(MerkleCap(
(0..cap_length)
.map(|_| self.read_hash::<F, HC, H>())
.collect::<Result<Vec<_>, _>>()?,
))
}
/// Reads a value of type [`OpeningSet`] from `self` with the given `common_data`.
#[inline]
fn read_opening_set<F, C, const D: usize>(
&mut self,
common_data: &CommonCircuitData<F, D>,
) -> IoResult<OpeningSet<F, D>>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
let config = &common_data.config;
let constants = self.read_field_ext_vec::<F, D>(common_data.num_constants)?;
let plonk_sigmas = self.read_field_ext_vec::<F, D>(config.num_routed_wires)?;
let wires = self.read_field_ext_vec::<F, D>(config.num_wires)?;
let plonk_zs = self.read_field_ext_vec::<F, D>(config.num_challenges)?;
let plonk_zs_next = self.read_field_ext_vec::<F, D>(config.num_challenges)?;
let partial_products = self
.read_field_ext_vec::<F, D>(common_data.num_partial_products * config.num_challenges)?;
let quotient_polys = self.read_field_ext_vec::<F, D>(
common_data.quotient_degree_factor * config.num_challenges,
)?;
Ok(OpeningSet {
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<F, HC, H>(&mut self) -> IoResult<MerkleProof<F, HC, H>>
where
F: RichField,
HC: HashConfig,
H: Hasher<F, HC>,
{
let length = self.read_u8()?;
Ok(MerkleProof {
siblings: (0..length)
.map(|_| self.read_hash::<F, HC, H>())
.collect::<Result<_, _>>()?,
})
}
/// Reads a value of type [`FriInitialTreeProof`] from `self` with the given `common_data`.
#[inline]
fn read_fri_initial_proof<F, C, const D: usize>(
&mut self,
common_data: &CommonCircuitData<F, D>,
) -> IoResult<FriInitialTreeProof<F, C::HCO, C::Hasher>>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
let config = &common_data.config;
let salt = salt_size(common_data.fri_params.hiding);
let mut evals_proofs = Vec::with_capacity(4);
let constants_sigmas_v =
self.read_field_vec(common_data.num_constants + config.num_routed_wires)?;
let constants_sigmas_p = self.read_merkle_proof()?;
evals_proofs.push((constants_sigmas_v, constants_sigmas_p));
let wires_v = self.read_field_vec(config.num_wires + salt)?;
let wires_p = self.read_merkle_proof()?;
evals_proofs.push((wires_v, wires_p));
let zs_partial_v = self.read_field_vec(
config.num_challenges * (1 + common_data.num_partial_products) + salt,
)?;
let zs_partial_p = self.read_merkle_proof()?;
evals_proofs.push((zs_partial_v, zs_partial_p));
let quotient_v =
self.read_field_vec(config.num_challenges * common_data.quotient_degree_factor + salt)?;
let quotient_p = self.read_merkle_proof()?;
evals_proofs.push((quotient_v, quotient_p));
Ok(FriInitialTreeProof { evals_proofs })
}
/// Reads a value of type [`FriQueryStep`] from `self` with the given `arity` and `compressed`
/// flag.
#[inline]
fn read_fri_query_step<F, C, const D: usize>(
&mut self,
arity: usize,
compressed: bool,
) -> IoResult<FriQueryStep<F, C::HCO, C::Hasher, D>>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
let evals = self.read_field_ext_vec::<F, D>(arity - usize::from(compressed))?;
let merkle_proof = self.read_merkle_proof()?;
Ok(FriQueryStep {
evals,
merkle_proof,
})
}
/// Reads a vector of [`FriQueryRound`]s from `self` with `common_data`.
#[inline]
#[allow(clippy::type_complexity)]
fn read_fri_query_rounds<F, C, const D: usize>(
&mut self,
common_data: &CommonCircuitData<F, D>,
) -> IoResult<Vec<FriQueryRound<F, C::HCO, C::Hasher, D>>>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
let config = &common_data.config;
let mut fqrs = Vec::with_capacity(config.fri_config.num_query_rounds);
for _ in 0..config.fri_config.num_query_rounds {
let initial_trees_proof = self.read_fri_initial_proof::<F, C, D>(common_data)?;
let steps = common_data
.fri_params
.reduction_arity_bits
.iter()
.map(|&ar| self.read_fri_query_step::<F, C, D>(1 << ar, false))
.collect::<Result<_, _>>()?;
fqrs.push(FriQueryRound {
initial_trees_proof,
steps,
})
}
Ok(fqrs)
}
/// Reads a value of type [`FriProof`] from `self` with `common_data`.
#[inline]
fn read_fri_proof<F, C, const D: usize>(
&mut self,
common_data: &CommonCircuitData<F, D>,
) -> IoResult<FriProof<F, C::HCO, C::Hasher, D>>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
let config = &common_data.config;
let commit_phase_merkle_caps = (0..common_data.fri_params.reduction_arity_bits.len())
.map(|_| self.read_merkle_cap(config.fri_config.cap_height))
.collect::<Result<Vec<_>, _>>()?;
let query_round_proofs = self.read_fri_query_rounds::<F, C, D>(common_data)?;
let final_poly = PolynomialCoeffs::new(
self.read_field_ext_vec::<F, D>(common_data.fri_params.final_poly_len())?,
);
let pow_witness = self.read_field()?;
Ok(FriProof {
commit_phase_merkle_caps,
query_round_proofs,
final_poly,
pow_witness,
})
}
/// Reads a value of type [`Proof`] from `self` with `common_data`.
#[inline]
fn read_proof<F, C, const D: usize>(
&mut self,
common_data: &CommonCircuitData<F, D>,
) -> IoResult<Proof<F, C, D>>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
let config = &common_data.config;
let wires_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
let plonk_zs_partial_products_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
let quotient_polys_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
let openings = self.read_opening_set::<F, C, D>(common_data)?;
let opening_proof = self.read_fri_proof::<F, C, D>(common_data)?;
Ok(Proof {
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<F, C, const D: usize>(
&mut self,
common_data: &CommonCircuitData<F, D>,
) -> IoResult<ProofWithPublicInputs<F, C, D>>
where
Self: Remaining,
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
let proof = self.read_proof(common_data)?;
let public_inputs = self.read_field_vec(self.remaining() / size_of::<u64>())?;
Ok(ProofWithPublicInputs {
proof,
public_inputs,
})
}
/// Reads a value of type [`CompressedFriQueryRounds`] from `self` with `common_data`.
#[inline]
fn read_compressed_fri_query_rounds<F, C, const D: usize>(
&mut self,
common_data: &CommonCircuitData<F, D>,
) -> IoResult<CompressedFriQueryRounds<F, C::HCO, C::Hasher, D>>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
let config = &common_data.config;
let original_indices = (0..config.fri_config.num_query_rounds)
.map(|_| self.read_u32().map(|i| i as usize))
.collect::<Result<Vec<_>, _>>()?;
let mut indices = original_indices.clone();
indices.sort_unstable();
indices.dedup();
let mut pairs = Vec::new();
for &i in &indices {
pairs.push((i, self.read_fri_initial_proof::<F, C, D>(common_data)?));
}
let initial_trees_proofs = HashMap::from_iter(pairs);
let mut steps = Vec::with_capacity(common_data.fri_params.reduction_arity_bits.len());
for &a in &common_data.fri_params.reduction_arity_bits {
indices.iter_mut().for_each(|x| {
*x >>= a;
});
indices.dedup();
let query_steps = (0..indices.len())
.map(|_| self.read_fri_query_step::<F, C, D>(1 << a, true))
.collect::<Result<Vec<_>, _>>()?;
steps.push(
indices
.iter()
.copied()
.zip(query_steps)
.collect::<HashMap<_, _>>(),
);
}
Ok(CompressedFriQueryRounds {
indices: original_indices,
initial_trees_proofs,
steps,
})
}
/// Reads a value of type [`CompressedFriProof`] from `self` with `common_data`.
#[inline]
fn read_compressed_fri_proof<F, C, const D: usize>(
&mut self,
common_data: &CommonCircuitData<F, D>,
) -> IoResult<CompressedFriProof<F, C::HCO, C::Hasher, D>>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
let config = &common_data.config;
let commit_phase_merkle_caps = (0..common_data.fri_params.reduction_arity_bits.len())
.map(|_| self.read_merkle_cap(config.fri_config.cap_height))
.collect::<Result<Vec<_>, _>>()?;
let query_round_proofs = self.read_compressed_fri_query_rounds::<F, C, D>(common_data)?;
let final_poly = PolynomialCoeffs::new(
self.read_field_ext_vec::<F, D>(common_data.fri_params.final_poly_len())?,
);
let pow_witness = self.read_field()?;
Ok(CompressedFriProof {
commit_phase_merkle_caps,
query_round_proofs,
final_poly,
pow_witness,
})
}
/// Reads a value of type [`CompressedProof`] from `self` with `common_data`.
#[inline]
fn read_compressed_proof<F, C, const D: usize>(
&mut self,
common_data: &CommonCircuitData<F, D>,
) -> IoResult<CompressedProof<F, C, D>>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
let config = &common_data.config;
let wires_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
let plonk_zs_partial_products_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
let quotient_polys_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
let openings = self.read_opening_set::<F, C, D>(common_data)?;
let opening_proof = self.read_compressed_fri_proof::<F, C, D>(common_data)?;
Ok(CompressedProof {
wires_cap,
plonk_zs_partial_products_cap,
quotient_polys_cap,
openings,
opening_proof,
})
}
/// Reads a value of type [`CompressedProofWithPublicInputs`] from `self` with `common_data`.
#[inline]
fn read_compressed_proof_with_public_inputs<F, C, const D: usize>(
&mut self,
common_data: &CommonCircuitData<F, D>,
) -> IoResult<CompressedProofWithPublicInputs<F, C, D>>
where
Self: Remaining,
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
let proof = self.read_compressed_proof(common_data)?;
let public_inputs = self.read_field_vec(self.remaining() / size_of::<u64>())?;
Ok(CompressedProofWithPublicInputs {
proof,
public_inputs,
})
}
}
/// Writing
pub trait Write {
/// Error Type
type Error;
/// Writes all `bytes` to `self`.
fn write_all(&mut self, bytes: &[u8]) -> IoResult<()>;
/// Writes a byte `x` to `self`.
#[inline]
fn write_u8(&mut self, x: u8) -> IoResult<()> {
self.write_all(&[x])
}
/// Writes a word `x` to `self.`
#[inline]
fn write_u32(&mut self, x: u32) -> IoResult<()> {
self.write_all(&x.to_le_bytes())
}
/// Writes an element `x` from the field `F` to `self`.
#[inline]
fn write_field<F>(&mut self, x: F) -> IoResult<()>
where
F: PrimeField64,
{
self.write_all(&x.to_canonical_u64().to_le_bytes())
}
/// Writes a vector `v` of elements from the field `F` to `self`.
#[inline]
fn write_field_vec<F>(&mut self, v: &[F]) -> IoResult<()>
where
F: PrimeField64,
{
for &a in v {
self.write_field(a)?;
}
Ok(())
}
/// Writes an element `x` from the field extension of `F` to `self`.
#[inline]
fn write_field_ext<F, const D: usize>(&mut self, x: F::Extension) -> IoResult<()>
where
F: RichField + Extendable<D>,
{
for &a in &x.to_basefield_array() {
self.write_field(a)?;
}
Ok(())
}
/// Writes a vector `v` of elements from the field extension of `F` to `self`.
#[inline]
fn write_field_ext_vec<F, const D: usize>(&mut self, v: &[F::Extension]) -> IoResult<()>
where
F: RichField + Extendable<D>,
{
for &a in v {
self.write_field_ext::<F, D>(a)?;
}
Ok(())
}
/// Writes a hash `h` to `self`.
#[inline]
fn write_hash<F, HC, H>(&mut self, h: H::Hash) -> IoResult<()>
where
F: RichField,
HC: HashConfig,
H: Hasher<F, HC>,
{
self.write_all(&h.to_bytes())
}
/// Writes `cap`, a value of type [`MerkleCap`], to `self`.
#[inline]
fn write_merkle_cap<F, HC, H>(&mut self, cap: &MerkleCap<F, HC, H>) -> IoResult<()>
where
F: RichField,
HC: HashConfig,
H: Hasher<F, HC>,
{
for &a in &cap.0 {
self.write_hash::<F, HC, H>(a)?;
}
Ok(())
}
/// Writes a value `os` of type [`OpeningSet`] to `self.`
#[inline]
fn write_opening_set<F, const D: usize>(&mut self, os: &OpeningSet<F, D>) -> IoResult<()>
where
F: RichField + Extendable<D>,
{
self.write_field_ext_vec::<F, D>(&os.constants)?;
self.write_field_ext_vec::<F, D>(&os.plonk_sigmas)?;
self.write_field_ext_vec::<F, D>(&os.wires)?;
self.write_field_ext_vec::<F, D>(&os.plonk_zs)?;
self.write_field_ext_vec::<F, D>(&os.plonk_zs_next)?;
self.write_field_ext_vec::<F, D>(&os.partial_products)?;
self.write_field_ext_vec::<F, D>(&os.quotient_polys)
}
/// Writes a value `p` of type [`MerkleProof`] to `self.`
#[inline]
fn write_merkle_proof<F, HC, H>(&mut self, p: &MerkleProof<F, HC, H>) -> IoResult<()>
where
F: RichField,
HC: HashConfig,
H: Hasher<F, HC>,
{
let length = p.siblings.len();
self.write_u8(
length
.try_into()
.expect("Merkle proof length must fit in u8."),
)?;
for &h in &p.siblings {
self.write_hash::<F, HC, H>(h)?;
}
Ok(())
}
/// Writes a value `fitp` of type [`FriInitialTreeProof`] to `self.`
#[inline]
fn write_fri_initial_proof<F, C, const D: usize>(
&mut self,
fitp: &FriInitialTreeProof<F, C::HCO, C::Hasher>,
) -> IoResult<()>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
for (v, p) in &fitp.evals_proofs {
self.write_field_vec(v)?;
self.write_merkle_proof(p)?;
}
Ok(())
}
/// Writes a value `fqs` of type [`FriQueryStep`] to `self.`
#[inline]
fn write_fri_query_step<F, C, const D: usize>(
&mut self,
fqs: &FriQueryStep<F, C::HCO, C::Hasher, D>,
) -> IoResult<()>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
self.write_field_ext_vec::<F, D>(&fqs.evals)?;
self.write_merkle_proof(&fqs.merkle_proof)
}
/// Writes a value `fqrs` of type [`FriQueryRound`] to `self.`
#[inline]
fn write_fri_query_rounds<F, C, const D: usize>(
&mut self,
fqrs: &[FriQueryRound<F, C::HCO, C::Hasher, D>],
) -> IoResult<()>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
for fqr in fqrs {
self.write_fri_initial_proof::<F, C, D>(&fqr.initial_trees_proof)?;
for fqs in &fqr.steps {
self.write_fri_query_step::<F, C, D>(fqs)?;
}
}
Ok(())
}
/// Writes a value `fq` of type [`FriProof`] to `self.`
#[inline]
fn write_fri_proof<F, C, const D: usize>(
&mut self,
fp: &FriProof<F, C::HCO, C::Hasher, D>,
) -> IoResult<()>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
for cap in &fp.commit_phase_merkle_caps {
self.write_merkle_cap(cap)?;
}
self.write_fri_query_rounds::<F, C, D>(&fp.query_round_proofs)?;
self.write_field_ext_vec::<F, D>(&fp.final_poly.coeffs)?;
self.write_field(fp.pow_witness)
}
/// Writes a value `proof` of type [`Proof`] to `self.`
#[inline]
fn write_proof<F, C, const D: usize>(&mut self, proof: &Proof<F, C, D>) -> IoResult<()>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
self.write_merkle_cap(&proof.wires_cap)?;
self.write_merkle_cap(&proof.plonk_zs_partial_products_cap)?;
self.write_merkle_cap(&proof.quotient_polys_cap)?;
self.write_opening_set(&proof.openings)?;
self.write_fri_proof::<F, C, D>(&proof.opening_proof)
}
/// Writes a value `proof_with_pis` of type [`ProofWithPublicInputs`] to `self.`
#[inline]
fn write_proof_with_public_inputs<F, C, const D: usize>(
&mut self,
proof_with_pis: &ProofWithPublicInputs<F, C, D>,
) -> IoResult<()>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
let ProofWithPublicInputs {
proof,
public_inputs,
} = proof_with_pis;
self.write_proof(proof)?;
self.write_field_vec(public_inputs)
}
/// Writes a value `cfqrs` of type [`CompressedFriQueryRounds`] to `self.`
#[inline]
fn write_compressed_fri_query_rounds<F, C, const D: usize>(
&mut self,
cfqrs: &CompressedFriQueryRounds<F, C::HCO, C::Hasher, D>,
) -> IoResult<()>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
for &i in &cfqrs.indices {
self.write_u32(i as u32)?;
}
let mut initial_trees_proofs = cfqrs.initial_trees_proofs.iter().collect::<Vec<_>>();
initial_trees_proofs.sort_by_key(|&x| x.0);
for (_, itp) in initial_trees_proofs {
self.write_fri_initial_proof::<F, C, D>(itp)?;
}
for h in &cfqrs.steps {
let mut fri_query_steps = h.iter().collect::<Vec<_>>();
fri_query_steps.sort_by_key(|&x| x.0);
for (_, fqs) in fri_query_steps {
self.write_fri_query_step::<F, C, D>(fqs)?;
}
}
Ok(())
}
/// Writes a value `fq` of type [`CompressedFriProof`] to `self.`
#[inline]
fn write_compressed_fri_proof<F, C, const D: usize>(
&mut self,
fp: &CompressedFriProof<F, C::HCO, C::Hasher, D>,
) -> IoResult<()>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
for cap in &fp.commit_phase_merkle_caps {
self.write_merkle_cap(cap)?;
}
self.write_compressed_fri_query_rounds::<F, C, D>(&fp.query_round_proofs)?;
self.write_field_ext_vec::<F, D>(&fp.final_poly.coeffs)?;
self.write_field(fp.pow_witness)
}
/// Writes a value `proof` of type [`CompressedProof`] to `self.`
#[inline]
fn write_compressed_proof<F, C, const D: usize>(
&mut self,
proof: &CompressedProof<F, C, D>,
) -> IoResult<()>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
self.write_merkle_cap(&proof.wires_cap)?;
self.write_merkle_cap(&proof.plonk_zs_partial_products_cap)?;
self.write_merkle_cap(&proof.quotient_polys_cap)?;
self.write_opening_set(&proof.openings)?;
self.write_compressed_fri_proof::<F, C, D>(&proof.opening_proof)
}
/// Writes a value `proof_with_pis` of type [`CompressedProofWithPublicInputs`] to `self.`
#[inline]
fn write_compressed_proof_with_public_inputs<F, C, const D: usize>(
&mut self,
proof_with_pis: &CompressedProofWithPublicInputs<F, C, D>,
) -> IoResult<()>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
let CompressedProofWithPublicInputs {
proof,
public_inputs,
} = proof_with_pis;
self.write_compressed_proof(proof)?;
self.write_field_vec(public_inputs)
}
}
impl Write for Vec<u8> {
type Error = Infallible;
#[inline]
fn write_all(&mut self, bytes: &[u8]) -> IoResult<()> {
self.extend_from_slice(bytes);
Ok(())
}
}
/// Buffer
#[cfg(feature = "std")]
#[derive(Debug)]
pub struct Buffer {
bytes: Vec<u8>,
pos: usize,
}
#[cfg(feature = "std")]
impl Buffer {
/// Builds a new [`Buffer`] over `buffer`.
#[inline]
pub fn new(bytes: Vec<u8>) -> Self {
Self { bytes, pos: 0 }
}
/// Returns the inner buffer.
#[inline]
pub fn bytes(self) -> Vec<u8> {
self.bytes
}
}
#[cfg(feature = "std")]
impl Remaining for Buffer {
fn remaining(&self) -> usize {
self.bytes.len() - self.pos
}
}
#[cfg(feature = "std")]
impl Read for Buffer {
#[inline]
fn read_exact(&mut self, bytes: &mut [u8]) -> IoResult<()> {
let n = bytes.len();
if self.remaining() < n {
Err(IoError)
} else {
bytes.copy_from_slice(&self.bytes[self.pos..][..n]);
self.pos += n;
Ok(())
}
}
}

View File

@ -0,0 +1,105 @@
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<F: RichField + Extendable<D>, const D: usize> {
fn read_gate(&self, buf: &mut Buffer) -> IoResult<GateRef<F, D>>;
fn write_gate(&self, buf: &mut Vec<u8>, gate: &GateRef<F, D>) -> 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<F, D>>::deserialize(buf)?;
Ok($crate::gates::gate::GateRef::<F, D>::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 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<F, D>> {
let tag = $crate::util::serialization::Read::read_u32(buf)?;
read_gate_impl!(buf, tag, $($gate_types),+)
}
fn write_gate(&self, buf: &mut Vec<u8>, gate: &$crate::gates::gate::GateRef<F, D>) -> $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::serialization::GateSerializer;
pub struct DefaultGateSerializer;
impl<F: RichField + Extendable<D>, const D: usize> GateSerializer<F, D> for DefaultGateSerializer {
impl_gate_serializer! {
DefaultGateSerializer,
ArithmeticGate,
ArithmeticExtensionGate<D>,
BaseSumGate<2>,
ConstantGate,
CosetInterpolationGate<F, D>,
ExponentiationGate<F, D>,
MulExtensionGate<D>,
NoopGate,
PoseidonMdsGate<F, D>,
PoseidonGate<F, D>,
PublicInputGate,
RandomAccessGate<F, D>,
ReducingExtensionGate<D>,
ReducingGate<D>
}
}
}

View File

@ -0,0 +1,164 @@
//! 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<F: RichField + Extendable<D>, const D: usize> {
fn read_generator(
&self,
buf: &mut Buffer,
common: &CommonCircuitData<F, D>,
) -> IoResult<WitnessGeneratorRef<F>>;
fn write_generator(
&self,
buf: &mut Vec<u8>,
generator: &WitnessGeneratorRef<F>,
) -> 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<F, C, D> =
$crate::recursion::dummy_circuit::DummyProofGenerator::deserialize_with_circuit_data(buf, $common)?;
return Ok($crate::iop::generator::WitnessGeneratorRef::<F>::new(
$crate::iop::generator::SimpleGenerator::<F>::adapter(generator),
));
}
$(if tag == i.next().unwrap() {
let generator =
<$generator_types as $crate::iop::generator::SimpleGenerator<F>>::deserialize(buf)?;
Ok($crate::iop::generator::WitnessGeneratorRef::<F>::new(
$crate::iop::generator::SimpleGenerator::<F>::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::<F>::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 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(
&self,
buf: &mut $crate::util::serialization::Buffer,
common: &$crate::plonk::circuit_data::CommonCircuitData<F, D>,
) -> $crate::util::serialization::IoResult<$crate::iop::generator::WitnessGeneratorRef<F>> {
let tag = $crate::util::serialization::Read::read_u32(buf)?;
read_generator_impl!(buf, tag, common, $($generator_types),+)
}
fn write_generator(
&self,
buf: &mut Vec<u8>,
generator: &$crate::iop::generator::WitnessGeneratorRef<F>,
) -> $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::serialization::WitnessGeneratorSerializer;
pub struct DefaultGeneratorSerializer<C: GenericConfig<D>, const D: usize> {
pub _phantom: PhantomData<C>,
}
impl<F, C, const D: usize> WitnessGeneratorSerializer<F, D> for DefaultGeneratorSerializer<C, D>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F> + 'static,
C::Hasher: AlgebraicHasher<F, C::HCO>,
{
impl_generator_serializer! {
DefaultGeneratorSerializer,
DummyProofGenerator<F, C, D>,
ArithmeticBaseGenerator<F, D>,
ArithmeticExtensionGenerator<F, D>,
BaseSplitGenerator<2>,
BaseSumGenerator<2>,
ConstantGenerator<F>,
CopyGenerator,
EqualityGenerator,
ExponentiationGenerator<F, D>,
InterpolationGenerator<F, D>,
LowHighGenerator,
MulExtensionGenerator<F, D>,
NonzeroTestGenerator,
PoseidonGenerator<F, D>,
PoseidonMdsGenerator<D>,
QuotientGeneratorExtension<D>,
RandomAccessGenerator<F, D>,
RandomValueGenerator,
ReducingGenerator<D>,
ReducingExtensionGenerator<D>,
SplitGenerator,
WireSplitGenerator
}
}
}

File diff suppressed because it is too large Load Diff