Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration of the Scroll’s LogUp Lookup Protocol Into the PSE’s Halo2 Version 0.3.0 #18

Open
wants to merge 6 commits into
base: pse/0.3.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion halo2_proofs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ harness = false
name = "dev_lookup"
harness = false

[[bench]]
name = "lookups"
harness = false

[[bench]]
name = "fft"
harness = false
Expand Down Expand Up @@ -80,7 +84,7 @@ serde_json = "1"
getrandom = { version = "0.2", features = ["js"] }

[features]
default = ["batch", "bits"]
default = ["batch", "bits", "logup_skip_inv"]
dev-graph = ["plotters", "tabbycat"]
test-dev-graph = [
"dev-graph",
Expand All @@ -97,6 +101,8 @@ circuit-params = []
cost-estimator = ["serde", "serde_derive"]
derive_serde = ["halo2curves/derive_serde"]

logup_skip_inv = []

[lib]
bench = false

Expand Down
239 changes: 239 additions & 0 deletions halo2_proofs/benches/lookups.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
#[macro_use]
extern crate criterion;

use ff::PrimeField;
use halo2_proofs::circuit::{Layouter, SimpleFloorPlanner, Value};
use halo2_proofs::plonk::*;
use halo2_proofs::poly::kzg::multiopen::VerifierGWC;
use halo2_proofs::poly::{commitment::ParamsProver, Rotation};
use halo2_proofs::transcript::{Blake2bRead, Blake2bWrite, Challenge255};
use halo2curves::bn256::{Bn256, G1Affine};
use halo2curves::pairing::Engine;
use rand_core::OsRng;

use halo2_proofs::{
poly::kzg::{
commitment::{KZGCommitmentScheme, ParamsKZG},
multiopen::ProverGWC,
strategy::SingleStrategy,
},
transcript::{TranscriptReadBuffer, TranscriptWriterBuffer},
};

use std::marker::PhantomData;

use criterion::{BenchmarkId, Criterion};

fn criterion_benchmark(c: &mut Criterion) {
#[derive(Clone, Default)]
struct MyCircuit<F: PrimeField> {
_marker: PhantomData<F>,
}

#[derive(Clone)]
struct MyConfig {
selector: Selector,
table: TableColumn,
advice: Column<Advice>,
other_advice: Column<Advice>,
}

impl<F: PrimeField> Circuit<F> for MyCircuit<F> {

Check failure on line 41 in halo2_proofs/benches/lookups.rs

View workflow job for this annotation

GitHub Actions / Clippy (beta)

not all trait items implemented, missing: `Params`

error[E0046]: not all trait items implemented, missing: `Params` --> halo2_proofs/benches/lookups.rs:41:5 | 41 | impl<F: PrimeField> Circuit<F> for MyCircuit<F> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `Params` in implementation | = help: implement the missing item: `type Params = Type;`

Check failure on line 41 in halo2_proofs/benches/lookups.rs

View workflow job for this annotation

GitHub Actions / Bitrot check

not all trait items implemented, missing: `Params`
type Config = MyConfig;
type FloorPlanner = SimpleFloorPlanner;

fn without_witnesses(&self) -> Self {
Self::default()
}

fn configure(meta: &mut ConstraintSystem<F>) -> MyConfig {
let config = MyConfig {
selector: meta.complex_selector(),
table: meta.lookup_table_column(),
advice: meta.advice_column(),
other_advice: meta.advice_column(),
};

let dummy_selector = meta.complex_selector();

meta.create_gate("degree 6 gate", |meta| {
let dummy_selector = meta.query_selector(dummy_selector);
let constraints = vec![dummy_selector.clone(); 4]
.iter()
.fold(dummy_selector.clone(), |acc, val| acc * val.clone());
Constraints::with_selector(dummy_selector, Some(constraints))
});

meta.lookup("lookup", |meta| {
let advice = meta.query_advice(config.advice, Rotation::cur());
vec![(advice, config.table)]
});

meta.lookup("lookup", |meta| {
let advice = meta.query_advice(config.advice, Rotation::cur());
vec![(advice, config.table)]
});

meta.lookup("lookup", |meta| {
let advice = meta.query_advice(config.advice, Rotation::cur());
vec![(advice, config.table)]
});

meta.lookup("lookup", |meta| {
let advice = meta.query_advice(config.advice, Rotation::cur());
vec![(advice, config.table)]
});

meta.lookup("lookup", |meta| {
let advice = meta.query_advice(config.advice, Rotation::cur());
vec![(advice, config.table)]
});

/*
- We need degree at least 6 because 6 - 1 = 5 and we need to go to extended domain of 8n
- Our goal is to get to max degree of 9 because now 9 - 1 = 8 and that will fit into domain

- base degree = table_deg + 2
- if we put input_expression_degree = 1
=> degree = base + 1 = 3 + 1 = 4
- we can batch one more with 5 more lookups
*/

config
}

fn synthesize(
&self,
config: MyConfig,
mut layouter: impl Layouter<F>,
) -> Result<(), Error> {
layouter.assign_table(
|| "8-bit table",
|mut table| {
for row in 0u64..(1 << 8) {
table.assign_cell(
|| format!("row {}", row),
config.table,
row as usize,
|| Value::known(F::from(row)),
)?;
}

Ok(())
},
)?;

layouter.assign_region(
|| "assign values",
|mut region| {
for offset in 0u64..(1 << 10) {
config.selector.enable(&mut region, offset as usize)?;
region.assign_advice(
|| format!("offset {}", offset),
config.advice,
offset as usize,
|| Value::known(F::from(offset % 256)),
)?;
}
for offset in 1u64..(1 << 10) {
config.selector.enable(&mut region, offset as usize)?;
region.assign_advice(
|| format!("offset {}", offset),
config.other_advice,
offset as usize - 1,
|| Value::known(F::from(offset % 256)),
)?;
}
Ok(())
},
)
}
}

fn keygen(k: u32) -> (ParamsKZG<Bn256>, ProvingKey<G1Affine>) {
let params: ParamsKZG<Bn256> = ParamsKZG::new(k);
let empty_circuit: MyCircuit<<Bn256 as Engine>::Fr> = MyCircuit {
_marker: PhantomData,
};
let vk = keygen_vk(&params, &empty_circuit).expect("keygen_vk should not fail");
let pk = keygen_pk(&params, vk, &empty_circuit).expect("keygen_pk should not fail");
(params, pk)
}

fn prover(_k: u32, params: &ParamsKZG<Bn256>, pk: &ProvingKey<G1Affine>) -> Vec<u8> {
let rng = OsRng;

let circuit: MyCircuit<<Bn256 as Engine>::Fr> = MyCircuit {
_marker: PhantomData,
};

let mut transcript = Blake2bWrite::<_, _, Challenge255<G1Affine>>::init(vec![]);
create_proof::<KZGCommitmentScheme<Bn256>, ProverGWC<'_, Bn256>, _, _, _, _>(
params,
pk,
&[circuit],
&[&[]],
rng,
&mut transcript,
)
.expect("proof generation should not fail");
transcript.finalize()
}

fn verifier(params: &ParamsKZG<Bn256>, vk: &VerifyingKey<G1Affine>, proof: &[u8]) {
let strategy = SingleStrategy::new(params);
let mut transcript = Blake2bRead::<_, _, Challenge255<G1Affine>>::init(proof);
assert!(verify_proof::<
KZGCommitmentScheme<Bn256>,
VerifierGWC<'_, Bn256>,
Challenge255<G1Affine>,
Blake2bRead<&[u8], G1Affine, Challenge255<G1Affine>>,
SingleStrategy<'_, Bn256>,
>(params, vk, strategy, &[&[]], &mut transcript)
.is_ok());
}

let k_range = 16..=16;

let mut keygen_group = c.benchmark_group("plonk-keygen");
keygen_group.sample_size(10);
for k in k_range.clone() {
keygen_group.bench_with_input(BenchmarkId::from_parameter(k), &k, |b, &k| {
b.iter(|| keygen(k));
});
}
keygen_group.finish();

let mut prover_group = c.benchmark_group("plonk-prover");
prover_group.sample_size(10);
for k in k_range.clone() {
let (params, pk) = keygen(k);

prover_group.bench_with_input(
BenchmarkId::from_parameter(k),
&(k, &params, &pk),
|b, &(k, params, pk)| {
b.iter(|| prover(k, params, pk));
},
);
}
prover_group.finish();

let mut verifier_group = c.benchmark_group("plonk-verifier");
for k in k_range {
let (params, pk) = keygen(k);
let proof = prover(k, &params, &pk);

verifier_group.bench_with_input(
BenchmarkId::from_parameter(k),
&(&params, pk.get_vk(), &proof[..]),
|b, &(params, vk, proof)| {
b.iter(|| verifier(params, vk, proof));
},
);
}
verifier_group.finish();
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
39 changes: 39 additions & 0 deletions halo2_proofs/src/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,14 @@ where
q
}

