Skip to content

Commit

Permalink
Single user inclusion opening benchmark for the amortized KZG approach (
Browse files Browse the repository at this point in the history
#266)

* Add single user opening benchmark for the amortized approach

Add circuit config generic type parameter.

* Add docs for open_single_user_point_amortized function

* Refactor contract to use the univariate grand sum zkSNARK verifier (#264)

* Refactor contract to use the univariate grand sum zkSNARK verifier

* Disable contract coverage in github action

* Check that the verification key is matching the number of currencies

* Fix balance range value

* Check balanceByteRange against the VK

* fix: backend and test on backend (#265)

* fix: backend and test on backend

* fix: use entries instead of 'user_data' in Round

* refactor: evaluating value in polynomial from 'advice_poly' instead of 'entries'

* fix: typo

* refactor: reuse setup artifcats on Round

---------

Co-authored-by: JinHwan <[email protected]>

* Fix tests

* Fix import in backend

* Toggle range check in benchmarks by a feature flag

---------

Co-authored-by: JinHwan <[email protected]>
  • Loading branch information
alxkzmn and sifnoc authored Feb 23, 2024
1 parent 5db994f commit 451d874
Show file tree
Hide file tree
Showing 11 changed files with 478 additions and 143 deletions.
9 changes: 6 additions & 3 deletions backend/examples/summa_solvency_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use summa_backend::{
};
use summa_solvency::{
circuits::{
univariate_grand_sum::UnivariateGrandSum,
univariate_grand_sum::{UnivariateGrandSum, UnivariateGrandSumConfig},
utils::{full_prover, generate_setup_artifacts},
},
cryptocurrency::Cryptocurrency,
Expand Down Expand Up @@ -71,8 +71,11 @@ async fn main() -> Result<(), Box<dyn Error>> {
let mut cryptos = vec![Cryptocurrency::init_empty(); N_CURRENCIES];
parse_csv_to_entries::<&str, N_CURRENCIES>(entry_csv, &mut entries, &mut cryptos).unwrap();

let univariate_grand_sum_circuit =
UnivariateGrandSum::<N_USERS, N_CURRENCIES>::init(entries.to_vec());
let univariate_grand_sum_circuit = UnivariateGrandSum::<
N_USERS,
N_CURRENCIES,
UnivariateGrandSumConfig<N_CURRENCIES, N_USERS>,
>::init(entries.to_vec());

let (params, pk, vk) =
generate_setup_artifacts(K, None, &univariate_grand_sum_circuit).unwrap();
Expand Down
16 changes: 11 additions & 5 deletions backend/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ mod test {
use crate::tests::initialize_test_env;
use summa_solvency::{
circuits::{
univariate_grand_sum::UnivariateGrandSum,
univariate_grand_sum::{UnivariateGrandSum, UnivariateGrandSumConfig},
utils::{full_prover, generate_setup_artifacts},
},
cryptocurrency::Cryptocurrency,
Expand Down Expand Up @@ -172,8 +172,11 @@ mod test {
let mut cryptos = vec![Cryptocurrency::init_empty(); N_CURRENCIES];
parse_csv_to_entries::<&str, N_CURRENCIES>(entry_csv, &mut entries, &mut cryptos).unwrap();

let univariate_grand_sum_circuit =
UnivariateGrandSum::<N_USERS, N_CURRENCIES>::init(entries.to_vec());
let univariate_grand_sum_circuit = UnivariateGrandSum::<
N_USERS,
N_CURRENCIES,
UnivariateGrandSumConfig<N_CURRENCIES, N_USERS>,
>::init(entries.to_vec());

let (params, pk, vk) =
generate_setup_artifacts(K, None, &univariate_grand_sum_circuit).unwrap();
Expand Down Expand Up @@ -269,8 +272,11 @@ mod test {
let mut cryptos = vec![Cryptocurrency::init_empty(); N_CURRENCIES];
parse_csv_to_entries::<&str, N_CURRENCIES>(entry_csv, &mut entries, &mut cryptos).unwrap();

let univariate_grand_sum_circuit =
UnivariateGrandSum::<N_USERS, N_CURRENCIES>::init(entries.to_vec());
let univariate_grand_sum_circuit = UnivariateGrandSum::<
N_USERS,
N_CURRENCIES,
UnivariateGrandSumConfig<N_CURRENCIES, N_USERS>,
>::init(entries.to_vec());

let (params, pk, vk) =
generate_setup_artifacts(K, None, &univariate_grand_sum_circuit).unwrap();
Expand Down
2 changes: 2 additions & 0 deletions kzg_prover/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ edition = "2021"
[features]
dev-graph = ["halo2_proofs/dev-graph", "plotters"]
profiling = []
no_range_check = []


[dependencies]
halo2_proofs = { git = "https://github.com/summa-dev/halo2"}
Expand Down
25 changes: 25 additions & 0 deletions kzg_prover/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,28 @@ cargo doc --no-deps --open

For testing purposes, it's not necessary to download the `ptau` file. The `generate_setup_artifacts` function can manage this by generating a new setup from a randomly generated value. This automated generation process is intended for testing and development convenience, and it should not be used in production.
For real-world situations, you must provide the path of a specific `ptau` file to the `generate_setup_artifacts`. The circuit will use the randomness from the given file. You can find an example that initializes a `Snapshot` instance [here](https://github.com/summa-dev/summa-solvency/blob/11d4fce5d18f6175804aa792fc9fc5ac27bf5c00/backend/src/apis/snapshot.rs#L115-L116) in the backend.

## Benchmarks

The following benchmarks are available in the `kzg` module:

- `range_check_proof`: the zk-SNARK proof generation time of the polynomial interpolation of user balances with range check;
- `opening_grand_sum`: the time to generate the KZG opening proof of the grand sum of user balances;
- `opening_user`: the time to generate the KZG opening proof of a single user inclusion;
- `calculate_h`: the time to calculate the h(X) for the amortized KZG approach;
- `amortized_opening_all`: the time to generate open proofs for all 2^K user inclusions using the amortized approach;
- `amortized_opening_user`: the time to generate the KZG opening proof of a single user inclusion using the precomputed h(x) from the amortized approach;
- `verifying_grand_sum`: the time to verify the KZG opening proof of the grand sum of user balances;
- `verifying_user`: the time to verify the KZG opening proof of a single user inclusion.

To run the benchmarks with the default full configuration of the circuit (range check enabled), use the following command:

```shell
cargo bench
```

To run the quick benchmarks with the range check disabled (K=9..12), use the following command:

```shell
cargo bench --features "no_range_check"
```
122 changes: 105 additions & 17 deletions kzg_prover/benches/kzg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ use halo2_proofs::{arithmetic::Field, halo2curves::bn256::Fr as Fp};
use num_bigint::BigUint;
use rand::{rngs::OsRng, Rng};

#[cfg(feature = "no_range_check")]
use summa_solvency::circuits::univariate_grand_sum::NoRangeCheckConfig;
#[cfg(not(feature = "no_range_check"))]
use summa_solvency::circuits::univariate_grand_sum::UnivariateGrandSumConfig;
use summa_solvency::{
circuits::{
univariate_grand_sum::UnivariateGrandSum,
univariate_grand_sum::{CircuitConfig, UnivariateGrandSum},
utils::{
full_prover, generate_setup_artifacts, open_grand_sums, open_grand_sums_gwc,
open_user_points, open_user_points_amortized, verify_grand_sum_openings,
compute_h_parallel, full_prover, generate_setup_artifacts,
open_all_user_points_amortized, open_grand_sums, open_grand_sums_gwc,
open_single_user_point_amortized, open_user_points, verify_grand_sum_openings,
verify_user_inclusion,
},
},
Expand All @@ -18,7 +23,13 @@ use summa_solvency::{
utils::{big_uint_to_fp, parse_csv_to_entries},
};

fn bench_kzg<const K: u32, const N_USERS: usize, const N_CURRENCIES: usize, const N_POINTS: usize>(
fn bench_kzg<
const K: u32,
const N_USERS: usize,
const N_CURRENCIES: usize,
const N_POINTS: usize,
CONFIG: CircuitConfig<N_CURRENCIES, N_USERS>,
>(
name: &str,
csv_path: &str,
) where
Expand All @@ -27,14 +38,22 @@ fn bench_kzg<const K: u32, const N_USERS: usize, const N_CURRENCIES: usize, cons
let mut c = Criterion::default().sample_size(10);

// Initialize an empty circuit
let circuit = UnivariateGrandSum::<N_USERS, N_CURRENCIES>::init_empty();
let circuit = UnivariateGrandSum::<N_USERS, N_CURRENCIES, CONFIG>::init_empty();
let (params, pk, vk) = generate_setup_artifacts(K, None, &circuit).unwrap();

let range_check_bench_name = format!("<{}> range check", name);
let range_check_proof_bench_name = format!("<{}> range check", name);
let opening_grand_sum_bench_name = format!("<{}> opening grand sum", name);
let opening_user_bench_name = format!("<{}> opening user inclusion", name);
let amortized_opening_user_bench_name =
format!("<{}> amortized opening all 2^{} user inclusions", name, K);
let opening_user_bench_name = format!("<{}> opening single user inclusion", name);
let calculate_h_bench_name =
format!("<{}> calculating h(X) for the amortized KZG approach", name);
let amortized_opening_all_bench_name = format!(
"<{}> opening all 2^{} user inclusions using the amortized approach",
name, K
);
let amortized_opening_user_bench_name = format!(
"<{}> opening single user inclusion using the amortized approach",
name
);
let verifying_grand_sum_bench_name = format!("<{}> verifying grand sum", name);
let verifying_user_bench_name = format!("<{}> verifying user inclusion", name);

Expand All @@ -51,9 +70,9 @@ fn bench_kzg<const K: u32, const N_USERS: usize, const N_CURRENCIES: usize, cons
}
}

let circuit = UnivariateGrandSum::<N_USERS, N_CURRENCIES>::init(entries.to_vec());
let circuit = UnivariateGrandSum::<N_USERS, N_CURRENCIES, CONFIG>::init(entries.to_vec());

c.bench_function(&range_check_bench_name, |b| {
c.bench_function(&range_check_proof_bench_name, |b| {
b.iter_batched(
|| circuit.clone(), // Setup function: clone the circuit for each iteration
|circuit| {
Expand Down Expand Up @@ -138,12 +157,30 @@ fn bench_kzg<const K: u32, const N_USERS: usize, const N_CURRENCIES: usize, cons
);
});

c.bench_function(&amortized_opening_user_bench_name, |b| {
c.bench_function(&calculate_h_bench_name, |b| {
b.iter_batched(
|| (0..N_CURRENCIES + 1),
|column_range| {
open_user_points_amortized(&advice_polys.advice_polys, &params, column_range, omega)
},
|column_range| compute_h_parallel(&advice_polys.advice_polys, &params, column_range),
criterion::BatchSize::SmallInput,
);
});

let h_vectors = compute_h_parallel(&advice_polys.advice_polys, &params, 0..N_CURRENCIES + 1);
let vec_of_slices = h_vectors.iter().map(|v| v.as_slice()).collect::<Vec<_>>();
let h_slices = vec_of_slices.as_slice();

c.bench_function(&amortized_opening_all_bench_name, |b| {
b.iter_batched(
|| {},
|_| open_all_user_points_amortized(h_slices, omega),
criterion::BatchSize::SmallInput,
);
});

c.bench_function(&amortized_opening_user_bench_name, |b| {
b.iter_batched(
|| {},
|_| open_single_user_point_amortized(h_slices, &params, omega),
criterion::BatchSize::SmallInput,
);
});
Expand Down Expand Up @@ -228,18 +265,69 @@ fn criterion_benchmark(_c: &mut Criterion) {
const N_POINTS: usize = 3;

// Demonstrating that a higher value of K has a more significant impact on benchmark performance than the number of users
#[cfg(not(feature = "no_range_check"))]
{
const K: u32 = 18;
const N_USERS: usize = 16;
bench_kzg::<K, N_USERS, N_CURRENCIES, N_POINTS>(
bench_kzg::<
K,
N_USERS,
N_CURRENCIES,
N_POINTS,
UnivariateGrandSumConfig<N_CURRENCIES, N_USERS>,
>(
format!("K = {K}, N_USERS = {N_USERS}, N_CURRENCIES = {N_CURRENCIES}").as_str(),
format!("../csv/entry_{N_USERS}.csv").as_str(),
);
}
#[cfg(not(feature = "no_range_check"))]
{
const K: u32 = 17;
const N_USERS: usize = 64;
bench_kzg::<K, N_USERS, N_CURRENCIES, N_POINTS>(
bench_kzg::<
K,
N_USERS,
N_CURRENCIES,
N_POINTS,
UnivariateGrandSumConfig<N_CURRENCIES, N_USERS>,
>(
format!("K = {K}, N_USERS = {N_USERS}, N_CURRENCIES = {N_CURRENCIES}").as_str(),
format!("../csv/entry_{N_USERS}.csv").as_str(),
);
}
//Use the following benchmarks for quick evaluation/prototyping (no range check)
#[cfg(feature = "no_range_check")]
{
const K: u32 = 9;
const N_USERS: usize = 64;
bench_kzg::<K, N_USERS, N_CURRENCIES, N_POINTS, NoRangeCheckConfig<N_CURRENCIES, N_USERS>>(
format!("K = {K}, N_USERS = {N_USERS}, N_CURRENCIES = {N_CURRENCIES}").as_str(),
format!("../csv/entry_{N_USERS}.csv").as_str(),
);
}
#[cfg(feature = "no_range_check")]
{
const K: u32 = 10;
const N_USERS: usize = 64;
bench_kzg::<K, N_USERS, N_CURRENCIES, N_POINTS, NoRangeCheckConfig<N_CURRENCIES, N_USERS>>(
format!("K = {K}, N_USERS = {N_USERS}, N_CURRENCIES = {N_CURRENCIES}").as_str(),
format!("../csv/entry_{N_USERS}.csv").as_str(),
);
}
#[cfg(feature = "no_range_check")]
{
const K: u32 = 11;
const N_USERS: usize = 64;
bench_kzg::<K, N_USERS, N_CURRENCIES, N_POINTS, NoRangeCheckConfig<N_CURRENCIES, N_USERS>>(
format!("K = {K}, N_USERS = {N_USERS}, N_CURRENCIES = {N_CURRENCIES}").as_str(),
format!("../csv/entry_{N_USERS}.csv").as_str(),
);
}
#[cfg(feature = "no_range_check")]
{
const K: u32 = 12;
const N_USERS: usize = 64;
bench_kzg::<K, N_USERS, N_CURRENCIES, N_POINTS, NoRangeCheckConfig<N_CURRENCIES, N_USERS>>(
format!("K = {K}, N_USERS = {N_USERS}, N_CURRENCIES = {N_CURRENCIES}").as_str(),
format!("../csv/entry_{N_USERS}.csv").as_str(),
);
Expand Down
9 changes: 6 additions & 3 deletions kzg_prover/bin/gen_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use serde_json::to_string_pretty;
use std::{fs::File, io::Write};
use summa_solvency::{
circuits::{
univariate_grand_sum::UnivariateGrandSum,
univariate_grand_sum::{UnivariateGrandSum, UnivariateGrandSumConfig},
utils::{full_prover, full_verifier, generate_setup_artifacts},
},
cryptocurrency::Cryptocurrency,
Expand All @@ -40,8 +40,11 @@ fn main() {
parse_csv_to_entries::<&str, N_CURRENCIES>("../csv/entry_16.csv", &mut entries, &mut cryptos)
.unwrap();

let univariate_grand_sum_circuit =
UnivariateGrandSum::<N_USERS, N_CURRENCIES>::init(entries.to_vec());
let univariate_grand_sum_circuit = UnivariateGrandSum::<
N_USERS,
N_CURRENCIES,
UnivariateGrandSumConfig<N_CURRENCIES, N_USERS>,
>::init(entries.to_vec());

let (params, pk, _) = generate_setup_artifacts(
K,
Expand Down
18 changes: 14 additions & 4 deletions kzg_prover/bin/gen_verifiers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ use halo2_solidity_verifier::{
use prelude::*;
use rand::rngs::OsRng;
use summa_solvency::{
circuits::{univariate_grand_sum::UnivariateGrandSum, utils::generate_setup_artifacts},
circuits::{
univariate_grand_sum::{UnivariateGrandSum, UnivariateGrandSumConfig},
utils::generate_setup_artifacts,
},
cryptocurrency::Cryptocurrency,
entry::Entry,
utils::parse_csv_to_entries,
Expand All @@ -27,7 +30,11 @@ const N_USERS: usize = 16;

fn main() {
// In order to generate the verifier we create the circuit using the init_empty() method, which means that the circuit is not initialized with any data.
let circuit = UnivariateGrandSum::<N_USERS, N_CURRENCIES>::init_empty();
let circuit = UnivariateGrandSum::<
N_USERS,
N_CURRENCIES,
UnivariateGrandSumConfig<N_CURRENCIES, N_USERS>,
>::init_empty();

let (params, pk, _) =
generate_setup_artifacts(K, Some("../backend/ptau/hermez-raw-17"), &circuit).unwrap();
Expand All @@ -39,8 +46,11 @@ fn main() {
parse_csv_to_entries::<&str, N_CURRENCIES>("../csv/entry_16.csv", &mut entries, &mut cryptos)
.unwrap();

let univariate_grand_sum_circuit =
UnivariateGrandSum::<N_USERS, N_CURRENCIES>::init(entries.to_vec());
let univariate_grand_sum_circuit = UnivariateGrandSum::<
N_USERS,
N_CURRENCIES,
UnivariateGrandSumConfig<N_CURRENCIES, N_USERS>,
>::init(entries.to_vec());

// 1. Generate Snark Verifier Contract and Verification Key
//
Expand Down
Loading

0 comments on commit 451d874

Please sign in to comment.