use crate::primitives::arithmetic::{Group, PairingCurve};
use crate::primitives::serialization::{DoryDeserialize, DorySerialize};
#[cfg(all(feature = "disk-persistence", not(target_arch = "wasm32")))]
use std::fs::{self, File};
#[cfg(all(feature = "disk-persistence", not(target_arch = "wasm32")))]
use std::io::{BufReader, BufWriter};
#[cfg(all(feature = "disk-persistence", not(target_arch = "wasm32")))]
use std::path::PathBuf;
#[derive(Clone, Debug, DorySerialize, DoryDeserialize)]
pub struct ProverSetup<E: PairingCurve> {
pub g1_vec: Vec<E::G1>,
pub g2_vec: Vec<E::G2>,
pub h1: E::G1,
pub h2: E::G2,
pub ht: E::GT,
}
#[derive(Clone, Debug, DorySerialize, DoryDeserialize)]
pub struct VerifierSetup<E: PairingCurve> {
pub delta_1l: Vec<E::GT>,
pub delta_1r: Vec<E::GT>,
pub delta_2l: Vec<E::GT>,
pub delta_2r: Vec<E::GT>,
pub chi: Vec<E::GT>,
pub g1_0: E::G1,
pub g2_0: E::G2,
pub h1: E::G1,
pub h2: E::G2,
pub ht: E::GT,
pub max_log_n: usize,
}
impl<E: PairingCurve> ProverSetup<E> {
pub fn new(max_log_n: usize) -> Self {
let n = 1 << max_log_n.div_ceil(2);
let g1_vec: Vec<E::G1> = (0..n).map(|_| E::G1::random()).collect();
let g2_vec: Vec<E::G2> = (0..n).map(|_| E::G2::random()).collect();
let h1 = E::G1::random();
let h2 = E::G2::random();
let ht = E::pair(&h1, &h2);
Self {
g1_vec,
g2_vec,
h1,
h2,
ht,
}
}
pub fn to_verifier_setup(&self) -> VerifierSetup<E> {
let max_num_rounds = self.g1_vec.len().trailing_zeros() as usize;
let mut delta_1l = Vec::with_capacity(max_num_rounds + 1);
let mut delta_1r = Vec::with_capacity(max_num_rounds + 1);
let mut delta_2r = Vec::with_capacity(max_num_rounds + 1);
let mut chi = Vec::with_capacity(max_num_rounds + 1);
for k in 0..=max_num_rounds {
if k == 0 {
delta_1l.push(E::GT::identity());
delta_1r.push(E::GT::identity());
delta_2r.push(E::GT::identity());
chi.push(E::pair(&self.g1_vec[0], &self.g2_vec[0]));
} else {
let half_len = 1 << (k - 1);
let full_len = 1 << k;
let g1_first_half = &self.g1_vec[..half_len];
let g1_second_half = &self.g1_vec[half_len..full_len];
let g2_first_half = &self.g2_vec[..half_len];
let g2_second_half = &self.g2_vec[half_len..full_len];
delta_1l.push(chi[k - 1]);
delta_1r.push(E::multi_pair(g1_second_half, g2_first_half));
delta_2r.push(E::multi_pair(g1_first_half, g2_second_half));
chi.push(chi[k - 1].add(&E::multi_pair(g1_second_half, g2_second_half)));
}
}
VerifierSetup {
delta_1l: delta_1l.clone(),
delta_1r,
delta_2l: delta_1l, delta_2r,
chi,
g1_0: self.g1_vec[0],
g2_0: self.g2_vec[0],
h1: self.h1,
h2: self.h2,
ht: self.ht,
max_log_n: max_num_rounds * 2, }
}
#[inline]
pub fn max_nu(&self) -> usize {
self.g1_vec.len().trailing_zeros() as usize
}
#[inline]
pub fn max_sigma(&self) -> usize {
self.max_nu()
}
#[inline]
pub fn max_log_n(&self) -> usize {
self.max_nu() * 2
}
}
#[cfg(all(feature = "disk-persistence", not(target_arch = "wasm32")))]
fn get_storage_path(max_log_n: usize) -> Option<PathBuf> {
let cache_directory = {
if let Ok(local_app_data) = std::env::var("LOCALAPPDATA") {
Some(PathBuf::from(local_app_data))
} else if let Ok(home) = std::env::var("HOME") {
let mut path = PathBuf::from(&home);
let macos_cache = {
let mut test_path = PathBuf::from(&home);
test_path.push("Library");
test_path.push("Caches");
test_path.exists()
};
if macos_cache {
path.push("Library");
path.push("Caches");
} else {
path.push(".cache");
}
Some(path)
} else {
None
}
};
cache_directory.map(|mut path| {
path.push("dory");
path.push(format!("dory_{max_log_n}.urs"));
path
})
}
#[cfg(all(feature = "disk-persistence", not(target_arch = "wasm32")))]
pub fn save_setup<E: PairingCurve>(
prover: &ProverSetup<E>,
verifier: &VerifierSetup<E>,
max_log_n: usize,
) where
ProverSetup<E>: DorySerialize,
VerifierSetup<E>: DorySerialize,
{
let storage_path = get_storage_path(max_log_n).expect("Failed to determine storage directory");
if let Some(parent) = storage_path.parent() {
fs::create_dir_all(parent)
.unwrap_or_else(|e| panic!("Failed to create storage directory: {e}"));
}
tracing::info!("Saving setup to {}", storage_path.display());
let file =
File::create(&storage_path).unwrap_or_else(|e| panic!("Failed to create setup file: {e}"));
let mut writer = BufWriter::new(file);
DorySerialize::serialize_compressed(prover, &mut writer)
.unwrap_or_else(|e| panic!("Failed to serialize prover setup: {e}"));
DorySerialize::serialize_compressed(verifier, &mut writer)
.unwrap_or_else(|e| panic!("Failed to serialize verifier setup: {e}"));
tracing::info!("Successfully saved setup to disk");
}
#[cfg(all(feature = "disk-persistence", not(target_arch = "wasm32")))]
pub fn load_setup<E: PairingCurve>(
max_log_n: usize,
) -> Result<(ProverSetup<E>, VerifierSetup<E>), crate::DoryError>
where
ProverSetup<E>: DoryDeserialize,
VerifierSetup<E>: DoryDeserialize,
{
let storage_path = get_storage_path(max_log_n).ok_or_else(|| {
crate::DoryError::InvalidURS("Failed to determine storage directory".to_string())
})?;
if !storage_path.exists() {
return Err(crate::DoryError::InvalidURS(format!(
"Setup file not found at {}",
storage_path.display()
)));
}
tracing::info!("Looking for saved setup at {}", storage_path.display());
let file = File::open(&storage_path)
.map_err(|e| crate::DoryError::InvalidURS(format!("Failed to open setup file: {e}")))?;
let mut reader = BufReader::new(file);
let prover = DoryDeserialize::deserialize_compressed(&mut reader).map_err(|e| {
crate::DoryError::InvalidURS(format!("Failed to deserialize prover setup: {e}"))
})?;
let verifier = DoryDeserialize::deserialize_compressed(&mut reader).map_err(|e| {
crate::DoryError::InvalidURS(format!("Failed to deserialize verifier setup: {e}"))
})?;
tracing::info!("Loaded setup for max_log_n={}", max_log_n);
Ok((prover, verifier))
}