Merge pull request #980 from mir-protocol/serialize_common_circuit_data

Serialize common circuit data
This commit is contained in:
Nicholas Ward 2023-04-26 18:36:33 -07:00 committed by GitHub
commit efd5a81bb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 109 additions and 12 deletions

View File

@ -31,6 +31,7 @@ plonky2_util = { version = "0.1.0", default-features = false }
rand = { version = "0.8.4", default-features = false }
rand_chacha = { version = "0.3.1", optional = true, default-features = false }
serde = { version = "1.0", default-features = false, features = ["derive"] }
serde_json = "1.0"
static_assertions = { version = "1.1.0", default-features = false }
unroll = { version = "0.1.5", default-features = false }

View File

@ -0,0 +1,79 @@
#![allow(clippy::upper_case_acronyms)]
use std::fs;
use anyhow::Result;
use plonky2::field::types::Field;
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
/// An example of using Plonky2 to prove a statement of the form
/// "I know the 100th element of the Fibonacci sequence, starting with constants a and b."
/// When a == 0 and b == 1, this is proving knowledge of the 100th (standard) Fibonacci number.
/// This example also serializes the circuit data and proof to JSON files.
fn main() -> Result<()> {
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
// The arithmetic circuit.
let initial_a = builder.add_virtual_target();
let initial_b = builder.add_virtual_target();
let mut prev_target = initial_a;
let mut cur_target = initial_b;
for _ in 0..99 {
let temp = builder.add(prev_target, cur_target);
prev_target = cur_target;
cur_target = temp;
}
// Public inputs are the two initial values (provided below) and the result (which is generated).
builder.register_public_input(initial_a);
builder.register_public_input(initial_b);
builder.register_public_input(cur_target);
// Provide initial values.
let mut pw = PartialWitness::new();
pw.set_target(initial_a, F::ZERO);
pw.set_target(initial_b, F::ONE);
let data = builder.build::<C>();
let common_circuit_data_serialized = serde_json::to_string(&data.common).unwrap();
fs::write("common_circuit_data.json", common_circuit_data_serialized)
.expect("Unable to write file");
let verifier_only_circuit_data_serialized = serde_json::to_string(&data.verifier_only).unwrap();
fs::write(
"verifier_only_circuit_data.json",
verifier_only_circuit_data_serialized,
)
.expect("Unable to write file");
let proof = data.prove(pw)?;
let proof_serialized = serde_json::to_string(&proof).unwrap();
fs::write("proof_with_public_inputs.json", proof_serialized).expect("Unable to write file");
let proof_challenges = proof
.get_challenges(
proof.get_public_inputs_hash(),
&data.verifier_only.circuit_digest,
&data.common,
)
.unwrap();
let proof_challenges_serialized = serde_json::to_string(&proof_challenges).unwrap();
fs::write("proof_challenges.json", proof_challenges_serialized).expect("Unable to write file");
println!(
"100th Fibonacci number mod |F| (starting with {}, {}) is: {}",
proof.public_inputs[0], proof.public_inputs[1], proof.public_inputs[2]
);
data.verify(proof)
}

View File

@ -1,5 +1,7 @@
use alloc::vec::Vec;
use serde::Serialize;
use crate::fri::reduction_strategies::FriReductionStrategy;
mod challenges;
@ -13,7 +15,7 @@ mod validate_shape;
pub mod verifier;
pub mod witness_util;
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
pub struct FriConfig {
/// `rate = 2^{-rate_bits}`.
pub rate_bits: usize,
@ -56,7 +58,7 @@ impl FriConfig {
/// FRI parameters, including generated parameters which are specific to an instance size, in
/// contrast to `FriConfig` which is user-specified and independent of instance size.
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
pub struct FriParams {
/// User-specified FRI configuration.
pub config: FriConfig,

View File

@ -393,6 +393,7 @@ impl<F: RichField + Extendable<D>, HCO: HashConfig, H: Hasher<F, HCO>, const D:
}
}
#[derive(Serialize)]
pub struct FriChallenges<F: RichField + Extendable<D>, const D: usize> {
// Scaling factor to combine polynomials.
pub fri_alpha: F::Extension,

View File

@ -4,9 +4,10 @@ use alloc::vec::Vec;
use std::time::Instant;
use log::debug;
use serde::Serialize;
/// A method for deciding what arity to use at each reduction layer.
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
pub enum FriReductionStrategy {
/// Specifies the exact sequence of arities (expressed in bits) to use.
Fixed(Vec<usize>),

View File

@ -2,6 +2,8 @@ use alloc::string::String;
use alloc::vec::Vec;
use alloc::{format, vec};
use serde::{Deserialize, Serialize};
use crate::field::extension::Extendable;
use crate::field::packed::PackedField;
use crate::gates::gate::Gate;
@ -18,7 +20,7 @@ use crate::plonk::vars::{
use crate::util::serialization::{Buffer, IoResult, Read, Write};
/// A gate which takes a single constant parameter and outputs that value.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct ConstantGate {
pub(crate) num_consts: usize,
}

View File

@ -8,6 +8,7 @@ use core::hash::{Hash, Hasher};
use core::ops::Range;
use hashbrown::HashMap;
use serde::{Serialize, Serializer};
use crate::field::batch_util::batch_multiply_inplace;
use crate::field::extension::{Extendable, FieldExtension};
@ -239,6 +240,12 @@ impl<F: RichField + Extendable<D>, const D: usize> Debug for GateRef<F, D> {
}
}
impl<F: RichField + Extendable<D>, const D: usize> Serialize for GateRef<F, D> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.0.id())
}
}
/// Map between gate parameters and available slots.
/// An available slot is of the form `(row, op)`, meaning the current available slot
/// is at gate index `row` in the `op`-th operation.

