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

V3 benchmarks #291

Merged
merged 6 commits into from
May 13, 2024
Merged
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
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."
sifnoc marked this conversation as resolved.
Show resolved Hide resolved
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
Loading