From 8082b600889409278c600cdcb2f555fc9959db28 Mon Sep 17 00:00:00 2001 From: Alex Kuzmin Date: Tue, 23 Apr 2024 19:01:20 +0800 Subject: [PATCH 1/5] Verify user inclusion in a test --- backend/Cargo.lock | 2 +- prover/Cargo.lock | 2 +- prover/Cargo.toml | 2 +- prover/src/chips/range/utils.rs | 6 +- prover/src/circuits/summa_circuit.rs | 9 +- prover/src/circuits/tests.rs | 183 ++++++++++++++++++-------- prover/src/utils/operation_helpers.rs | 54 +++++++- 7 files changed, 191 insertions(+), 67 deletions(-) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index f6a50e97..687679d1 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -2427,7 +2427,7 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plonkish_backend" version = "0.1.0" -source = "git+https://github.com/han0110/plonkish#303cf244803ea56d1ac8c24829ec4c67e4e798ab" +source = "git+https://github.com/summa-dev/plonkish?branch=summa-changes#50093af18280f4e1efd79ac258ae9c65b9401999" dependencies = [ "bincode", "bitvec 1.0.1", diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 97fe6344..9ddf20f5 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -989,7 +989,7 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plonkish_backend" version = "0.1.0" -source = "git+https://github.com/han0110/plonkish#303cf244803ea56d1ac8c24829ec4c67e4e798ab" +source = "git+https://github.com/summa-dev/plonkish?branch=summa-changes#50093af18280f4e1efd79ac258ae9c65b9401999" dependencies = [ "bincode", "bitvec", diff --git a/prover/Cargo.toml b/prover/Cargo.toml index f469265c..f44bd0d1 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -13,7 +13,7 @@ parallel = ["dep:rayon"] frontend-halo2 = ["dep:halo2_proofs"] [dependencies] -plonkish_backend = { git = "https://github.com/han0110/plonkish", package = "plonkish_backend", features= ["frontend-halo2", "benchmark"] } +plonkish_backend = { git = "https://github.com/summa-dev/plonkish", branch="summa-changes", package = "plonkish_backend", features= ["frontend-halo2", "benchmark"] } plotters = { version = "0.3.4", optional = true } rand = "0.8" csv = "1.1" diff --git a/prover/src/chips/range/utils.rs b/prover/src/chips/range/utils.rs index 058fbd4a..1c9fa41c 100644 --- a/prover/src/chips/range/utils.rs +++ b/prover/src/chips/range/utils.rs @@ -11,7 +11,7 @@ use crate::utils::{big_uint_to_fp, fp_to_big_uint}; /// Example: /// decompose_fp_to_bytes(0x1f2f3f, 2) -> [0x3f, 0x2f] pub fn decompose_fp_to_bytes(value: Fp, n: usize) -> Vec { - let value_biguint = fp_to_big_uint(value); + let value_biguint = fp_to_big_uint(&value); let mut bytes = value_biguint.to_bytes_le(); @@ -33,7 +33,7 @@ pub fn decompose_fp_to_bytes(value: Fp, n: usize) -> Vec { /// 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 { - let value_biguint = fp_to_big_uint(value); + 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. @@ -80,7 +80,7 @@ mod testing { #[test] fn test_fp_to_big_uint() { let f = Fp::from(5); - let big_uint = fp_to_big_uint(f); + let big_uint = fp_to_big_uint(&f); assert_eq!(big_uint, BigUint::from(5u8)); } diff --git a/prover/src/circuits/summa_circuit.rs b/prover/src/circuits/summa_circuit.rs index 2e2dfd0d..ea6e8bdb 100644 --- a/prover/src/circuits/summa_circuit.rs +++ b/prover/src/circuits/summa_circuit.rs @@ -150,11 +150,11 @@ pub mod summa_hyperplonk { running_sum_values.push(vec![]); region.assign_advice( - || "username", + || format!("username {}", i), config.username, i, || { - Value::known(big_uint_to_fp( + Value::known(big_uint_to_fp::( self.entries[i].username_as_big_uint(), )) }, @@ -163,7 +163,8 @@ pub mod summa_hyperplonk { let mut assigned_balances_row = vec![]; for (j, balance) in self.entries[i].balances().iter().enumerate() { - let balance_value = Value::known(big_uint_to_fp(balance)); + let balance_value: Value = + Value::known(big_uint_to_fp(balance)); let assigned_balance = region.assign_advice( || format!("balance {}", j), @@ -266,7 +267,7 @@ pub mod summa_hyperplonk { fn instances(&self) -> Vec> { // The last decomposition of each range check chip should be zero let mut instances = vec![Fp::ZERO]; - instances.extend(self.grand_total.iter().map(big_uint_to_fp)); + instances.extend(self.grand_total.iter().map(big_uint_to_fp::)); vec![instances] } } diff --git a/prover/src/circuits/tests.rs b/prover/src/circuits/tests.rs index ba9c2f24..a93db350 100644 --- a/prover/src/circuits/tests.rs +++ b/prover/src/circuits/tests.rs @@ -1,67 +1,94 @@ +use halo2_proofs::arithmetic::Field; use plonkish_backend::{ - backend::{hyperplonk::HyperPlonk, PlonkishBackend, PlonkishCircuit, PlonkishCircuitInfo}, + backend::{hyperplonk::HyperPlonk, PlonkishBackend, PlonkishCircuit}, frontend::halo2::Halo2Circuit, halo2_curves::bn256::{Bn256, Fr as Fp}, pcs::{multilinear::MultilinearKzg, PolynomialCommitmentScheme}, - util::{ - arithmetic::PrimeField, - transcript::{InMemoryTranscript, Keccak256Transcript, TranscriptRead, TranscriptWrite}, - DeserializeOwned, Serialize, + util::transcript::{ + FieldTranscriptRead, FieldTranscriptWrite, InMemoryTranscript, Keccak256Transcript, }, Error::InvalidSumcheck, }; -use std::hash::Hash; use rand::{ rngs::{OsRng, StdRng}, - CryptoRng, RngCore, SeedableRng, + CryptoRng, Rng, RngCore, SeedableRng, }; use crate::{ - circuits::summa_circuit::summa_hyperplonk::SummaHyperplonk, utils::generate_dummy_entries, + circuits::summa_circuit::summa_hyperplonk::SummaHyperplonk, + utils::{ + big_uint_to_fp, fp_to_big_uint, generate_dummy_entries, uni_to_multivar_binary_index, + MultilinearAsUnivariate, + }, }; const K: u32 = 17; const N_CURRENCIES: usize = 2; const N_USERS: usize = 1 << 16; -pub fn run_plonkish_backend( - num_vars: usize, - circuit_fn: impl Fn(usize) -> (PlonkishCircuitInfo, C), -) where - F: PrimeField + Hash + Serialize + DeserializeOwned, - Pb: PlonkishBackend, - T: TranscriptRead<>::CommitmentChunk, F> - + TranscriptWrite<>::CommitmentChunk, F> - + InMemoryTranscript, - C: PlonkishCircuit, -{ - let (circuit_info, circuit) = circuit_fn(num_vars); +pub fn seeded_std_rng() -> impl RngCore + CryptoRng { + StdRng::seed_from_u64(OsRng.next_u64()) +} + +#[test] +fn test_summa_hyperplonk() { + type ProvingBackend = HyperPlonk>; + let entries = generate_dummy_entries::().unwrap(); + let circuit = SummaHyperplonk::::init(entries.to_vec()); + let num_vars = K; + + let circuit_fn = |num_vars| { + let circuit = Halo2Circuit::>::new::< + ProvingBackend, + >(num_vars, circuit.clone()); + (circuit.circuit_info().unwrap(), circuit) + }; + + let (circuit_info, circuit) = circuit_fn(num_vars as usize); 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 (prover_parameters, verifier_parameters) = + ProvingBackend::preprocess(¶m, &circuit_info).unwrap(); - let (pp, vp) = Pb::preprocess(¶m, &circuit_info).unwrap(); + let (witness_polys, proof_transcript) = { + let mut proof_transcript = Keccak256Transcript::new(()); - let proof = { - let mut transcript = T::new(()); - Pb::prove(&pp, &circuit, &mut transcript, seeded_std_rng()).unwrap(); - transcript.into_proof() + let witness_polys = ProvingBackend::prove( + &prover_parameters, + &circuit, + &mut proof_transcript, + seeded_std_rng(), + ) + .unwrap(); + (witness_polys, proof_transcript) }; - let result = { - let mut transcript = T::from_proof((), proof.as_slice()); - Pb::verify(&vp, instances, &mut transcript, seeded_std_rng()) + let num_points = 3; + + let proof = proof_transcript.into_proof(); + + let mut transcript; + let result: Result<(), plonkish_backend::Error> = { + transcript = Keccak256Transcript::from_proof((), proof.as_slice()); + ProvingBackend::verify( + &verifier_parameters, + instances, + &mut transcript, + seeded_std_rng(), + ) }; assert_eq!(result, Ok(())); let wrong_instances = instances[0] .iter() - .map(|instance| *instance + F::ONE) + .map(|instance| *instance + Fp::ONE) .collect::>(); let wrong_result = { - let mut transcript = T::from_proof((), proof.as_slice()); - Pb::verify( - &vp, + let mut transcript = Keccak256Transcript::from_proof((), proof.as_slice()); + ProvingBackend::verify( + &verifier_parameters, &vec![wrong_instances], &mut transcript, seeded_std_rng(), @@ -73,28 +100,80 @@ pub fn run_plonkish_backend( "Consistency failure at round 1".to_string() )) ); -} -pub fn seeded_std_rng() -> impl RngCore + CryptoRng { - StdRng::seed_from_u64(OsRng.next_u64()) -} + //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; -#[test] -fn test_summa_hyperplonk() { - type Pb = HyperPlonk>; - let entries = generate_dummy_entries::().unwrap(); - let circuit = SummaHyperplonk::::init(entries.to_vec()); - let num_vars = K; - run_plonkish_backend::, _>( - num_vars.try_into().unwrap(), - |num_vars| { - let circuit = Halo2Circuit::>::new::( - num_vars, - circuit.clone(), - ); - (circuit.circuit_info().unwrap(), circuit) - }, + assert_eq!( + fp_to_big_uint(&witness_polys[0].evaluate_as_univariate(&random_user_index)), + *entries[random_user_index].username_as_big_uint() + ); + assert_eq!( + fp_to_big_uint(&witness_polys[1].evaluate_as_univariate(&random_user_index)), + entries[random_user_index].balances()[0] ); + + // Convert challenge into a multivariate form + let multivariate_challenge = + uni_to_multivar_binary_index(&random_user_index, num_vars as usize); + + let mut kzg_transcript = Keccak256Transcript::new(()); + + let mut transcript = Keccak256Transcript::from_proof((), proof.as_slice()); + + let user_entry_commitments = MultilinearKzg::::read_commitments( + &verifier_parameters.pcs, + num_points, + &mut transcript, + ) + .unwrap(); + let user_entry_polynomials = witness_polys.iter().take(num_points).collect::>(); + + //Store the user index multi-variable in the transcript for the verifier + for binary_var in multivariate_challenge.iter() { + kzg_transcript.write_field_element(binary_var).unwrap(); + } + + MultilinearKzg::::open( + &prover_parameters.pcs, + user_entry_polynomials[0], + &user_entry_commitments[0], + &multivariate_challenge, + &user_entry_polynomials[0].evaluate(&multivariate_challenge), + &mut kzg_transcript, + ) + .unwrap(); + + let kzg_proof = kzg_transcript.into_proof(); + + // Verifier side + let mut kzg_transcript = Keccak256Transcript::from_proof((), kzg_proof.as_slice()); + + // The verifier knows the ZK-SNARK proof, can extract the polynomial commitments + let mut transcript = Keccak256Transcript::from_proof((), proof.as_slice()); + let user_entry_commitments = MultilinearKzg::::read_commitments( + &verifier_parameters.pcs, + num_points, + &mut transcript, + ) + .unwrap(); + + //The verifier doesn't know the mapping of their "user index" to the multi-variable index, reads it from the transcript + let mut multivariate_challenge = Vec::new(); + for _ in 0..num_vars { + multivariate_challenge.push(kzg_transcript.read_field_element().unwrap()); + } + + MultilinearKzg::::verify( + &verifier_parameters.pcs, + &user_entry_commitments[0], + &multivariate_challenge, + //The user knows their evaluation at the challenge point + &big_uint_to_fp(entries[random_user_index].username_as_big_uint()), + &mut kzg_transcript, + ) + .unwrap(); } #[cfg(feature = "dev-graph")] diff --git a/prover/src/utils/operation_helpers.rs b/prover/src/utils/operation_helpers.rs index 81704c63..7e2f8f9e 100644 --- a/prover/src/utils/operation_helpers.rs +++ b/prover/src/utils/operation_helpers.rs @@ -1,5 +1,9 @@ -use halo2_proofs::halo2curves::{bn256::Fr as Fp, group::ff::PrimeField}; +use halo2_proofs::{arithmetic::Field, halo2curves::group::ff::PrimeField}; use num_bigint::BigUint; +use plonkish_backend::{ + poly::multilinear::MultilinearPolynomial, + util::expression::rotate::{BinaryField, Rotatable}, +}; /// Return a BigUint representation of the username pub fn big_intify_username(username: &str) -> BigUint { @@ -7,11 +11,51 @@ pub fn big_intify_username(username: &str) -> BigUint { BigUint::from_bytes_be(utf8_bytes) } /// Converts a BigUint to a Field Element -pub fn big_uint_to_fp(big_uint: &BigUint) -> Fp { - Fp::from_str_vartime(&big_uint.to_str_radix(10)[..]).unwrap() +pub fn big_uint_to_fp(big_uint: &BigUint) -> F { + F::from_str_vartime(&big_uint.to_str_radix(10)[..]).unwrap() } /// Converts a Field element to a BigUint -pub fn fp_to_big_uint(f: Fp) -> BigUint { - BigUint::from_bytes_le(f.to_bytes().as_slice()) +pub fn fp_to_big_uint(f: &F) -> BigUint { + BigUint::from_bytes_le(f.to_repr().as_ref()) +} + +/// Trait to evaluate a multilinear polynomial in binary field as a univariate polynomial +pub trait MultilinearAsUnivariate { + /// Evaluate the multilinear polynomial as a univariate polynomial + /// at the point x + fn evaluate_as_univariate(&self, x: &usize) -> F; +} + +impl MultilinearAsUnivariate for MultilinearPolynomial { + fn evaluate_as_univariate(&self, x: &usize) -> F { + let x_as_binary_vars = uni_to_multivar_binary_index(x, self.num_vars()); + self.evaluate(x_as_binary_vars.as_slice()) + } +} + +/// Converts a single-variable polynomial index into a multivariate index in the binary field +pub fn uni_to_multivar_binary_index(x: &usize, num_vars: usize) -> Vec { + //The binary field is necessary to map an index to an evaluation point + let binary_field = BinaryField::new(num_vars).usable_indices(); + //Mapping the univariate point index to a multivariate evaluation point + let x_in_binary_field = binary_field[*x]; + let x_as_big_uint = BigUint::from(x_in_binary_field); + let bits = x_as_big_uint.bits(); + let mut result = vec![]; + assert!( + bits <= num_vars as u64, + "Number of bits in x exceeds num_vars" + ); + + // Ensure that bits are extended to match num_vars with 0-padding + for i in 0..num_vars { + result.push(if x_as_big_uint.bit(i as u64) { + F::ONE + } else { + F::ZERO + }); + } + + result } From 4731f29e049a58426a94df9d03df404b7328eb7e Mon Sep 17 00:00:00 2001 From: Alex Kuzmin Date: Wed, 24 Apr 2024 18:02:49 +0800 Subject: [PATCH 2/5] Perform batch KZG opening/verification --- prover/src/circuits/tests.rs | 61 ++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/prover/src/circuits/tests.rs b/prover/src/circuits/tests.rs index a93db350..64ae921c 100644 --- a/prover/src/circuits/tests.rs +++ b/prover/src/circuits/tests.rs @@ -3,9 +3,12 @@ use plonkish_backend::{ backend::{hyperplonk::HyperPlonk, PlonkishBackend, PlonkishCircuit}, frontend::halo2::Halo2Circuit, halo2_curves::bn256::{Bn256, Fr as Fp}, - pcs::{multilinear::MultilinearKzg, PolynomialCommitmentScheme}, - util::transcript::{ - FieldTranscriptRead, FieldTranscriptWrite, InMemoryTranscript, Keccak256Transcript, + pcs::{multilinear::MultilinearKzg, Evaluation, PolynomialCommitmentScheme}, + util::{ + transcript::{ + FieldTranscriptRead, FieldTranscriptWrite, InMemoryTranscript, Keccak256Transcript, + }, + Itertools, }, Error::InvalidSumcheck, }; @@ -89,7 +92,7 @@ fn test_summa_hyperplonk() { let mut transcript = Keccak256Transcript::from_proof((), proof.as_slice()); ProvingBackend::verify( &verifier_parameters, - &vec![wrong_instances], + &[wrong_instances], &mut transcript, seeded_std_rng(), ) @@ -115,7 +118,7 @@ fn test_summa_hyperplonk() { ); // Convert challenge into a multivariate form - let multivariate_challenge = + let multivariate_challenge: Vec = uni_to_multivar_binary_index(&random_user_index, num_vars as usize); let mut kzg_transcript = Keccak256Transcript::new(()); @@ -135,12 +138,20 @@ fn test_summa_hyperplonk() { kzg_transcript.write_field_element(binary_var).unwrap(); } - MultilinearKzg::::open( + let evals = user_entry_polynomials + .iter() + .enumerate() + .map(|(poly_idx, poly)| { + Evaluation::new(poly_idx, 0, poly.evaluate(&multivariate_challenge)) + }) + .collect_vec(); + + MultilinearKzg::::batch_open( &prover_parameters.pcs, - user_entry_polynomials[0], - &user_entry_commitments[0], - &multivariate_challenge, - &user_entry_polynomials[0].evaluate(&multivariate_challenge), + user_entry_polynomials, + &user_entry_commitments, + &[multivariate_challenge], + &evals, &mut kzg_transcript, ) .unwrap(); @@ -160,17 +171,35 @@ fn test_summa_hyperplonk() { .unwrap(); //The verifier doesn't know the mapping of their "user index" to the multi-variable index, reads it from the transcript - let mut multivariate_challenge = Vec::new(); + let mut multivariate_challenge: Vec = Vec::new(); for _ in 0..num_vars { multivariate_challenge.push(kzg_transcript.read_field_element().unwrap()); } - MultilinearKzg::::verify( + //The user knows their evaluation at the challenge point + let evals: Vec> = (0..N_CURRENCIES + 1) + .map(|i| { + if i == 0 { + Evaluation::new( + i, + 0, + big_uint_to_fp::(entries[random_user_index].username_as_big_uint()), + ) + } else { + Evaluation::new( + i, + 0, + big_uint_to_fp::(&entries[random_user_index].balances()[i - 1]), + ) + } + }) + .collect(); + + MultilinearKzg::::batch_verify( &verifier_parameters.pcs, - &user_entry_commitments[0], - &multivariate_challenge, - //The user knows their evaluation at the challenge point - &big_uint_to_fp(entries[random_user_index].username_as_big_uint()), + &user_entry_commitments, + &[multivariate_challenge], + &evals, &mut kzg_transcript, ) .unwrap(); From c9c05b173c5147388761590b587096b64b827316 Mon Sep 17 00:00:00 2001 From: Alex Kuzmin Date: Mon, 29 Apr 2024 14:56:08 +0800 Subject: [PATCH 3/5] Remove magic number --- prover/src/circuits/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prover/src/circuits/tests.rs b/prover/src/circuits/tests.rs index 64ae921c..8cfdeb0c 100644 --- a/prover/src/circuits/tests.rs +++ b/prover/src/circuits/tests.rs @@ -68,7 +68,7 @@ fn test_summa_hyperplonk() { (witness_polys, proof_transcript) }; - let num_points = 3; + let num_points = N_CURRENCIES + 1; let proof = proof_transcript.into_proof(); From d137e663fdaee0184a8b66b3d5458bbf8bf2d2b2 Mon Sep 17 00:00:00 2001 From: Alex Kuzmin Date: Mon, 29 Apr 2024 18:10:45 +0800 Subject: [PATCH 4/5] Add user inclusion benchmarks and verification benchmarks --- .github/workflows/benchmark.yml | 6 +- prover/benches/proof_of_liabilities.rs | 156 ++++++++++++++++++++++--- 2 files changed, 140 insertions(+), 22 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 6843de34..5d0762b2 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -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: @@ -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] diff --git a/prover/benches/proof_of_liabilities.rs b/prover/benches/proof_of_liabilities.rs index b47d2075..402cd2c0 100644 --- a/prover/benches/proof_of_liabilities.rs +++ b/prover/benches/proof_of_liabilities.rs @@ -3,7 +3,7 @@ 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}, @@ -11,10 +11,11 @@ use plonkish_backend::{ }; 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() { @@ -22,38 +23,36 @@ fn bench_summa() 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>; + let grand_sum_verification_bench_name = format!("<{}> grand sum verification", name); + let inclusion_verification_bench_name = format!("<{}> user inclusion verification", name); + + type ProvingBackend = HyperPlonk>; let entries = generate_dummy_entries::().unwrap(); let halo2_circuit = SummaHyperplonk::::init(entries.to_vec()); - let circuit = Halo2Circuit::>::new::( + let circuit = Halo2Circuit::>::new::( 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(¶m, &circuit_info).unwrap(); + let (pp, vp) = ProvingBackend::preprocess(¶m, &circuit_info).unwrap(); let mut transcript = Keccak256Transcript::default(); let proof = { - Pb::prove(&pp, &circuit, &mut transcript, std_rng()).unwrap(); + ProvingBackend::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); - c.bench_function(&grand_sum_proof_bench_name, |b| { b.iter_batched( || { - Halo2Circuit::>::new::( + Halo2Circuit::>::new::( K as usize, halo2_circuit.clone(), ) @@ -61,20 +60,139 @@ fn bench_summa() |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::SmallInput, + ) + }); + + let (prover_parameters, verifier_parameters) = + ProvingBackend::preprocess(¶m, &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::>(); + let mut transcript = Keccak256Transcript::from_proof((), proof.as_slice()); + + let user_entry_commitments = MultilinearKzg::::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::(entries[random_user_index].username_as_big_uint()), + )); + } else { + evals.push(Evaluation::new( + i, + 0, + big_uint_to_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::::batch_open( + &prover_parameters.pcs, + user_entry_polynomials, + &user_entry_commitments, + &[multivariate_challenge], + &evals, + &mut kzg_transcript, + ) + .unwrap(); + }, + criterion::BatchSize::SmallInput, + ) + }); + + 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::SmallInput, + ) + }); + + c.bench_function(&inclusion_verification_bench_name, |b| { + b.iter_batched( + || { + let mut kzg_transcript = Keccak256Transcript::new(()); + MultilinearKzg::::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::::batch_verify( + &verifier_parameters.pcs, + &user_entry_commitments, + &[multivariate_challenge], + &evals, + &mut kzg_transcript, + ) + .unwrap(); + }, + criterion::BatchSize::SmallInput, ) }); } 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::(); } } From 5261bd108008756bf44d9edb8974ee289d2a70c1 Mon Sep 17 00:00:00 2001 From: Alex Kuzmin Date: Fri, 10 May 2024 16:32:18 +0800 Subject: [PATCH 5/5] Code review fixes --- prover/benches/proof_of_liabilities.rs | 21 ++++---- prover/src/chips/range/range_check.rs | 2 +- prover/src/chips/range/utils.rs | 68 ++------------------------ 3 files changed, 17 insertions(+), 74 deletions(-) diff --git a/prover/benches/proof_of_liabilities.rs b/prover/benches/proof_of_liabilities.rs index 402cd2c0..5e866eaf 100644 --- a/prover/benches/proof_of_liabilities.rs +++ b/prover/benches/proof_of_liabilities.rs @@ -43,12 +43,6 @@ fn bench_summa() let (pp, vp) = ProvingBackend::preprocess(¶m, &circuit_info).unwrap(); - let mut transcript = Keccak256Transcript::default(); - let proof = { - ProvingBackend::prove(&pp, &circuit, &mut transcript, std_rng()).unwrap(); - transcript.into_proof() - }; - c.bench_function(&grand_sum_proof_bench_name, |b| { b.iter_batched( || { @@ -63,7 +57,7 @@ fn bench_summa() ProvingBackend::prove(&pp, &circuit, &mut transcript, std_rng()).unwrap(); transcript.into_proof(); }, - criterion::BatchSize::SmallInput, + criterion::BatchSize::LargeInput, ) }); @@ -84,6 +78,13 @@ fn bench_summa() }; let num_points = N_CURRENCIES + 1; let user_entry_polynomials = witness_polys.iter().take(num_points).collect::>(); + + 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::::read_commitments( @@ -140,7 +141,7 @@ fn bench_summa() ) .unwrap(); }, - criterion::BatchSize::SmallInput, + criterion::BatchSize::LargeInput, ) }); @@ -152,7 +153,7 @@ fn bench_summa() { ProvingBackend::verify(&vp, instances, &mut transcript, std_rng()).is_ok() }; assert!(accept); }, - criterion::BatchSize::SmallInput, + criterion::BatchSize::LargeInput, ) }); @@ -182,7 +183,7 @@ fn bench_summa() ) .unwrap(); }, - criterion::BatchSize::SmallInput, + criterion::BatchSize::LargeInput, ) }); } diff --git a/prover/src/chips/range/range_check.rs b/prover/src/chips/range/range_check.rs index c814f211..26598aa4 100644 --- a/prover/src/chips/range/range_check.rs +++ b/prover/src/chips/range/range_check.rs @@ -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. diff --git a/prover/src/chips/range/utils.rs b/prover/src/chips/range/utils.rs index 1c9fa41c..48d798d7 100644 --- a/prover/src/chips/range/utils.rs +++ b/prover/src/chips/range/utils.rs @@ -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 { - 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 { - let value_biguint = fp_to_big_uint(&value); +pub fn decompose_fp_to_byte_pairs(value: &Fp, n: usize) -> Vec { + 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. @@ -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);