#[macro_export]
macro_rules! checksum {
($bytes: expr) => {{
use sha2::Digest;
hex::encode(&sha2::Sha256::digest($bytes))
}};
}
#[macro_export]
macro_rules! checksum_error {
($expected: expr, $candidate: expr) => {
Err($crate::errors::ParameterError::ChecksumMismatch($expected, $candidate))
};
}
#[macro_export]
macro_rules! remove_file {
($filepath:expr) => {
#[cfg(not(feature = "wasm"))]
if std::path::PathBuf::from(&$filepath).exists() {
match std::fs::remove_file(&$filepath) {
Ok(()) => println!("Removed {:?}. Please retry the command.", $filepath),
Err(err) => eprintln!("Failed to remove {:?}: {err}", $filepath),
}
}
};
}
macro_rules! impl_store_and_remote_fetch {
() => {
#[cfg(not(feature = "wasm"))]
fn store_bytes(buffer: &[u8], file_path: &std::path::Path) -> Result<(), $crate::errors::ParameterError> {
use snarkvm_utilities::Write;
#[cfg(not(feature = "no_std_out"))]
{
use colored::*;
let output = format!("{:>15} - Storing file in {:?}", "Installation", file_path);
println!("{}", output.dimmed());
}
let mut directory_path = file_path.to_path_buf();
directory_path.pop();
let _ = std::fs::create_dir_all(directory_path)?;
match std::fs::File::create(file_path) {
Ok(mut file) => file.write_all(&buffer)?,
Err(error) => eprintln!("{}", error),
}
Ok(())
}
#[cfg(all(not(feature = "wasm"), not(target_env = "sgx")))]
fn remote_fetch(buffer: &mut Vec<u8>, url: &str) -> Result<(), $crate::errors::ParameterError> {
#[cfg(not(feature = "no_std_out"))]
{
use colored::*;
let output = format!("{:>15} - Downloading \"{}\"", "Installation", url);
println!("{}", output.dimmed());
}
let response = reqwest::blocking::get(url)?;
let bytes = response.bytes()?;
buffer.extend_from_slice(&bytes);
#[cfg(not(feature = "no_std_out"))]
{
use colored::*;
let size_in_megabytes = bytes.len() as u64 / 1_048_576;
let output = format!("{:>15} - Download complete ({} MB)", "Installation", size_in_megabytes);
println!("{}", output.dimmed());
}
Ok(())
}
#[cfg(feature = "wasm")]
fn remote_fetch(url: &str) -> Result<Vec<u8>, $crate::errors::ParameterError> {
let xhr = web_sys::XmlHttpRequest::new().map_err(|_| {
$crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest object not found".to_string())
})?;
xhr.override_mime_type("octet/binary; charset=ISO-8859-5").unwrap();
xhr.open_with_async("GET", url, false).map_err(|_| {
$crate::errors::ParameterError::Wasm(
"Download failed - This browser does not support synchronous requests".to_string(),
)
})?;
xhr.send()
.map_err(|_| $crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest failed".to_string()))?;
if xhr.response().is_ok() && xhr.status().unwrap() == 200 {
let rust_text = xhr
.response_text()
.map_err(|_| $crate::errors::ParameterError::Wasm("XMLHttpRequest failed".to_string()))?
.ok_or($crate::errors::ParameterError::Wasm(
"The request was successful but no parameters were received".to_string(),
))?;
use encoding::Encoding;
encoding::all::ISO_8859_5
.encode(&rust_text, encoding::EncoderTrap::Strict)
.map_err(|_| $crate::errors::ParameterError::Wasm("Parameter decoding failed".to_string()))
} else {
Err($crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest failed".to_string()))
}
}
};
}
macro_rules! impl_load_bytes_logic_local {
($filepath: expr, $buffer: expr, $expected_size: expr, $expected_checksum: expr) => {
if $expected_size != $buffer.len() {
remove_file!($filepath);
return Err($crate::errors::ParameterError::SizeMismatch($expected_size, $buffer.len()));
}
let candidate_checksum = checksum!($buffer);
if $expected_checksum != candidate_checksum {
return checksum_error!($expected_checksum, candidate_checksum);
}
return Ok($buffer.to_vec());
};
}
macro_rules! impl_load_bytes_logic_remote {
($remote_urls: expr, $local_dir: expr, $filename: expr, $metadata: expr, $expected_checksum: expr, $expected_size: expr) => {
cfg_if::cfg_if! {
if #[cfg(all(feature = "filesystem", not(feature="wasm")))] {
let mut file_path = aleo_std::aleo_dir();
file_path.push($local_dir);
file_path.push($filename);
let buffer = if file_path.exists() {
std::fs::read(&file_path)?
} else {
#[cfg(not(feature = "no_std_out"))]
{
use colored::*;
let path = format!("(in {:?})", file_path);
eprintln!(
"\n⚠️ \"{}\" does not exist. Downloading and storing it {}.\n",
$filename, path.dimmed()
);
}
cfg_if::cfg_if!{
if #[cfg(all(not(feature = "wasm"), not(target_env = "sgx")))] {
let remote_urls: &[&str] = &$remote_urls;
let mut buffer = vec![];
let mut last_error: Option<($crate::errors::ParameterError, &str)> = None;
for base_url in remote_urls.iter() {
cfg_if::cfg_if!{
if #[cfg(feature = "no_std_out")] {
last_error = None;
} else {
use colored::Colorize;
if let Some((err, url)) = last_error.take() {
eprintln!("{:>15} - {err}", "Warning".yellow());
eprintln!("{:>15} - Failed to fetch from \"{url}\". Trying next source...", "Warning".yellow());
}
}
}
let url = format!("{}/{}", base_url, $filename);
buffer.clear();
match Self::remote_fetch(&mut buffer, &url) {
Ok(()) => {
let candidate_checksum = checksum!(&buffer);
if $expected_checksum == candidate_checksum {
break;
} else {
last_error = Some(($crate::errors::ParameterError::ChecksumMismatch(
$expected_checksum.to_string(),
candidate_checksum,
), base_url));
}
}
Err(err) => {
last_error = Some((err, base_url));
}
}
}
if let Some((err, _)) = last_error {
return Err(err);
}
match Self::store_bytes(&buffer, &file_path) {
Ok(()) => buffer,
Err(_) => {
eprintln!(
"\n❗ Error - Failed to store \"{}\" locally. Please download this file manually and ensure it is stored in {:?}.\n",
$filename, file_path
);
buffer
}
}
} else {
return Err($crate::errors::ParameterError::RemoteFetchDisabled);
}
}
};
if $expected_size != buffer.len() {
remove_file!(file_path);
return Err($crate::errors::ParameterError::SizeMismatch($expected_size, buffer.len()));
}
let candidate_checksum = checksum!(buffer.as_slice());
if $expected_checksum != candidate_checksum {
return checksum_error!($expected_checksum, candidate_checksum)
}
return Ok(buffer);
} else {
cfg_if::cfg_if! {
if #[cfg(feature = "wasm")] {
let remote_urls: &[&str] = &$remote_urls;
let mut buffer = vec![];
let mut last_error: Option<$crate::errors::ParameterError> = None;
for base_url in remote_urls.iter() {
let url = format!("{}/{}", base_url, $filename);
match Self::remote_fetch(&url) {
Ok(fetched_buffer) => {
let candidate_checksum = checksum!(&fetched_buffer);
if $expected_checksum == candidate_checksum {
buffer = fetched_buffer;
last_error = None;
break;
} else {
last_error = Some($crate::errors::ParameterError::ChecksumMismatch(
$expected_checksum.to_string(),
candidate_checksum,
));
}
}
Err(e) => {
last_error = Some(e);
}
}
}
if let Some(e) = last_error {
return Err(e);
}
if $expected_size != buffer.len() {
return Err($crate::errors::ParameterError::SizeMismatch($expected_size, buffer.len()));
}
return Ok(buffer)
} else {
return Err($crate::errors::ParameterError::FilesystemDisabled);
}
}
}
}
}
}
#[macro_export]
macro_rules! impl_local {
($name: ident, $local_dir: expr, $fname: tt, "usrs") => {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct $name;
impl $name {
pub const METADATA: &'static str = include_str!(concat!($local_dir, $fname, ".metadata"));
pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
let expected_checksum: String = metadata["checksum"].as_str().expect("Failed to parse checksum").to_string();
let expected_size: usize = metadata["size"].to_string().parse().expect("Failed to retrieve the file size");
let _filepath = concat!($local_dir, $fname, ".", "usrs");
let buffer = include_bytes!(concat!($local_dir, $fname, ".", "usrs"));
impl_load_bytes_logic_local!(_filepath, buffer, expected_size, expected_checksum);
}
}
paste::item! {
#[cfg(test)]
#[test]
fn [< test_ $fname _usrs >]() {
if let Err(err) = $name::load_bytes() {
panic!("Failed to load bytes: {err}");
}
}
}
};
($name: ident, $local_dir: expr, $fname: tt, $ftype: tt, $credits_version: tt) => {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct $name;
impl $name {
pub const METADATA: &'static str = include_str!(concat!($local_dir, $credits_version, "/", $fname, ".metadata"));
pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
let expected_checksum: String =
metadata[concat!($ftype, "_checksum")].as_str().expect("Failed to parse checksum").to_string();
let expected_size: usize =
metadata[concat!($ftype, "_size")].to_string().parse().expect("Failed to retrieve the file size");
let _filepath = concat!($local_dir, $credits_version, "/", $fname, ".", $ftype);
let buffer = include_bytes!(concat!($local_dir, $credits_version, "/", $fname, ".", $ftype));
impl_load_bytes_logic_local!(_filepath, buffer, expected_size, expected_checksum);
}
}
paste::item! {
#[cfg(test)]
#[test]
fn [< test_ $credits_version _ $fname _ $ftype >]() {
if let Err(err) = $name::load_bytes() {
panic!("Failed to load bytes: {err}");
}
}
}
};
}
#[macro_export]
macro_rules! impl_remote {
($name: ident, $remote_url: expr, $local_dir: expr, $fname: tt, "usrs") => {
pub struct $name;
impl $name {
pub const METADATA: &'static str = include_str!(concat!($local_dir, $fname, ".metadata"));
impl_store_and_remote_fetch!();
pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
let expected_checksum: String = metadata["checksum"].as_str().expect("Failed to parse checksum").to_string();
let expected_size: usize = metadata["size"].to_string().parse().expect("Failed to retrieve the file size");
let filename = match expected_checksum.get(0..7) {
Some(sum) => format!("{}.{}.{}", $fname, "usrs", sum),
_ => format!("{}.{}", $fname, "usrs"),
};
impl_load_bytes_logic_remote!($remote_url, $local_dir, &filename, metadata, expected_checksum, expected_size);
}
}
paste::item! {
#[cfg(test)]
#[test]
fn [< test_ $fname _usrs >]() {
assert!($name::load_bytes().is_ok());
}
}
};
($name: ident, $remote_url: expr, $local_dir: expr, $fname: tt, $ftype: tt, $credits_version: tt) => {
pub struct $name;
impl $name {
pub const METADATA: &'static str = include_str!(concat!($local_dir, $credits_version, "/", $fname, ".metadata"));
impl_store_and_remote_fetch!();
pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
let expected_checksum: String =
metadata[concat!($ftype, "_checksum")].as_str().expect("Failed to parse checksum").to_string();
let expected_size: usize =
metadata[concat!($ftype, "_size")].to_string().parse().expect("Failed to retrieve the file size");
// Construct the versioned filename.
let filename = match expected_checksum.get(0..7) {
Some(sum) => format!("{}.{}.{}", $fname, $ftype, sum),
_ => format!("{}.{}", $fname, $ftype),
};
impl_load_bytes_logic_remote!($remote_url, $local_dir, &filename, metadata, expected_checksum, expected_size);
}
#[cfg(feature = "wasm")]
/// Verify external bytes.
pub fn verify_bytes(buffer: &[u8]) -> Result<(), $crate::errors::ParameterError> {
let metadata: serde_json::Value = serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
let expected_checksum: String =
metadata[concat!($ftype, "_checksum")].as_str().expect("Failed to parse checksum").to_string();
let expected_size: usize =
metadata[concat!($ftype, "_size")].to_string().parse().expect("Failed to retrieve the file size");
// Ensure the size matches.
if buffer.len() != expected_size {
return Err($crate::errors::ParameterError::SizeMismatch(expected_size, buffer.len()));
}
// Ensure the checksum matches.
let candidate_checksum = checksum!(buffer);
if expected_checksum != candidate_checksum {
return checksum_error!(expected_checksum, candidate_checksum);
}
Ok(())
}
}
paste::item! {
#[cfg(test)]
#[test]
fn [< test_ $credits_version _ $fname _ $ftype >]() {
if let Err(err) = $name::load_bytes() {
panic!("Failed to load bytes: {err}");
}
}
}
};
}