View File

@ -2,6 +2,8 @@ use alloc::vec;
use alloc::vec::Vec;
use core::ops::Range;
use serde::Serialize;
use crate::field::extension::Extendable;
use crate::field::polynomial::PolynomialValues;
use crate::gates::gate::{GateInstance, GateRef};
@ -10,7 +12,7 @@ use crate::hash::hash_types::RichField;
/// Placeholder value to indicate that a gate doesn't use a selector polynomial.
pub(crate) const UNUSED_SELECTOR: usize = u32::MAX as usize;
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
pub struct SelectorsInfo {
pub(crate) selector_indices: Vec<usize>,
pub(crate) groups: Vec<Range<usize>>,

View File

@ -4,6 +4,7 @@ use alloc::vec::Vec;
use core::ops::{Range, RangeFrom};
use anyhow::Result;
use serde::Serialize;
use crate::field::extension::Extendable;
use crate::field::fft::FftRootTable;
@ -35,7 +36,7 @@ use crate::util::serialization::{
};
use crate::util::timing::TimingTree;
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct CircuitConfig {
pub num_wires: usize,
pub num_routed_wires: usize,
@ -347,7 +348,7 @@ pub struct ProverOnlyCircuitData<
}
/// Circuit data required by the verifier, but not the prover.
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
pub struct VerifierOnlyCircuitData<C: GenericConfig<D>, const D: usize> {
/// A commitment to each constant polynomial and each permutation polynomial.
pub constants_sigmas_cap: MerkleCap<C::F, C::HCO, C::Hasher>,
@ -370,7 +371,7 @@ impl<C: GenericConfig<D>, const D: usize> VerifierOnlyCircuitData<C, D> {
}
/// Circuit data required by both the prover and the verifier.
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
pub struct CommonCircuitData<F: RichField + Extendable<D>, const D: usize> {
pub config: CircuitConfig,

View File

@ -117,7 +117,7 @@ impl HashConfig for PoseidonHashConfig {
const WIDTH: usize = 12;
}
/// Configuration using Poseidon over the Goldilocks field.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize)]
pub struct PoseidonGoldilocksConfig;
impl GenericConfig<2> for PoseidonGoldilocksConfig {
type F = GoldilocksField;

View File

@ -94,7 +94,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
}
/// Computes all Fiat-Shamir challenges used in the Plonk proof.
pub(crate) fn get_challenges(
pub fn get_challenges(
&self,
public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F, C::HCI>>::Hash,
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F, C::HCO>>::Hash,

View File

@ -102,7 +102,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
})
}
pub(crate) fn get_public_inputs_hash(
pub fn get_public_inputs_hash(
&self,
) -> <<C as GenericConfig<D>>::InnerHasher as Hasher<F, C::HCI>>::Hash
where
@ -276,7 +276,8 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
}
}
pub(crate) struct ProofChallenges<F: RichField + Extendable<D>, const D: usize> {
#[derive(Serialize)]
pub struct ProofChallenges<F: RichField + Extendable<D>, const D: usize> {
/// Random values used in Plonk's permutation argument.
pub plonk_betas: Vec<F>,