1#[macro_export]
17macro_rules! checksum {
18 ($bytes: expr) => {{
19 use sha2::Digest;
20 hex::encode(&sha2::Sha256::digest($bytes))
21 }};
22}
23
24#[macro_export]
25macro_rules! checksum_error {
26 ($expected: expr, $candidate: expr) => {
27 Err($crate::errors::ParameterError::ChecksumMismatch($expected, $candidate))
28 };
29}
30
31#[macro_export]
32macro_rules! remove_file {
33 ($filepath:expr) => {
34 #[cfg(not(feature = "wasm"))]
36 if std::path::PathBuf::from(&$filepath).exists() {
37 match std::fs::remove_file(&$filepath) {
38 Ok(()) => println!("Removed {:?}. Please retry the command.", $filepath),
39 Err(err) => eprintln!("Failed to remove {:?}: {err}", $filepath),
40 }
41 }
42 };
43}
44
45macro_rules! impl_store_and_remote_fetch {
46 () => {
47 #[cfg(not(feature = "wasm"))]
48 fn store_bytes(buffer: &[u8], file_path: &std::path::Path) -> Result<(), $crate::errors::ParameterError> {
49 use snarkvm_utilities::Write;
50
51 #[cfg(not(feature = "no_std_out"))]
52 {
53 use colored::*;
54 let output = format!("{:>15} - Storing file in {:?}", "Installation", file_path);
55 println!("{}", output.dimmed());
56 }
57
58 let mut directory_path = file_path.to_path_buf();
60 directory_path.pop();
61 let _ = std::fs::create_dir_all(directory_path)?;
62
63 match std::fs::File::create(file_path) {
65 Ok(mut file) => file.write_all(&buffer)?,
66 Err(error) => eprintln!("{}", error),
67 }
68 Ok(())
69 }
70
71 #[cfg(all(not(feature = "wasm"), not(target_env = "sgx")))]
72 fn remote_fetch(buffer: &mut Vec<u8>, url: &str) -> Result<(), $crate::errors::ParameterError> {
73 let mut easy = curl::easy::Easy::new();
74 easy.follow_location(true)?;
75 easy.url(url)?;
76
77 #[cfg(not(feature = "no_std_out"))]
78 {
79 use colored::*;
80
81 let output = format!("{:>15} - Downloading \"{}\"", "Installation", url);
82 println!("{}", output.dimmed());
83
84 easy.progress(true)?;
85 easy.progress_function(|total_download, current_download, _, _| {
86 let percent = (current_download / total_download) * 100.0;
87 let size_in_megabytes = total_download as u64 / 1_048_576;
88 let output =
89 format!("\r{:>15} - {:.2}% complete ({:#} MB total)", "Installation", percent, size_in_megabytes);
90 print!("{}", output.dimmed());
91 true
92 })?;
93 }
94
95 let mut transfer = easy.transfer();
96 transfer.write_function(|data| {
97 buffer.extend_from_slice(data);
98 Ok(data.len())
99 })?;
100 Ok(transfer.perform()?)
101 }
102
103 #[cfg(feature = "wasm")]
104 fn remote_fetch(url: &str) -> Result<Vec<u8>, $crate::errors::ParameterError> {
105 let xhr = web_sys::XmlHttpRequest::new().map_err(|_| {
110 $crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest object not found".to_string())
111 })?;
112
113 xhr.override_mime_type("octet/binary; charset=ISO-8859-5").unwrap();
117
118 xhr.open_with_async("GET", url, false).map_err(|_| {
120 $crate::errors::ParameterError::Wasm(
121 "Download failed - This browser does not support synchronous requests".to_string(),
122 )
123 })?;
124 xhr.send()
125 .map_err(|_| $crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest failed".to_string()))?;
126
127 if xhr.response().is_ok() && xhr.status().unwrap() == 200 {
129 let rust_text = xhr
131 .response_text()
132 .map_err(|_| $crate::errors::ParameterError::Wasm("XMLHttpRequest failed".to_string()))?
133 .ok_or($crate::errors::ParameterError::Wasm(
134 "The request was successful but no parameters were received".to_string(),
135 ))?;
136
137 use encoding::Encoding;
139 encoding::all::ISO_8859_5
140 .encode(&rust_text, encoding::EncoderTrap::Strict)
141 .map_err(|_| $crate::errors::ParameterError::Wasm("Parameter decoding failed".to_string()))
142 } else {
143 Err($crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest failed".to_string()))
144 }
145 }
146 };
147}
148
149macro_rules! impl_load_bytes_logic_local {
150 ($filepath: expr, $buffer: expr, $expected_size: expr, $expected_checksum: expr) => {
151 if $expected_size != $buffer.len() {
153 remove_file!($filepath);
154 return Err($crate::errors::ParameterError::SizeMismatch($expected_size, $buffer.len()));
155 }
156
157 let candidate_checksum = checksum!($buffer);
159 if $expected_checksum != candidate_checksum {
160 return checksum_error!($expected_checksum, candidate_checksum);
161 }
162
163 return Ok($buffer.to_vec());
164 };
165}
166
167macro_rules! impl_load_bytes_logic_remote {
168 ($remote_url: expr, $local_dir: expr, $filename: expr, $metadata: expr, $expected_checksum: expr, $expected_size: expr) => {
169 cfg_if::cfg_if! {
170 if #[cfg(all(feature = "filesystem", not(feature="wasm")))] {
171 let mut file_path = aleo_std::aleo_dir();
173 file_path.push($local_dir);
174 file_path.push($filename);
175
176 let buffer = if file_path.exists() {
177 std::fs::read(&file_path)?
179 } else {
180 #[cfg(not(feature = "no_std_out"))]
182 {
183 use colored::*;
184 let path = format!("(in {:?})", file_path);
185 eprintln!(
186 "\n⚠️ \"{}\" does not exist. Downloading and storing it {}.\n",
187 $filename, path.dimmed()
188 );
189 }
190
191 cfg_if::cfg_if!{
193 if #[cfg(all(not(feature = "wasm"), not(target_env = "sgx")))] {
194 let url = format!("{}/{}", $remote_url, $filename);
195 let mut buffer = vec![];
196 Self::remote_fetch(&mut buffer, &url)?;
197
198 let candidate_checksum = checksum!(&buffer);
200 if $expected_checksum != candidate_checksum {
201 return checksum_error!($expected_checksum, candidate_checksum)
202 }
203
204 match Self::store_bytes(&buffer, &file_path) {
205 Ok(()) => buffer,
206 Err(_) => {
207 eprintln!(
208 "\n❗ Error - Failed to store \"{}\" locally. Please download this file manually and ensure it is stored in {:?}.\n",
209 $filename, file_path
210 );
211 buffer
212 }
213 }
214 } else {
215 return Err($crate::errors::ParameterError::RemoteFetchDisabled);
216 }
217 }
218 };
219
220 if $expected_size != buffer.len() {
222 remove_file!(file_path);
223 return Err($crate::errors::ParameterError::SizeMismatch($expected_size, buffer.len()));
224 }
225
226 let candidate_checksum = checksum!(buffer.as_slice());
228 if $expected_checksum != candidate_checksum {
229 return checksum_error!($expected_checksum, candidate_checksum)
230 }
231 return Ok(buffer);
232 } else {
233 cfg_if::cfg_if! {
234 if #[cfg(feature = "wasm")] {
235 let url = format!("{}/{}", $remote_url, $filename);
236 let buffer = Self::remote_fetch(&url)?;
237
238 if $expected_size != buffer.len() {
240 remove_file!(file_path);
241 return Err($crate::errors::ParameterError::SizeMismatch($expected_size, buffer.len()));
242 }
243
244 let candidate_checksum = checksum!(&buffer);
246 if $expected_checksum != candidate_checksum {
247 return checksum_error!($expected_checksum, candidate_checksum)
248 }
249
250 return Ok(buffer)
251 } else {
252 return Err($crate::errors::ParameterError::FilesystemDisabled);
253 }
254 }
255 }
256 }
257 }
258}
259
260#[macro_export]
261macro_rules! impl_local {
262 ($name: ident, $local_dir: expr, $fname: tt, "usrs") => {
263 #[derive(Clone, Debug, PartialEq, Eq)]
264 pub struct $name;
265
266 impl $name {
267 pub const METADATA: &'static str = include_str!(concat!($local_dir, $fname, ".metadata"));
268
269 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
270 let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
271 let expected_checksum: String = metadata["checksum"].as_str().expect("Failed to parse checksum").to_string();
272 let expected_size: usize = metadata["size"].to_string().parse().expect("Failed to retrieve the file size");
273
274 let _filepath = concat!($local_dir, $fname, ".", "usrs");
275 let buffer = include_bytes!(concat!($local_dir, $fname, ".", "usrs"));
276
277 impl_load_bytes_logic_local!(_filepath, buffer, expected_size, expected_checksum);
278 }
279 }
280
281 paste::item! {
282 #[cfg(test)]
283 #[test]
284 fn [< test_ $fname _usrs >]() {
285 assert!($name::load_bytes().is_ok());
286 }
287 }
288 };
289 ($name: ident, $local_dir: expr, $fname: tt, $ftype: tt, $credits_version: tt) => {
290 #[derive(Clone, Debug, PartialEq, Eq)]
291 pub struct $name;
292
293 impl $name {
294 pub const METADATA: &'static str = include_str!(concat!($local_dir, $credits_version, "/", $fname, ".metadata"));
295
296 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
297 let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
298 let expected_checksum: String =
299 metadata[concat!($ftype, "_checksum")].as_str().expect("Failed to parse checksum").to_string();
300 let expected_size: usize =
301 metadata[concat!($ftype, "_size")].to_string().parse().expect("Failed to retrieve the file size");
302
303 let _filepath = concat!($local_dir, $credits_version, "/", $fname, ".", $ftype);
304 let buffer = include_bytes!(concat!($local_dir, $credits_version, "/", $fname, ".", $ftype));
305
306 impl_load_bytes_logic_local!(_filepath, buffer, expected_size, expected_checksum);
307 }
308 }
309
310 paste::item! {
311 #[cfg(test)]
312 #[test]
313 fn [< test_ $credits_version _ $fname _ $ftype >]() {
314 assert!($name::load_bytes().is_ok());
315 }
316 }
317 };
318}
319
320#[macro_export]
321macro_rules! impl_remote {
322 ($name: ident, $remote_url: expr, $local_dir: expr, $fname: tt, "usrs") => {
323 pub struct $name;
324
325 impl $name {
326 pub const METADATA: &'static str = include_str!(concat!($local_dir, $fname, ".metadata"));
327
328 impl_store_and_remote_fetch!();
329
330 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
331 let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
332 let expected_checksum: String = metadata["checksum"].as_str().expect("Failed to parse checksum").to_string();
333 let expected_size: usize = metadata["size"].to_string().parse().expect("Failed to retrieve the file size");
334
335 let filename = match expected_checksum.get(0..7) {
337 Some(sum) => format!("{}.{}.{}", $fname, "usrs", sum),
338 _ => format!("{}.{}", $fname, "usrs"),
339 };
340
341 impl_load_bytes_logic_remote!($remote_url, $local_dir, &filename, metadata, expected_checksum, expected_size);
342 }
343 }
344 paste::item! {
345 #[cfg(test)]
346 #[test]
347 fn [< test_ $fname _usrs >]() {
348 assert!($name::load_bytes().is_ok());
349 }
350 }
351 };
352 ($name: ident, $remote_url: expr, $local_dir: expr, $fname: tt, $ftype: tt, $credits_version: tt) => {
353 pub struct $name;
354
355 impl $name {
356 pub const METADATA: &'static str = include_str!(concat!($local_dir, $credits_version, "/", $fname, ".metadata"));
357
358 impl_store_and_remote_fetch!();
359
360 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
361 let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
362 let expected_checksum: String =
363 metadata[concat!($ftype, "_checksum")].as_str().expect("Failed to parse checksum").to_string();
364 let expected_size: usize =
365 metadata[concat!($ftype, "_size")].to_string().parse().expect("Failed to retrieve the file size");
366
367 let filename = match expected_checksum.get(0..7) {
369 Some(sum) => format!("{}.{}.{}", $fname, $ftype, sum),
370 _ => format!("{}.{}", $fname, $ftype),
371 };
372
373 impl_load_bytes_logic_remote!($remote_url, $local_dir, &filename, metadata, expected_checksum, expected_size);
374 }
375 }
376
377 paste::item! {
378 #[cfg(test)]
379 #[test]
380 fn [< test_ $credits_version _ $fname _ $ftype >]() {
381 assert!($name::load_bytes().is_ok());
382 }
383 }
384 };
385}