#[cfg(feature = "benchmarks")]
use bellman::groth16::prepare_verifying_key;
use bellman::groth16::{PreparedVerifyingKey, Proof};
use bellman::{SynthesisError, groth16};
use bls12_381::Bls12;
use group::GroupEncoding;
use masp_primitives::transaction::components::sapling::{Authorized, Bundle};
use pairing::Engine;
use rand_core::{CryptoRng, RngCore};
use super::SaplingVerificationContextInner;
#[derive(Default, Clone, Debug)]
pub struct Batch {
proofs: Vec<Proof<Bls12>>,
inputs: Vec<Vec<<Bls12 as Engine>::Fr>>,
}
impl Batch {
pub fn verify(
&self,
vk: &PreparedVerifyingKey<Bls12>,
rng: &mut impl RngCore,
) -> Result<bool, SynthesisError> {
let proofs = self.proofs.iter().collect::<Vec<_>>();
groth16::verify_proofs_batch(vk, rng, proofs.as_slice(), self.inputs.as_slice())
}
pub fn queue(&mut self, proof: Proof<Bls12>, inputs: Vec<<Bls12 as Engine>::Fr>) {
self.proofs.push(proof);
self.inputs.push(inputs);
}
}
pub struct BatchValidator {
bundles_added: bool,
spend_proofs: Batch,
convert_proofs: Batch,
output_proofs: Batch,
signatures: redjubjub::batch::Verifier,
}
impl Default for BatchValidator {
fn default() -> Self {
Self::new()
}
}
impl BatchValidator {
pub fn new() -> Self {
BatchValidator {
bundles_added: false,
spend_proofs: Default::default(),
convert_proofs: Default::default(),
output_proofs: Default::default(),
signatures: redjubjub::batch::Verifier::new(),
}
}
pub fn check_bundle(&mut self, bundle: Bundle<Authorized>, sighash: [u8; 32]) -> bool {
self.bundles_added = true;
let mut ctx = SaplingVerificationContextInner::new();
for spend in bundle.shielded_spends {
let zkproof = match groth16::Proof::read(&spend.zkproof[..]) {
Ok(p) => p,
Err(_) => return false,
};
let consensus_rules_passed = ctx.check_spend(
spend.cv,
spend.anchor,
&spend.nullifier.0,
spend.rk,
&sighash,
spend.spend_auth_sig,
zkproof,
self,
|this, rk, _, spend_auth_sig| {
let rk = redjubjub::VerificationKeyBytes::<redjubjub::SpendAuth>::from(
rk.0.to_bytes(),
);
let spend_auth_sig = {
let mut buf = [0; 64];
spend_auth_sig.write(&mut buf[..]).unwrap();
redjubjub::Signature::<redjubjub::SpendAuth>::from(buf)
};
this.signatures.queue((rk, spend_auth_sig, &sighash));
true
},
|this, proof, public_inputs| {
this.spend_proofs.queue(proof, public_inputs.to_vec());
true
},
);
if !consensus_rules_passed {
return false;
}
}
for convert in bundle.shielded_converts {
let zkproof = match groth16::Proof::read(&convert.zkproof[..]) {
Ok(p) => p,
Err(_) => return false,
};
let consensus_rules_passed = ctx.check_convert(
convert.cv,
convert.anchor,
zkproof,
self,
|this, proof, public_inputs| {
this.convert_proofs.queue(proof, public_inputs.to_vec());
true
},
);
if !consensus_rules_passed {
return false;
}
}
for output in bundle.shielded_outputs {
let epk = match jubjub::ExtendedPoint::from_bytes(&output.ephemeral_key.0).into() {
Some(p) => p,
None => return false,
};
let zkproof = match groth16::Proof::read(&output.zkproof[..]) {
Ok(p) => p,
Err(_) => return false,
};
let consensus_rules_passed = ctx.check_output(
output.cv,
output.cmu,
epk,
zkproof,
|proof, public_inputs| {
self.output_proofs.queue(proof, public_inputs.to_vec());
true
},
);
if !consensus_rules_passed {
return false;
}
}
ctx.final_check(
bundle.value_balance,
&sighash,
bundle.authorization.binding_sig,
|bvk, _, binding_sig| {
let bvk =
redjubjub::VerificationKeyBytes::<redjubjub::Binding>::from(bvk.0.to_bytes());
let binding_sig = {
let mut buf = [0; 64];
binding_sig.write(&mut buf[..]).unwrap();
redjubjub::Signature::<redjubjub::Binding>::from(buf)
};
self.signatures.queue((bvk, binding_sig, &sighash));
true
},
)
}
pub fn validate<R: RngCore + CryptoRng>(
self,
spend_vk: &groth16::VerifyingKey<Bls12>,
convert_vk: &groth16::VerifyingKey<Bls12>,
output_vk: &groth16::VerifyingKey<Bls12>,
mut rng: R,
) -> bool {
if !self.bundles_added {
return true;
}
if let Err(e) = self.signatures.verify(&mut rng) {
tracing::debug!("Signature batch validation failed: {}", e);
return false;
}
let prepared_spend_key = groth16::prepare_verifying_key(spend_vk);
let prepared_conv_key = groth16::prepare_verifying_key(convert_vk);
let prepared_out_key = groth16::prepare_verifying_key(output_vk);
let mut verify_proofs = |batch: &Batch, vk| batch.verify(vk, &mut rng);
if verify_proofs(&self.spend_proofs, &prepared_spend_key).is_err() {
tracing::debug!("Spend proof batch validation failed");
return false;
}
if verify_proofs(&self.convert_proofs, &prepared_conv_key).is_err() {
tracing::debug!("Convert proof batch validation failed");
return false;
}
if verify_proofs(&self.output_proofs, &prepared_out_key).is_err() {
tracing::debug!("Output proof batch validation failed");
return false;
}
true
}
}
#[cfg(feature = "benchmarks")]
impl BatchValidator {
pub fn verify_signatures<R: RngCore + CryptoRng>(
self,
mut rng: R,
) -> Result<(), redjubjub::Error> {
self.signatures.verify(&mut rng)
}
pub fn verify_spend_proofs<R: RngCore>(
self,
spend_vk: &groth16::VerifyingKey<Bls12>,
rng: &mut R,
) -> Result<bool, bellman::SynthesisError> {
let prepared = prepare_verifying_key(spend_vk);
self.spend_proofs.verify(&prepared, rng)
}
pub fn verify_convert_proofs<R: RngCore>(
self,
convert_vk: &groth16::VerifyingKey<Bls12>,
rng: &mut R,
) -> Result<bool, bellman::SynthesisError> {
let prepared = prepare_verifying_key(convert_vk);
self.convert_proofs.verify(&prepared, rng)
}
pub fn verify_output_proofs<R: RngCore>(
self,
output_vk: &groth16::VerifyingKey<Bls12>,
rng: &mut R,
) -> Result<bool, bellman::SynthesisError> {
let prepared = prepare_verifying_key(output_vk);
self.output_proofs.verify(&prepared, rng)
}
}