Skip to content

Commit

Permalink
fix: following review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
sifnoc committed Jul 16, 2024
1 parent e756b15 commit aa47d62
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 40 deletions.
2 changes: 1 addition & 1 deletion backend/examples/summa_solvency_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
//
// Initialize the `Round` instance to submit the liability commitment.
let entry_csv = "../csv/entry_16.csv";
let mut entries: Vec<Entry<N_CURRENCIES>> = vec![Entry::init_empty(); N_USERS];
let mut entries: Vec<Entry<N_USERS, N_CURRENCIES>> = vec![Entry::init_empty(); N_USERS];
let mut cryptos = vec![Cryptocurrency::init_empty(); N_CURRENCIES];
parse_csv_to_entries::<&str, N_CURRENCIES>(entry_csv, &mut entries, &mut cryptos).unwrap();

Expand Down
4 changes: 2 additions & 2 deletions backend/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ mod test {
.await?;

let entry_csv = "../csv/entry_16.csv";
let mut entries: Vec<Entry<N_CURRENCIES>> = vec![Entry::init_empty(); N_USERS];
let mut entries: Vec<Entry<N_USERS, N_CURRENCIES>> = vec![Entry::init_empty(); N_USERS];
let mut cryptos = vec![Cryptocurrency::init_empty(); N_CURRENCIES];
parse_csv_to_entries::<&str, N_CURRENCIES>(entry_csv, &mut entries, &mut cryptos).unwrap();

Expand Down Expand Up @@ -299,7 +299,7 @@ mod test {

// Initialize Round.
let entry_csv = "../csv/entry_16.csv";
let mut entries: Vec<Entry<N_CURRENCIES>> = vec![Entry::init_empty(); N_USERS];
let mut entries: Vec<Entry<N_USERS, N_CURRENCIES>> = vec![Entry::init_empty(); N_USERS];
let mut cryptos = vec![Cryptocurrency::init_empty(); N_CURRENCIES];
parse_csv_to_entries::<&str, N_CURRENCIES>(entry_csv, &mut entries, &mut cryptos).unwrap();

Expand Down
1 change: 1 addition & 0 deletions prover/rust-toolchain
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nightly-2023-07-11
8 changes: 6 additions & 2 deletions prover/src/circuits/config/circuit_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub trait CircuitConfig<const N_CURRENCIES: usize, const N_USERS: usize>: Clone
fn synthesize(
&self,
mut layouter: impl Layouter<Fp>,
entries: &[Entry<N_CURRENCIES>],
entries: &[Entry<N_USERS, N_CURRENCIES>],
concatenated_grand_total: &Fp,
) -> Result<(), Error> {
// Initiate the range check chips
Expand All @@ -58,7 +58,11 @@ pub trait CircuitConfig<const N_CURRENCIES: usize, const N_USERS: usize>: Clone
|| "concatenated balance",
self.get_concatenated_balance(),
0,
|| Value::known(big_uint_to_fp::<Fp>(&entry.concatenated_balance())),
|| {
Value::known(big_uint_to_fp::<Fp>(
&entry.concatenated_balance().unwrap(),
))
},
)?;

// Decompose the balances
Expand Down
60 changes: 40 additions & 20 deletions prover/src/circuits/summa_circuit.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
use std::marker::PhantomData;

use halo2_proofs::{
arithmetic::Field,
circuit::{Layouter, SimpleFloorPlanner},
halo2curves::{bn256::Fr as Fp, ff::PrimeField},
plonk::{Circuit, ConstraintSystem, Error, Expression},
poly::Rotation,
};

use crate::{entry::Entry, utils::big_uint_to_fp};

use halo2_proofs::arithmetic::Field;
use halo2_proofs::halo2curves::bn256::Fr as Fp;
use plonkish_backend::frontend::halo2::CircuitExt;
use rand::RngCore;
use std::marker::PhantomData;

use super::config::circuit_config::CircuitConfig;
use crate::{
entry::Entry,
utils::{big_uint_to_fp, calculate_shift_bits},
};

#[derive(Clone, Default)]
pub struct SummaHyperplonk<
const N_USERS: usize,
const N_CURRENCIES: usize,
CONFIG: CircuitConfig<N_CURRENCIES, N_USERS>,
> {
pub entries: Vec<Entry<N_CURRENCIES>>,
pub entries: Vec<Entry<N_USERS, N_CURRENCIES>>,
pub concatenated_grand_total: Fp,
_marker: PhantomData<CONFIG>,
}
Expand All @@ -32,11 +32,12 @@ impl<
CONFIG: CircuitConfig<N_CURRENCIES, N_USERS>,
> SummaHyperplonk<N_USERS, N_CURRENCIES, CONFIG>
{
pub fn init(user_entries: Vec<Entry<N_CURRENCIES>>) -> Self {
pub fn init(user_entries: Vec<Entry<N_USERS, N_CURRENCIES>>) -> Self {
let mut concatenated_grand_total = Fp::ZERO;

for entry in user_entries.iter() {
concatenated_grand_total += big_uint_to_fp::<Fp>(&entry.concatenated_balance());
concatenated_grand_total +=
big_uint_to_fp::<Fp>(&entry.concatenated_balance().unwrap());
}

Self {
Expand All @@ -49,7 +50,7 @@ impl<
/// Initialize the circuit with an invalid grand total
/// (for testing purposes only).
#[cfg(test)]
pub fn init_invalid_grand_total(user_entries: Vec<Entry<N_CURRENCIES>>) -> Self {
pub fn init_invalid_grand_total(user_entries: Vec<Entry<N_USERS, N_CURRENCIES>>) -> Self {
use plonkish_backend::util::test::seeded_std_rng;

let concatenated_grand_total = Fp::random(seeded_std_rng());
Expand Down Expand Up @@ -103,19 +104,38 @@ impl<
// Right-most balance column is for the least significant balance in concatenated balance.
let mut balances_expr = meta.query_advice(balances[N_CURRENCIES - 1], Rotation::cur());

// Base shift value for 84 bits.
let base_shift = Fp::from(1 << 63).mul(&Fp::from(1 << 21));
let shift_bits = calculate_shift_bits::<N_USERS, N_CURRENCIES>().unwrap();

let mut current_shift = Expression::Constant(base_shift);
// The shift bits would not be exceed 93 bits
let base_shift = Fp::from_u128(1u128 << shift_bits);

for i in (0..N_CURRENCIES - 1).rev() {
let balance = meta.query_advice(balances[i], Rotation::cur());
let shifted_balance = balance * current_shift.clone();
balances_expr = balances_expr + shifted_balance;
let mut current_shift = Expression::Constant(base_shift);

if i != 0 {
current_shift = current_shift * Expression::Constant(base_shift);
// The number of currencies is limited to 3 because the range check bits are 64 for each currency.
// In other words, more than 3 currencies would exceed the maximum bit count of 254, which is number of bits in Bn254.
match N_CURRENCIES {
1 => {
// No need to add any shift for the only balance
}
2 => {
let balance = meta.query_advice(balances[0], Rotation::cur());
let shifted_balance = balance * current_shift.clone();
balances_expr = balances_expr + shifted_balance;
}
3 => {
for i in (0..N_CURRENCIES - 1).rev() {
let balance = meta.query_advice(balances[i], Rotation::cur());
let shifted_balance = balance * current_shift.clone();
balances_expr = balances_expr + shifted_balance;

if i != 0 {
current_shift = current_shift * Expression::Constant(base_shift);
}
}
}
_ => panic!(
"Unsupported number of currencies, Only 1, 2, and 3 currencies are supported"
),
}

// Ensure that the whole expression equals to the concatenated_balance
Expand Down
4 changes: 2 additions & 2 deletions prover/src/circuits/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ fn test_summa_hyperplonk_e2e() {
);
assert_eq!(
fp_to_big_uint(&witness_polys[1].evaluate_as_univariate(&random_user_index)),
entries[random_user_index].concatenated_balance()
entries[random_user_index].concatenated_balance().unwrap()
);

// Convert challenge into a multivariate form
Expand Down Expand Up @@ -239,7 +239,7 @@ fn test_summa_hyperplonk_e2e() {
Evaluation::new(
1,
0,
big_uint_to_fp::<Fp>(&entries[random_user_index].concatenated_balance()),
big_uint_to_fp::<Fp>(&entries[random_user_index].concatenated_balance().unwrap()),
),
];

Expand Down
17 changes: 9 additions & 8 deletions prover/src/entry.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
use num_bigint::BigUint;

use crate::utils::big_intify_username;
use crate::utils::{big_intify_username, calculate_shift_bits};

/// An entry in the Merkle Sum Tree from the database of the CEX.
/// It contains the username and the balances of the user.
#[derive(Clone, Debug)]
pub struct Entry<const N_CURRENCIES: usize> {
pub struct Entry<const N_USERS: usize, const N_CURRENCIES: usize> {
username_as_big_uint: BigUint,
balances: [BigUint; N_CURRENCIES],
username: String,
}

impl<const N_CURRENCIES: usize> Entry<N_CURRENCIES> {
impl<const N_USERS: usize, const N_CURRENCIES: usize> Entry<N_USERS, N_CURRENCIES> {
pub fn new(username: String, balances: [BigUint; N_CURRENCIES]) -> Result<Self, &'static str> {
Ok(Entry {
username_as_big_uint: big_intify_username(&username),
Expand All @@ -34,16 +34,17 @@ impl<const N_CURRENCIES: usize> Entry<N_CURRENCIES> {
&self.balances
}

pub fn concatenated_balance(&self) -> BigUint {
pub fn concatenated_balance(&self) -> Result<BigUint, String> {
let shift_bits = calculate_shift_bits::<N_USERS, N_CURRENCIES>().unwrap();

let mut concatenated_balance = BigUint::from(0u32);

// To arragne the balances in the correct order, we need to reverse the array
// Reverse the array to correctly order the balances
for (i, balance) in self.balances.iter().rev().enumerate() {
// Shift bits: 1 for buffer; 19 is highest bit for two power of userbase; 64 is for the maximum range check limit
concatenated_balance += balance << ((1 + 19 + 64) * i);
concatenated_balance += balance << (shift_bits * i);
}

concatenated_balance
Ok(concatenated_balance)
}

pub fn username_as_big_uint(&self) -> &BigUint {
Expand Down
7 changes: 2 additions & 5 deletions prover/src/utils/dummy_entries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ use crate::entry::Entry;

// This is for testing purposes with a large dataset instead of using a CSV file
pub fn generate_dummy_entries<const N_USERS: usize, const N_CURRENCIES: usize>(
) -> Result<Vec<Entry<N_CURRENCIES>>, Box<dyn Error>> {
) -> Result<Vec<Entry<N_USERS, N_CURRENCIES>>, Box<dyn Error>> {
// Ensure N_CURRENCIES is greater than 0.
if N_CURRENCIES == 0 {
return Err("N_CURRENCIES must be greater than 0".into());
}

let mut entries: Vec<Entry<N_CURRENCIES>> = vec![Entry::init_empty(); N_USERS];
let mut entries: Vec<Entry<N_USERS, N_CURRENCIES>> = vec![Entry::init_empty(); N_USERS];

entries.par_iter_mut().for_each(|entry| {
let mut rng = rand::thread_rng();
Expand All @@ -31,9 +31,6 @@ pub fn generate_dummy_entries<const N_USERS: usize, const N_CURRENCIES: usize>(

#[cfg(test)]
mod tests {
use crate::utils::big_uint_to_fp;
use halo2_proofs::halo2curves::bn256::Fr as Fp;

use super::*;

#[test]
Expand Down
84 changes: 84 additions & 0 deletions prover/src/utils/operation_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,87 @@ pub fn uni_to_multivar_binary_index<F: Field + PrimeField>(x: &usize, num_vars:

result
}

pub fn calculate_shift_bits<const N_USERS: usize, const N_CURRENCIES: usize>(
) -> Result<usize, String> {
// Define the maximum number of bits that can be used, based on the modulus in bn254.
const MAX_ALLOWANCE_BITS: usize = 253;

// Calculate the maximum number of bits that can be allocated to the user base,
// taking into account the number of currencies and the bits needed for the balances range check and buffer.
let maximum_allowance_user_base_bits = (MAX_ALLOWANCE_BITS / N_CURRENCIES) - 64 - 1;

// Determine the number of bits needed to represent the user base.
// For example, if `N_USERS` is 1025, the user base bit count would be 11.
let user_base_bits = N_USERS.next_power_of_two().ilog2() as usize;

if user_base_bits > maximum_allowance_user_base_bits {
return Err(format!(
"The bit count for the user base exceeds the maximum limit of {}",
maximum_allowance_user_base_bits
));
}

// Define shift bits: 1 for buffer, bits for user base that not exceed 19, and 64 bits for the balances range check
let shift_bits: usize = (1 + user_base_bits + 64).try_into().unwrap();

Ok(shift_bits)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_calculate_shift_bits() {
{
// Practical Nnumber of users cases
const N_USERS: usize = 1 << 28;
const N_CURRENCIES: usize = 1;

let result = calculate_shift_bits::<N_USERS, N_CURRENCIES>();
assert_eq!(result.unwrap(), 93);
assert_eq!(93 * N_CURRENCIES < 253, true);
}
{
const N_USERS: usize = 1 << 28;
const N_CURRENCIES: usize = 2;

let result = calculate_shift_bits::<N_USERS, N_CURRENCIES>();
assert_eq!(result.unwrap(), 93);
assert_eq!(93 * N_CURRENCIES < 253, true);
}
{
// Maximum number of user when N_CURRENCIES = 3
const N_USERS: usize = 1 << 19;
const N_CURRENCIES: usize = 3;

let result = calculate_shift_bits::<N_USERS, N_CURRENCIES>();
assert_eq!(result.unwrap(), 84);
assert_eq!(84 * N_CURRENCIES < 253, true);
}
{
// Error case in N_CURRENCIES = 2 with infeasible N_USERS
const N_USERS: usize = 1 << 63;
const N_CURRENCIES: usize = 2;

let result = calculate_shift_bits::<N_USERS, N_CURRENCIES>();
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
"The bit count for the user base exceeds the maximum limit of 61"
);
}
{
const N_USERS: usize = 1 << 63;
const N_CURRENCIES: usize = 3;

let result = calculate_shift_bits::<N_USERS, N_CURRENCIES>();
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
"The bit count for the user base exceeds the maximum limit of 19"
);
}
}
}

0 comments on commit aa47d62

Please sign in to comment.