diff --git a/zcash_proofs/src/sapling/verifier.rs b/zcash_proofs/src/sapling/verifier.rs index 3084a9501..b35bfcf0d 100644 --- a/zcash_proofs/src/sapling/verifier.rs +++ b/zcash_proofs/src/sapling/verifier.rs @@ -1,37 +1,34 @@ -use bellman::{ - gadgets::multipack, - groth16::{verify_proof, PreparedVerifyingKey, Proof}, -}; +use bellman::{gadgets::multipack, groth16::Proof}; use bls12_381::Bls12; use group::{Curve, GroupEncoding}; use zcash_primitives::{ - constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR}, sapling::redjubjub::{PublicKey, Signature}, transaction::components::Amount, }; use super::compute_value_balance; +mod single; +pub use single::SaplingVerificationContext; + /// A context object for verifying the Sapling components of a Zcash transaction. -pub struct SaplingVerificationContext { +struct SaplingVerificationContextInner { // (sum of the Spend value commitments) - (sum of the Output value commitments) cv_sum: jubjub::ExtendedPoint, - zip216_enabled: bool, } -impl SaplingVerificationContext { +impl SaplingVerificationContextInner { /// Construct a new context to be used with a single transaction. - pub fn new(zip216_enabled: bool) -> Self { - SaplingVerificationContext { + fn new() -> Self { + SaplingVerificationContextInner { cv_sum: jubjub::ExtendedPoint::identity(), - zip216_enabled, } } /// Perform consensus checks on a Sapling SpendDescription, while /// accumulating its value commitment inside the context for later use. #[allow(clippy::too_many_arguments)] - pub fn check_spend( + fn check_spend( &mut self, cv: jubjub::ExtendedPoint, anchor: bls12_381::Scalar, @@ -40,7 +37,8 @@ impl SaplingVerificationContext { sighash_value: &[u8; 32], spend_auth_sig: Signature, zkproof: Proof, - verifying_key: &PreparedVerifyingKey, + spend_auth_sig_verifier: impl FnOnce(PublicKey, [u8; 64], Signature) -> bool, + proof_verifier: impl FnOnce(Proof, [bls12_381::Scalar; 7]) -> bool, ) -> bool { if (cv.is_small_order() | rk.0.is_small_order()).into() { return false; @@ -58,19 +56,15 @@ impl SaplingVerificationContext { (&mut data_to_be_signed[32..64]).copy_from_slice(&sighash_value[..]); // Verify the spend_auth_sig - if !rk.verify_with_zip216( - &data_to_be_signed, - &spend_auth_sig, - SPENDING_KEY_GENERATOR, - self.zip216_enabled, - ) { + let rk_affine = rk.0.to_affine(); + if !spend_auth_sig_verifier(rk, data_to_be_signed, spend_auth_sig) { return false; } // Construct public input for circuit let mut public_input = [bls12_381::Scalar::zero(); 7]; { - let affine = rk.0.to_affine(); + let affine = rk_affine; let (u, v) = (affine.get_u(), affine.get_v()); public_input[0] = u; public_input[1] = v; @@ -95,18 +89,18 @@ impl SaplingVerificationContext { } // Verify the proof - verify_proof(verifying_key, &zkproof, &public_input[..]).is_ok() + proof_verifier(zkproof, public_input) } /// Perform consensus checks on a Sapling OutputDescription, while /// accumulating its value commitment inside the context for later use. - pub fn check_output( + fn check_output( &mut self, cv: jubjub::ExtendedPoint, cmu: bls12_381::Scalar, epk: jubjub::ExtendedPoint, zkproof: Proof, - verifying_key: &PreparedVerifyingKey, + proof_verifier: impl FnOnce(Proof, [bls12_381::Scalar; 5]) -> bool, ) -> bool { if (cv.is_small_order() | epk.is_small_order()).into() { return false; @@ -132,17 +126,18 @@ impl SaplingVerificationContext { public_input[4] = cmu; // Verify the proof - verify_proof(verifying_key, &zkproof, &public_input[..]).is_ok() + proof_verifier(zkproof, public_input) } /// 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( + fn final_check( &self, value_balance: Amount, sighash_value: &[u8; 32], binding_sig: Signature, + binding_sig_verifier: impl FnOnce(PublicKey, [u8; 64], Signature) -> bool, ) -> bool { // Obtain current cv_sum from the context let mut bvk = PublicKey(self.cv_sum); @@ -162,11 +157,6 @@ impl SaplingVerificationContext { (&mut data_to_be_signed[32..64]).copy_from_slice(&sighash_value[..]); // Verify the binding_sig - bvk.verify_with_zip216( - &data_to_be_signed, - &binding_sig, - VALUE_COMMITMENT_RANDOMNESS_GENERATOR, - self.zip216_enabled, - ) + binding_sig_verifier(bvk, data_to_be_signed, binding_sig) } } diff --git a/zcash_proofs/src/sapling/verifier/single.rs b/zcash_proofs/src/sapling/verifier/single.rs new file mode 100644 index 000000000..0d2f7cc6a --- /dev/null +++ b/zcash_proofs/src/sapling/verifier/single.rs @@ -0,0 +1,100 @@ +use bellman::groth16::{verify_proof, PreparedVerifyingKey, Proof}; +use bls12_381::Bls12; +use zcash_primitives::{ + constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR}, + sapling::redjubjub::{PublicKey, Signature}, + transaction::components::Amount, +}; + +use super::SaplingVerificationContextInner; + +/// A context object for verifying the Sapling components of a single Zcash transaction. +pub struct SaplingVerificationContext { + inner: SaplingVerificationContextInner, + zip216_enabled: bool, +} + +impl SaplingVerificationContext { + /// Construct a new context to be used with a single transaction. + pub fn new(zip216_enabled: bool) -> Self { + SaplingVerificationContext { + inner: SaplingVerificationContextInner::new(), + zip216_enabled, + } + } + + /// Perform consensus checks on a Sapling SpendDescription, while + /// accumulating its value commitment inside the context for later use. + #[allow(clippy::too_many_arguments)] + pub fn check_spend( + &mut self, + cv: jubjub::ExtendedPoint, + anchor: bls12_381::Scalar, + nullifier: &[u8; 32], + rk: PublicKey, + sighash_value: &[u8; 32], + spend_auth_sig: Signature, + zkproof: Proof, + verifying_key: &PreparedVerifyingKey, + ) -> bool { + let zip216_enabled = self.zip216_enabled; + self.inner.check_spend( + cv, + anchor, + nullifier, + rk, + sighash_value, + spend_auth_sig, + zkproof, + |rk, msg, spend_auth_sig| { + rk.verify_with_zip216( + &msg, + &spend_auth_sig, + SPENDING_KEY_GENERATOR, + zip216_enabled, + ) + }, + |proof, public_inputs| verify_proof(verifying_key, &proof, &public_inputs[..]).is_ok(), + ) + } + + /// 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: jubjub::ExtendedPoint, + cmu: bls12_381::Scalar, + epk: jubjub::ExtendedPoint, + zkproof: Proof, + verifying_key: &PreparedVerifyingKey, + ) -> bool { + self.inner + .check_output(cv, cmu, epk, zkproof, |proof, public_inputs| { + verify_proof(verifying_key, &proof, &public_inputs[..]).is_ok() + }) + } + + /// 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: Amount, + sighash_value: &[u8; 32], + binding_sig: Signature, + ) -> bool { + self.inner.final_check( + value_balance, + sighash_value, + binding_sig, + |bvk, msg, binding_sig| { + bvk.verify_with_zip216( + &msg, + &binding_sig, + VALUE_COMMITMENT_RANDOMNESS_GENERATOR, + self.zip216_enabled, + ) + }, + ) + } +}