use std::marker::PhantomData;
use crate::{
circuits::{constraints::ConstraintSystem, gate::CircuitGate, polynomial::COLUMNS},
curve::KimchiCurve,
groupmap::GroupMap,
mina_poseidon::FqSponge,
plonk_sponge::FrSponge,
proof::ProverProof,
prover_index::ProverIndex,
verifier::verify,
verifier_index::VerifierIndex,
};
use ark_ec::AffineRepr;
use ark_ff::PrimeField;
use log::debug;
use poly_commitment::{commitment::CommitmentCurve, OpenProof, SRS};
use super::{errors::SnarkyResult, runner::RunState, snarky_type::SnarkyType};
#[derive(Debug)]
pub struct Witness<F>(pub [Vec<F>; COLUMNS]);
type ScalarField<C> = <C as AffineRepr>::ScalarField;
type BaseField<C> = <C as AffineRepr>::BaseField;
pub struct ProverIndexWrapper<Circuit>
where
Circuit: SnarkyCircuit,
{
compiled_circuit: CompiledCircuit<Circuit>,
index: ProverIndex<Circuit::Curve, Circuit::Proof>,
}
type Proof<C> = ProverProof<<C as SnarkyCircuit>::Curve, <C as SnarkyCircuit>::Proof>;
type Output<C> = <<C as SnarkyCircuit>::PublicOutput as SnarkyType<
ScalarField<<C as SnarkyCircuit>::Curve>,
>>::OutOfCircuit;
impl<Circuit> ProverIndexWrapper<Circuit>
where
Circuit: SnarkyCircuit,
{
pub fn asm(&self) -> String {
crate::circuits::gate::Circuit::new(
self.compiled_circuit.public_input_size,
&self.compiled_circuit.gates,
)
.generate_asm()
}
pub fn prove<EFqSponge, EFrSponge>(
&mut self,
public_input: <Circuit::PublicInput as SnarkyType<ScalarField<Circuit::Curve>>>::OutOfCircuit,
private_input: Circuit::PrivateInput,
debug: bool,
) -> SnarkyResult<(Proof<Circuit>, Box<Output<Circuit>>)>
where
<Circuit::Curve as AffineRepr>::BaseField: PrimeField,
EFqSponge: Clone
+ FqSponge<BaseField<Circuit::Curve>, Circuit::Curve, ScalarField<Circuit::Curve>>,
EFrSponge: FrSponge<ScalarField<Circuit::Curve>>,
{
let public_input_without_output =
Circuit::PublicInput::value_to_field_elements(&public_input).0;
self.compiled_circuit
.sys
.generate_witness_init(public_input_without_output.clone())?;
let public_input_var: Circuit::PublicInput = self.compiled_circuit.sys.public_input();
let return_var = self.compiled_circuit.circuit.circuit(
&mut self.compiled_circuit.sys,
public_input_var,
Some(&private_input),
)?;
let (return_cvars, aux) = return_var.to_cvars();
let mut public_output_values = vec![];
for cvar in &return_cvars {
public_output_values.push(cvar.eval(&self.compiled_circuit.sys));
}
{
let eval_constraints = self.compiled_circuit.sys.eval_constraints;
self.compiled_circuit.sys.eval_constraints = false;
self.compiled_circuit.sys.wire_public_output(return_var)?;
self.compiled_circuit.sys.eval_constraints = eval_constraints;
}
let mut witness = self.compiled_circuit.sys.generate_witness();
let start = Circuit::PublicInput::SIZE_IN_FIELD_ELEMENTS;
let end = start + Circuit::PublicOutput::SIZE_IN_FIELD_ELEMENTS;
for (cell, val) in &mut witness.0[0][start..end]
.iter_mut()
.zip(&public_output_values)
{
*cell = *val;
}
let mut public_input_and_output = public_input_without_output;
public_input_and_output.extend(public_output_values.clone());
let public_output =
Circuit::PublicOutput::value_of_field_elements(public_output_values, aux);
if debug {
witness.debug();
self.index
.verify(&witness.0, &public_input_and_output)
.unwrap();
}
let group_map = <Circuit::Curve as CommitmentCurve>::Map::setup();
let proof: ProverProof<Circuit::Curve, Circuit::Proof> =
ProverProof::create::<EFqSponge, EFrSponge, _>(
&group_map,
witness.0,
&[],
&self.index,
&mut rand::rngs::OsRng,
)
.unwrap();
Ok((proof, Box::new(public_output)))
}
}
pub struct VerifierIndexWrapper<Circuit>
where
Circuit: SnarkyCircuit,
{
index: VerifierIndex<Circuit::Curve, Circuit::Proof>,
}
impl<Circuit> VerifierIndexWrapper<Circuit>
where
Circuit: SnarkyCircuit,
{
pub fn verify<EFqSponge, EFrSponge>(
&self,
proof: ProverProof<Circuit::Curve, Circuit::Proof>,
public_input: <Circuit::PublicInput as SnarkyType<ScalarField<Circuit::Curve>>>::OutOfCircuit,
public_output: <Circuit::PublicOutput as SnarkyType<ScalarField<Circuit::Curve>>>::OutOfCircuit,
) where
<Circuit::Curve as AffineRepr>::BaseField: PrimeField,
EFqSponge: Clone
+ FqSponge<BaseField<Circuit::Curve>, Circuit::Curve, ScalarField<Circuit::Curve>>,
EFrSponge: FrSponge<ScalarField<Circuit::Curve>>,
{
let mut public_input = Circuit::PublicInput::value_to_field_elements(&public_input).0;
public_input.extend(Circuit::PublicOutput::value_to_field_elements(&public_output).0);
let group_map = <Circuit::Curve as CommitmentCurve>::Map::setup();
verify::<Circuit::Curve, EFqSponge, EFrSponge, Circuit::Proof>(
&group_map,
&self.index,
&proof,
&public_input,
)
.unwrap()
}
}
pub struct CompiledCircuit<Circuit>
where
Circuit: SnarkyCircuit,
{
circuit: Circuit,
sys: RunState<ScalarField<Circuit::Curve>>,
public_input_size: usize,
pub gates: Vec<CircuitGate<ScalarField<Circuit::Curve>>>,
phantom: PhantomData<Circuit>,
}
fn compile<Circuit: SnarkyCircuit>(circuit: Circuit) -> SnarkyResult<CompiledCircuit<Circuit>> {
let public_input_size = Circuit::PublicInput::SIZE_IN_FIELD_ELEMENTS
+ Circuit::PublicOutput::SIZE_IN_FIELD_ELEMENTS;
let mut sys = RunState::new::<Circuit::Curve>(
Circuit::PublicInput::SIZE_IN_FIELD_ELEMENTS,
Circuit::PublicOutput::SIZE_IN_FIELD_ELEMENTS,
true,
);
let public_input: Circuit::PublicInput = sys.public_input();
let return_var = circuit.circuit(&mut sys, public_input, None)?;
let gates = sys.wire_output_and_compile(return_var).unwrap();
let gates = gates.to_vec();
let compiled_circuit = CompiledCircuit {
circuit,
sys,
public_input_size,
gates,
phantom: PhantomData,
};
Ok(compiled_circuit)
}
pub trait SnarkyCircuit: Sized {
type Curve: KimchiCurve;
type Proof: OpenProof<Self::Curve>;
type PrivateInput;
type PublicInput: SnarkyType<ScalarField<Self::Curve>>;
type PublicOutput: SnarkyType<ScalarField<Self::Curve>>;
fn circuit(
&self,
sys: &mut RunState<ScalarField<Self::Curve>>,
public_input: Self::PublicInput,
private_input: Option<&Self::PrivateInput>,
) -> SnarkyResult<Self::PublicOutput>;
fn compile_to_indexes(
self,
) -> SnarkyResult<(ProverIndexWrapper<Self>, VerifierIndexWrapper<Self>)>
where
<Self::Curve as AffineRepr>::BaseField: PrimeField,
{
let compiled_circuit = compile(self)?;
let cs = ConstraintSystem::create(compiled_circuit.gates.clone())
.public(compiled_circuit.public_input_size)
.build()
.unwrap();
let srs = <<Self::Proof as OpenProof<Self::Curve>>::SRS as SRS<Self::Curve>>::create(
cs.domain.d1.size as usize,
);
srs.get_lagrange_basis(cs.domain.d1);
let srs = std::sync::Arc::new(srs);
debug!("using an SRS of size {}", srs.size());
let endo_q = <<Self as SnarkyCircuit>::Curve as KimchiCurve>::other_curve_endo();
let prover_index =
crate::prover_index::ProverIndex::<Self::Curve, Self::Proof>::create(cs, *endo_q, srs);
let verifier_index = prover_index.verifier_index();
let prover_index = ProverIndexWrapper {
compiled_circuit,
index: prover_index,
};
let verifier_index = VerifierIndexWrapper {
index: verifier_index,
};
Ok((prover_index, verifier_index))
}
}