librustzcash/zcash_proofs/src/lib.rs

203 lines
7.5 KiB
Rust

//! *Zcash circuits and proofs.*
//!
//! `zcash_proofs` contains the zk-SNARK circuits used by Zcash, and the APIs for creating
//! and verifying proofs.
#![cfg_attr(docsrs, feature(doc_cfg))]
// Catch documentation errors caused by code changes.
#![deny(intra_doc_link_resolution_failure)]
use bellman::groth16::{prepare_verifying_key, Parameters, PreparedVerifyingKey, VerifyingKey};
use bls12_381::Bls12;
use std::fs::File;
use std::io::{self, BufReader};
use std::path::Path;
#[cfg(feature = "directories")]
use directories::BaseDirs;
#[cfg(feature = "directories")]
use std::path::PathBuf;
pub mod circuit;
mod constants;
mod hashreader;
pub mod sapling;
pub mod sprout;
#[cfg(any(feature = "local-prover", feature = "bundled-prover"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "local-prover", feature = "bundled-prover")))
)]
pub mod prover;
// Circuit names
#[cfg(feature = "local-prover")]
const SAPLING_SPEND_NAME: &str = "sapling-spend.params";
#[cfg(feature = "local-prover")]
const SAPLING_OUTPUT_NAME: &str = "sapling-output.params";
// Circuit hashes
const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c";
const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028";
const SPROUT_HASH: &str = "e9b238411bd6c0ec4791e9d04245ec350c9c5744f5610dfcce4365d5ca49dfefd5054e371842b3f88fa1b9d7e8e075249b3ebabd167fa8b0f3161292d36c180a";
#[cfg(feature = "download-params")]
const DOWNLOAD_URL: &str = "https://download.z.cash/downloads";
/// Returns the default folder that the Zcash proving parameters are located in.
#[cfg(feature = "directories")]
#[cfg_attr(docsrs, doc(cfg(feature = "directories")))]
pub fn default_params_folder() -> Option<PathBuf> {
BaseDirs::new().map(|base_dirs| {
if cfg!(any(windows, target_os = "macos")) {
base_dirs.data_dir().join("ZcashParams")
} else {
base_dirs.home_dir().join(".zcash-params")
}
})
}
/// Download the Zcash Sapling parameters, storing them in the default location.
///
/// This mirrors the behaviour of the `fetch-params.sh` script from `zcashd`.
#[cfg(feature = "download-params")]
#[cfg_attr(docsrs, doc(cfg(feature = "download-params")))]
pub fn download_parameters() -> Result<(), minreq::Error> {
// Ensure that the default Zcash parameters location exists.
let params_dir = default_params_folder().ok_or(io::Error::new(
io::ErrorKind::Other,
"Could not load default params folder",
))?;
std::fs::create_dir_all(&params_dir)?;
let fetch_params = |name: &str, expected_hash: &str| -> Result<(), minreq::Error> {
use std::io::Write;
// Download the parts directly (Sapling parameters are small enough for this).
let part_1 = minreq::get(format!("{}/{}.part.1", DOWNLOAD_URL, name)).send()?;
let part_2 = minreq::get(format!("{}/{}.part.2", DOWNLOAD_URL, name)).send()?;
// Verify parameter file hash.
let hash = blake2b_simd::State::new()
.update(part_1.as_bytes())
.update(part_2.as_bytes())
.finalize()
.to_hex();
if &hash != expected_hash {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"{} failed validation (expected: {}, actual: {}, fetched {} bytes)",
name,
expected_hash,
hash,
part_1.as_bytes().len() + part_2.as_bytes().len()
),
)
.into());
}
// Write parameter file.
let mut f = File::create(params_dir.join(name))?;
f.write_all(part_1.as_bytes())?;
f.write_all(part_2.as_bytes())?;
Ok(())
};
fetch_params(SAPLING_SPEND_NAME, SAPLING_SPEND_HASH)?;
fetch_params(SAPLING_OUTPUT_NAME, SAPLING_OUTPUT_HASH)?;
Ok(())
}
pub fn load_parameters(
spend_path: &Path,
output_path: &Path,
sprout_path: Option<&Path>,
) -> (
Parameters<Bls12>,
PreparedVerifyingKey<Bls12>,
Parameters<Bls12>,
PreparedVerifyingKey<Bls12>,
Option<PreparedVerifyingKey<Bls12>>,
) {
// Load from each of the paths
let spend_fs = File::open(spend_path).expect("couldn't load Sapling spend parameters file");
let output_fs = File::open(output_path).expect("couldn't load Sapling output parameters file");
let sprout_fs =
sprout_path.map(|p| File::open(p).expect("couldn't load Sprout groth16 parameters file"));
parse_parameters(
BufReader::with_capacity(1024 * 1024, spend_fs),
BufReader::with_capacity(1024 * 1024, output_fs),
sprout_fs.map(|fs| BufReader::with_capacity(1024 * 1024, fs)),
)
}
fn parse_parameters<R: io::Read>(
spend_fs: R,
output_fs: R,
sprout_fs: Option<R>,
) -> (
Parameters<Bls12>,
PreparedVerifyingKey<Bls12>,
Parameters<Bls12>,
PreparedVerifyingKey<Bls12>,
Option<PreparedVerifyingKey<Bls12>>,
) {
let mut spend_fs = hashreader::HashReader::new(spend_fs);
let mut output_fs = hashreader::HashReader::new(output_fs);
let mut sprout_fs = sprout_fs.map(|fs| hashreader::HashReader::new(fs));
// Deserialize params
let spend_params = Parameters::<Bls12>::read(&mut spend_fs, false)
.expect("couldn't deserialize Sapling spend parameters file");
let output_params = Parameters::<Bls12>::read(&mut output_fs, false)
.expect("couldn't deserialize Sapling spend parameters file");
// We only deserialize the verifying key for the Sprout parameters, which
// appears at the beginning of the parameter file. The rest is loaded
// during proving time.
let sprout_vk = sprout_fs.as_mut().map(|mut fs| {
VerifyingKey::<Bls12>::read(&mut fs)
.expect("couldn't deserialize Sprout Groth16 verifying key")
});
// There is extra stuff (the transcript) at the end of the parameter file which is
// used to verify the parameter validity, but we're not interested in that. We do
// want to read it, though, so that the BLAKE2b computed afterward is consistent
// with `b2sum` on the files.
let mut sink = io::sink();
io::copy(&mut spend_fs, &mut sink)
.expect("couldn't finish reading Sapling spend parameter file");
io::copy(&mut output_fs, &mut sink)
.expect("couldn't finish reading Sapling output parameter file");
if let Some(mut sprout_fs) = sprout_fs.as_mut() {
io::copy(&mut sprout_fs, &mut sink)
.expect("couldn't finish reading Sprout groth16 parameter file");
}
if spend_fs.into_hash() != SAPLING_SPEND_HASH {
panic!("Sapling spend parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`.");
}
if output_fs.into_hash() != SAPLING_OUTPUT_HASH {
panic!("Sapling output parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`.");
}
if sprout_fs
.map(|fs| fs.into_hash() != SPROUT_HASH)
.unwrap_or(false)
{
panic!("Sprout groth16 parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`.");
}
// Prepare verifying keys
let spend_vk = prepare_verifying_key(&spend_params.vk);
let output_vk = prepare_verifying_key(&output_params.vk);
let sprout_vk = sprout_vk.map(|vk| prepare_verifying_key(&vk));
(spend_params, spend_vk, output_params, output_vk, sprout_vk)
}