Skip to content

Commit

Permalink
Copies parse_parameters() function and other private functions it d…
Browse files Browse the repository at this point in the history
…epends on from `zcash_proofs` (#8548)
  • Loading branch information
arya2 authored May 18, 2024
1 parent d7ad189 commit 7b17e4d
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 60 deletions.
23 changes: 4 additions & 19 deletions zebra-consensus/src/primitives/groth16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,6 @@ pub type BatchVerifyingKey = VerifyingKey<Bls12>;
/// This is the key used to verify individual items.
pub type ItemVerifyingKey = PreparedVerifyingKey<Bls12>;

lazy_static::lazy_static! {
/// The Sapling Groth16 verifying key for spends.
pub static ref SAPLING_SPEND_VERIFYING_KEY: crate::groth16::VerifyingKey<Bls12> =
GROTH16_PARAMETERS.sapling.spend.verifying_key().inner().clone();
/// The Sapling Groth16 verifying key for outputs.
pub static ref SAPLING_OUTPUT_VERIFYING_KEY: crate::groth16::VerifyingKey<Bls12> =
GROTH16_PARAMETERS.sapling.output.verifying_key().inner().clone();
}

/// Global batch verification context for Groth16 proofs of Spend statements.
///
/// This service transparently batches contemporaneous proof verifications,
Expand All @@ -93,7 +84,7 @@ pub static SPEND_VERIFIER: Lazy<
> = Lazy::new(|| {
Fallback::new(
Batch::new(
Verifier::new(&SAPLING_SPEND_VERIFYING_KEY),
Verifier::new(&GROTH16_PARAMETERS.sapling.spend.vk),
super::MAX_BATCH_SIZE,
None,
super::MAX_BATCH_LATENCY,
Expand All @@ -111,10 +102,7 @@ pub static SPEND_VERIFIER: Lazy<
(|item: Item| {
Verifier::verify_single_spawning(
item,
&GROTH16_PARAMETERS
.sapling
.spend_prepared_verifying_key
.inner(),
&GROTH16_PARAMETERS.sapling.spend_prepared_verifying_key,
)
.boxed()
}) as fn(_) -> _,
Expand All @@ -138,7 +126,7 @@ pub static OUTPUT_VERIFIER: Lazy<
> = Lazy::new(|| {
Fallback::new(
Batch::new(
Verifier::new(&SAPLING_OUTPUT_VERIFYING_KEY),
Verifier::new(&GROTH16_PARAMETERS.sapling.output.vk),
super::MAX_BATCH_SIZE,
None,
super::MAX_BATCH_LATENCY,
Expand All @@ -151,10 +139,7 @@ pub static OUTPUT_VERIFIER: Lazy<
(|item: Item| {
Verifier::verify_single_spawning(
item,
&GROTH16_PARAMETERS
.sapling
.output_prepared_verifying_key
.inner(),
&GROTH16_PARAMETERS.sapling.output_prepared_verifying_key,
)
.boxed()
}) as fn(_) -> _,
Expand Down
23 changes: 9 additions & 14 deletions zebra-consensus/src/primitives/groth16/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
use bellman::groth16;
use bls12_381::Bls12;

mod parse_parameters;

use parse_parameters::parse_sapling_parameters;

lazy_static::lazy_static! {
/// Groth16 Zero-Knowledge Proof parameters for the Sapling and Sprout circuits.
///
Expand All @@ -25,11 +29,11 @@ pub struct Groth16Parameters {

/// Groth16 Zero-Knowledge Proof spend and output parameters for the Sapling circuit.
pub struct SaplingParameters {
pub spend: sapling::circuit::SpendParameters,
pub spend_prepared_verifying_key: sapling::circuit::PreparedSpendVerifyingKey,
pub spend: groth16::Parameters<Bls12>,
pub spend_prepared_verifying_key: groth16::PreparedVerifyingKey<Bls12>,

pub output: sapling::circuit::OutputParameters,
pub output_prepared_verifying_key: sapling::circuit::PreparedOutputVerifyingKey,
pub output: groth16::Parameters<Bls12>,
pub output_prepared_verifying_key: groth16::PreparedVerifyingKey<Bls12>,
}

/// Groth16 Zero-Knowledge Proof spend parameters for the Sprout circuit.
Expand All @@ -53,20 +57,11 @@ impl Groth16Parameters {
wagyu_zcash_parameters::load_sapling_parameters();
let sprout_vk_bytes = include_bytes!("sprout-groth16.vk");

let sapling_parameters = zcash_proofs::parse_parameters(
let sapling = parse_sapling_parameters(
sapling_spend_bytes.as_slice(),
sapling_output_bytes.as_slice(),
// This API expects the full sprout parameter file, not just the verifying key.
None,
);

let sapling = SaplingParameters {
spend: sapling_parameters.spend_params,
spend_prepared_verifying_key: sapling_parameters.spend_vk,
output: sapling_parameters.output_params,
output_prepared_verifying_key: sapling_parameters.output_vk,
};

let sprout_vk = groth16::VerifyingKey::<Bls12>::read(&sprout_vk_bytes[..])
.expect("should be able to parse Sprout verification key");
let sprout_vk = groth16::prepare_verifying_key(&sprout_vk);
Expand Down
189 changes: 189 additions & 0 deletions zebra-consensus/src/primitives/groth16/params/parse_parameters.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
//! Copied from `zcash_proofs` v1.14.0 with minor modifications to the `parse_parameters` function (renamed to `parse_sapling_parameters`).

use std::{
fmt::Write,
io::{self, Read},
};

use bellman::groth16;
use blake2b_simd::State;
use bls12_381::Bls12;
use zcash_proofs::{SAPLING_OUTPUT_NAME, SAPLING_SPEND_NAME};

use super::SaplingParameters;

// Circuit names

// Circuit hashes
const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c";
const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028";

// Circuit parameter file sizes
const SAPLING_SPEND_BYTES: u64 = 47958396;
const SAPLING_OUTPUT_BYTES: u64 = 3592860;

/// Parse Bls12 keys from bytes as serialized by [`groth16::Parameters::write`].
///
/// This function will panic if it encounters unparsable data.
///
/// [`groth16::Parameters::write`]: bellman::groth16::Parameters::write
pub fn parse_sapling_parameters<R: io::Read>(spend_fs: R, output_fs: R) -> SaplingParameters {
let mut spend_fs = HashReader::new(spend_fs);
let mut output_fs = HashReader::new(output_fs);

// Deserialize params
let spend_params = groth16::Parameters::<Bls12>::read(&mut spend_fs, false)
.expect("couldn't deserialize Sapling spend parameters");
let output_params = groth16::Parameters::<Bls12>::read(&mut output_fs, false)
.expect("couldn't deserialize Sapling spend parameters");

// 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();

// TODO: use the correct paths for Windows and macOS
// use the actual file paths supplied by the caller
verify_hash(
spend_fs,
&mut sink,
SAPLING_SPEND_HASH,
SAPLING_SPEND_BYTES,
SAPLING_SPEND_NAME,
"a file",
)
.expect(
"Sapling spend parameter file is not correct, \
please clean your `~/.zcash-params/` and re-run `fetch-params`.",
);

verify_hash(
output_fs,
&mut sink,
SAPLING_OUTPUT_HASH,
SAPLING_OUTPUT_BYTES,
SAPLING_OUTPUT_NAME,
"a file",
)
.expect(
"Sapling output parameter file is not correct, \
please clean your `~/.zcash-params/` and re-run `fetch-params`.",
);

// Prepare verifying keys
let spend_vk = groth16::prepare_verifying_key(&spend_params.vk);
let output_vk = groth16::prepare_verifying_key(&output_params.vk);

SaplingParameters {
spend: spend_params,
spend_prepared_verifying_key: spend_vk,
output: output_params,
output_prepared_verifying_key: output_vk,
}
}

/// Abstraction over a reader which hashes the data being read.
pub struct HashReader<R: Read> {
reader: R,
hasher: State,
byte_count: u64,
}

impl<R: Read> HashReader<R> {
/// Construct a new `HashReader` given an existing `reader` by value.
pub fn new(reader: R) -> Self {
HashReader {
reader,
hasher: State::new(),
byte_count: 0,
}
}

/// Destroy this reader and return the hash of what was read.
pub fn into_hash(self) -> String {
let hash = self.hasher.finalize();

let mut s = String::new();
for c in hash.as_bytes().iter() {
write!(&mut s, "{:02x}", c).expect("writing to a string never fails");
}

s
}

/// Return the number of bytes read so far.
pub fn byte_count(&self) -> u64 {
self.byte_count
}
}

impl<R: Read> Read for HashReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let bytes = self.reader.read(buf)?;

if bytes > 0 {
self.hasher.update(&buf[0..bytes]);
let byte_count = u64::try_from(bytes).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
"Could not fit the number of read bytes into u64.",
)
})?;
self.byte_count += byte_count;
}

Ok(bytes)
}
}

/// Check if the Blake2b hash from `hash_reader` matches `expected_hash`,
/// while streaming from `hash_reader` into `sink`.
///
/// `hash_reader` can be used to partially read its inner reader's data,
/// before verifying the hash using this function.
///
/// Returns an error containing `name` and `params_source` on failure.
fn verify_hash<R: io::Read, W: io::Write>(
mut hash_reader: HashReader<R>,
mut sink: W,
expected_hash: &str,
expected_bytes: u64,
name: &str,
params_source: &str,
) -> Result<(), io::Error> {
let read_result = io::copy(&mut hash_reader, &mut sink);

if let Err(read_error) = read_result {
return Err(io::Error::new(
read_error.kind(),
format!(
"{} failed reading:\n\
expected: {} bytes,\n\
actual: {} bytes from {:?},\n\
error: {:?}",
name,
expected_bytes,
hash_reader.byte_count(),
params_source,
read_error,
),
));
}

let byte_count = hash_reader.byte_count();
let hash = hash_reader.into_hash();
if hash != expected_hash {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"{} failed validation:\n\
expected: {} hashing {} bytes,\n\
actual: {} hashing {} bytes from {:?}",
name, expected_hash, expected_bytes, hash, byte_count, params_source,
),
));
}

Ok(())
}
33 changes: 6 additions & 27 deletions zebra-consensus/src/primitives/groth16/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,41 +73,27 @@ async fn verify_sapling_groth16() {
// Use separate verifiers so shared batch tasks aren't killed when the test ends (#2390)
let mut spend_verifier = Fallback::new(
Batch::new(
Verifier::new(&SAPLING_SPEND_VERIFYING_KEY),
Verifier::new(&GROTH16_PARAMETERS.sapling.spend.vk),
crate::primitives::MAX_BATCH_SIZE,
None,
crate::primitives::MAX_BATCH_LATENCY,
),
tower::service_fn(
(|item: Item| {
ready(
item.verify_single(
&GROTH16_PARAMETERS
.sapling
.spend_prepared_verifying_key
.inner(),
),
)
ready(item.verify_single(&GROTH16_PARAMETERS.sapling.spend_prepared_verifying_key))
}) as fn(_) -> _,
),
);
let mut output_verifier = Fallback::new(
Batch::new(
Verifier::new(&SAPLING_OUTPUT_VERIFYING_KEY),
Verifier::new(&GROTH16_PARAMETERS.sapling.output.vk),
crate::primitives::MAX_BATCH_SIZE,
None,
crate::primitives::MAX_BATCH_LATENCY,
),
tower::service_fn(
(|item: Item| {
ready(
item.verify_single(
&GROTH16_PARAMETERS
.sapling
.output_prepared_verifying_key
.inner(),
),
)
ready(item.verify_single(&GROTH16_PARAMETERS.sapling.output_prepared_verifying_key))
}) as fn(_) -> _,
),
);
Expand Down Expand Up @@ -193,21 +179,14 @@ async fn correctly_err_on_invalid_output_proof() {
// Also, since we expect these to fail, we don't want to slow down the communal verifiers.
let mut output_verifier = Fallback::new(
Batch::new(
Verifier::new(&SAPLING_SPEND_VERIFYING_KEY),
Verifier::new(&GROTH16_PARAMETERS.sapling.output.vk),
crate::primitives::MAX_BATCH_SIZE,
None,
crate::primitives::MAX_BATCH_LATENCY,
),
tower::service_fn(
(|item: Item| {
ready(
item.verify_single(
&GROTH16_PARAMETERS
.sapling
.output_prepared_verifying_key
.inner(),
),
)
ready(item.verify_single(&GROTH16_PARAMETERS.sapling.output_prepared_verifying_key))
}) as fn(_) -> _,
),
);
Expand Down

0 comments on commit 7b17e4d

Please sign in to comment.