diff --git a/Cargo.lock b/Cargo.lock index cc86c77..7b27f92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -344,6 +344,7 @@ dependencies = [ "pairing 0.14.2", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "sapling-crypto 0.0.1", + "zcash_proofs 0.0.0", "zip32 0.0.0", ] @@ -716,6 +717,11 @@ dependencies = [ [[package]] name = "zcash_proofs" version = "0.0.0" +dependencies = [ + "bellman 0.1.0", + "pairing 0.14.2", + "sapling-crypto 0.0.1", +] [[package]] name = "zcash_wallet" diff --git a/librustzcash/Cargo.toml b/librustzcash/Cargo.toml index b9b4334..bb42b6c 100644 --- a/librustzcash/Cargo.toml +++ b/librustzcash/Cargo.toml @@ -21,6 +21,7 @@ lazy_static = "1" byteorder = "1" rand = "0.4" sapling-crypto = { path = "../sapling-crypto" } +zcash_proofs = { path = "../zcash_proofs" } zip32 = { path = "../zip32" } [dependencies.blake2-rfc] diff --git a/librustzcash/src/rustzcash.rs b/librustzcash/src/rustzcash.rs index c126b28..1909afa 100644 --- a/librustzcash/src/rustzcash.rs +++ b/librustzcash/src/rustzcash.rs @@ -5,6 +5,7 @@ extern crate libc; extern crate pairing; extern crate rand; extern crate sapling_crypto; +extern crate zcash_proofs; extern crate zip32; mod hashreader; @@ -62,6 +63,7 @@ use std::ffi::OsString; use std::os::windows::ffi::OsStringExt; use sapling_crypto::primitives::{ProofGenerationKey, ValueCommitment, ViewingKey}; +use zcash_proofs::sapling::{compute_value_balance, SaplingVerificationContext}; pub mod equihash; @@ -80,10 +82,6 @@ static mut SAPLING_SPEND_PARAMS: Option> = None; static mut SAPLING_OUTPUT_PARAMS: Option> = None; static mut SPROUT_GROTH16_PARAMS_PATH: Option = None; -fn is_small_order(p: &edwards::Point) -> bool { - p.double(&JUBJUB).double(&JUBJUB).double(&JUBJUB) == edwards::Point::zero() -} - /// Writes an FrRepr to [u8] of length 32 fn write_le(f: FrRepr, to: &mut [u8]) { assert_eq!(to.len(), 32); @@ -648,16 +646,10 @@ pub extern "system" fn librustzcash_eh_isvalid( equihash::is_valid_solution(n, k, rs_input, rs_nonce, rs_soln) } -pub struct SaplingVerificationContext { - bvk: edwards::Point, -} - #[no_mangle] pub extern "system" fn librustzcash_sapling_verification_ctx_init( ) -> *mut SaplingVerificationContext { - let ctx = Box::new(SaplingVerificationContext { - bvk: edwards::Point::zero(), - }); + let ctx = Box::new(SaplingVerificationContext::new()); Box::into_raw(ctx) } @@ -715,79 +707,17 @@ pub extern "system" fn librustzcash_sapling_check_spend( Err(_) => return false, }; - if is_small_order(&cv) { - return false; - } - - if is_small_order(&rk.0) { - return false; - } - - // Accumulate the value commitment in the context - { - let mut tmp = cv.clone(); - tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB); - - // Update the context - unsafe { &mut *ctx }.bvk = tmp; - } - - // Grab the nullifier as a sequence of bytes - let nullifier = &unsafe { &*nullifier }[..]; - - // Compute the signature's message for rk/spend_auth_sig - let mut data_to_be_signed = [0u8; 64]; - rk.0.write(&mut data_to_be_signed[0..32]) - .expect("message buffer should be 32 bytes"); - (&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash_value })[..]); - - // Verify the spend_auth_sig - if !rk.verify( - &data_to_be_signed, - &spend_auth_sig, - FixedGenerators::SpendingKeyGenerator, - &JUBJUB, - ) { - return false; - } - - // Construct public input for circuit - let mut public_input = [Fr::zero(); 7]; - { - let (x, y) = rk.0.into_xy(); - public_input[0] = x; - public_input[1] = y; - } - { - let (x, y) = cv.into_xy(); - public_input[2] = x; - public_input[3] = y; - } - public_input[4] = anchor; - - // Add the nullifier through multiscalar packing - { - let nullifier = multipack::bytes_to_bits_le(nullifier); - let nullifier = multipack::compute_multipacking::(&nullifier); - - assert_eq!(nullifier.len(), 2); - - public_input[5] = nullifier[0]; - public_input[6] = nullifier[1]; - } - - // Verify the proof - match verify_proof( + unsafe { &mut *ctx }.check_spend( + cv, + anchor, + unsafe { &*nullifier }, + rk, + unsafe { &*sighash_value }, + spend_auth_sig, + zkproof, unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(), - &zkproof, - &public_input[..], - ) { - // No error, and proof verification successful - Ok(true) => true, - - // Any other case - _ => false, - } + &JUBJUB, + ) } #[no_mangle] @@ -823,76 +753,14 @@ pub extern "system" fn librustzcash_sapling_check_output( Err(_) => return false, }; - if is_small_order(&cv) { - return false; - } - - if is_small_order(&epk) { - return false; - } - - // Accumulate the value commitment in the context - { - let mut tmp = cv.clone(); - tmp = tmp.negate(); // Outputs subtract from the total. - tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB); - - // Update the context - unsafe { &mut *ctx }.bvk = tmp; - } - - // Construct public input for circuit - let mut public_input = [Fr::zero(); 5]; - { - let (x, y) = cv.into_xy(); - public_input[0] = x; - public_input[1] = y; - } - { - let (x, y) = epk.into_xy(); - public_input[2] = x; - public_input[3] = y; - } - public_input[4] = cm; - - // Verify the proof - match verify_proof( + unsafe { &mut *ctx }.check_output( + cv, + cm, + epk, + zkproof, unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap(), - &zkproof, - &public_input[..], - ) { - // No error, and proof verification successful - Ok(true) => true, - - // Any other case - _ => false, - } -} - -// This function computes `value` in the exponent of the value commitment base -fn compute_value_balance(value: int64_t) -> Option> { - // Compute the absolute value (failing if -i64::MAX is - // the value) - let abs = match value.checked_abs() { - Some(a) => a as u64, - None => return None, - }; - - // Is it negative? We'll have to negate later if so. - let is_negative = value.is_negative(); - - // Compute it in the exponent - let mut value_balance = JUBJUB - .generator(FixedGenerators::ValueCommitmentValue) - .mul(FsRepr::from(abs), &JUBJUB); - - // Negate if necessary - if is_negative { - value_balance = value_balance.negate(); - } - - // Convert to unknown order point - Some(value_balance.into()) + &JUBJUB, + ) } #[no_mangle] @@ -908,37 +776,12 @@ pub extern "system" fn librustzcash_sapling_final_check( Err(_) => return false, }; - // Obtain current bvk from the context - let mut bvk = redjubjub::PublicKey(unsafe { &*ctx }.bvk.clone()); - - // Compute value balance - let mut value_balance = match compute_value_balance(value_balance) { - Some(a) => a, - None => return false, - }; - - // Subtract value_balance from current bvk to get final bvk - value_balance = value_balance.negate(); - bvk.0 = bvk.0.add(&value_balance, &JUBJUB); - - // Compute the signature's message for bvk/binding_sig - let mut data_to_be_signed = [0u8; 64]; - bvk.0 - .write(&mut data_to_be_signed[0..32]) - .expect("bvk is 32 bytes"); - (&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash_value })[..]); - - // Verify the binding_sig - if !bvk.verify( - &data_to_be_signed, - &binding_sig, - FixedGenerators::ValueCommitmentRandomness, + unsafe { &*ctx }.final_check( + value_balance, + unsafe { &*sighash_value }, + binding_sig, &JUBJUB, - ) { - return false; - } - - true + ) } #[no_mangle] @@ -1324,7 +1167,7 @@ pub extern "system" fn librustzcash_sapling_binding_sig( // against our derived bvk. { // Compute value balance - let mut value_balance = match compute_value_balance(value_balance) { + let mut value_balance = match compute_value_balance(value_balance, &JUBJUB) { Some(a) => a, None => return false, }; diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 353bff8..91cba88 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -6,3 +6,6 @@ authors = [ ] [dependencies] +bellman = { path = "../bellman" } +pairing = { path = "../pairing" } +sapling-crypto = { path = "../sapling-crypto" } diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index 31e1bb2..6e2af65 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -1,7 +1,5 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} +extern crate bellman; +extern crate pairing; +extern crate sapling_crypto; + +pub mod sapling; diff --git a/zcash_proofs/src/sapling/mod.rs b/zcash_proofs/src/sapling/mod.rs new file mode 100644 index 0000000..87b5fed --- /dev/null +++ b/zcash_proofs/src/sapling/mod.rs @@ -0,0 +1,37 @@ +use pairing::bls12_381::Bls12; +use sapling_crypto::jubjub::{ + edwards, fs::FsRepr, FixedGenerators, JubjubBls12, JubjubParams, Unknown, +}; + +mod verifier; + +pub use self::verifier::SaplingVerificationContext; + +// This function computes `value` in the exponent of the value commitment base +pub fn compute_value_balance( + value: i64, + params: &JubjubBls12, +) -> Option> { + // Compute the absolute value (failing if -i64::MAX is + // the value) + let abs = match value.checked_abs() { + Some(a) => a as u64, + None => return None, + }; + + // Is it negative? We'll have to negate later if so. + let is_negative = value.is_negative(); + + // Compute it in the exponent + let mut value_balance = params + .generator(FixedGenerators::ValueCommitmentValue) + .mul(FsRepr::from(abs), params); + + // Negate if necessary + if is_negative { + value_balance = value_balance.negate(); + } + + // Convert to unknown order point + Some(value_balance.into()) +} diff --git a/zcash_proofs/src/sapling/verifier.rs b/zcash_proofs/src/sapling/verifier.rs new file mode 100644 index 0000000..e9a5f2f --- /dev/null +++ b/zcash_proofs/src/sapling/verifier.rs @@ -0,0 +1,207 @@ +use bellman::groth16::{verify_proof, PreparedVerifyingKey, Proof}; +use pairing::{ + bls12_381::{Bls12, Fr}, + Field, +}; +use sapling_crypto::{ + circuit::multipack, + jubjub::{edwards, FixedGenerators, JubjubBls12, Unknown}, + redjubjub::{PublicKey, Signature}, +}; + +use super::compute_value_balance; + +fn is_small_order(p: &edwards::Point, params: &JubjubBls12) -> bool { + p.double(params).double(params).double(params) == edwards::Point::zero() +} + +/// A context object for verifying the Sapling components of a Zcash transaction. +pub struct SaplingVerificationContext { + bvk: edwards::Point, +} + +impl SaplingVerificationContext { + /// Construct a new context to be used with a single transaction. + pub fn new() -> Self { + SaplingVerificationContext { + bvk: edwards::Point::zero(), + } + } + + /// Perform consensus checks on a Sapling SpendDescription, while + /// accumulating its value commitment inside the context for later use. + pub fn check_spend( + &mut self, + cv: edwards::Point, + anchor: Fr, + nullifier: &[u8; 32], + rk: PublicKey, + sighash_value: &[u8; 32], + spend_auth_sig: Signature, + zkproof: Proof, + verifying_key: &PreparedVerifyingKey, + params: &JubjubBls12, + ) -> bool { + if is_small_order(&cv, params) { + return false; + } + + if is_small_order(&rk.0, params) { + return false; + } + + // Accumulate the value commitment in the context + { + let mut tmp = cv.clone(); + tmp = tmp.add(&self.bvk, params); + + // Update the context + self.bvk = tmp; + } + + // Grab the nullifier as a sequence of bytes + let nullifier = &nullifier[..]; + + // Compute the signature's message for rk/spend_auth_sig + let mut data_to_be_signed = [0u8; 64]; + rk.0.write(&mut data_to_be_signed[0..32]) + .expect("message buffer should be 32 bytes"); + (&mut data_to_be_signed[32..64]).copy_from_slice(&sighash_value[..]); + + // Verify the spend_auth_sig + if !rk.verify( + &data_to_be_signed, + &spend_auth_sig, + FixedGenerators::SpendingKeyGenerator, + params, + ) { + return false; + } + + // Construct public input for circuit + let mut public_input = [Fr::zero(); 7]; + { + let (x, y) = rk.0.into_xy(); + public_input[0] = x; + public_input[1] = y; + } + { + let (x, y) = cv.into_xy(); + public_input[2] = x; + public_input[3] = y; + } + public_input[4] = anchor; + + // Add the nullifier through multiscalar packing + { + let nullifier = multipack::bytes_to_bits_le(nullifier); + let nullifier = multipack::compute_multipacking::(&nullifier); + + assert_eq!(nullifier.len(), 2); + + public_input[5] = nullifier[0]; + public_input[6] = nullifier[1]; + } + + // Verify the proof + match verify_proof(verifying_key, &zkproof, &public_input[..]) { + // No error, and proof verification successful + Ok(true) => true, + + // Any other case + _ => false, + } + } + + /// Perform consensus checks on a Sapling OutputDescription, while + /// accumulating its value commitment inside the context for later use. + pub fn check_output( + &mut self, + cv: edwards::Point, + cm: Fr, + epk: edwards::Point, + zkproof: Proof, + verifying_key: &PreparedVerifyingKey, + params: &JubjubBls12, + ) -> bool { + if is_small_order(&cv, params) { + return false; + } + + if is_small_order(&epk, params) { + return false; + } + + // Accumulate the value commitment in the context + { + let mut tmp = cv.clone(); + tmp = tmp.negate(); // Outputs subtract from the total. + tmp = tmp.add(&self.bvk, params); + + // Update the context + self.bvk = tmp; + } + + // Construct public input for circuit + let mut public_input = [Fr::zero(); 5]; + { + let (x, y) = cv.into_xy(); + public_input[0] = x; + public_input[1] = y; + } + { + let (x, y) = epk.into_xy(); + public_input[2] = x; + public_input[3] = y; + } + public_input[4] = cm; + + // Verify the proof + match verify_proof(verifying_key, &zkproof, &public_input[..]) { + // No error, and proof verification successful + Ok(true) => true, + + // Any other case + _ => false, + } + } + + /// Perform consensus checks on the valueBalance and bindingSig parts of a + /// Sapling transaction. All SpendDescriptions and OutputDescriptions must + /// have been checked before calling this function. + pub fn final_check( + &self, + value_balance: i64, + sighash_value: &[u8; 32], + binding_sig: Signature, + params: &JubjubBls12, + ) -> bool { + // Obtain current bvk from the context + let mut bvk = PublicKey(self.bvk.clone()); + + // Compute value balance + let mut value_balance = match compute_value_balance(value_balance, params) { + Some(a) => a, + None => return false, + }; + + // Subtract value_balance from current bvk to get final bvk + value_balance = value_balance.negate(); + bvk.0 = bvk.0.add(&value_balance, params); + + // Compute the signature's message for bvk/binding_sig + let mut data_to_be_signed = [0u8; 64]; + bvk.0 + .write(&mut data_to_be_signed[0..32]) + .expect("bvk is 32 bytes"); + (&mut data_to_be_signed[32..64]).copy_from_slice(&sighash_value[..]); + + // Verify the binding_sig + bvk.verify( + &data_to_be_signed, + &binding_sig, + FixedGenerators::ValueCommitmentRandomness, + params, + ) + } +}