Skip to content

Commit

Permalink
Make the Sapling parameters optional for documentation builds
Browse files Browse the repository at this point in the history
This is to allow our documentation to build on platforms like docs.rs,
which need to build the documentation for our crates, but don't have
access to the Sapling parameters.

This also slightly improves the experience for developers who use this
crate for the first time: if they use the crate without the Sapling
parameters, they'll now get a helpful warning instead of a panic.
  • Loading branch information
andiflabs committed Oct 31, 2024
1 parent 8350e0b commit 8d2664d
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 69 deletions.
19 changes: 16 additions & 3 deletions ironfish-rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,22 @@ fn verify_integrity(checksum_path: &Path, files_dir: &Path) {
let expected_hash = hex::decode(parts[0])
.unwrap_or_else(|_| panic!("{}: invalid syntax", checksum_path.display()));

let path = files_dir.join(parts[1]);
let mut file = fs::File::open(&path)
.unwrap_or_else(|err| panic!("failed to open {}: {}", path.display(), err));
let file_name = parts[1];
let path = files_dir.join(file_name);
let mut file = match fs::File::open(&path) {
Ok(file) => file,
Err(err) if err.kind() == io::ErrorKind::NotFound => {
println!(
"cargo:warning=could not find `{file_name}`. \
Consider enabling the `download-params` feature \
so that this file can be downloaded and verified \
automatically"
);
continue;
}
Err(err) => panic!("failed to open {}: {}", path.display(), err),
};

let mut hasher = Sha512::new();
io::copy(&mut file, &mut hasher)
.unwrap_or_else(|err| panic!("failed to read {}: {}", path.display(), err));
Expand Down
71 changes: 5 additions & 66 deletions ironfish-rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use blstrs::Bls12;
use ironfish_bellperson::groth16;

#[cfg(feature = "transaction-proofs")]
mod sapling;

pub mod assets;
pub mod errors;
Expand Down Expand Up @@ -39,69 +40,7 @@ pub use {

#[cfg(feature = "benchmark")]
pub use ironfish_zkp::primitives::ValueCommitment;

#[cfg(feature = "transaction-proofs")]
pub use transaction::ProposedTransaction;

// The main entry-point to the sapling API. Construct this with loaded parameters, and then call
// methods on it to do the actual work.
//
// spend and output are two arithmetic circuits for use in zksnark calculations provided by bellperson.
// Though the *_params have a verifying key on them, they are not the prepared verifying keys,
// so we store the prepared keys separately at the time of loading the params.
//
// The values are all loaded from a file in serialized form.
pub struct Sapling {
pub spend_params: groth16::Parameters<Bls12>,
pub output_params: groth16::Parameters<Bls12>,
pub mint_params: groth16::Parameters<Bls12>,
pub spend_verifying_key: groth16::PreparedVerifyingKey<Bls12>,
pub output_verifying_key: groth16::PreparedVerifyingKey<Bls12>,
pub mint_verifying_key: groth16::PreparedVerifyingKey<Bls12>,
}

pub use sapling::Sapling;
#[cfg(feature = "transaction-proofs")]
impl Sapling {
/// Initialize a Sapling instance and prepare for proving. Load the parameters from files
/// at a known location (`$OUT_DIR/sapling_params`).
pub fn load() -> Self {
let spend_bytes = include_bytes!(concat!(
env!("OUT_DIR"),
"/sapling_params/sapling-spend.params"
));
let output_bytes = include_bytes!(concat!(
env!("OUT_DIR"),
"/sapling_params/sapling-output.params"
));
let mint_bytes = include_bytes!(concat!(
env!("OUT_DIR"),
"/sapling_params/sapling-mint.params"
));

let spend_params = Sapling::load_params(&spend_bytes[..]);
let output_params = Sapling::load_params(&output_bytes[..]);
let mint_params = Sapling::load_params(&mint_bytes[..]);

let spend_vk = groth16::prepare_verifying_key(&spend_params.vk);
let output_vk = groth16::prepare_verifying_key(&output_params.vk);
let mint_vk = groth16::prepare_verifying_key(&mint_params.vk);

Sapling {
spend_verifying_key: spend_vk,
output_verifying_key: output_vk,
mint_verifying_key: mint_vk,
spend_params,
output_params,
mint_params,
}
}

/// Load sapling parameters from a provided filename. The parameters are huge and take a
/// couple seconds to load. They primarily contain the "toxic waste" for a specific sapling
/// curve.
///
/// NOTE: If this is stupidly slow for you, try compiling in --release mode
fn load_params(bytes: &[u8]) -> groth16::Parameters<Bls12> {
groth16::Parameters::read(bytes, false).unwrap()
}
}
pub use transaction::ProposedTransaction;
76 changes: 76 additions & 0 deletions ironfish-rust/src/sapling.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use blstrs::Bls12;
use ironfish_bellperson::groth16;

#[cfg(not(doc))]
macro_rules! include_params {
( $name:literal ) => {
include_bytes!(concat!(env!("OUT_DIR"), "/sapling_params/", $name))
};
}

// When building documentation (especially on docs.rs), it's quite possible that the parameter
// files won't be available, so don't even attempt to include them. This will also speed up
// documentation builds.
#[cfg(doc)]
macro_rules! include_params {
( $name:literal ) => {
b""
};
}

static SAPLING_SPEND_PARAMS: &[u8] = include_params!("sapling-spend.params");
static SAPLING_OUTPUT_PARAMS: &[u8] = include_params!("sapling-output.params");
static SAPLING_MINT_PARAMS: &[u8] = include_params!("sapling-mint.params");

// The main entry-point to the sapling API. Construct this with loaded parameters, and then call
// methods on it to do the actual work.
//
// spend and output are two arithmetic circuits for use in zksnark calculations provided by bellperson.
// Though the *_params have a verifying key on them, they are not the prepared verifying keys,
// so we store the prepared keys separately at the time of loading the params.
//
// The values are all loaded from a file in serialized form.
pub struct Sapling {
pub spend_params: groth16::Parameters<Bls12>,
pub output_params: groth16::Parameters<Bls12>,
pub mint_params: groth16::Parameters<Bls12>,
pub spend_verifying_key: groth16::PreparedVerifyingKey<Bls12>,
pub output_verifying_key: groth16::PreparedVerifyingKey<Bls12>,
pub mint_verifying_key: groth16::PreparedVerifyingKey<Bls12>,
}

impl Sapling {
/// Initialize a Sapling instance and prepare for proving. Load the parameters from files
/// at a known location (`$OUT_DIR/sapling_params`).
pub fn load() -> Self {
let spend_params = Sapling::load_params(SAPLING_SPEND_PARAMS);
let output_params = Sapling::load_params(SAPLING_OUTPUT_PARAMS);
let mint_params = Sapling::load_params(SAPLING_MINT_PARAMS);

let spend_verifying_key = groth16::prepare_verifying_key(&spend_params.vk);
let output_verifying_key = groth16::prepare_verifying_key(&output_params.vk);
let mint_verifying_key = groth16::prepare_verifying_key(&mint_params.vk);

Sapling {
spend_verifying_key,
output_verifying_key,
mint_verifying_key,
spend_params,
output_params,
mint_params,
}
}

/// Load sapling parameters from a provided filename. The parameters are huge and take a
/// couple seconds to load. They primarily contain the "toxic waste" for a specific sapling
/// curve.
///
/// NOTE: If this is stupidly slow for you, try compiling in --release mode
fn load_params(bytes: &[u8]) -> groth16::Parameters<Bls12> {
groth16::Parameters::read(bytes, false).unwrap()
}
}

0 comments on commit 8d2664d

Please sign in to comment.