zcash_proofs: Split out internals of `SaplingVerificationContext`

The new `SaplingVerificationContextInner` struct handles accumulation of
`cv`, and preparation of the inputs to proof and signature verification.
`SaplingVerificationContext` uses it to maintain its existing inline
unbatched verification API.
This commit is contained in:
Jack Grigg 2022-07-03 22:51:27 +00:00
parent 739cfca68a
commit 9d72e87125
2 changed files with 121 additions and 31 deletions

View File

@ -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<Bls12>,
verifying_key: &PreparedVerifyingKey<Bls12>,
spend_auth_sig_verifier: impl FnOnce(PublicKey, [u8; 64], Signature) -> bool,
proof_verifier: impl FnOnce(Proof<Bls12>, [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<Bls12>,
verifying_key: &PreparedVerifyingKey<Bls12>,
proof_verifier: impl FnOnce(Proof<Bls12>, [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)
}
}

View File

@ -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<Bls12>,
verifying_key: &PreparedVerifyingKey<Bls12>,
) -> 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<Bls12>,
verifying_key: &PreparedVerifyingKey<Bls12>,
) -> 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,
)
},
)
}
}