Automatically download and load sprout parameters (#3085)

* Download and load Sprout parameters using zcash_proofs

Also update some librustzcash dependencies, to avoid duplicate dependencies.

* Update upstream orchard to avoid a compilation error

* Skip librustzcash batch refactor for now, to avoid compilation errors

* Change the cache ID, so we actually cache Sprout

* Move existing file checks into zcash_proofs

* Add a 1 hour timeout to parameter file downloads

* Give other tasks priority, before spawning the download task

* Update to the latest version of our modified librustzcash fork

* Change the cache key for Sprout

* Add 40 minutes to CI timeouts for occasional sprout downloads

* Update to zcash_proofs with split downloads

* Check file sizes to help debug parameter load failures in zcash_proofs

* Start the second download once the first has finished in zcash_proofs

* Document the parameter download task

* Stop hashing existing files twice
This commit is contained in:
teor 2021-11-26 02:26:32 +10:00 committed by GitHub
parent a61eae0065
commit 0ef4629232
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 141 additions and 84 deletions

View File

@ -11,8 +11,10 @@ jobs:
test:
name: Test (+${{ matrix.rust }}) on ${{ matrix.os }}
# The large timeout is to accommodate Windows builds
timeout-minutes: 75
# The large timeout is to accommodate:
# - Windows builds (75 minutes, typically 30-50 minutes)
# - parameter downloads (40 minutes, but only when the cache expires)
timeout-minutes: 115
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
@ -87,7 +89,7 @@ jobs:
uses: actions/cache@v2
with:
path: ${{ env.ZCASH_PARAMS }}
key: ${{ runner.os }}-params
key: ${{ runner.os }}-sprout-and-sapling-params
- name: Fetch Zcash parameters
if: steps.cache-params.outputs.cache-hit != 'true'
working-directory: ./zebra-consensus

View File

@ -13,8 +13,10 @@ jobs:
coverage:
name: Coverage (+nightly)
# The large timeout is to accommodate nightly builds
timeout-minutes: 60
# The large timeout is to accommodate:
# - nightly builds (75 minutes, typically 30-50 minutes)
# - parameter downloads (40 minutes, but only when the cache expires)
timeout-minutes: 115
runs-on: ubuntu-latest
env:
CARGO_INCREMENTAL: 0
@ -62,7 +64,7 @@ jobs:
uses: actions/cache@v2
with:
path: ${{ env.ZCASH_PARAMS }}
key: ${{ runner.os }}-params
key: ${{ runner.os }}-sprout-and-sapling-params
- name: Fetch Zcash parameters
if: steps.cache-params.outputs.cache-hit != 'true'
working-directory: ./zebra-consensus

12
Cargo.lock generated
View File

@ -1086,7 +1086,7 @@ dependencies = [
[[package]]
name = "equihash"
version = "0.1.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e"
source = "git+https://github.com/ZcashFoundation/librustzcash.git?rev=dea8d97d5ed09bdb6673cb64d0a63706f71c2f61#dea8d97d5ed09bdb6673cb64d0a63706f71c2f61"
dependencies = [
"blake2b_simd",
"byteorder",
@ -4181,7 +4181,7 @@ dependencies = [
[[package]]
name = "zcash_encoding"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e"
source = "git+https://github.com/ZcashFoundation/librustzcash.git?rev=dea8d97d5ed09bdb6673cb64d0a63706f71c2f61#dea8d97d5ed09bdb6673cb64d0a63706f71c2f61"
dependencies = [
"byteorder",
"nonempty",
@ -4190,7 +4190,7 @@ dependencies = [
[[package]]
name = "zcash_history"
version = "0.2.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e"
source = "git+https://github.com/ZcashFoundation/librustzcash.git?rev=dea8d97d5ed09bdb6673cb64d0a63706f71c2f61#dea8d97d5ed09bdb6673cb64d0a63706f71c2f61"
dependencies = [
"bigint",
"blake2b_simd",
@ -4200,7 +4200,7 @@ dependencies = [
[[package]]
name = "zcash_note_encryption"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e"
source = "git+https://github.com/ZcashFoundation/librustzcash.git?rev=dea8d97d5ed09bdb6673cb64d0a63706f71c2f61#dea8d97d5ed09bdb6673cb64d0a63706f71c2f61"
dependencies = [
"blake2b_simd",
"byteorder",
@ -4215,7 +4215,7 @@ dependencies = [
[[package]]
name = "zcash_primitives"
version = "0.5.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e"
source = "git+https://github.com/ZcashFoundation/librustzcash.git?rev=dea8d97d5ed09bdb6673cb64d0a63706f71c2f61#dea8d97d5ed09bdb6673cb64d0a63706f71c2f61"
dependencies = [
"aes",
"bip0039",
@ -4249,7 +4249,7 @@ dependencies = [
[[package]]
name = "zcash_proofs"
version = "0.5.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=53d0a51d33a421cb76d3e3124d1e4c1c9036068e#53d0a51d33a421cb76d3e3124d1e4c1c9036068e"
source = "git+https://github.com/ZcashFoundation/librustzcash.git?rev=dea8d97d5ed09bdb6673cb64d0a63706f71c2f61#dea8d97d5ed09bdb6673cb64d0a63706f71c2f61"
dependencies = [
"bellman",
"blake2b_simd",

View File

@ -22,11 +22,21 @@ panic = "abort"
[patch.crates-io]
# TODO: replace with upstream orchard when these changes are merged
# https://github.com/ZcashFoundation/zebra/issues/3056
# TODO: replace with upstream orchard (#3056)
orchard = { git = "https://github.com/ZcashFoundation/orchard.git", rev = "568e24cd5f129158375d7ac7d98c89ebff4f982f" }
# TODO: remove these after a new librustzcash release.
# TODO: replace with upstream librustzcash when these changes are merged (#3037)
#
# This zcash_proofs patch doesn't work, maybe because of features?
#zcash_proofs = { git = "https://github.com/ZcashFoundation/librustzcash.git", rev = "dea8d97d5ed09bdb6673cb64d0a63706f71c2f61" }
# Use the ZcashFoundation fork where possible, to avoid duplicate dependencies
equihash = { git = "https://github.com/ZcashFoundation/librustzcash.git", rev = "dea8d97d5ed09bdb6673cb64d0a63706f71c2f61" }
zcash_note_encryption = { git = "https://github.com/ZcashFoundation/librustzcash.git", rev = "dea8d97d5ed09bdb6673cb64d0a63706f71c2f61" }
zcash_primitives = { git = "https://github.com/ZcashFoundation/librustzcash.git", rev = "dea8d97d5ed09bdb6673cb64d0a63706f71c2f61" }
zcash_history = { git = "https://github.com/ZcashFoundation/librustzcash.git", rev = "dea8d97d5ed09bdb6673cb64d0a63706f71c2f61" }
# TODO: remove these after a new librustzcash release (#2982)
# These are librustzcash git requirements specified in its workspace Cargo.toml,
# that we must replicate here
@ -34,13 +44,15 @@ incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree.
# Replaced by the ZcashFoundation fork above
#orchard = { git = "https://github.com/zcash/orchard.git", rev = "2c8241f25b943aa05203eacf9905db117c69bd29" }
# Replaced by the ZcashFoundation fork above
# These are librustzcash file requirements specified in its workspace Cargo.toml,
# that we must replace with git requirements
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" }
#zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" }
# These patches are not strictly required,
# but they help avoid duplicate dependencies
equihash = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" }
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" }
#equihash = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" }
#zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" }
#zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" }
#zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" }

View File

@ -33,7 +33,6 @@ hex = "0.4"
incrementalmerkletree = "0.1.0"
jubjub = "0.8.0"
lazy_static = "1.4.0"
orchard = "0.0"
rand_core = "0.6"
ripemd160 = "0.9"
secp256k1 = { version = "0.20.3", features = ["serde"] }
@ -45,9 +44,15 @@ subtle = "2.4"
thiserror = "1"
uint = "0.9.1"
x25519-dalek = { version = "1.2.0", features = ["serde"] }
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" }
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e" }
# TODO: replace with upstream orchard (#3056)
orchard = { git = "https://github.com/ZcashFoundation/orchard.git", rev = "568e24cd5f129158375d7ac7d98c89ebff4f982f" }
# TODO: replace with upstream librustzcash when these changes are merged (#3037)
equihash = { git = "https://github.com/ZcashFoundation/librustzcash.git", rev = "dea8d97d5ed09bdb6673cb64d0a63706f71c2f61" }
zcash_note_encryption = { git = "https://github.com/ZcashFoundation/librustzcash.git", rev = "dea8d97d5ed09bdb6673cb64d0a63706f71c2f61" }
zcash_primitives = { git = "https://github.com/ZcashFoundation/librustzcash.git", rev = "dea8d97d5ed09bdb6673cb64d0a63706f71c2f61" }
zcash_history = { git = "https://github.com/ZcashFoundation/librustzcash.git", rev = "dea8d97d5ed09bdb6673cb64d0a63706f71c2f61" }
proptest = { version = "0.10", optional = true }
proptest-derive = { version = "0.3.0", optional = true }
@ -57,7 +62,6 @@ rand_chacha = { version = "0.3", optional = true }
# ZF deps
ed25519-zebra = "3.0.0"
equihash = "0.1"
# TODO: Update to 0.5 release when published
redjubjub = { git = "https://github.com/ZcashFoundation/redjubjub.git", rev = "f772176560b0b7daf25eff2460e08dc127ac8407" }

View File

@ -17,9 +17,6 @@ jubjub = "0.8.0"
rand = "0.8"
halo2 = "=0.1.0-beta.1"
# TODO: replace with upstream orchard when these changes are merged
# https://github.com/ZcashFoundation/zebra/issues/3056
orchard = "0.0.0"
chrono = "0.4.19"
dirs = "4.0.0"
@ -37,7 +34,11 @@ tower = { version = "0.4.11", features = ["timeout", "util", "buffer"] }
tracing = "0.1.29"
tracing-futures = "0.2.5"
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "53d0a51d33a421cb76d3e3124d1e4c1c9036068e", features = ["local-prover", "multicore", "download-params"] }
# TODO: replace with upstream orchard (#3056)
orchard = { git = "https://github.com/ZcashFoundation/orchard.git", rev = "568e24cd5f129158375d7ac7d98c89ebff4f982f" }
# TODO: replace with upstream librustzcash when these changes are merged (#3037)
zcash_proofs = { git = "https://github.com/ZcashFoundation/librustzcash.git", rev = "dea8d97d5ed09bdb6673cb64d0a63706f71c2f61", features = ["local-prover", "multicore", "download-params"] }
tower-fallback = { path = "../tower-fallback/" }
tower-batch = { path = "../tower-batch/" }

View File

@ -198,11 +198,13 @@ where
S::Future: Send + 'static,
{
// Pre-download Groth16 parameters in a separate thread.
// This thread must be launched first, so the download doesn't happen on the startup thread.
// Give other tasks priority, before spawning the download task.
tokio::task::yield_now().await;
// The parameter download thread must be launched before initializing any verifiers.
// Otherwise, the download might happen on the startup thread.
let groth16_download_handle = spawn_blocking(|| {
tracing::info!("checking if Zcash Sapling and Sprout parameters have been downloaded");
// The lazy static initializer does the download, if needed,
// and the file hash checks.
lazy_static::initialize(&crate::groth16::GROTH16_PARAMETERS);

View File

@ -5,6 +5,13 @@ use std::path::PathBuf;
use bellman::groth16;
use bls12_381::Bls12;
/// The timeout for each parameter file download, in seconds.
///
/// Zebra assumes that it's running on at least a 10 Mbps connection.
/// So the parameter files should download in about 15 minutes using `zebrad download`.
/// But `zebrad start` downloads blocks at the same time, so we allow some extra time.
pub const PARAMETER_DOWNLOAD_TIMEOUT: u64 = 60 * 60;
lazy_static::lazy_static! {
/// Groth16 Zero-Knowledge Proof parameters for the Sapling and Sprout circuits.
///
@ -20,10 +27,28 @@ lazy_static::lazy_static! {
}
/// Groth16 Zero-Knowledge Proof parameters for the Sapling and Sprout circuits.
#[non_exhaustive]
pub struct Groth16Parameters {
/// The Sapling circuit Groth16 parameters.
pub sapling: SaplingParameters,
/// The Sprout circuit Groth16 spend parameter.
pub sprout: SproutParameters,
}
/// Groth16 Zero-Knowledge Proof spend and output parameters for the Sapling circuit.
pub struct SaplingParameters {
pub spend: groth16::Parameters<Bls12>,
pub spend_prepared_verifying_key: groth16::PreparedVerifyingKey<Bls12>,
pub output: groth16::Parameters<Bls12>,
pub output_prepared_verifying_key: groth16::PreparedVerifyingKey<Bls12>,
}
/// Groth16 Zero-Knowledge Proof spend parameters for the Sprout circuit.
///
/// New Sprout outputs were disabled by the Canopy network upgrade.
pub struct SproutParameters {
pub spend_prepared_verifying_key: groth16::PreparedVerifyingKey<Bls12>,
}
impl Groth16Parameters {
@ -33,9 +58,64 @@ impl Groth16Parameters {
///
/// If the downloaded or pre-existing parameter files are invalid.
fn new() -> Groth16Parameters {
Groth16Parameters {
sapling: SaplingParameters::new(),
let params_directory = Groth16Parameters::directory();
let sapling_spend_path = params_directory.join(zcash_proofs::SAPLING_SPEND_NAME);
let sapling_output_path = params_directory.join(zcash_proofs::SAPLING_OUTPUT_NAME);
let sprout_path = params_directory.join(zcash_proofs::SPROUT_NAME);
// TODO: instead of the path check, add a zcash_proofs argument to skip hashing existing files
// (we check them on load anyway)
if !sapling_spend_path.exists() || !sapling_output_path.exists() {
tracing::info!("downloading Zcash Sapling parameters");
let new_sapling_paths =
zcash_proofs::download_sapling_parameters(Some(PARAMETER_DOWNLOAD_TIMEOUT))
.unwrap_or_else(|error| {
panic!(
"error downloading Sapling parameter files: {:?}. {}",
error,
Groth16Parameters::failure_hint()
)
});
assert_eq!(sapling_spend_path, new_sapling_paths.spend);
assert_eq!(sapling_output_path, new_sapling_paths.output);
}
if !sprout_path.exists() {
tracing::info!("downloading Zcash Sprout parameters");
let new_sprout_path =
zcash_proofs::download_sprout_parameters(Some(PARAMETER_DOWNLOAD_TIMEOUT))
.unwrap_or_else(|error| {
panic!(
"error downloading Sprout parameter files: {:?}. {}",
error,
Groth16Parameters::failure_hint()
)
});
assert_eq!(sprout_path, new_sprout_path);
}
// TODO: if loading fails, log a message including `failure_hint`
tracing::info!("checking and loading Zcash Sapling and Sprout parameters");
let parameters = zcash_proofs::load_parameters(
&sapling_spend_path,
&sapling_output_path,
Some(&sprout_path),
);
let sapling = SaplingParameters {
spend: parameters.spend_params,
spend_prepared_verifying_key: parameters.spend_vk,
output: parameters.output_params,
output_prepared_verifying_key: parameters.output_vk,
};
let sprout = SproutParameters {
spend_prepared_verifying_key: parameters
.sprout_vk
.expect("unreachable code: sprout loader panics on failure"),
};
Groth16Parameters { sapling, sprout }
}
/// Returns the path to the Groth16 parameters directory.
@ -51,52 +131,3 @@ impl Groth16Parameters {
)
}
}
/// Groth16 Zero-Knowledge Proof spend and output parameters for the Sapling circuit.
#[non_exhaustive]
pub struct SaplingParameters {
pub spend: groth16::Parameters<Bls12>,
pub spend_prepared_verifying_key: groth16::PreparedVerifyingKey<Bls12>,
pub output: groth16::Parameters<Bls12>,
pub output_prepared_verifying_key: groth16::PreparedVerifyingKey<Bls12>,
}
impl SaplingParameters {
/// Download if needed, cache, check, and load the Sapling Groth16 parameters.
///
/// # Panics
///
/// If the downloaded or pre-existing parameter files are invalid.
fn new() -> SaplingParameters {
// TODO: Sprout
let params_directory = Groth16Parameters::directory();
let spend_path = params_directory.join("sapling-spend.params");
let output_path = params_directory.join("sapling-output.params");
// Download parameters if needed.
//
// TODO: use try_exists when it stabilises, to exit early on permissions errors (#83186)
if !spend_path.exists() || !output_path.exists() {
tracing::info!("downloading Zcash Sapling parameters");
zcash_proofs::download_parameters().unwrap_or_else(|_| {
panic!(
"error downloading parameter files. {}",
Groth16Parameters::failure_hint()
)
});
}
// TODO: if loading fails, log a message including `failure_hint`
tracing::info!("checking and loading Zcash Sapling parameters");
let parameters = zcash_proofs::load_parameters(&spend_path, &output_path, None);
SaplingParameters {
spend: parameters.spend_params,
spend_prepared_verifying_key: parameters.spend_vk,
output: parameters.output_params,
output_prepared_verifying_key: parameters.output_vk,
}
}
}

View File

@ -19,6 +19,9 @@
//! * verifies blocks using zebra-chain, then stores verified blocks in zebra-state
//! * verifies mempool and block transactions using zebra-chain and zebra-script,
//! and returns verified mempool transactions for mempool storage
//! * Groth16 Parameters Download Task
//! * downloads the Sprout and Sapling Groth16 circuit parameter files
//! * finishes when the download is complete and the download file hashes have been checked
//! * Inbound Service
//! * handles requests from peers for network data, chain data, and mempool transactions
//! * spawns download and verify tasks for each gossiped block