1#![deny(unused_crate_dependencies)]
8#![deny(unused_extern_crates)]
9
10use std::fs::{self, read, remove_file, write};
11use std::path::{Path, PathBuf};
12use std::{env, io};
13
14use dirs::home_dir;
15use sha2::{Digest, Sha256};
16use tracing::{info, warn};
17
18mod theme;
19pub use theme::Theme;
20
21mod circuit;
22pub use circuit::Circuit;
23
24pub static CRS_17_HASH: &str =
27 "6161605616b62356cf09fa28252c672ef53b2c8489ad5f81d87af26e105f6059";
28
29const CRS_FNAME: &str = "devnet-piecrust.crs";
30
31fn extension(p: &Path) -> Option<&str> {
32 p.extension()?.to_str()
33}
34
35fn file_stem(p: &Path) -> Option<&str> {
36 p.file_stem()?.to_str()
37}
38
39fn file_name(p: &Path) -> Option<&str> {
40 p.file_name()?.to_str()
41}
42
43pub fn get_rusk_profile_dir() -> io::Result<PathBuf> {
56 env::var("RUSK_PROFILE_PATH")
57 .map_or_else(
58 |e| {
59 home_dir()
60 .ok_or(io::Error::new(io::ErrorKind::InvalidInput, e))
61 .map(|p| p.join(".dusk").join("rusk"))
62 },
63 |profile_path| Ok(PathBuf::from(profile_path)),
64 )
65 .and_then(|p| fs::create_dir_all(&p).map(|_| p))
66 .map_err(|e| {
67 warn!("rusk-profile dir not found and impossible to create: {e}");
68 e
69 })
70}
71
72pub fn get_rusk_circuits_dir() -> io::Result<PathBuf> {
86 env::var("RUSK_CIRCUITS_PATH")
87 .map_or_else(
88 |_| get_rusk_profile_dir().map(|p| p.join("circuits")),
89 |circuits_path| Ok(PathBuf::from(circuits_path)),
90 )
91 .and_then(|p| fs::create_dir_all(&p).map(|_| p))
92 .map_err(|e| {
93 warn!("rusk-profile circuits dir not found and impossible to create: {e}");
94 e
95 })
96}
97
98pub fn get_rusk_keys_dir() -> io::Result<PathBuf> {
111 env::var("RUSK_KEYS_PATH")
112 .map_or_else(
113 |_| get_rusk_profile_dir().map(|p| p.join("keys")),
114 |keys_path| Ok(PathBuf::from(keys_path)),
115 )
116 .and_then(|p| fs::create_dir_all(&p).map(|_| p))
117 .map_err(|e| {
118 warn!("rusk-profile key's dir not found and impossible to create: {e}");
119 e
120 })
121}
122
123pub fn get_rusk_state_dir() -> io::Result<PathBuf> {
136 env::var("RUSK_STATE_PATH")
137 .map_or_else(
138 |_| get_rusk_profile_dir().map(|p| p.join("state")),
139 |state_path| Ok(PathBuf::from(state_path)),
140 )
141 .and_then(|p| fs::create_dir_all(&p).map(|_| p))
142 .map_err(|e| {
143 warn!("rusk-profile state dir not found and impossible to create: {e}");
144 e
145 })
146}
147
148pub fn get_rusk_driver_storage_dir() -> io::Result<PathBuf> {
149 get_rusk_profile_dir().map(|p|
150 p.join("drivers")
151 ).and_then(|p| fs::create_dir_all(&p).map(|_| p))
152 .map_err(|e| {
153 warn!("rusk-profile driver storage dir not found and impossible to create: {e}");
154 e
155 })
156}
157
158pub fn to_rusk_state_id_path<P: AsRef<Path>>(dir: P) -> PathBuf {
159 let dir = dir.as_ref();
160 dir.join("state.id")
161}
162
163pub fn get_common_reference_string() -> io::Result<Vec<u8>> {
164 let crs = get_rusk_profile_dir()?.join(CRS_FNAME);
165 read(crs)
166}
167
168pub fn set_common_reference_string(buffer: Vec<u8>) -> io::Result<()> {
169 if !verify_common_reference_string(&buffer[..]) {
170 return Err(io::Error::new(
171 io::ErrorKind::InvalidInput,
172 "CRS Mismatch",
173 ));
174 }
175 let crs = get_rusk_profile_dir()?.join(CRS_FNAME);
176 write(crs, buffer)?;
177 info!("{} CRS to cache", Theme::default().success("Added"),);
178
179 Ok(())
180}
181
182pub fn delete_common_reference_string() -> io::Result<()> {
183 let crs = get_rusk_profile_dir()?.join(CRS_FNAME);
184 remove_file(crs)?;
185 warn!("{} CRS", Theme::default().warn("Removed"),);
186
187 Ok(())
188}
189
190pub fn verify_common_reference_string(buff: &[u8]) -> bool {
191 info!("{} CRS integrity", Theme::default().info("Checking"));
192 let mut hasher = Sha256::new();
193 hasher.update(buff);
194 let hash = format!("{:x}", hasher.finalize());
195
196 hash == CRS_17_HASH
197}
198
199pub fn clean_outdated(circuits: &[Circuit]) -> io::Result<()> {
200 let ids_as_string: Vec<&str> =
201 circuits.iter().map(|c| c.id_str()).collect();
202
203 clean_outdated_circuits(&ids_as_string)?;
204 clean_outdated_keys(&ids_as_string)
205}
206
207fn clean_outdated_circuits(ids: &[&str]) -> io::Result<()> {
208 fs::read_dir(get_rusk_circuits_dir()?)?
210 .map(|res| res.map(|e| e.path()))
211 .filter_map(|res| res.ok())
212 .filter(|e| e.is_file())
213 .filter(|p| match extension(p) {
214 Some("cd" | "toml") => {
215 file_stem(p).filter(|id| ids.contains(id)).is_none()
216 }
217 _ => true,
218 })
219 .try_for_each(|p| {
220 warn!(
221 "{} /circuits/{}",
222 Theme::default().warn("Removing"),
223 file_name(&p).expect("file should be valid")
224 );
225 remove_file(p)?;
226 Ok(())
227 })
228}
229
230fn clean_outdated_keys(ids: &[&str]) -> io::Result<()> {
231 fs::read_dir(get_rusk_keys_dir()?)?
233 .map(|res| res.map(|e| e.path()))
234 .filter_map(|res| res.ok())
235 .filter(|e| e.is_file())
236 .filter(|p| match extension(p) {
237 Some("pk" | "vd") => {
238 file_stem(p).filter(|id| ids.contains(id)).is_none()
239 }
240 _ => true,
241 })
242 .try_for_each(|p| {
243 warn!(
244 "{} /keys/{}",
245 Theme::default().warn("Removing"),
246 file_name(&p).expect("file should be valid")
247 );
248 remove_file(p)
249 })
250}
251
252pub fn clear_all() -> io::Result<()> {
253 clear_all_circuits()?;
254 clear_all_keys()
255}
256
257fn clear_all_keys() -> io::Result<()> {
258 info!(
259 "{} all keys directory contents",
260 Theme::default().warn("Clearing")
261 );
262
263 fs::read_dir(get_rusk_keys_dir()?)?
264 .map(|res| res.map(|e| e.path()))
265 .filter_map(|res| res.ok())
266 .filter(|e| e.is_file())
267 .try_for_each(remove_file)
268}
269
270fn clear_all_circuits() -> io::Result<()> {
271 info!(
272 "{} all circuit directory contents",
273 Theme::default().warn("Clearing")
274 );
275
276 fs::read_dir(get_rusk_circuits_dir()?)?
277 .map(|res| res.map(|e| e.path()))
278 .filter_map(|res| res.ok())
279 .filter(|e| e.is_file())
280 .try_for_each(remove_file)
281}