Skip to content

Commit

Permalink
Implement Protostar without IVC (#23)
Browse files Browse the repository at this point in the history
* feat: duplicate sangria into protostar

* feat: implement `Protostar` in the way Appendix A describes

* fix: just compute powers of challenge on verifier side and track them

* refactor: simplify sangria structures and refactor `HadamardEvaluator`

* refactor: use same `HadamardEvaluator` from sangria

* feat: hardcoded `power_of_zeta` cross term computation (1x faster due to sparsity)

* tmp: protostar folding verifier circuit proof of concept

* refactor: simplify `PlonkishBackend` api

* feat: introduce `FoldingScheme` and unify `sangria` and `protostar`

* feat: update module `ivc` with skeleton

* feat: allow generic `AssignedCell`

* feat: add a strawman impl of `Chips`

* chore: add timer

* refactor: Replace `CurveCycle` with `TwoChainCurve`

* chore: rename mod `folding` to `accumulation`

* feat: rename to `Gemini` and take optimization from Aztec

* feat: refactor `MultilinearKzg` and implement `Zeromorph` without zero-knowledge

* chore: remove extra field of kzg param and rename fn

* refactor

* chore: staged for later PR

* doc: add source of `Gemini`
  • Loading branch information
han0110 authored Jul 28, 2023
1 parent 415f976 commit f595a0d
Show file tree
Hide file tree
Showing 47 changed files with 3,197 additions and 1,832 deletions.
8 changes: 4 additions & 4 deletions benchmark/benches/proof_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ fn bench_hyperplonk<C: CircuitExt<Fr>>(k: usize) {

let circuit = C::rand(k, std_rng());
let circuit = Halo2Circuit::new::<HyperPlonk>(k, circuit);
let instances = circuit.instance_slices();
let circuit_info = circuit.circuit_info().unwrap();
let instances = circuit.instances();

let timer = start_timer(|| format!("hyperplonk_setup-{k}"));
let param = HyperPlonk::setup(&circuit_info, std_rng()).unwrap();
Expand All @@ -64,14 +64,14 @@ fn bench_hyperplonk<C: CircuitExt<Fr>>(k: usize) {
let proof = sample(System::HyperPlonk, k, || {
let _timer = start_timer(|| format!("hyperplonk_prove-{k}"));
let mut transcript = Keccak256Transcript::default();
HyperPlonk::prove(&pp, (), &instances, &circuit, &mut transcript, std_rng()).unwrap();
HyperPlonk::prove(&pp, &circuit, &mut transcript, std_rng()).unwrap();
transcript.into_proof()
});

let _timer = start_timer(|| format!("hyperplonk_verify-{k}"));
let accept = {
let mut transcript = Keccak256Transcript::from_proof(proof.as_slice());
HyperPlonk::verify(&vp, (), &instances, &mut transcript, std_rng()).is_ok()
let mut transcript = Keccak256Transcript::from_proof((), proof.as_slice());
HyperPlonk::verify(&vp, instances, &mut transcript, std_rng()).is_ok()
};
assert!(accept);
}
Expand Down
4 changes: 4 additions & 0 deletions benchmark/src/bin/plotter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,10 @@ impl Log {
let mut stack = Vec::new();
logs.iter().fold(Vec::new(), |mut logs, log| {
let (indent, log) = log.rsplit_once('·').unwrap_or(("", log));
if log.len() < 9 {
return logs;
}

let (prefix, log) = log.split_at(9);
let depth = (indent.len() + 2) / 4;
if depth == stack.len() && prefix.starts_with("Start:") {
Expand Down
8 changes: 0 additions & 8 deletions benchmark/src/halo2/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,10 +380,6 @@ mod aggregation {
}
}

fn num_instances() -> Vec<usize> {
vec![4 * LIMBS]
}

fn instances(&self) -> Vec<Vec<M::Scalar>> {
vec![self.instances.clone()]
}
Expand Down Expand Up @@ -476,10 +472,6 @@ mod sha256 {
Self { input_size }
}

fn num_instances() -> Vec<usize> {
Vec::new()
}

fn instances(&self) -> Vec<Vec<Fr>> {
Vec::new()
}
Expand Down
8 changes: 7 additions & 1 deletion plonkish_backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ version = "0.1.0"
edition = "2021"

[dependencies]
halo2_curves = { git = "https://github.com/privacy-scaling-explorations/halo2curves", tag = "0.3.3", package = "halo2curves" }
halo2_curves = { git = "https://github.com/privacy-scaling-explorations/halo2curves", tag = "0.3.3", package = "halo2curves", features = ["derive_serde"] }
pasta_curves = { version = "0.5.0", features = ["serde"] }
generic-array = { version = "0.14.7", features = ["serde"] }
bitvec = "1.0.1"
itertools = "0.10.5"
num-bigint = "0.4.3"
num-integer = "0.1.45"
rand = "0.8"
serde = { version = "1.0", features = ["derive"] }
bincode = "1.3.3"
sha3 = "0.10.6"
poseidon = { git = "https://github.com/han0110/poseidon", branch = "feature/with-spec" }

# timer
ark-std = { version = "^0.4.0", default-features = false, optional = true }
Expand Down
262 changes: 262 additions & 0 deletions plonkish_backend/src/accumulation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
use crate::{
backend::{PlonkishCircuit, PlonkishCircuitInfo},
pcs::{CommitmentChunk, PolynomialCommitmentScheme},
util::{
arithmetic::Field,
transcript::{TranscriptRead, TranscriptWrite},
DeserializeOwned, Serialize,
},
Error,
};
use rand::RngCore;
use std::{borrow::BorrowMut, fmt::Debug};

pub mod protostar;
pub mod sangria;

pub trait AccumulationScheme<F: Field>: Clone + Debug {
type Pcs: PolynomialCommitmentScheme<F>;
type ProverParam: Debug + Serialize + DeserializeOwned;
type VerifierParam: Debug + Serialize + DeserializeOwned;
type Accumulator: Debug + AsRef<Self::AccumulatorInstance>;
type AccumulatorInstance: Clone + Debug + Serialize + DeserializeOwned;

fn setup(
circuit_info: &PlonkishCircuitInfo<F>,
rng: impl RngCore,
) -> Result<<Self::Pcs as PolynomialCommitmentScheme<F>>::Param, Error>;

fn preprocess(
param: &<Self::Pcs as PolynomialCommitmentScheme<F>>::Param,
circuit_info: &PlonkishCircuitInfo<F>,
) -> Result<(Self::ProverParam, Self::VerifierParam), Error>;

fn init_accumulator(pp: &Self::ProverParam) -> Result<Self::Accumulator, Error>;

fn init_accumulator_from_nark(
pp: &Self::ProverParam,
nark: PlonkishNark<F, Self::Pcs>,
) -> Result<Self::Accumulator, Error>;

fn prove_nark(
pp: &Self::ProverParam,
circuit: &impl PlonkishCircuit<F>,
transcript: &mut impl TranscriptWrite<CommitmentChunk<F, Self::Pcs>, F>,
rng: impl RngCore,
) -> Result<PlonkishNark<F, Self::Pcs>, Error>;

fn prove_accumulation<const IS_INCOMING_ABSORBED: bool>(
pp: &Self::ProverParam,
accumulator: impl BorrowMut<Self::Accumulator>,
incoming: &Self::Accumulator,
transcript: &mut impl TranscriptWrite<CommitmentChunk<F, Self::Pcs>, F>,
rng: impl RngCore,
) -> Result<(), Error>;

fn prove_accumulation_from_nark(
pp: &Self::ProverParam,
accumulator: impl BorrowMut<Self::Accumulator>,
circuit: &impl PlonkishCircuit<F>,
transcript: &mut impl TranscriptWrite<CommitmentChunk<F, Self::Pcs>, F>,
mut rng: impl RngCore,
) -> Result<(), Error> {
let nark = Self::prove_nark(pp, circuit, transcript, &mut rng)?;
let incoming = Self::init_accumulator_from_nark(pp, nark)?;
Self::prove_accumulation::<true>(pp, accumulator, &incoming, transcript, &mut rng)?;
Ok(())
}

fn verify_accumulation_from_nark(
vp: &Self::VerifierParam,
accumulator: impl BorrowMut<Self::AccumulatorInstance>,
instances: &[Vec<F>],
transcript: &mut impl TranscriptRead<CommitmentChunk<F, Self::Pcs>, F>,
rng: impl RngCore,
) -> Result<(), Error>;

fn prove_decider(
pp: &Self::ProverParam,
accumulator: &Self::Accumulator,
transcript: &mut impl TranscriptWrite<CommitmentChunk<F, Self::Pcs>, F>,
rng: impl RngCore,
) -> Result<(), Error>;

fn prove_decider_with_last_nark(
pp: &Self::ProverParam,
mut accumulator: impl BorrowMut<Self::Accumulator>,
circuit: &impl PlonkishCircuit<F>,
transcript: &mut impl TranscriptWrite<CommitmentChunk<F, Self::Pcs>, F>,
mut rng: impl RngCore,
) -> Result<(), Error> {
Self::prove_accumulation_from_nark(
pp,
accumulator.borrow_mut(),
circuit,
transcript,
&mut rng,
)?;
Self::prove_decider(pp, accumulator.borrow(), transcript, &mut rng)?;
Ok(())
}

fn verify_decider(
vp: &Self::VerifierParam,
accumulator: &Self::AccumulatorInstance,
transcript: &mut impl TranscriptRead<CommitmentChunk<F, Self::Pcs>, F>,
rng: impl RngCore,
) -> Result<(), Error>;

fn verify_decider_with_last_nark(
vp: &Self::VerifierParam,
mut accumulator: impl BorrowMut<Self::AccumulatorInstance>,
instances: &[Vec<F>],
transcript: &mut impl TranscriptRead<CommitmentChunk<F, Self::Pcs>, F>,
mut rng: impl RngCore,
) -> Result<(), Error> {
Self::verify_accumulation_from_nark(
vp,
accumulator.borrow_mut(),
instances,
transcript,
&mut rng,
)?;
Self::verify_decider(vp, accumulator.borrow(), transcript, &mut rng)?;
Ok(())
}
}

#[derive(Clone, Debug)]
pub struct PlonkishNark<F, Pcs>
where
F: Field,
Pcs: PolynomialCommitmentScheme<F>,
{
instance: PlonkishNarkInstance<F, Pcs::Commitment>,
witness_polys: Vec<Pcs::Polynomial>,
}

impl<F, Pcs> PlonkishNark<F, Pcs>
where
F: Field,
Pcs: PolynomialCommitmentScheme<F>,
{
fn new(
instances: Vec<Vec<F>>,
challenges: Vec<F>,
witness_comms: Vec<Pcs::Commitment>,
witness_polys: Vec<Pcs::Polynomial>,
) -> Self {
Self {
instance: PlonkishNarkInstance::new(instances, challenges, witness_comms),
witness_polys,
}
}
}

#[derive(Clone, Debug)]
pub struct PlonkishNarkInstance<F, C> {
instances: Vec<Vec<F>>,
challenges: Vec<F>,
witness_comms: Vec<C>,
}

impl<F, C> PlonkishNarkInstance<F, C> {
fn new(instances: Vec<Vec<F>>, challenges: Vec<F>, witness_comms: Vec<C>) -> Self {
Self {
instances,
challenges,
witness_comms,
}
}
}

#[cfg(test)]
pub(crate) mod test {
use crate::{
accumulation::AccumulationScheme,
backend::{PlonkishCircuit, PlonkishCircuitInfo},
pcs::PolynomialCommitmentScheme,
util::{
arithmetic::PrimeField,
end_timer, start_timer,
test::seeded_std_rng,
transcript::{InMemoryTranscript, TranscriptRead, TranscriptWrite},
DeserializeOwned, Serialize,
},
};
use std::{hash::Hash, ops::Range};

pub(crate) fn run_accumulation_scheme<F, Fs, T, C>(
num_vars_range: Range<usize>,
circuit_fn: impl Fn(usize) -> (PlonkishCircuitInfo<F>, Vec<C>),
) where
F: PrimeField + Hash + Serialize + DeserializeOwned,
Fs: AccumulationScheme<F>,
T: TranscriptRead<<Fs::Pcs as PolynomialCommitmentScheme<F>>::CommitmentChunk, F>
+ TranscriptWrite<<Fs::Pcs as PolynomialCommitmentScheme<F>>::CommitmentChunk, F>
+ InMemoryTranscript<Param = ()>,
C: PlonkishCircuit<F>,
{
for num_vars in num_vars_range {
let (circuit_info, circuits) = circuit_fn(num_vars);
let last_circuit = circuits.last().unwrap();

let timer = start_timer(|| format!("setup-{num_vars}"));
let param = Fs::setup(&circuit_info, seeded_std_rng()).unwrap();
end_timer(timer);

let timer = start_timer(|| format!("preprocess-{num_vars}"));
let (pp, vp) = Fs::preprocess(&param, &circuit_info).unwrap();
end_timer(timer);

let (accumulator_before_last, proof) = {
let mut accumulator = Fs::init_accumulator(&pp).unwrap();
for circuit in circuits[..circuits.len() - 1].iter() {
let timer = start_timer(|| format!("prove_accumulation_from_nark-{num_vars}"));
Fs::prove_accumulation_from_nark(
&pp,
&mut accumulator,
circuit,
&mut T::new(()),
seeded_std_rng(),
)
.unwrap();
end_timer(timer);
}

let accumulator_before_last = accumulator.as_ref().clone();

let timer = start_timer(|| format!("prove_decider_with_last_nark-{num_vars}"));
let proof = {
let mut transcript = T::new(());
Fs::prove_decider_with_last_nark(
&pp,
&mut accumulator,
last_circuit,
&mut transcript,
seeded_std_rng(),
)
.unwrap();
transcript.into_proof()
};
end_timer(timer);

(accumulator_before_last, proof)
};

let timer = start_timer(|| format!("verify_decider_with_last_nark-{num_vars}"));
let result = {
let mut transcript = T::from_proof((), proof.as_slice());
Fs::verify_decider_with_last_nark(
&vp,
accumulator_before_last,
last_circuit.instances(),
&mut transcript,
seeded_std_rng(),
)
};
assert!(matches!(result, Ok(_)));
end_timer(timer);
}
}
}
Loading

0 comments on commit f595a0d

Please sign in to comment.