ironfish_proofs/
lib.rs

1#![doc = include_str!("../README.md")]
2
3#![cfg_attr(docsrs, feature(doc_cfg))]
4// Temporary until we have addressed all Result<T, ()> cases.
5#![allow(clippy::result_unit_err)]
6
7use ironfish_bellperson::groth16::{prepare_verifying_key, Parameters, PreparedVerifyingKey, VerifyingKey};
8use blstrs::Bls12;
9use std::fs::File;
10use std::io::{self, BufReader};
11use std::path::Path;
12
13#[cfg(feature = "directories")]
14use directories::BaseDirs;
15#[cfg(feature = "directories")]
16use std::path::PathBuf;
17
18pub mod circuit;
19pub mod constants;
20mod hashreader;
21pub mod sapling;
22pub mod sprout;
23
24#[cfg(any(feature = "local-prover", feature = "bundled-prover"))]
25#[cfg_attr(
26    docsrs,
27    doc(cfg(any(feature = "local-prover", feature = "bundled-prover")))
28)]
29pub mod prover;
30
31// Circuit names
32#[cfg(feature = "local-prover")]
33const SAPLING_SPEND_NAME: &str = "sapling-spend.params";
34#[cfg(feature = "local-prover")]
35const SAPLING_OUTPUT_NAME: &str = "sapling-output.params";
36
37// Circuit hashes
38const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c";
39const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028";
40const SPROUT_HASH: &str = "e9b238411bd6c0ec4791e9d04245ec350c9c5744f5610dfcce4365d5ca49dfefd5054e371842b3f88fa1b9d7e8e075249b3ebabd167fa8b0f3161292d36c180a";
41
42#[cfg(feature = "download-params")]
43const DOWNLOAD_URL: &str = "https://download.z.cash/downloads";
44
45/// Returns the default folder that the Zcash proving parameters are located in.
46#[cfg(feature = "directories")]
47#[cfg_attr(docsrs, doc(cfg(feature = "directories")))]
48pub fn default_params_folder() -> Option<PathBuf> {
49    BaseDirs::new().map(|base_dirs| {
50        if cfg!(any(windows, target_os = "macos")) {
51            base_dirs.data_dir().join("ZcashParams")
52        } else {
53            base_dirs.home_dir().join(".zcash-params")
54        }
55    })
56}
57
58/// Download the Zcash Sapling parameters, storing them in the default location.
59///
60/// This mirrors the behaviour of the `fetch-params.sh` script from `zcashd`.
61#[cfg(feature = "download-params")]
62#[cfg_attr(docsrs, doc(cfg(feature = "download-params")))]
63pub fn download_parameters() -> Result<(), minreq::Error> {
64    // Ensure that the default Zcash parameters location exists.
65    let params_dir = default_params_folder().ok_or_else(|| {
66        io::Error::new(io::ErrorKind::Other, "Could not load default params folder")
67    })?;
68    std::fs::create_dir_all(&params_dir)?;
69
70    let fetch_params = |name: &str, expected_hash: &str| -> Result<(), minreq::Error> {
71        use std::io::Write;
72
73        // Download the parts directly (Sapling parameters are small enough for this).
74        let part_1 = minreq::get(format!("{}/{}.part.1", DOWNLOAD_URL, name)).send()?;
75        let part_2 = minreq::get(format!("{}/{}.part.2", DOWNLOAD_URL, name)).send()?;
76
77        // Verify parameter file hash.
78        let hash = blake2b_simd::State::new()
79            .update(part_1.as_bytes())
80            .update(part_2.as_bytes())
81            .finalize()
82            .to_hex();
83        if &hash != expected_hash {
84            return Err(io::Error::new(
85                io::ErrorKind::InvalidData,
86                format!(
87                    "{} failed validation (expected: {}, actual: {}, fetched {} bytes)",
88                    name,
89                    expected_hash,
90                    hash,
91                    part_1.as_bytes().len() + part_2.as_bytes().len()
92                ),
93            )
94            .into());
95        }
96
97        // Write parameter file.
98        let mut f = File::create(params_dir.join(name))?;
99        f.write_all(part_1.as_bytes())?;
100        f.write_all(part_2.as_bytes())?;
101        Ok(())
102    };
103
104    fetch_params(SAPLING_SPEND_NAME, SAPLING_SPEND_HASH)?;
105    fetch_params(SAPLING_OUTPUT_NAME, SAPLING_OUTPUT_HASH)?;
106
107    Ok(())
108}
109
110pub struct ZcashParameters {
111    pub spend_params: Parameters<Bls12>,
112    pub spend_vk: PreparedVerifyingKey<Bls12>,
113    pub output_params: Parameters<Bls12>,
114    pub output_vk: PreparedVerifyingKey<Bls12>,
115    pub sprout_vk: Option<PreparedVerifyingKey<Bls12>>,
116}
117
118pub fn load_parameters(
119    spend_path: &Path,
120    output_path: &Path,
121    sprout_path: Option<&Path>,
122) -> ZcashParameters {
123    // Load from each of the paths
124    let spend_fs = File::open(spend_path).expect("couldn't load Sapling spend parameters file");
125    let output_fs = File::open(output_path).expect("couldn't load Sapling output parameters file");
126    let sprout_fs =
127        sprout_path.map(|p| File::open(p).expect("couldn't load Sprout groth16 parameters file"));
128
129    parse_parameters(
130        BufReader::with_capacity(1024 * 1024, spend_fs),
131        BufReader::with_capacity(1024 * 1024, output_fs),
132        sprout_fs.map(|fs| BufReader::with_capacity(1024 * 1024, fs)),
133    )
134}
135
136/// Parse Bls12 keys from bytes as serialized by [`Parameters::write`].
137///
138/// This function will panic if it encounters unparsable data.
139pub fn parse_parameters<R: io::Read>(
140    spend_fs: R,
141    output_fs: R,
142    sprout_fs: Option<R>,
143) -> ZcashParameters {
144    let mut spend_fs = hashreader::HashReader::new(spend_fs);
145    let mut output_fs = hashreader::HashReader::new(output_fs);
146    let mut sprout_fs = sprout_fs.map(hashreader::HashReader::new);
147
148    // Deserialize params
149    let spend_params = Parameters::<Bls12>::read(&mut spend_fs, false)
150        .expect("couldn't deserialize Sapling spend parameters file");
151    let output_params = Parameters::<Bls12>::read(&mut output_fs, false)
152        .expect("couldn't deserialize Sapling spend parameters file");
153
154    // We only deserialize the verifying key for the Sprout parameters, which
155    // appears at the beginning of the parameter file. The rest is loaded
156    // during proving time.
157    let sprout_vk = sprout_fs.as_mut().map(|mut fs| {
158        VerifyingKey::<Bls12>::read(&mut fs)
159            .expect("couldn't deserialize Sprout Groth16 verifying key")
160    });
161
162    // There is extra stuff (the transcript) at the end of the parameter file which is
163    // used to verify the parameter validity, but we're not interested in that. We do
164    // want to read it, though, so that the BLAKE2b computed afterward is consistent
165    // with `b2sum` on the files.
166    let mut sink = io::sink();
167    io::copy(&mut spend_fs, &mut sink)
168        .expect("couldn't finish reading Sapling spend parameter file");
169    io::copy(&mut output_fs, &mut sink)
170        .expect("couldn't finish reading Sapling output parameter file");
171    if let Some(mut sprout_fs) = sprout_fs.as_mut() {
172        io::copy(&mut sprout_fs, &mut sink)
173            .expect("couldn't finish reading Sprout groth16 parameter file");
174    }
175
176    if spend_fs.into_hash() != SAPLING_SPEND_HASH {
177        panic!("Sapling spend parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`.");
178    }
179
180    if output_fs.into_hash() != SAPLING_OUTPUT_HASH {
181        panic!("Sapling output parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`.");
182    }
183
184    if sprout_fs
185        .map(|fs| fs.into_hash() != SPROUT_HASH)
186        .unwrap_or(false)
187    {
188        panic!("Sprout groth16 parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`.");
189    }
190
191    // Prepare verifying keys
192    let spend_vk = prepare_verifying_key(&spend_params.vk);
193    let output_vk = prepare_verifying_key(&output_params.vk);
194    let sprout_vk = sprout_vk.map(|vk| prepare_verifying_key(&vk));
195
196    ZcashParameters {
197        spend_params,
198        spend_vk,
199        output_params,
200        output_vk,
201        sprout_vk,
202    }
203}