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 use std::io::Read;
74
75 #[cfg(not(feature = "no_std_out"))]
76 {
77 use colored::*;
78 let output = format!("{:>15} - Downloading \"{}\"", "Installation", url);
79 println!("{}", output.dimmed());
80 }
81
82 let mut attempts = 3u32;
84 loop {
85 match ureq::get(url).config().max_redirects(10).build().call() {
86 Ok(mut response) => {
87 response.body_mut().as_reader().read_to_end(buffer)?;
88 break;
89 }
90 Err(ureq::Error::StatusCode(code)) if attempts > 0 && (code >= 500 || code == 429) => {
91 attempts -= 1;
92 }
93 Err(ureq::Error::Io(_) | ureq::Error::Timeout(_)) if attempts > 0 => {
94 attempts -= 1;
95 }
96 Err(err) => return Err(err.into()),
97 }
98 }
99
100 #[cfg(not(feature = "no_std_out"))]
101 {
102 use colored::*;
103 let size_in_megabytes = buffer.len() as u64 / 1_048_576;
104 let output = format!("{:>15} - Download complete ({} MB)", "Installation", size_in_megabytes);
105 println!("{}", output.dimmed());
106 }
107
108 Ok(())
109 }
110
111 #[cfg(feature = "wasm")]
112 fn remote_fetch(url: &str) -> Result<Vec<u8>, $crate::errors::ParameterError> {
113 let xhr = web_sys::XmlHttpRequest::new().map_err(|_| {
118 $crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest object not found".to_string())
119 })?;
120
121 xhr.override_mime_type("octet/binary; charset=ISO-8859-5").unwrap();
125
126 xhr.open_with_async("GET", url, false).map_err(|_| {
128 $crate::errors::ParameterError::Wasm(
129 "Download failed - This browser does not support synchronous requests".to_string(),
130 )
131 })?;
132 xhr.send()
133 .map_err(|_| $crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest failed".to_string()))?;
134
135 if xhr.response().is_ok() && xhr.status().unwrap() == 200 {
137 let rust_text = xhr
139 .response_text()
140 .map_err(|_| $crate::errors::ParameterError::Wasm("XMLHttpRequest failed".to_string()))?
141 .ok_or($crate::errors::ParameterError::Wasm(
142 "The request was successful but no parameters were received".to_string(),
143 ))?;
144
145 use encoding::Encoding;
147 encoding::all::ISO_8859_5
148 .encode(&rust_text, encoding::EncoderTrap::Strict)
149 .map_err(|_| $crate::errors::ParameterError::Wasm("Parameter decoding failed".to_string()))
150 } else {
151 Err($crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest failed".to_string()))
152 }
153 }
154 };
155}
156
157macro_rules! impl_load_bytes_logic_local {
158 ($filepath: expr, $buffer: expr, $expected_size: expr, $expected_checksum: expr) => {
159 if $expected_size != $buffer.len() {
161 remove_file!($filepath);
162 return Err($crate::errors::ParameterError::SizeMismatch($expected_size, $buffer.len()));
163 }
164
165 let candidate_checksum = checksum!($buffer);
167 if $expected_checksum != candidate_checksum {
168 return checksum_error!($expected_checksum, candidate_checksum);
169 }
170
171 return Ok($buffer.to_vec());
172 };
173}
174
175macro_rules! impl_load_bytes_logic_remote {
176 ($remote_urls: expr, $local_dir: expr, $filename: expr, $metadata: expr, $expected_checksum: expr, $expected_size: expr) => {
177 cfg_if::cfg_if! {
178 if #[cfg(all(feature = "filesystem", not(feature="wasm")))] {
179 let mut file_path = aleo_std::aleo_dir();
181 file_path.push($local_dir);
182 file_path.push($filename);
183
184 let buffer = if file_path.exists() {
185 std::fs::read(&file_path)?
187 } else {
188 #[cfg(not(feature = "no_std_out"))]
190 {
191 use colored::*;
192 let path = format!("(in {:?})", file_path);
193 eprintln!(
194 "\n⚠️ \"{}\" does not exist. Downloading and storing it {}.\n",
195 $filename, path.dimmed()
196 );
197 }
198
199 cfg_if::cfg_if!{
201 if #[cfg(all(not(feature = "wasm"), not(target_env = "sgx")))] {
202 let remote_urls: &[&str] = &$remote_urls;
204 let mut buffer = vec![];
205 let mut last_error: Option<($crate::errors::ParameterError, &str)> = None;
206
207 for base_url in remote_urls.iter() {
208 cfg_if::cfg_if!{
210 if #[cfg(feature = "no_std_out")] {
211 last_error = None;
212 } else {
213 use colored::Colorize;
214 if let Some((err, url)) = last_error.take() {
216 eprintln!("{:>15} - {err}", "Warning".yellow());
217 eprintln!("{:>15} - Failed to fetch from \"{url}\". Trying next source...", "Warning".yellow());
218 }
219 }
220 }
221
222 let url = format!("{}/{}", base_url, $filename);
223 buffer.clear();
224
225 match Self::remote_fetch(&mut buffer, &url) {
226 Ok(()) => {
227 let candidate_checksum = checksum!(&buffer);
229 if $expected_checksum == candidate_checksum {
230 break;
232 } else {
233 last_error = Some(($crate::errors::ParameterError::ChecksumMismatch(
234 $expected_checksum.to_string(),
235 candidate_checksum,
236 ), base_url));
237 }
238 }
239 Err(err) => {
240 last_error = Some((err, base_url));
241 }
242 }
243 }
244
245 if let Some((err, _)) = last_error {
247 return Err(err);
248 }
249
250 match Self::store_bytes(&buffer, &file_path) {
251 Ok(()) => buffer,
252 Err(_) => {
253 eprintln!(
254 "\n❗ Error - Failed to store \"{}\" locally. Please download this file manually and ensure it is stored in {:?}.\n",
255 $filename, file_path
256 );
257 buffer
258 }
259 }
260 } else {
261 return Err($crate::errors::ParameterError::RemoteFetchDisabled);
262 }
263 }
264 };
265
266 if $expected_size != buffer.len() {
268 remove_file!(file_path);
269 return Err($crate::errors::ParameterError::SizeMismatch($expected_size, buffer.len()));
270 }
271
272 let candidate_checksum = checksum!(buffer.as_slice());
274 if $expected_checksum != candidate_checksum {
275 return checksum_error!($expected_checksum, candidate_checksum)
276 }
277 return Ok(buffer);
278 } else {
279 cfg_if::cfg_if! {
280 if #[cfg(feature = "wasm")] {
281 let remote_urls: &[&str] = &$remote_urls;
283 let mut buffer = vec![];
284 let mut last_error: Option<$crate::errors::ParameterError> = None;
285
286 for base_url in remote_urls.iter() {
287 let url = format!("{}/{}", base_url, $filename);
288
289 match Self::remote_fetch(&url) {
290 Ok(fetched_buffer) => {
291 let candidate_checksum = checksum!(&fetched_buffer);
293 if $expected_checksum == candidate_checksum {
294 buffer = fetched_buffer;
295 last_error = None;
296 break;
297 } else {
298 last_error = Some($crate::errors::ParameterError::ChecksumMismatch(
299 $expected_checksum.to_string(),
300 candidate_checksum,
301 ));
302 }
303 }
304 Err(e) => {
305 last_error = Some(e);
306 }
307 }
308 }
309
310 if let Some(e) = last_error {
312 return Err(e);
313 }
314
315 if $expected_size != buffer.len() {
317 return Err($crate::errors::ParameterError::SizeMismatch($expected_size, buffer.len()));
318 }
319
320 return Ok(buffer)
321 } else {
322 return Err($crate::errors::ParameterError::FilesystemDisabled);
323 }
324 }
325 }
326 }
327 }
328}
329
330#[macro_export]
331macro_rules! impl_local {
332 ($name: ident, $local_dir: expr, $fname: tt, "usrs") => {
333 #[derive(Clone, Debug, PartialEq, Eq)]
334 pub struct $name;
335
336 impl $name {
337 pub const METADATA: &'static str = include_str!(concat!($local_dir, $fname, ".metadata"));
338
339 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
340 let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
341 let expected_checksum: String = metadata["checksum"].as_str().expect("Failed to parse checksum").to_string();
342 let expected_size: usize = metadata["size"].to_string().parse().expect("Failed to retrieve the file size");
343
344 let _filepath = concat!($local_dir, $fname, ".", "usrs");
345 let buffer = include_bytes!(concat!($local_dir, $fname, ".", "usrs"));
346
347 impl_load_bytes_logic_local!(_filepath, buffer, expected_size, expected_checksum);
348 }
349 }
350
351 paste::item! {
352 #[cfg(test)]
353 #[test]
354 fn [< test_ $fname _usrs >]() {
355 if let Err(err) = $name::load_bytes() {
357 panic!("Failed to load bytes: {err}");
358 }
359 }
360 }
361 };
362 ($name: ident, $local_dir: expr, $fname: tt, $ftype: tt, $credits_version: tt) => {
363 #[derive(Clone, Debug, PartialEq, Eq)]
364 pub struct $name;
365
366 impl $name {
367 pub const METADATA: &'static str = include_str!(concat!($local_dir, $credits_version, "/", $fname, ".metadata"));
368
369 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
370 let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
371 let expected_checksum: String =
372 metadata[concat!($ftype, "_checksum")].as_str().expect("Failed to parse checksum").to_string();
373 let expected_size: usize =
374 metadata[concat!($ftype, "_size")].to_string().parse().expect("Failed to retrieve the file size");
375
376 let _filepath = concat!($local_dir, $credits_version, "/", $fname, ".", $ftype);
377 let buffer = include_bytes!(concat!($local_dir, $credits_version, "/", $fname, ".", $ftype));
378
379 impl_load_bytes_logic_local!(_filepath, buffer, expected_size, expected_checksum);
380 }
381 }
382
383 paste::item! {
384 #[cfg(test)]
385 #[test]
386 fn [< test_ $credits_version _ $fname _ $ftype >]() {
387 if let Err(err) = $name::load_bytes() {
388 panic!("Failed to load bytes: {err}");
389 }
390 }
391 }
392 };
393}
394
395#[macro_export]
396macro_rules! impl_remote {
397 ($name: ident, $remote_url: expr, $local_dir: expr, $fname: tt, "usrs") => {
398 pub struct $name;
399
400 impl $name {
401 pub const METADATA: &'static str = include_str!(concat!($local_dir, $fname, ".metadata"));
402
403 impl_store_and_remote_fetch!();
404
405 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
406 let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
407 let expected_checksum: String = metadata["checksum"].as_str().expect("Failed to parse checksum").to_string();
408 let expected_size: usize = metadata["size"].to_string().parse().expect("Failed to retrieve the file size");
409
410 let filename = match expected_checksum.get(0..7) {
412 Some(sum) => format!("{}.{}.{}", $fname, "usrs", sum),
413 _ => format!("{}.{}", $fname, "usrs"),
414 };
415
416 impl_load_bytes_logic_remote!($remote_url, $local_dir, &filename, metadata, expected_checksum, expected_size);
417 }
418 }
419 paste::item! {
420 #[cfg(test)]
421 #[test]
422 fn [< test_ $fname _usrs >]() {
423 assert!($name::load_bytes().is_ok());
424 }
425 }
426 };
427 ($name: ident, $remote_url: expr, $local_dir: expr, $fname: tt, $ftype: tt, $credits_version: tt) => {
428 pub struct $name;
429
430 impl $name {
431 pub const METADATA: &'static str = include_str!(concat!($local_dir, $credits_version, "/", $fname, ".metadata"));
432
433 impl_store_and_remote_fetch!();
434
435 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
436 let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
437 let expected_checksum: String =
438 metadata[concat!($ftype, "_checksum")].as_str().expect("Failed to parse checksum").to_string();
439 let expected_size: usize =
440 metadata[concat!($ftype, "_size")].to_string().parse().expect("Failed to retrieve the file size");
441
442 let filename = match expected_checksum.get(0..7) {
444 Some(sum) => format!("{}.{}.{}", $fname, $ftype, sum),
445 _ => format!("{}.{}", $fname, $ftype),
446 };
447
448 impl_load_bytes_logic_remote!($remote_url, $local_dir, &filename, metadata, expected_checksum, expected_size);
449 }
450
451 #[cfg(feature = "wasm")]
452 pub fn verify_bytes(buffer: &[u8]) -> Result<(), $crate::errors::ParameterError> {
454 let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
455 let expected_checksum: String =
456 metadata[concat!($ftype, "_checksum")].as_str().expect("Failed to parse checksum").to_string();
457 let expected_size: usize =
458 metadata[concat!($ftype, "_size")].to_string().parse().expect("Failed to retrieve the file size");
459
460 if buffer.len() != expected_size {
462 return Err($crate::errors::ParameterError::SizeMismatch(expected_size, buffer.len()));
463 }
464
465 let candidate_checksum = checksum!(buffer);
467 if expected_checksum != candidate_checksum {
468 return checksum_error!(expected_checksum, candidate_checksum);
469 }
470 Ok(())
471 }
472 }
473
474 paste::item! {
475 #[cfg(test)]
476 #[test]
477 fn [< test_ $credits_version _ $fname _ $ftype >]() {
478 if let Err(err) = $name::load_bytes() {
479 panic!("Failed to load bytes: {err}");
480 }
481 }
482 }
483 };
484}