Skip to content

Commit

Permalink
Implement user inclusion proof
Browse files Browse the repository at this point in the history
  • Loading branch information
alxkzmn committed Dec 5, 2023
1 parent e8658ef commit 6b84f82
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 21 deletions.
63 changes: 54 additions & 9 deletions kzg_prover/src/circuits/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ mod test {

use crate::circuits::solvency_v2::SolvencyV2;
use crate::circuits::utils::{
full_prover, full_verifier, generate_setup_artifacts, open_grand_sums,
verify_grand_sum_openings, verify_kzg_proof,
full_prover, full_verifier, generate_setup_artifacts, open_grand_sums, open_user_balances,
verify_grand_sum_openings, verify_user_inclusion,
};
use crate::cryptocurrency::Cryptocurrency;
use crate::entry::Entry;
use crate::utils::parse_csv_to_entries;
use halo2_proofs::{
dev::{FailureLocation, MockProver, VerifyFailure},
halo2curves::bn256::Fr as Fp,
plonk::Any,
};
use num_bigint::{BigUint, ToBigUint};
use num_bigint::BigUint;

const K: u32 = 9;

Expand All @@ -24,9 +26,13 @@ mod test {
fn test_valid_solvency_v2() {
let path = "src/csv/entry_16.csv";

let (_, entries) = parse_csv_to_entries::<&str, N_CURRENCIES, N_BYTES>(path).unwrap();
let mut entries: Vec<Entry<N_CURRENCIES>> = vec![Entry::init_empty(); N_USERS];
let mut cryptos = vec![Cryptocurrency::init_empty(); N_CURRENCIES];
let _ =
parse_csv_to_entries::<&str, N_CURRENCIES, N_BYTES>(path, &mut entries, &mut cryptos)
.unwrap();

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

let valid_prover = MockProver::run(K, &circuit, vec![vec![]]).unwrap();

Expand Down Expand Up @@ -76,7 +82,12 @@ mod test {
// Only now we can instantiate the circuit with the actual inputs
let path = "src/csv/entry_16.csv";

let (_, entries) = parse_csv_to_entries::<&str, N_CURRENCIES, N_BYTES>(path).unwrap();
let mut entries: Vec<Entry<N_CURRENCIES>> = vec![Entry::init_empty(); N_USERS];
let mut cryptos = vec![Cryptocurrency::init_empty(); N_CURRENCIES];

let _ =
parse_csv_to_entries::<&str, N_CURRENCIES, N_BYTES>(path, &mut entries, &mut cryptos)
.unwrap();

// Calculate total for all entry columns
let mut csv_total: Vec<BigUint> = vec![BigUint::from(0u32); N_CURRENCIES];
Expand All @@ -87,15 +98,16 @@ mod test {
}
}

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

let valid_prover = MockProver::run(K, &circuit, vec![vec![]]).unwrap();

valid_prover.assert_satisfied();

// 1. Proving phase
// The Custodian generates the ZK proof
let (zk_proof, advice_polys) = full_prover(&params, &pk, circuit.clone(), vec![vec![]]);
let (zk_proof, advice_polys, omega) =
full_prover(&params, &pk, circuit.clone(), vec![vec![]]);

// Both the Custodian and the Verifier know what column range are the balance columns
let balance_column_range = 1..N_CURRENCIES + 1;
Expand All @@ -109,10 +121,26 @@ mod test {
balance_column_range,
);

// The Custodian creates a KZG proof of the 4th user balances inclusion
let user_index = 3_u16;

let balance_column_range = 1..N_CURRENCIES + 1;
let user_balances_kzg_proofs = open_user_balances::<N_CURRENCIES>(
&advice_polys.advice_polys,
&advice_polys.advice_blinds,
&params,
balance_column_range,
omega,
user_index,
);

// 2. Verification phase
// The Verifier verifies the ZK proof
assert!(full_verifier(&params, &vk, &zk_proof, vec![vec![]]));

// The Verifier is able to independently extract the omega from the verification key
let omega = pk.get_vk().get_domain().get_omega();

// The Custodian communicates the polynomial degree to the Verifier
let poly_degree = u64::try_from(advice_polys.advice_polys[0].len()).unwrap();

Expand All @@ -134,7 +162,24 @@ mod test {
assert_eq!(csv_total[i], grand_sum[i]);
}

//TODO next: make openings at "user" points
let balance_column_range = 1..N_CURRENCIES + 1;
let (balances_verified, balance_values) = verify_user_inclusion::<N_CURRENCIES>(
&params,
&zk_proof,
user_balances_kzg_proofs,
balance_column_range,
omega,
user_index,
);

let fourth_user_csv_entry = entries.get(user_index as usize).unwrap();
for i in 0..N_CURRENCIES {
assert!(balances_verified[i]);
assert_eq!(
*fourth_user_csv_entry.balances().get(i).unwrap(),
balance_values[i]
);
}
}

#[cfg(feature = "dev-graph")]
Expand Down
85 changes: 83 additions & 2 deletions kzg_prover/src/circuits/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{fs::File, ops::Range};
use ark_std::{end_timer, start_timer};
use ethers::types::U256;
use halo2_proofs::{
arithmetic::eval_polynomial,
arithmetic::{eval_polynomial, Field},
halo2curves::{
bn256::{Bn256, Fr as Fp, G1Affine},
ff::{PrimeField, WithSmallOrderMulGroup},
Expand Down Expand Up @@ -89,6 +89,7 @@ pub fn full_prover<C: Circuit<Fp>>(
) -> (
Vec<u8>,
AdviceSingle<halo2_proofs::halo2curves::bn256::G1Affine, Coeff>,
Fp,
) {
let pf_time = start_timer!(|| "Creating proof");

Expand All @@ -111,7 +112,10 @@ pub fn full_prover<C: Circuit<Fp>>(

end_timer!(pf_time);
let advice_polys = advice_polys[0].clone();
(proof, advice_polys)

let omega = pk.get_vk().get_domain().get_omega();

(proof, advice_polys, omega)
}

/// Creates the univariate polynomial grand sum openings
Expand All @@ -138,6 +142,35 @@ pub fn open_grand_sums<const N_CURRENCIES: usize>(
kzg_proofs
}

pub fn open_user_balances<const N_CURRENCIES: usize>(
advice_poly: &[Polynomial<Fp, Coeff>],
advice_blind: &[Blind<Fp>],
params: &ParamsKZG<Bn256>,
balance_column_range: Range<usize>,
omega: Fp,
user_index: u16,
) -> Vec<Vec<u8>> {
let omega_raised = omega.pow_vartime([user_index as u64]);
let mut kzg_proofs = Vec::new();
balance_column_range.for_each(|i| {
kzg_proofs.push(
create_kzg_proof::<
KZGCommitmentScheme<Bn256>,
ProverSHPLONK<'_, Bn256>,
Challenge255<G1Affine>,
Blake2bWrite<Vec<u8>, G1Affine, Challenge255<G1Affine>>,
>(
params,
advice_poly[i].clone(),
advice_blind[i],
omega_raised,
)
.to_vec(),
)
});
kzg_proofs
}

/// Verifies the univariate polynomial grand sum openings
pub fn verify_grand_sum_openings<const N_CURRENCIES: usize>(
params: &ParamsKZG<Bn256>,
Expand Down Expand Up @@ -182,6 +215,54 @@ pub fn verify_grand_sum_openings<const N_CURRENCIES: usize>(
(verification_results, constant_terms)
}

pub fn verify_user_inclusion<const N_CURRENCIES: usize>(
params: &ParamsKZG<Bn256>,
zk_proof: &[u8],
kzg_proofs: Vec<Vec<u8>>,
balance_column_range: Range<usize>,
omega: Fp,
user_index: u16,
) -> (Vec<bool>, Vec<BigUint>) {
let mut transcript: Blake2bRead<&[u8], G1Affine, Challenge255<G1Affine>> =
Blake2bRead::<_, _, Challenge255<_>>::init(zk_proof);

//Read the commitment points for all the advice polynomials from the proof transcript and put them into a vector
let mut advice_commitments = Vec::new();
for i in 0..N_CURRENCIES + balance_column_range.start {
let point = transcript.read_point().unwrap();
// Skip the balances column commitment
if i != 0 {
advice_commitments.push(point);
}
}

let mut verification_results = Vec::<bool>::new();
let mut balances = Vec::<BigUint>::new();

for (i, advice_commitment) in advice_commitments.iter().enumerate() {
let (verified, eval_at_challenge) = verify_kzg_proof::<
KZGCommitmentScheme<Bn256>,
VerifierSHPLONK<'_, Bn256>,
Challenge255<G1Affine>,
Blake2bRead<_, _, Challenge255<_>>,
AccumulatorStrategy<_>,
>(
params,
&kzg_proofs[i],
omega.pow_vartime([user_index as u64]),
*advice_commitment,
);
verification_results.push(verified);

if verified {
balances.push(fp_to_big_uint(eval_at_challenge));
} else {
balances.push(BigUint::from(0u8));
}
}
(verification_results, balances)
}

/// Creates a KZG proof for a polynomial evaluation at a challenge
fn create_kzg_proof<
'params,
Expand Down
9 changes: 9 additions & 0 deletions kzg_prover/src/cryptocurrency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,12 @@ pub struct Cryptocurrency {
pub name: String,
pub chain: String,
}

impl Cryptocurrency {
pub fn init_empty() -> Self {
Cryptocurrency {
name: "".to_string(),
chain: "".to_string(),
}
}
}
20 changes: 10 additions & 10 deletions kzg_prover/src/utils/csv_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,37 @@ use crate::entry::Entry;

pub fn parse_csv_to_entries<P: AsRef<Path>, const N_ASSETS: usize, const N_BYTES: usize>(
path: P,
) -> Result<(Vec<Cryptocurrency>, Vec<Entry<N_ASSETS>>), Box<dyn Error>> {
entries: &mut [Entry<N_ASSETS>],
cryptocurrencies: &mut [Cryptocurrency],
) -> Result<(), Box<dyn Error>> {
let file = File::open(path)?;
let mut rdr = csv::ReaderBuilder::new().from_reader(file);

let headers = rdr.headers()?.clone();
let mut cryptocurrencies: Vec<Cryptocurrency> = Vec::with_capacity(N_ASSETS);

// Extracting cryptocurrency names from column names
for header in headers.iter().skip(1) {
for (i, header) in headers.iter().skip(1).enumerate() {
// Skipping 'username' column
let parts: Vec<&str> = header.split('_').collect();
if parts.len() == 3 && parts[0] == "balance" {
cryptocurrencies.push(Cryptocurrency {
cryptocurrencies[i] = Cryptocurrency {
name: parts[1].to_owned(),
chain: parts[2].to_owned(),
});
};
} else {
// Throw an error if the header is malformed
return Err(format!("Invalid header: {}", header).into());
}
}

let mut entries = Vec::new();
let mut balances_acc: Vec<BigUint> = vec![BigUint::from(0_usize); N_ASSETS];

for result in rdr.deserialize() {
for (i, result) in rdr.deserialize().enumerate() {
let record: HashMap<String, String> = result?;
let username = record.get("username").ok_or("Username not found")?.clone();

let mut balances_big_int = Vec::new();
for cryptocurrency in &cryptocurrencies {
for cryptocurrency in &mut *cryptocurrencies {
let balance_str = record
.get(format!("balance_{}_{}", cryptocurrency.name, cryptocurrency.chain).as_str())
.ok_or(format!(
Expand All @@ -60,7 +60,7 @@ pub fn parse_csv_to_entries<P: AsRef<Path>, const N_ASSETS: usize, const N_BYTES
.collect();

let entry = Entry::new(username, balances_big_int.try_into().unwrap())?;
entries.push(entry);
entries[i] = entry;
}

// Iterate through the balance accumulator and throw error if any balance is not in range 0, 2 ^ (8 * N_BYTES):
Expand All @@ -73,5 +73,5 @@ pub fn parse_csv_to_entries<P: AsRef<Path>, const N_ASSETS: usize, const N_BYTES
}
}

Ok((cryptocurrencies, entries))
Ok(())
}

0 comments on commit 6b84f82

Please sign in to comment.