macros.rs
1 // Copyright (c) 2025-2026 ACDC Network 2 // This file is part of the alphavm library. 3 // 4 // Alpha Chain | Delta Chain Protocol 5 // International Monetary Graphite. 6 // 7 // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com). 8 // They built world-class ZK infrastructure. We installed the EASY button. 9 // Their cryptography: elegant. Our modifications: bureaucracy-compatible. 10 // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours. 11 // 12 // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0 13 // All modifications and new work: CC0 1.0 Universal Public Domain Dedication. 14 // No rights reserved. No permission required. No warranty. No refunds. 15 // 16 // https://creativecommons.org/publicdomain/zero/1.0/ 17 // SPDX-License-Identifier: CC0-1.0 18 19 #[macro_export] 20 macro_rules! checksum { 21 ($bytes: expr) => {{ 22 use sha2::Digest; 23 hex::encode(&sha2::Sha256::digest($bytes)) 24 }}; 25 } 26 27 #[macro_export] 28 macro_rules! checksum_error { 29 ($expected: expr, $candidate: expr) => { 30 Err($crate::errors::ParameterError::ChecksumMismatch($expected, $candidate)) 31 }; 32 } 33 34 #[macro_export] 35 macro_rules! remove_file { 36 ($filepath:expr) => { 37 // Safely remove the corrupt file, if it exists. 38 #[cfg(not(feature = "wasm"))] 39 if std::path::PathBuf::from(&$filepath).exists() { 40 match std::fs::remove_file(&$filepath) { 41 Ok(()) => println!("Removed {:?}. Please retry the command.", $filepath), 42 Err(err) => eprintln!("Failed to remove {:?}: {err}", $filepath), 43 } 44 } 45 }; 46 } 47 48 macro_rules! impl_store_and_remote_fetch { 49 () => { 50 #[cfg(not(feature = "wasm"))] 51 fn store_bytes(buffer: &[u8], file_path: &std::path::Path) -> Result<(), $crate::errors::ParameterError> { 52 use alphavm_utilities::Write; 53 54 #[cfg(not(feature = "no_std_out"))] 55 { 56 use colored::*; 57 let output = format!("{:>15} - Storing file in {:?}", "Installation", file_path); 58 println!("{}", output.dimmed()); 59 } 60 61 // Ensure the folders up to the file path all exist. 62 let mut directory_path = file_path.to_path_buf(); 63 directory_path.pop(); 64 let _ = std::fs::create_dir_all(directory_path)?; 65 66 // Attempt to write the parameter buffer to a file. 67 match std::fs::File::create(file_path) { 68 Ok(mut file) => file.write_all(&buffer)?, 69 Err(error) => eprintln!("{}", error), 70 } 71 Ok(()) 72 } 73 74 #[cfg(all(not(feature = "wasm"), not(target_env = "sgx")))] 75 fn remote_fetch(buffer: &mut Vec<u8>, url: &str) -> Result<(), $crate::errors::ParameterError> { 76 let mut easy = curl::easy::Easy::new(); 77 easy.follow_location(true)?; 78 easy.url(url)?; 79 80 #[cfg(not(feature = "no_std_out"))] 81 { 82 use colored::*; 83 84 let output = format!("{:>15} - Downloading \"{}\"", "Installation", url); 85 println!("{}", output.dimmed()); 86 87 easy.progress(true)?; 88 easy.progress_function(|total_download, current_download, _, _| { 89 let percent = (current_download / total_download) * 100.0; 90 let size_in_megabytes = total_download as u64 / 1_048_576; 91 let output = 92 format!("\r{:>15} - {:.2}% complete ({:#} MB total)", "Installation", percent, size_in_megabytes); 93 print!("{}", output.dimmed()); 94 true 95 })?; 96 } 97 98 let mut transfer = easy.transfer(); 99 transfer.write_function(|data| { 100 buffer.extend_from_slice(data); 101 Ok(data.len()) 102 })?; 103 Ok(transfer.perform()?) 104 } 105 106 #[cfg(feature = "wasm")] 107 fn remote_fetch(url: &str) -> Result<Vec<u8>, $crate::errors::ParameterError> { 108 // Use the browser's XmlHttpRequest object to download the parameter file synchronously. 109 // 110 // This method blocks the event loop while the parameters are downloaded, and should be 111 // executed in a web worker to prevent the main browser window from freezing. 112 let xhr = web_sys::XmlHttpRequest::new().map_err(|_| { 113 $crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest object not found".to_string()) 114 })?; 115 116 // XmlHttpRequest if specified as synchronous cannot use the responseType property. It 117 // cannot thus download bytes directly and enforces a text encoding. To get back the 118 // original binary, a charset that does not corrupt the original bytes must be used. 119 xhr.override_mime_type("octet/binary; charset=ISO-8859-5").unwrap(); 120 121 // Initialize and send the request. 122 xhr.open_with_async("GET", url, false).map_err(|_| { 123 $crate::errors::ParameterError::Wasm( 124 "Download failed - This browser does not support synchronous requests".to_string(), 125 ) 126 })?; 127 xhr.send() 128 .map_err(|_| $crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest failed".to_string()))?; 129 130 // Wait for the response in a blocking fashion. 131 if xhr.response().is_ok() && xhr.status().unwrap() == 200 { 132 // Get the text from the response. 133 let rust_text = xhr 134 .response_text() 135 .map_err(|_| $crate::errors::ParameterError::Wasm("XMLHttpRequest failed".to_string()))? 136 .ok_or($crate::errors::ParameterError::Wasm( 137 "The request was successful but no parameters were received".to_string(), 138 ))?; 139 140 // Re-encode the text back into bytes using the chosen encoding. 141 use encoding::Encoding; 142 encoding::all::ISO_8859_5 143 .encode(&rust_text, encoding::EncoderTrap::Strict) 144 .map_err(|_| $crate::errors::ParameterError::Wasm("Parameter decoding failed".to_string())) 145 } else { 146 Err($crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest failed".to_string())) 147 } 148 } 149 }; 150 } 151 152 macro_rules! impl_load_bytes_logic_local { 153 ($filepath: expr, $buffer: expr, $expected_size: expr, $expected_checksum: expr) => { 154 // Ensure the size matches. 155 if $expected_size != $buffer.len() { 156 remove_file!($filepath); 157 return Err($crate::errors::ParameterError::SizeMismatch($expected_size, $buffer.len())); 158 } 159 160 // Ensure the checksum matches. 161 let candidate_checksum = checksum!($buffer); 162 if $expected_checksum != candidate_checksum { 163 return checksum_error!($expected_checksum, candidate_checksum); 164 } 165 166 return Ok($buffer.to_vec()); 167 }; 168 } 169 170 macro_rules! impl_load_bytes_logic_remote { 171 ($remote_url: expr, $local_dir: expr, $filename: expr, $metadata: expr, $expected_checksum: expr, $expected_size: expr) => { 172 cfg_if::cfg_if! { 173 if #[cfg(all(feature = "filesystem", not(feature="wasm")))] { 174 // Compose the correct file path for the parameter file. 175 let mut file_path = acdc_std::alpha_dir(); 176 file_path.push($local_dir); 177 file_path.push($filename); 178 179 let buffer = if file_path.exists() { 180 // Attempts to load the parameter file locally with an absolute path. 181 std::fs::read(&file_path)? 182 } else { 183 // Downloads the missing parameters and stores it in the local directory for use. 184 #[cfg(not(feature = "no_std_out"))] 185 { 186 use colored::*; 187 let path = format!("(in {:?})", file_path); 188 eprintln!( 189 "\n⚠️ \"{}\" does not exist. Downloading and storing it {}.\n", 190 $filename, path.dimmed() 191 ); 192 } 193 194 // Load remote file 195 cfg_if::cfg_if!{ 196 if #[cfg(all(not(feature = "wasm"), not(target_env = "sgx")))] { 197 let url = format!("{}/{}", $remote_url, $filename); 198 let mut buffer = vec![]; 199 Self::remote_fetch(&mut buffer, &url)?; 200 201 // Ensure the checksum matches. 202 let candidate_checksum = checksum!(&buffer); 203 if $expected_checksum != candidate_checksum { 204 return checksum_error!($expected_checksum, candidate_checksum) 205 } 206 207 match Self::store_bytes(&buffer, &file_path) { 208 Ok(()) => buffer, 209 Err(_) => { 210 eprintln!( 211 "\n❗ Error - Failed to store \"{}\" locally. Please download this file manually and ensure it is stored in {:?}.\n", 212 $filename, file_path 213 ); 214 buffer 215 } 216 } 217 } else { 218 return Err($crate::errors::ParameterError::RemoteFetchDisabled); 219 } 220 } 221 }; 222 223 // Ensure the size matches. 224 if $expected_size != buffer.len() { 225 remove_file!(file_path); 226 return Err($crate::errors::ParameterError::SizeMismatch($expected_size, buffer.len())); 227 } 228 229 // Ensure the checksum matches. 230 let candidate_checksum = checksum!(buffer.as_slice()); 231 if $expected_checksum != candidate_checksum { 232 return checksum_error!($expected_checksum, candidate_checksum) 233 } 234 return Ok(buffer); 235 } else { 236 cfg_if::cfg_if! { 237 if #[cfg(feature = "wasm")] { 238 let url = format!("{}/{}", $remote_url, $filename); 239 let buffer = Self::remote_fetch(&url)?; 240 241 // Ensure the size matches. 242 if $expected_size != buffer.len() { 243 remove_file!(file_path); 244 return Err($crate::errors::ParameterError::SizeMismatch($expected_size, buffer.len())); 245 } 246 247 // Ensure the checksum matches. 248 let candidate_checksum = checksum!(&buffer); 249 if $expected_checksum != candidate_checksum { 250 return checksum_error!($expected_checksum, candidate_checksum) 251 } 252 253 return Ok(buffer) 254 } else { 255 return Err($crate::errors::ParameterError::FilesystemDisabled); 256 } 257 } 258 } 259 } 260 } 261 } 262 263 #[macro_export] 264 macro_rules! impl_local { 265 ($name: ident, $local_dir: expr, $fname: tt, "usrs") => { 266 #[derive(Clone, Debug, PartialEq, Eq)] 267 pub struct $name; 268 269 impl $name { 270 pub const METADATA: &'static str = include_str!(concat!($local_dir, $fname, ".metadata")); 271 272 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> { 273 let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted"); 274 let expected_checksum: String = metadata["checksum"].as_str().expect("Failed to parse checksum").to_string(); 275 let expected_size: usize = metadata["size"].to_string().parse().expect("Failed to retrieve the file size"); 276 277 let _filepath = concat!($local_dir, $fname, ".", "usrs"); 278 let buffer = include_bytes!(concat!($local_dir, $fname, ".", "usrs")); 279 280 impl_load_bytes_logic_local!(_filepath, buffer, expected_size, expected_checksum); 281 } 282 } 283 284 paste::item! { 285 #[cfg(test)] 286 #[test] 287 fn [< test_ $fname _usrs >]() { 288 assert!($name::load_bytes().is_ok()); 289 } 290 } 291 }; 292 ($name: ident, $local_dir: expr, $fname: tt, $ftype: tt, $credits_version: tt) => { 293 #[derive(Clone, Debug, PartialEq, Eq)] 294 pub struct $name; 295 296 impl $name { 297 pub const METADATA: &'static str = include_str!(concat!($local_dir, $credits_version, "/", $fname, ".metadata")); 298 299 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> { 300 let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted"); 301 let expected_checksum: String = 302 metadata[concat!($ftype, "_checksum")].as_str().expect("Failed to parse checksum").to_string(); 303 let expected_size: usize = 304 metadata[concat!($ftype, "_size")].to_string().parse().expect("Failed to retrieve the file size"); 305 306 let _filepath = concat!($local_dir, $credits_version, "/", $fname, ".", $ftype); 307 let buffer = include_bytes!(concat!($local_dir, $credits_version, "/", $fname, ".", $ftype)); 308 309 impl_load_bytes_logic_local!(_filepath, buffer, expected_size, expected_checksum); 310 } 311 } 312 313 paste::item! { 314 #[cfg(test)] 315 #[test] 316 fn [< test_ $credits_version _ $fname _ $ftype >]() { 317 assert!($name::load_bytes().is_ok()); 318 } 319 } 320 }; 321 } 322 323 #[macro_export] 324 macro_rules! impl_remote { 325 ($name: ident, $remote_url: expr, $local_dir: expr, $fname: tt, "usrs") => { 326 pub struct $name; 327 328 impl $name { 329 pub const METADATA: &'static str = include_str!(concat!($local_dir, $fname, ".metadata")); 330 331 impl_store_and_remote_fetch!(); 332 333 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> { 334 let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted"); 335 let expected_checksum: String = metadata["checksum"].as_str().expect("Failed to parse checksum").to_string(); 336 let expected_size: usize = metadata["size"].to_string().parse().expect("Failed to retrieve the file size"); 337 338 // Construct the versioned filename. 339 let filename = match expected_checksum.get(0..7) { 340 Some(sum) => format!("{}.{}.{}", $fname, "usrs", sum), 341 _ => format!("{}.{}", $fname, "usrs"), 342 }; 343 344 impl_load_bytes_logic_remote!($remote_url, $local_dir, &filename, metadata, expected_checksum, expected_size); 345 } 346 } 347 paste::item! { 348 #[cfg(test)] 349 #[test] 350 fn [< test_ $fname _usrs >]() { 351 assert!($name::load_bytes().is_ok()); 352 } 353 } 354 }; 355 ($name: ident, $remote_url: expr, $local_dir: expr, $fname: tt, $ftype: tt, $credits_version: tt) => { 356 pub struct $name; 357 358 impl $name { 359 pub const METADATA: &'static str = include_str!(concat!($local_dir, $credits_version, "/", $fname, ".metadata")); 360 361 impl_store_and_remote_fetch!(); 362 363 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> { 364 let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted"); 365 let expected_checksum: String = 366 metadata[concat!($ftype, "_checksum")].as_str().expect("Failed to parse checksum").to_string(); 367 let expected_size: usize = 368 metadata[concat!($ftype, "_size")].to_string().parse().expect("Failed to retrieve the file size"); 369 370 // Construct the versioned filename. 371 let filename = match expected_checksum.get(0..7) { 372 Some(sum) => format!("{}.{}.{}", $fname, $ftype, sum), 373 _ => format!("{}.{}", $fname, $ftype), 374 }; 375 376 impl_load_bytes_logic_remote!($remote_url, $local_dir, &filename, metadata, expected_checksum, expected_size); 377 } 378 379 #[cfg(feature = "wasm")] 380 /// Verify external bytes. 381 pub fn verify_bytes(buffer: &[u8]) -> Result<(), $crate::errors::ParameterError> { 382 let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted"); 383 let expected_checksum: String = 384 metadata[concat!($ftype, "_checksum")].as_str().expect("Failed to parse checksum").to_string(); 385 let expected_size: usize = 386 metadata[concat!($ftype, "_size")].to_string().parse().expect("Failed to retrieve the file size"); 387 388 // Ensure the size matches. 389 if buffer.len() != expected_size { 390 return Err($crate::errors::ParameterError::SizeMismatch(expected_size, buffer.len())); 391 } 392 393 // Ensure the checksum matches. 394 let candidate_checksum = checksum!(buffer); 395 if expected_checksum != candidate_checksum { 396 return checksum_error!(expected_checksum, candidate_checksum); 397 } 398 Ok(()) 399 } 400 } 401 402 paste::item! { 403 #[cfg(test)] 404 #[test] 405 fn [< test_ $credits_version _ $fname _ $ftype >]() { 406 assert!($name::load_bytes().is_ok()); 407 } 408 } 409 }; 410 }