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