Skip to content

Commit

Permalink
V3 benchmarks (#291)
Browse files Browse the repository at this point in the history
* Verify user inclusion in a test

* Perform batch KZG opening/verification

* Remove magic number

* Add user inclusion benchmarks and verification benchmarks

* Code review fixes
  • Loading branch information
alxkzmn authored May 13, 2024
1 parent 62aaf80 commit 2e84f40
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 91 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ on:
workflow_dispatch:
inputs:
run_benchmark:
description: 'Run benchmark tests (yes/no)'
description: "Please confirm running the benchmarks by typing 'yes' in the input box."
required: true
default: 'no'
default: "no"

jobs:
wakeup:
Expand All @@ -26,7 +26,7 @@ jobs:
aws-region: us-west-2

- name: Wakeup runner
run: .github/scripts/wakeup.sh
run: .github/scripts/wakeup.sh

benchmark:
runs-on: [summa-solvency-runner]
Expand Down
167 changes: 143 additions & 24 deletions prover/benches/proof_of_liabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,78 +3,197 @@ use plonkish_backend::{
backend::{hyperplonk::HyperPlonk, PlonkishBackend, PlonkishCircuit, PlonkishCircuitInfo},
frontend::halo2::Halo2Circuit,
halo2_curves::bn256::{Bn256, Fr as Fp},
pcs::multilinear::MultilinearKzg,
pcs::{multilinear::MultilinearKzg, Evaluation, PolynomialCommitmentScheme},
util::{
test::std_rng,
transcript::{InMemoryTranscript, Keccak256Transcript},
},
};
use rand::{
rngs::{OsRng, StdRng},
CryptoRng, RngCore, SeedableRng,
CryptoRng, Rng, RngCore, SeedableRng,
};
use summa_hyperplonk::{
circuits::summa_circuit::summa_hyperplonk::SummaHyperplonk, utils::generate_dummy_entries,
circuits::summa_circuit::summa_hyperplonk::SummaHyperplonk,
utils::{big_uint_to_fp, generate_dummy_entries, uni_to_multivar_binary_index},
};

fn bench_summa<const K: u32, const N_USERS: usize, const N_CURRENCIES: usize>() {
let name = format!("K = {K}, N_USERS = {N_USERS}, N_CURRENCIES = {N_CURRENCIES}");
let mut c = Criterion::default().sample_size(10);

let grand_sum_proof_bench_name = format!("<{}> grand sum proof", name);
let inclusion_proof_bench_name = format!("<{}> user inclusion proof", name);

type Pb = HyperPlonk<MultilinearKzg<Bn256>>;
let grand_sum_verification_bench_name = format!("<{}> grand sum verification", name);
let inclusion_verification_bench_name = format!("<{}> user inclusion verification", name);

type ProvingBackend = HyperPlonk<MultilinearKzg<Bn256>>;
let entries = generate_dummy_entries::<N_USERS, N_CURRENCIES>().unwrap();
let halo2_circuit = SummaHyperplonk::<N_USERS, N_CURRENCIES>::init(entries.to_vec());

let circuit = Halo2Circuit::<Fp, SummaHyperplonk<N_USERS, N_CURRENCIES>>::new::<Pb>(
let circuit = Halo2Circuit::<Fp, SummaHyperplonk<N_USERS, N_CURRENCIES>>::new::<ProvingBackend>(
K as usize,
halo2_circuit.clone(),
);

let circuit_info: PlonkishCircuitInfo<_> = circuit.circuit_info().unwrap();
let instances = circuit.instances();
let param = Pb::setup(&circuit_info, seeded_std_rng()).unwrap();
let param = ProvingBackend::setup(&circuit_info, seeded_std_rng()).unwrap();

let (pp, vp) = Pb::preprocess(&param, &circuit_info).unwrap();

let mut transcript = Keccak256Transcript::default();
let proof = {
Pb::prove(&pp, &circuit, &mut transcript, std_rng()).unwrap();
transcript.into_proof()
};

let accept = {
let mut transcript = Keccak256Transcript::from_proof((), proof.as_slice());
Pb::verify(&vp, instances, &mut transcript, std_rng()).is_ok()
};
assert!(accept);
let (pp, vp) = ProvingBackend::preprocess(&param, &circuit_info).unwrap();

c.bench_function(&grand_sum_proof_bench_name, |b| {
b.iter_batched(
|| {
Halo2Circuit::<Fp, SummaHyperplonk<N_USERS, N_CURRENCIES>>::new::<Pb>(
Halo2Circuit::<Fp, SummaHyperplonk<N_USERS, N_CURRENCIES>>::new::<ProvingBackend>(
K as usize,
halo2_circuit.clone(),
)
},
|circuit| {
let mut transcript = Keccak256Transcript::default();

Pb::prove(&pp, &circuit, &mut transcript, std_rng()).unwrap();
ProvingBackend::prove(&pp, &circuit, &mut transcript, std_rng()).unwrap();
transcript.into_proof();
},
criterion::BatchSize::SmallInput, // Choose an appropriate batch size
criterion::BatchSize::LargeInput,
)
});

let (prover_parameters, verifier_parameters) =
ProvingBackend::preprocess(&param, &circuit_info).unwrap();

let (witness_polys, _) = {
let mut proof_transcript = Keccak256Transcript::new(());

let witness_polys = ProvingBackend::prove(
&prover_parameters,
&circuit,
&mut proof_transcript,
seeded_std_rng(),
)
.unwrap();
(witness_polys, proof_transcript)
};
let num_points = N_CURRENCIES + 1;
let user_entry_polynomials = witness_polys.iter().take(num_points).collect::<Vec<_>>();

let mut transcript = Keccak256Transcript::default();
let proof = {
ProvingBackend::prove(&pp, &circuit, &mut transcript, std_rng()).unwrap();
transcript.into_proof()
};

let mut transcript = Keccak256Transcript::from_proof((), proof.as_slice());

let user_entry_commitments = MultilinearKzg::<Bn256>::read_commitments(
&verifier_parameters.pcs,
num_points,
&mut transcript,
)
.unwrap();

//Create an evaluation challenge at a random "user index"
let fraction: f64 = rand::thread_rng().gen();
let random_user_index = (fraction * (entries.len() as f64)) as usize;

let num_vars = K;

let multivariate_challenge =
uni_to_multivar_binary_index(&random_user_index, num_vars as usize);

let mut evals = vec![];

for i in 0..N_CURRENCIES + 1 {
if i == 0 {
evals.push(Evaluation::new(
i,
0,
big_uint_to_fp::<Fp>(entries[random_user_index].username_as_big_uint()),
));
} else {
evals.push(Evaluation::new(
i,
0,
big_uint_to_fp::<Fp>(&entries[random_user_index].balances()[i - 1]),
));
}
}

c.bench_function(&inclusion_proof_bench_name, |b| {
b.iter_batched(
|| {
(
user_entry_polynomials.clone(),
multivariate_challenge.clone(),
)
},
|(user_entry_polynomials, multivariate_challenge)| {
let mut kzg_transcript = Keccak256Transcript::new(());
MultilinearKzg::<Bn256>::batch_open(
&prover_parameters.pcs,
user_entry_polynomials,
&user_entry_commitments,
&[multivariate_challenge],
&evals,
&mut kzg_transcript,
)
.unwrap();
},
criterion::BatchSize::LargeInput,
)
});

c.bench_function(&grand_sum_verification_bench_name, |b| {
b.iter_batched(
|| (Keccak256Transcript::from_proof((), proof.as_slice())),
|mut transcript| {
let accept =
{ ProvingBackend::verify(&vp, instances, &mut transcript, std_rng()).is_ok() };
assert!(accept);
},
criterion::BatchSize::LargeInput,
)
});

c.bench_function(&inclusion_verification_bench_name, |b| {
b.iter_batched(
|| {
let mut kzg_transcript = Keccak256Transcript::new(());
MultilinearKzg::<Bn256>::batch_open(
&prover_parameters.pcs,
user_entry_polynomials.clone(),
&user_entry_commitments,
&[multivariate_challenge.clone()],
&evals,
&mut kzg_transcript,
)
.unwrap();
(kzg_transcript.into_proof(), multivariate_challenge.clone())
},
|(kzg_proof, multivariate_challenge)| {
let mut kzg_transcript = Keccak256Transcript::from_proof((), kzg_proof.as_slice());
MultilinearKzg::<Bn256>::batch_verify(
&verifier_parameters.pcs,
&user_entry_commitments,
&[multivariate_challenge],
&evals,
&mut kzg_transcript,
)
.unwrap();
},
criterion::BatchSize::LargeInput,
)
});
}

fn criterion_benchmark(_c: &mut Criterion) {
const N_CURRENCIES: usize = 2;
const N_CURRENCIES: usize = 1;

{
const K: u32 = 17;
const N_USERS: usize = 1 << 16 as usize;
const N_USERS: usize = (1 << K as usize) - 6;
bench_summa::<K, N_USERS, N_CURRENCIES>();
}
}
Expand Down
2 changes: 1 addition & 1 deletion prover/src/chips/range/range_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ impl RangeCheckU64Chip {
let ks = element
.value()
.copied()
.map(|x| decompose_fp_to_byte_pairs(x, 4))
.map(|x| decompose_fp_to_byte_pairs(&x, 4))
.transpose_vec(4);

// Initialize an empty vector of cells for the truncated right-shifted values of the element to be checked.
Expand Down
68 changes: 5 additions & 63 deletions prover/src/chips/range/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,11 @@ use num_bigint::BigUint;

use crate::utils::{big_uint_to_fp, fp_to_big_uint};

/// Converts value Fp to n bytes of bytes in little endian order.
/// If value is decomposed in #bytes which are less than n, then the returned bytes are padded with 0s at the most significant bytes.
/// Example:
/// decompose_fp_to_bytes(0x1f2f3f, 4) -> [0x3f, 0x2f, 0x1f, 0x00]
/// If value is decomposed in #bytes which are greater than n, then the most significant bytes are truncated. A warning is printed.
/// Example:
/// decompose_fp_to_bytes(0x1f2f3f, 2) -> [0x3f, 0x2f]
pub fn decompose_fp_to_bytes(value: Fp, n: usize) -> Vec<u8> {
let value_biguint = fp_to_big_uint(&value);

let mut bytes = value_biguint.to_bytes_le();

// Pad with 0s at the most significant bytes if bytes length is less than n.
while bytes.len() < n {
bytes.push(0);
}

// If the bytes length exceeds n, print a warning and truncate the byte array at the most significant bytes.
if bytes.len() > n {
println!("Warning: `decompose_fp_to_bytes` value is decomposed in #bytes which are greater than n. Truncating the output to fit the specified length.");
bytes.truncate(n);
}

bytes
}

/// Converts value Fp to array of n byte pairs in little endian order.
/// If value is decomposed in #byte pairs which are less than n, then the returned byte pairs are padded with 0s at the most significant byte pairs.
/// If value is decomposed in #byte pairs which are greater than n, then the most significant byte pairs are truncated. A warning is printed.
pub fn decompose_fp_to_byte_pairs(value: Fp, n: usize) -> Vec<u16> {
let value_biguint = fp_to_big_uint(&value);
pub fn decompose_fp_to_byte_pairs(value: &Fp, n: usize) -> Vec<u16> {
let value_biguint = fp_to_big_uint(value);
let mut bytes = value_biguint.to_bytes_le();

// Ensure the bytes vector has an even length for pairs of bytes.
Expand Down Expand Up @@ -84,62 +58,30 @@ mod testing {
assert_eq!(big_uint, BigUint::from(5u8));
}

// convert a 32 bit number in 4 bytes. Should correctly convert to 4 bytes
#[test]
fn test_decompose_fp_to_bytes_no_padding() {
let f = Fp::from(0x1f2f3f4f);
let bytes = decompose_fp_to_bytes(f, 4);
assert_eq!(bytes, vec![0x4f, 0x3f, 0x2f, 0x1f]);
}

// convert a 32 bit number in 2 byte pairs. Should correctly convert to 2 byte pairs
#[test]
fn test_decompose_fp_byte_pairs_no_padding() {
let f = Fp::from(0x1f2f3f4f);
let bytes = decompose_fp_to_byte_pairs(f, 2);
let bytes = decompose_fp_to_byte_pairs(&f, 2);
assert_eq!(bytes, vec![0x3f4f, 0x1f2f]);
}

// convert a 32 bit number in 6 bytes. Should correctly convert to 6 bytes in which the last 2 bytes are 0 padded.
#[test]
fn test_decompose_fp_to_bytes_padding() {
let f = Fp::from(0x1f2f3f4f);
let bytes = decompose_fp_to_bytes(f, 6);
assert_eq!(bytes, vec![0x4f, 0x3f, 0x2f, 0x1f, 0x00, 0x00]);
}

// convert a 32 bit number in 3 byte pairs. Should correctly convert to 3 byte pairs in which the last pair is 0 padded.
#[test]
fn test_decompose_fp_to_byte_pairs_padding() {
let f = Fp::from(0x1f2f3f4f);
let bytes = decompose_fp_to_byte_pairs(f, 3);
let bytes = decompose_fp_to_byte_pairs(&f, 3);
assert_eq!(bytes, vec![0x3f4f, 0x1f2f, 0x00]);
}

// convert a 32 bit number in 2 bytes. Should convert to 2 bytes and truncate the most significant bytes and emit a warning
#[test]
fn test_decompose_fp_to_bytes_overflow() {
let f = Fp::from(0x1f2f3f4f);
let bytes = decompose_fp_to_bytes(f, 2);
assert_eq!(bytes, vec![0x4f, 0x3f]);
}

// convert a 32 bit number in 1 byte pair. Should convert to a byte pair and truncate the most significant byte pair and emit a warning
#[test]
fn test_decompose_fp_to_byte_pairs_overflow() {
let f = Fp::from(0x1f2f3f4f);
let bytes = decompose_fp_to_byte_pairs(f, 1);
let bytes = decompose_fp_to_byte_pairs(&f, 1);
assert_eq!(bytes, vec![0x3f4f]);
}

// convert a 40 bit number in 2 bytes. Should convert to 2 most significant bytes and truncate the least significant byte
#[test]
fn test_decompose_fp_to_bytes_overflow_2() {
let f = Fp::from(0xf1f2f3f);
let bytes = decompose_fp_to_bytes(f, 2);
assert_eq!(bytes, vec![0x3f, 0x2f]);
}

#[test]
fn test_pow_2() {
let pow = pow_of_two(8);
Expand Down

0 comments on commit 2e84f40

Please sign in to comment.