/// Inverts multiplicatively each nonzero entry in the slice. Zero-valued
/// entries are not changed. The inversion process is parallelized.
pub fn par_invert<F: Field>(values: &mut [F]) {
parallelize(values, |values, _start| {
values.batch_invert();
});
}

/// This utility function will parallelize an operation that is to be
/// performed over a mutable slice.
pub fn parallelize<T: Send, F: Fn(&mut [T], usize) + Send + Sync + Clone>(v: &mut [T], f: F) {
Expand Down Expand Up @@ -433,6 +441,37 @@ pub fn parallelize<T: Send, F: Fn(&mut [T], usize) + Send + Sync + Clone>(v: &mu
});
}

/// This simple utility function will parallelize an operation that is to be
/// performed over a mutable slice.
/// This naive version will have all chunks except the last one of the same size.
/// !! This is important for the mv_lookup prover parallel scan implementation at the moment. !!
pub(crate) fn parallelize_naive<T: Send, F: Fn(&mut [T], usize) + Send + Sync + Clone>(
v: &mut [T],
f: F,
) -> Vec<usize> {
let n = v.len();
let num_threads = multicore::current_num_threads();
let mut chunk = n / num_threads;
if chunk < num_threads {
chunk = 1;
}

multicore::scope(|scope| {
let mut chunk_starts = vec![];
for (chunk_num, v) in v.chunks_mut(chunk).enumerate() {
let f = f.clone();
scope.spawn(move |_| {
let start = chunk_num * chunk;
f(v, start);
});
let start = chunk_num * chunk;
chunk_starts.push(start);
}

chunk_starts
})
}

fn log2_floor(num: usize) -> u32 {
assert!(num > 0);

Expand Down
Loading
Loading