use sha2::Digest;
use sha2::Sha512;
use std::env;
use std::fs;
use std::io;
use std::io::BufRead;
use std::io::BufReader;
use std::path::Path;
use std::path::PathBuf;
fn copy_if_present(src: &Path, dst: &Path) {
if let Err(err) = fs::copy(src, dst) {
match err.kind() {
io::ErrorKind::NotFound => (),
err => panic!(
"failed to copy {} to {}: {}",
src.display(),
dst.display(),
err
),
}
}
}
#[cfg(not(feature = "download-params"))]
fn fetch_if_missing(_dst: &Path) {}
#[cfg(feature = "download-params")]
fn fetch_if_missing(dst: &Path) {
if dst.exists() {
return;
}
let name = dst.file_name().unwrap().to_str().unwrap();
println!("cargo:warning=fetching {name} from GitHub");
let tmp = dst.with_file_name(format!("{name}.part"));
let mut file = fs::OpenOptions::new()
.write(true)
.create(true)
.open(&tmp)
.unwrap_or_else(|err| panic!("failed to open {}: {}", tmp.display(), err));
let url = format!(
"https://github.com/iron-fish/ironfish/raw/master/ironfish-rust/src/sapling_params/{}",
name
);
reqwest::blocking::get(&url)
.unwrap_or_else(|err| panic!("failed to fetch {url}: {err}"))
.copy_to(&mut file)
.unwrap_or_else(|err| panic!("failed to write {}: {}", tmp.display(), err));
fs::rename(&tmp, dst).unwrap_or_else(|err| {
panic!(
"failed to rename {} to {}: {}",
tmp.display(),
dst.display(),
err
)
});
}
fn verify_integrity(checksum_path: &Path, files_dir: &Path) {
let checksum_file = fs::File::open(checksum_path)
.unwrap_or_else(|err| panic!("failed to open {}: {}", checksum_path.display(), err));
let checksum_file_reader = BufReader::new(checksum_file);
for line in checksum_file_reader.lines() {
let line = line
.unwrap_or_else(|err| panic!("failed to read {}: {}", checksum_path.display(), err));
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() != 2 {
panic!("{}: invalid syntax", checksum_path.display());
}
let expected_hash = hex::decode(parts[0])
.unwrap_or_else(|_| panic!("{}: invalid syntax", checksum_path.display()));
let path = files_dir.join(parts[1]);
let mut file = fs::File::open(&path)
.unwrap_or_else(|err| panic!("failed to open {}: {}", path.display(), err));
let mut hasher = Sha512::new();
io::copy(&mut file, &mut hasher)
.unwrap_or_else(|err| panic!("failed to read {}: {}", path.display(), err));
let actual_hash = hasher.finalize();
if expected_hash != actual_hash.as_slice() {
panic!("integrity verification failed for {}", path.display());
}
}
}
fn prepare_sapling_params() {
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let params_src_path = Path::new("src/sapling_params");
let params_dst_path = out_dir.join("sapling_params");
println!("cargo:rerun-if-changed={}", params_src_path.display());
fs::create_dir_all(¶ms_dst_path)
.unwrap_or_else(|err| panic!("failed to create {}: {}", params_dst_path.display(), err));
let param_files = [
"sapling-mint.params",
"sapling-output.params",
"sapling-spend.params",
];
for name in param_files.iter() {
let src = params_src_path.join(name);
let dst = params_dst_path.join(name);
copy_if_present(&src, &dst);
fetch_if_missing(&dst);
}
let checksum = params_src_path.join("params-sha512.txt");
verify_integrity(&checksum, ¶ms_dst_path);
}
fn main() {
prepare_sapling_params();
}