From 90126721641a9a398fcb683d53e52db9f5c60e9e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 26 Jun 2020 12:38:40 +1200 Subject: [PATCH] zcash_proofs: Add API for downloading the Sapling parameters Includes an example that exposes the API as a binary. --- zcash_proofs/Cargo.toml | 6 +++ zcash_proofs/examples/download-params.rs | 3 ++ zcash_proofs/src/lib.rs | 65 ++++++++++++++++++++++-- 3 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 zcash_proofs/examples/download-params.rs diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 3d2a8d4be..62d568cef 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -17,6 +17,7 @@ blake2b_simd = "0.5" byteorder = "1" directories = { version = "1", optional = true } ff = { version = "0.6", path = "../ff" } +minreq = { version = "2", features = ["https"], optional = true } pairing = { version = "0.16", path = "../pairing" } rand_core = "0.5.1" zcash_primitives = { version = "0.2", path = "../zcash_primitives" } @@ -26,8 +27,13 @@ rand_xorshift = "0.2" [features] default = ["local-prover", "multicore"] +download-params = ["minreq"] local-prover = ["directories"] multicore = ["bellman/multicore"] +[[example]] +name = "download-params" +required-features = ["download-params"] + [badges] maintenance = { status = "actively-developed" } diff --git a/zcash_proofs/examples/download-params.rs b/zcash_proofs/examples/download-params.rs new file mode 100644 index 000000000..b23cb75fa --- /dev/null +++ b/zcash_proofs/examples/download-params.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), minreq::Error> { + zcash_proofs::download_parameters() +} diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index 7c579bf24..e952be6be 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -29,6 +29,14 @@ pub mod prover; const SAPLING_SPEND_NAME: &str = "sapling-spend.params"; 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")] fn default_params_folder() -> Option { @@ -41,6 +49,58 @@ fn default_params_folder() -> Option { }) } +/// 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")] +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(¶ms_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, @@ -52,11 +112,6 @@ pub fn load_parameters( PreparedVerifyingKey, Option>, ) { - // Sapling circuit hashes - const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c"; - const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028"; - const SPROUT_HASH: &str = "e9b238411bd6c0ec4791e9d04245ec350c9c5744f5610dfcce4365d5ca49dfefd5054e371842b3f88fa1b9d7e8e075249b3ebabd167fa8b0f3161292d36c180a"; - // 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");