1use alloc::{
5 borrow::ToOwned,
6 format,
7 string::{String, ToString},
8 vec::Vec,
9};
10use anyhow::{anyhow, Context, Result};
11use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, Write};
12use ark_std::rand::{distributions::Alphanumeric, Rng as _};
13use directories::ProjectDirs;
14use sha2::{Digest, Sha256};
15use std::{
16 fs::{self, create_dir_all, File},
17 io::BufReader,
18 path::{Path, PathBuf},
19};
20
21pub fn store_data<T: CanonicalSerialize>(data: T, dest: PathBuf) -> Result<()> {
23 let mut f = File::create(dest)?;
24 let mut bytes = Vec::new();
25 data.serialize_uncompressed(&mut bytes)?;
26 Ok(f.write_all(&bytes)?)
27}
28
29pub fn load_data<T: CanonicalDeserialize>(src: PathBuf) -> Result<T> {
31 let f = File::open(src)?;
32 let mut reader = BufReader::with_capacity(8000, f);
34 let mut bytes = Vec::new();
35 reader.read_to_end(&mut bytes)?;
36
37 Ok(T::deserialize_uncompressed_unchecked(&bytes[..])?)
38}
39
40pub fn download_srs_file(basename: &str, dest: impl AsRef<Path>) -> Result<()> {
45 create_dir_all(dest.as_ref().parent().context("no parent dir")?)
47 .context("Unable to create directory")?;
48
49 let version = "0.2.0"; let url = format!(
51 "https://github.com/EspressoSystems/ark-srs/releases/download/v{version}/{basename}",
52 );
53 tracing::info!("Downloading SRS from {url}");
54 let mut buf: Vec<u8> = Vec::new();
55 ureq::get(&url)
56 .call()?
57 .into_reader()
58 .read_to_end(&mut buf)?;
59
60 let mut temp_path = dest.as_ref().as_os_str().to_owned();
65 let suffix: String = rand::thread_rng()
66 .sample_iter(&Alphanumeric)
67 .take(16)
68 .map(char::from)
69 .collect();
70 temp_path.push(format!(".temp.{suffix}"));
71 {
72 let mut f = File::create(&temp_path)?;
73 f.write_all(&buf)?;
74 }
75 std::fs::rename(temp_path, dest.as_ref())?;
76 tracing::info!("Saved SRS to {:?}", dest.as_ref());
77 Ok(())
78}
79
80fn get_project_root() -> Result<PathBuf> {
82 Ok(ProjectDirs::from("", "", "ark-srs")
85 .context("Failed to get project root")?
86 .data_dir()
87 .to_path_buf())
88}
89
90pub mod kzg10 {
92 use super::*;
93 use ark_poly_commit::kzg10;
94
95 pub mod bn254 {
97 use super::*;
98 use ark_bn254::Bn254;
99
100 pub mod aztec {
102 use crate::constants::AZTEC20_CHECKSUMS;
103
104 use super::*;
105
106 pub fn default_path(project_root: Option<PathBuf>, degree: usize) -> Result<PathBuf> {
108 let mut path = if let Some(root) = project_root {
109 root
110 } else {
111 get_project_root()?
112 };
113 path.push("aztec20");
114 path.push(degree_to_basename(degree));
115 path.set_extension("bin");
116 Ok(path)
117 }
118
119 pub(crate) fn degree_to_basename(degree: usize) -> String {
120 format!("kzg10-aztec20-srs-{degree}.bin").to_string()
121 }
122
123 pub fn load_aztec_srs(
132 degree: usize,
133 src: PathBuf,
134 ) -> Result<kzg10::UniversalParams<Bn254>> {
135 let mut f = File::open(&src).map_err(|_| anyhow!("{} not found", src.display()))?;
136 let f_degree = src
139 .file_stem()
140 .unwrap()
141 .to_str()
142 .unwrap()
143 .rsplit_once('-')
144 .expect("unconventional filename")
145 .1
146 .parse::<usize>()
147 .expect("fail to parse to uint");
148
149 let mut bytes = Vec::new();
150 f.read_to_end(&mut bytes)?;
151
152 let checksum: [u8; 32] = Sha256::digest(&bytes).into();
153 if !AZTEC20_CHECKSUMS
154 .iter()
155 .any(|(d, cksum)| *d == f_degree && checksum == *cksum)
156 {
157 tracing::error!("Checksum failed, removing {}", src.display());
158 fs::remove_file(src)?;
159 return Err(anyhow!("Checksum failed!"));
160 }
161
162 let mut srs = kzg10::UniversalParams::<Bn254>::deserialize_uncompressed_unchecked(
163 &bytes[..],
164 )?;
165
166 srs.powers_of_g.truncate(degree + 1);
168 Ok(srs)
169 }
170 }
171 }
172}