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