zebra_consensus/primitives/groth16/params/
parse_parameters.rs

1//! Copied from `zcash_proofs` v1.14.0 with minor modifications to the `parse_parameters` function (renamed to `parse_sapling_parameters`).
2//!
3//! Sapling related constants: <https://github.com/zcash/librustzcash/blob/zcash_proofs-0.14.0/zcash_proofs/src/lib.rs#L52-L58>
4//! Parse parameters: <https://github.com/zcash/librustzcash/blob/zcash_proofs-0.14.0/zcash_proofs/src/lib.rs#L353>
5//! Hash reader: <https://github.com/zcash/librustzcash/blob/zcash_proofs-0.14.0/zcash_proofs/src/hashreader.rs#L10>
6//! Verify hash: <https://github.com/zcash/librustzcash/blob/zcash_proofs-0.14.0/zcash_proofs/src/lib.rs#L472>
7use std::{
8    fmt::Write,
9    io::{self, Read},
10};
11
12use bellman::groth16;
13use blake2b_simd::State;
14use bls12_381::Bls12;
15use zcash_proofs::{SAPLING_OUTPUT_NAME, SAPLING_SPEND_NAME};
16
17use super::SaplingParameters;
18
19// Circuit names
20
21// Circuit hashes
22const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c";
23const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028";
24
25// Circuit parameter file sizes
26const SAPLING_SPEND_BYTES: u64 = 47958396;
27const SAPLING_OUTPUT_BYTES: u64 = 3592860;
28
29/// Parse Bls12 keys from bytes as serialized by [`groth16::Parameters::write`].
30///
31/// This function will panic if it encounters unparsable data.
32///
33/// [`groth16::Parameters::write`]: bellman::groth16::Parameters::write
34pub fn parse_sapling_parameters<R: io::Read>(spend_fs: R, output_fs: R) -> SaplingParameters {
35    let mut spend_fs = HashReader::new(spend_fs);
36    let mut output_fs = HashReader::new(output_fs);
37
38    // Deserialize params
39    let spend_params = groth16::Parameters::<Bls12>::read(&mut spend_fs, false)
40        .expect("couldn't deserialize Sapling spend parameters");
41    let output_params = groth16::Parameters::<Bls12>::read(&mut output_fs, false)
42        .expect("couldn't deserialize Sapling spend parameters");
43
44    // There is extra stuff (the transcript) at the end of the parameter file which is
45    // used to verify the parameter validity, but we're not interested in that. We do
46    // want to read it, though, so that the BLAKE2b computed afterward is consistent
47    // with `b2sum` on the files.
48    let mut sink = io::sink();
49
50    // TODO: use the correct paths for Windows and macOS
51    //       use the actual file paths supplied by the caller
52    verify_hash(
53        spend_fs,
54        &mut sink,
55        SAPLING_SPEND_HASH,
56        SAPLING_SPEND_BYTES,
57        SAPLING_SPEND_NAME,
58        "a file",
59    )
60    .expect(
61        "Sapling spend parameter file is not correct, \
62         please clean your `~/.zcash-params/` and re-run `fetch-params`.",
63    );
64
65    verify_hash(
66        output_fs,
67        &mut sink,
68        SAPLING_OUTPUT_HASH,
69        SAPLING_OUTPUT_BYTES,
70        SAPLING_OUTPUT_NAME,
71        "a file",
72    )
73    .expect(
74        "Sapling output parameter file is not correct, \
75         please clean your `~/.zcash-params/` and re-run `fetch-params`.",
76    );
77
78    // Prepare verifying keys
79    let spend_vk = groth16::prepare_verifying_key(&spend_params.vk);
80    let output_vk = groth16::prepare_verifying_key(&output_params.vk);
81
82    SaplingParameters {
83        spend: spend_params,
84        spend_prepared_verifying_key: spend_vk,
85        output: output_params,
86        output_prepared_verifying_key: output_vk,
87    }
88}
89
90/// Abstraction over a reader which hashes the data being read.
91pub struct HashReader<R: Read> {
92    reader: R,
93    hasher: State,
94    byte_count: u64,
95}
96
97impl<R: Read> HashReader<R> {
98    /// Construct a new `HashReader` given an existing `reader` by value.
99    pub fn new(reader: R) -> Self {
100        HashReader {
101            reader,
102            hasher: State::new(),
103            byte_count: 0,
104        }
105    }
106
107    /// Destroy this reader and return the hash of what was read.
108    pub fn into_hash(self) -> String {
109        let hash = self.hasher.finalize();
110
111        let mut s = String::new();
112        for c in hash.as_bytes().iter() {
113            write!(&mut s, "{:02x}", c).expect("writing to a string never fails");
114        }
115
116        s
117    }
118
119    /// Return the number of bytes read so far.
120    pub fn byte_count(&self) -> u64 {
121        self.byte_count
122    }
123}
124
125impl<R: Read> Read for HashReader<R> {
126    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
127        let bytes = self.reader.read(buf)?;
128
129        if bytes > 0 {
130            self.hasher.update(&buf[0..bytes]);
131            let byte_count = u64::try_from(bytes).map_err(|_| {
132                io::Error::new(
133                    io::ErrorKind::InvalidData,
134                    "Could not fit the number of read bytes into u64.",
135                )
136            })?;
137            self.byte_count += byte_count;
138        }
139
140        Ok(bytes)
141    }
142}
143
144/// Check if the Blake2b hash from `hash_reader` matches `expected_hash`,
145/// while streaming from `hash_reader` into `sink`.
146///
147/// `hash_reader` can be used to partially read its inner reader's data,
148/// before verifying the hash using this function.
149///
150/// Returns an error containing `name` and `params_source` on failure.
151fn verify_hash<R: io::Read, W: io::Write>(
152    mut hash_reader: HashReader<R>,
153    mut sink: W,
154    expected_hash: &str,
155    expected_bytes: u64,
156    name: &str,
157    params_source: &str,
158) -> Result<(), io::Error> {
159    let read_result = io::copy(&mut hash_reader, &mut sink);
160
161    if let Err(read_error) = read_result {
162        return Err(io::Error::new(
163            read_error.kind(),
164            format!(
165                "{} failed reading:\n\
166                 expected: {} bytes,\n\
167                 actual:   {} bytes from {:?},\n\
168                 error: {:?}",
169                name,
170                expected_bytes,
171                hash_reader.byte_count(),
172                params_source,
173                read_error,
174            ),
175        ));
176    }
177
178    let byte_count = hash_reader.byte_count();
179    let hash = hash_reader.into_hash();
180    if hash != expected_hash {
181        return Err(io::Error::new(
182            io::ErrorKind::InvalidData,
183            format!(
184                "{} failed validation:\n\
185                 expected: {} hashing {} bytes,\n\
186                 actual:   {} hashing {} bytes from {:?}",
187                name, expected_hash, expected_bytes, hash, byte_count, params_source,
188            ),
189        ));
190    }
191
192    Ok(())
193}