diff --git a/Cargo.lock b/Cargo.lock index fac19e33d..a9a4f97df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,6 +185,15 @@ dependencies = [ "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "directories" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fake-simd" version = "0.1.2" @@ -531,6 +540,25 @@ name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "zcash_client_backend" version = "0.0.0" @@ -569,6 +597,7 @@ dependencies = [ "bellman 0.1.0", "blake2b_simd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "ff 0.4.0", "pairing 0.14.2", "rand_os 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -600,6 +629,7 @@ dependencies = [ "checksum crypto_api_chachapoly 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9ee35dbace0831b5fe7cb9b43eb029aa14a10f594a115025d4628a2baa63ab" "checksum digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603" "checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" +"checksum directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72d337a64190607d4fcca2cb78982c5dd57f4916e19696b48a575fa746b6cb0f" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fpe 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce3371c82bfbd984f624cab093f55e7336f5a6e589f8518e1258f54f011b89ad" "checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c" @@ -636,3 +666,6 @@ dependencies = [ "checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index 90d69e65a..797b9b6ff 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -20,6 +20,7 @@ pub mod block; pub mod keys; pub mod merkle_tree; pub mod note_encryption; +pub mod prover; pub mod sapling; mod serialize; pub mod transaction; diff --git a/zcash_primitives/src/prover.rs b/zcash_primitives/src/prover.rs new file mode 100644 index 000000000..87a76c7f8 --- /dev/null +++ b/zcash_primitives/src/prover.rs @@ -0,0 +1,162 @@ +//! Abstractions over the proving system and parameters. + +use pairing::bls12_381::{Bls12, Fr}; +use sapling_crypto::{ + jubjub::{edwards, fs::Fs, Unknown}, + primitives::{Diversifier, PaymentAddress, ProofGenerationKey}, + redjubjub::{PublicKey, Signature}, +}; + +use crate::{ + merkle_tree::CommitmentTreeWitness, sapling::Node, transaction::components::GROTH_PROOF_SIZE, +}; + +/// Interface for creating zero-knowledge proofs for shielded transactions. +pub trait TxProver { + /// Type for persisting any necessary context across multiple Sapling proofs. + type SaplingProvingContext; + + /// Instantiate a new Sapling proving context. + fn new_sapling_proving_context(&self) -> Self::SaplingProvingContext; + + /// Create the value commitment, re-randomized key, and proof for a Sapling + /// [`SpendDescription`], while accumulating its value commitment randomness inside + /// the context for later use. + /// + /// [`SpendDescription`]: crate::transaction::components::SpendDescription + fn spend_proof( + &self, + ctx: &mut Self::SaplingProvingContext, + proof_generation_key: ProofGenerationKey, + diversifier: Diversifier, + rcm: Fs, + ar: Fs, + value: u64, + anchor: Fr, + witness: CommitmentTreeWitness, + ) -> Result< + ( + [u8; GROTH_PROOF_SIZE], + edwards::Point, + PublicKey, + ), + (), + >; + + /// Create the value commitment and proof for a Sapling [`OutputDescription`], + /// while accumulating its value commitment randomness inside the context for later + /// use. + /// + /// [`OutputDescription`]: crate::transaction::components::OutputDescription + fn output_proof( + &self, + ctx: &mut Self::SaplingProvingContext, + esk: Fs, + payment_address: PaymentAddress, + rcm: Fs, + value: u64, + ) -> ([u8; GROTH_PROOF_SIZE], edwards::Point); + + /// Create the `bindingSig` for a Sapling transaction. All calls to + /// [`TxProver::spend_proof`] and [`TxProver::output_proof`] must be completed before + /// calling this function. + fn binding_sig( + &self, + ctx: &mut Self::SaplingProvingContext, + value_balance: i64, + sighash: &[u8; 32], + ) -> Result; +} + +#[cfg(test)] +pub(crate) mod mock { + use ff::Field; + use pairing::bls12_381::{Bls12, Fr}; + use rand_os::OsRng; + use sapling_crypto::{ + jubjub::{edwards, fs::Fs, FixedGenerators, Unknown}, + primitives::{Diversifier, PaymentAddress, ProofGenerationKey, ValueCommitment}, + redjubjub::{PublicKey, Signature}, + }; + + use crate::{ + merkle_tree::CommitmentTreeWitness, sapling::Node, + transaction::components::GROTH_PROOF_SIZE, JUBJUB, + }; + + use super::TxProver; + + pub(crate) struct MockTxProver; + + #[cfg(test)] + impl TxProver for MockTxProver { + type SaplingProvingContext = (); + + fn new_sapling_proving_context(&self) -> Self::SaplingProvingContext {} + + fn spend_proof( + &self, + _ctx: &mut Self::SaplingProvingContext, + proof_generation_key: ProofGenerationKey, + _diversifier: Diversifier, + _rcm: Fs, + ar: Fs, + value: u64, + _anchor: Fr, + _witness: CommitmentTreeWitness, + ) -> Result< + ( + [u8; GROTH_PROOF_SIZE], + edwards::Point, + PublicKey, + ), + (), + > { + let mut rng = OsRng; + + let cv = ValueCommitment:: { + value, + randomness: Fs::random(&mut rng), + } + .cm(&JUBJUB) + .into(); + + let rk = PublicKey::(proof_generation_key.ak.clone().into()).randomize( + ar, + FixedGenerators::SpendingKeyGenerator, + &JUBJUB, + ); + + Ok(([0u8; GROTH_PROOF_SIZE], cv, rk)) + } + + fn output_proof( + &self, + _ctx: &mut Self::SaplingProvingContext, + _esk: Fs, + _payment_address: PaymentAddress, + _rcm: Fs, + value: u64, + ) -> ([u8; GROTH_PROOF_SIZE], edwards::Point) { + let mut rng = OsRng; + + let cv = ValueCommitment:: { + value, + randomness: Fs::random(&mut rng), + } + .cm(&JUBJUB) + .into(); + + ([0u8; GROTH_PROOF_SIZE], cv) + } + + fn binding_sig( + &self, + _ctx: &mut Self::SaplingProvingContext, + _value_balance: i64, + _sighash: &[u8; 32], + ) -> Result { + Err(()) + } + } +} diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index b8153f310..141d0ff38 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -11,7 +11,7 @@ use serialize::Vector; use JUBJUB; // π_A + π_B + π_C -const GROTH_PROOF_SIZE: usize = (48 + 96 + 48); +pub const GROTH_PROOF_SIZE: usize = (48 + 96 + 48); // π_A + π_A' + π_B + π_B' + π_C + π_C' + π_K + π_H const PHGR_PROOF_SIZE: usize = (33 + 33 + 65 + 33 + 33 + 33 + 33 + 33); diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 1eca6fdd5..9e989fa1d 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -9,6 +9,7 @@ authors = [ bellman = { path = "../bellman" } blake2b_simd = "0.5" byteorder = "1" +directories = "1" ff = { path = "../ff" } pairing = { path = "../pairing" } rand_os = "0.2" diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index dd6975c0c..26aa2f16b 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -1,6 +1,7 @@ extern crate bellman; extern crate blake2b_simd; extern crate byteorder; +extern crate directories; extern crate ff; extern crate pairing; extern crate rand_os; @@ -14,6 +15,7 @@ use std::io::{self, BufReader}; use std::path::Path; mod hashreader; +pub mod prover; pub mod sapling; pub fn load_parameters( diff --git a/zcash_proofs/src/prover.rs b/zcash_proofs/src/prover.rs new file mode 100644 index 000000000..005d7eb94 --- /dev/null +++ b/zcash_proofs/src/prover.rs @@ -0,0 +1,190 @@ +//! Abstractions over the proving system and parameters for ease of use. + +use bellman::groth16::{Parameters, PreparedVerifyingKey}; +use directories::BaseDirs; +use pairing::bls12_381::{Bls12, Fr}; +use sapling_crypto::{ + jubjub::{edwards, fs::Fs, Unknown}, + primitives::{Diversifier, PaymentAddress, ProofGenerationKey}, + redjubjub::{PublicKey, Signature}, +}; +use std::path::Path; +use zcash_primitives::{ + merkle_tree::CommitmentTreeWitness, prover::TxProver, sapling::Node, + transaction::components::GROTH_PROOF_SIZE, JUBJUB, +}; + +use crate::{load_parameters, sapling::SaplingProvingContext}; + +const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c"; +const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028"; + +/// An implementation of [`TxProver`] using Sapling Spend and Output parameters from +/// locally-accessible paths. +pub struct LocalTxProver { + spend_params: Parameters, + spend_vk: PreparedVerifyingKey, + output_params: Parameters, +} + +impl LocalTxProver { + /// Creates a `LocalTxProver` using parameters from the given local paths. + /// + /// # Examples + /// + /// ```should_panic + /// use std::path::Path; + /// use zcash_proofs::prover::LocalTxProver; + /// + /// let tx_prover = LocalTxProver::new( + /// Path::new("/path/to/sapling-spend.params"), + /// Path::new("/path/to/sapling-output.params"), + /// ); + /// ``` + /// + /// # Panics + /// + /// This function will panic if the paths do not point to valid parameter files with + /// the expected hashes. + pub fn new(spend_path: &Path, output_path: &Path) -> Self { + let (spend_params, spend_vk, output_params, _, _) = load_parameters( + spend_path, + SAPLING_SPEND_HASH, + output_path, + SAPLING_OUTPUT_HASH, + None, + None, + ); + LocalTxProver { + spend_params, + spend_vk, + output_params, + } + } + + /// Attempts to create a `LocalTxProver` using parameters from the default local + /// location. + /// + /// Returns `None` if any of the parameters cannot be found in the default local + /// location. + /// + /// # Examples + /// + /// ``` + /// use zcash_proofs::prover::LocalTxProver; + /// + /// match LocalTxProver::with_default_location() { + /// Some(tx_prover) => (), + /// None => println!("Please run zcash-fetch-params or fetch-params.sh to download the parameters."), + /// } + /// ``` + /// + /// # Panics + /// + /// This function will panic if the parameters in the default local location do not + /// have the expected hashes. + pub fn with_default_location() -> Option { + let base_dirs = BaseDirs::new()?; + let unix_params_dir = base_dirs.home_dir().join(".zcash-params"); + let win_osx_params_dir = base_dirs.data_dir().join("ZcashParams"); + let (spend_path, output_path) = if unix_params_dir.exists() { + ( + unix_params_dir.join("sapling-spend.params"), + unix_params_dir.join("sapling-output.params"), + ) + } else if win_osx_params_dir.exists() { + ( + win_osx_params_dir.join("sapling-spend.params"), + win_osx_params_dir.join("sapling-output.params"), + ) + } else { + return None; + }; + if !(spend_path.exists() && output_path.exists()) { + return None; + } + + Some(LocalTxProver::new(&spend_path, &output_path)) + } +} + +impl TxProver for LocalTxProver { + type SaplingProvingContext = SaplingProvingContext; + + fn new_sapling_proving_context(&self) -> Self::SaplingProvingContext { + SaplingProvingContext::new() + } + + fn spend_proof( + &self, + ctx: &mut Self::SaplingProvingContext, + proof_generation_key: ProofGenerationKey, + diversifier: Diversifier, + rcm: Fs, + ar: Fs, + value: u64, + anchor: Fr, + witness: CommitmentTreeWitness, + ) -> Result< + ( + [u8; GROTH_PROOF_SIZE], + edwards::Point, + PublicKey, + ), + (), + > { + let (proof, cv, rk) = ctx.spend_proof( + proof_generation_key, + diversifier, + rcm, + ar, + value, + anchor, + witness, + &self.spend_params, + &self.spend_vk, + &JUBJUB, + )?; + + let mut zkproof = [0u8; GROTH_PROOF_SIZE]; + proof + .write(&mut zkproof[..]) + .expect("should be able to serialize a proof"); + + Ok((zkproof, cv, rk)) + } + + fn output_proof( + &self, + ctx: &mut Self::SaplingProvingContext, + esk: Fs, + payment_address: PaymentAddress, + rcm: Fs, + value: u64, + ) -> ([u8; GROTH_PROOF_SIZE], edwards::Point) { + let (proof, cv) = ctx.output_proof( + esk, + payment_address, + rcm, + value, + &self.output_params, + &JUBJUB, + ); + + let mut zkproof = [0u8; GROTH_PROOF_SIZE]; + proof + .write(&mut zkproof[..]) + .expect("should be able to serialize a proof"); + + (zkproof, cv) + } + + fn binding_sig( + &self, + ctx: &mut Self::SaplingProvingContext, + value_balance: i64, + sighash: &[u8; 32], + ) -> Result { + ctx.binding_sig(value_balance, sighash, &JUBJUB) + } +}