rusk_profile/
lib.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7#![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
24/// HEX representation of the SHA-256 hash of the CRS uncompressed bytes.
25/// This is the hash of the contribution number 15 of the Dusk Trusted Setup.
26pub 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
43/// Return Rusk profile directory, ensuring that all parents directory are
44/// created
45///
46/// Default to [`home_dir`]/.dusk/rusk
47///
48/// `RUSK_PROFILE_PATH` env can be used to override
49///
50/// E.g:
51/// RUSK_PROFILE_PATH | result
52/// -- | --
53/// None | $HOME/.dusk/rusk
54/// Set | $RUSK_PROFILE_PATH
55pub 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
72/// Return Rusk circuits directory, ensuring that all parents directory are
73/// created
74///
75/// Default to [`get_rusk_profile_dir`]/circuits
76///
77/// `RUSK_CIRCUITS_PATH` env can be used to override
78///
79/// E.g:
80/// RUSK_PROFILE_PATH | RUSK_CIRCUITS_PATH | result
81/// -- | -- | --
82/// None | None | $HOME/.dusk/rusk/circuits
83/// Set | None | $RUSK_PROFILE_PATH/circuits
84/// _ | Set | $RUSK_CIRCUITS_PATH
85pub 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
98/// Return Rusk keys directory, ensuring that all parents directory are created
99///
100/// Default to [`get_rusk_profile_dir`]/keys
101///
102/// `RUSK_KEYS_PATH` env can be used to override
103///
104/// E.g:
105/// RUSK_PROFILE_PATH | RUSK_KEYS_PATH | result
106/// -- | -- | --
107/// None | None | $HOME/.dusk/rusk/keys
108/// Set | None | $RUSK_PROFILE_PATH/keys
109/// _ | Set | $RUSK_KEYS_PATH
110pub 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
123/// Return Rusk keys directory, ensuring that all parents directory are created
124///
125/// Default  to [get_rusk_profile_dir]/state
126///
127/// `RUSK_STATE_PATH` env can be used to override
128///
129/// E.g:
130/// RUSK_PROFILE_PATH | RUSK_STATE_PATH | result
131/// -- | -- | --
132/// None | None | $HOME/.dusk/rusk/state
133/// Set | None | $RUSK_PROFILE_PATH/state
134/// _ | Set | $RUSK_STATE_PATH
135pub 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    // removing all untracked files in circuits directory
209    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    // removing all untracked files in keys directory
232    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}