use rand;
use num_bigint::{BigUint, RandBigInt};
use num_traits::{One, Num};
use crate::{Result, ConstraintSystem, Variables, Witness, StatementBuilder, Sink, WorkspaceSink};
use std::path::Path;
use rand::Rng;
const BENCHMARK_PRIMES: [&str; 4] = [
"2", "11", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43", ];
const BENCHMARK_CS_WITNESS_NUMBER: [u64; 4] = [
3,
100,
1000,
1000000
];
const BENCHMARK_CS_INSTANCES_NUMBER: [u64; 2] = [
10,
10000,
];
struct BenchmarkParameter {
pub ins_number: u64,
pub wit_number: u64,
pub modulus: BigUint,
}
impl BenchmarkParameter {
pub fn new(ins_number_: u64, wit_number_: u64, hexaprime: &str) -> Result<BenchmarkParameter> {
let prime = BigUint::from_str_radix(hexaprime, 16)?;
Ok(BenchmarkParameter {
ins_number: ins_number_,
wit_number: wit_number_,
modulus: prime,
})
}
}
macro_rules! bits_to_bytes {
($val:expr) => (($val + 7) / 8);
}
pub fn generate_all_metrics_data(workspace: impl AsRef<Path>) -> Result<()> {
for hexaprime in BENCHMARK_PRIMES.iter() {
for wit_nbr in BENCHMARK_CS_WITNESS_NUMBER.iter() {
for ins_nbr in BENCHMARK_CS_INSTANCES_NUMBER.iter() {
println!("Generating R1CS system for prime:{} / witness number: {} / instance number: {}", &hexaprime, *wit_nbr, *ins_nbr);
let sink = WorkspaceSink::new(workspace.as_ref().to_path_buf().join(format!("metrics_{}_{}_{}/", &hexaprime, *ins_nbr, *wit_nbr)))?;
generate_metrics_data(sink, &hexaprime, *wit_nbr, *ins_nbr)?;
}
}
}
Ok(())
}
pub fn generate_some_metrics_data(workspace: impl AsRef<Path>) -> Result<()> {
for hexaprime in BENCHMARK_PRIMES.iter() {
for wit_nbr in [3, 100].iter() {
for ins_nbr in [3, 10].iter() {
println!("Generating R1CS system for prime:{} / witness number: {} / instance number: {}", &hexaprime, *wit_nbr, *ins_nbr);
let sink = WorkspaceSink::new(workspace.as_ref().to_path_buf().join(format!("metrics_{}_{}_{}/", &hexaprime, *ins_nbr, *wit_nbr)))?;
generate_metrics_data(sink, &hexaprime, *wit_nbr, *ins_nbr)?;
}
}
}
Ok(())
}
pub fn generate_metrics_data(sink: impl Sink, hexaprime: &str, wit_nbr: u64, ins_nbr: u64) -> Result<()> {
let mut rng = rand::thread_rng();
let bp = BenchmarkParameter::new(ins_nbr, wit_nbr, &hexaprime)?;
let size_in_bytes = bits_to_bytes!(&bp.modulus.bits()) as usize;
let witnesses: Vec<BigUint> = (0..wit_nbr).map(|_| rng.gen_biguint_below(&bp.modulus)).collect();
let mut builder = StatementBuilder::new(sink);
builder.header.field_maximum = Some(serialize_biguint(&bp.modulus - BigUint::one(), size_in_bytes));
let wit_idx = ((ins_nbr+1)..(ins_nbr + wit_nbr + 1)).collect::<Vec<u64>>();
let constraints_start: &[((Vec<u64>, Vec<u8>), (Vec<u64>, Vec<u8>), (Vec<u64>, Vec<u8>))] = &[
((vec![0], vec![1]), (wit_idx.clone(), vec![1; wit_idx.len()]), (wit_idx.clone(), vec![1; wit_idx.len()])),
];
builder.push_constraints(ConstraintSystem::from(constraints_start))?;
for _i in 0..ins_nbr {
let b1: Vec<u8> = (0..wit_nbr).map(|_| rng.gen_range(0, 2)).collect();
let b2: Vec<u8> = (0..wit_nbr).map(|_| rng.gen_range(0, 2)).collect();
let mut result_of_equation = compute_equation(&witnesses, &b1) * compute_equation(&witnesses, &b2);
result_of_equation %= &bp.modulus;
let buf = serialize_biguint(result_of_equation, size_in_bytes);
let instance_id = builder.allocate_instance_var(&buf);
let new_constraint: &[((Vec<u64>, Vec<u8>), (Vec<u64>, Vec<u8>), (Vec<u64>, Vec<u8>))] = &[
((wit_idx.clone(), b1), (wit_idx.clone(), b2), (vec![instance_id], vec![1]))
];
builder.push_constraints(ConstraintSystem::from(new_constraint))?;
}
let witness_buffer = serialize_biguints(witnesses, size_in_bytes);
builder.push_witness(Witness {
assigned_variables: Variables {
variable_ids: wit_idx, values: Some(witness_buffer),
}
})?;
builder.header.free_variable_id += wit_nbr;
builder.finish_header()?;
Ok(())
}
fn serialize_biguint(biguint: BigUint, byte_len: usize) -> Vec<u8> {
let mut ret: Vec<u8> = Vec::new();
let mut binary = biguint.to_bytes_le();
let len = binary.len();
ret.append(&mut binary);
ret.append(&mut vec![0; byte_len - len]);
ret
}
fn serialize_biguints(biguints: Vec<BigUint>, byte_len: usize) -> Vec<u8> {
let mut ret: Vec<u8> = Vec::new();
for biguint in biguints.iter() {
ret.append(&mut serialize_biguint(biguint.clone(), byte_len));
}
ret
}
fn compute_equation(witnesses: &Vec<BigUint>, bit_vector: &[u8]) -> BigUint {
witnesses.iter().zip(bit_vector.iter()).map(|(buint, bit)| buint * bit ).sum::<BigUint>()
}
#[test]
fn test_generate_metrics() {
use std::fs::remove_dir_all;
use std::path::PathBuf;
use crate::consumers::simulator::Simulator;
use crate::consumers::workspace::Workspace;
let wit_nbr = 500;
let ins_nbr = 500;
let workspace = PathBuf::from("local/test_metrics");
let _ = remove_dir_all(&workspace);
match WorkspaceSink::new(workspace.join(format!("metrics_{}_{}_{}/", &BENCHMARK_PRIMES[0], ins_nbr, wit_nbr))) {
Err(_) => eprintln!(""),
Ok(sink) => {
match generate_metrics_data(sink, &BENCHMARK_PRIMES[0], wit_nbr, ins_nbr) {
Err(_) => eprintln!("Error"),
_ => {
let mut simulator = Simulator::default();
let ws: Workspace = Workspace::from_dir(&workspace.join(format!("metrics_{}_500_500/", &BENCHMARK_PRIMES[0])) ).unwrap();
for msg in ws.iter_messages() {
simulator.ingest_message(&msg);
}
assert_eq!(simulator.get_violations().len(), 0);
}
}
}
}
}