use clap::Parser;
use reqwest::blocking::Client;
use sha1::{Digest, Sha1};
use std::error::Error;
use std::time::Instant;
use valar_ypir::client::YPIRClient;
use valar_ypir::params::*;
use valar_ypir::serialize::ToBytes;
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
num_items: usize,
item_size_bits: usize,
#[clap(long)]
target_row: Option<usize>,
#[clap(long)]
target_item: Option<String>,
#[clap(long, short, action)]
is_simplepir: bool,
#[clap(long, default_value_t = 2048)]
poly_len: usize,
#[clap(long, short, default_value = "8080")]
port: u16,
#[clap(long, short, action)]
verbose: bool,
}
const SHA1_HASH_BYTES: usize = 20;
fn main() {
let args = Args::parse();
let Args {
target_row,
target_item,
num_items,
item_size_bits,
verbose,
is_simplepir,
poly_len,
port,
} = args;
let log2_num_items = (num_items as f64).log2().ceil() as usize;
let (target_row, item_hash) = if let Some(target_row) = target_row {
(target_row, None)
} else {
let target_item = target_item.expect("Must provide either target_row or target_item");
let mut hasher: sha1::digest::core_api::CoreWrapper<sha1::Sha1Core> = Sha1::new();
hasher.update(target_item.as_bytes());
let item_hash = hasher.finalize();
let top_idx = u32::from_be_bytes(item_hash[0..4].try_into().unwrap());
let bucket = top_idx >> (32 - log2_num_items);
println!("Bucket: {}", bucket);
(bucket as usize, Some(item_hash))
};
if !is_simplepir {
panic!("Must use YPIR-SP for now.");
}
if verbose {
println!("Running in verbose mode.");
env_logger::Builder::new()
.filter_level(log::LevelFilter::Debug)
.write_style(env_logger::WriteStyle::Always)
.init();
} else {
env_logger::init();
}
let client = YPIRClient::from_db_sz_simplepir_with_config(
num_items as u64,
item_size_bits as u64,
YPIRSPConfig::for_poly_len(poly_len),
);
assert!(target_row < client.params().db_rows());
let (query, client_seed) = client.generate_query_simplepir(target_row);
let query_bytes = query.to_bytes();
let now = Instant::now();
let response_data: Vec<u8> =
make_http_request(&format!("http://localhost:{}/query", port), query_bytes).unwrap();
println!("Query time: {:?}", now.elapsed().as_secs_f64());
let result = client.decode_response_simplepir(client_seed, &response_data);
println!(
"Result: {:?}..{:?}",
&result[..32],
&result[result.len() - 32..]
);
let mut end = result.len() - 1;
while end > 0 && result[end] == 0 {
end -= 1;
}
println!("Result[{}-36..]: {:?}", end, &result[end - 36..end + 36]);
println!("Result[32940..]: {:?}", &result[32940..32940 + 36],);
if let Some(item_hash) = item_hash {
let omitted_bytes = log2_num_items / 8;
let hash_bytes = SHA1_HASH_BYTES - omitted_bytes;
println!("Hash bytes: {}", hash_bytes);
let looking_for = &item_hash[omitted_bytes..];
println!("looking_for: {:?}", looking_for);
let mut found = false;
let mut i = 0;
for chunk in result.chunks_exact(hash_bytes) {
if chunk == looking_for {
println!("Found at index {}", i);
found = true;
break;
}
i += hash_bytes;
}
if found {
println!("Item found!");
} else {
println!("Item not found.");
}
}
}
fn make_http_request(url: &str, query_bytes: Vec<u8>) -> Result<Vec<u8>, Box<dyn Error>> {
let client = Client::new();
let response = client.post(url).body(query_bytes).send()?.bytes()?;
Ok(response.to_vec())
}