use std::fmt;
use bellman::{
gadgets::multipack,
groth16::{batch, PreparedVerifyingKey, VerifyingKey},
VerificationError,
};
use bls12_381::Bls12;
use futures::{future::BoxFuture, FutureExt};
use once_cell::sync::Lazy;
use tokio::sync::watch;
use tower::util::ServiceFn;
use tower_batch_control::RequestWeight;
use tower_fallback::BoxedError;
use zebra_chain::{
primitives::{
ed25519::{self, VerificationKeyBytes},
Groth16Proof,
},
sprout::{JoinSplit, Nullifier, RandomSeed},
};
use crate::BoxError;
use super::spawn_fifo_and_convert;
mod params;
#[cfg(test)]
mod tests;
#[cfg(test)]
mod vectors;
pub use params::{SAPLING, SPROUT};
use crate::error::TransactionError;
type VerifyResult = Result<(), VerificationError>;
type Sender = watch::Sender<Option<VerifyResult>>;
#[derive(Clone, Debug)]
pub struct Item(batch::Item<Bls12>);
impl RequestWeight for Item {}
impl<T: Into<batch::Item<Bls12>>> From<T> for Item {
fn from(value: T) -> Self {
Self(value.into())
}
}
impl Item {
pub fn verify_single(self, pvk: &PreparedVerifyingKey<Bls12>) -> VerifyResult {
self.0.verify_single(pvk)
}
}
pub type BatchVerifyingKey = VerifyingKey<Bls12>;
pub type ItemVerifyingKey = PreparedVerifyingKey<Bls12>;
pub static JOINSPLIT_VERIFIER: Lazy<
ServiceFn<fn(Item) -> BoxFuture<'static, Result<(), BoxedError>>>,
> = Lazy::new(|| {
tower::service_fn(
(|item: Item| {
Verifier::verify_single_spawning(item, SPROUT.prepared_verifying_key())
.map(|result| {
result
.map_err(|e| TransactionError::Groth16(e.to_string()))
.map_err(tower_fallback::BoxedError::from)
})
.boxed()
}) as fn(_) -> _,
)
});
pub trait Description {
fn proof(&self) -> &Groth16Proof;
fn primary_inputs(&self) -> Vec<jubjub::Fq>;
}
pub(super) fn h_sig(
random_seed: &RandomSeed,
nf1: &Nullifier,
nf2: &Nullifier,
joinsplit_pub_key: &VerificationKeyBytes,
) -> [u8; 32] {
let h_sig: [u8; 32] = blake2b_simd::Params::new()
.hash_length(32)
.personal(b"ZcashComputehSig")
.to_state()
.update(&(<[u8; 32]>::from(random_seed))[..])
.update(&(<[u8; 32]>::from(nf1))[..])
.update(&(<[u8; 32]>::from(nf2))[..])
.update(joinsplit_pub_key.as_ref())
.finalize()
.as_bytes()
.try_into()
.expect("32 byte array");
h_sig
}
impl Description for (&JoinSplit<Groth16Proof>, &ed25519::VerificationKeyBytes) {
#[allow(clippy::needless_borrow)]
fn primary_inputs(&self) -> Vec<jubjub::Fq> {
let (joinsplit, joinsplit_pub_key) = self;
let rt: [u8; 32] = joinsplit.anchor.into();
let mac1: [u8; 32] = (&joinsplit.vmacs[0]).into();
let mac2: [u8; 32] = (&joinsplit.vmacs[1]).into();
let nf1: [u8; 32] = (&joinsplit.nullifiers[0]).into();
let nf2: [u8; 32] = (&joinsplit.nullifiers[1]).into();
let cm1: [u8; 32] = (&joinsplit.commitments[0]).into();
let cm2: [u8; 32] = (&joinsplit.commitments[1]).into();
let vpub_old = joinsplit.vpub_old.to_bytes();
let vpub_new = joinsplit.vpub_new.to_bytes();
let h_sig = h_sig(
&joinsplit.random_seed,
&joinsplit.nullifiers[0],
&joinsplit.nullifiers[1],
joinsplit_pub_key,
);
let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2));
public_input.extend(rt);
public_input.extend(h_sig);
public_input.extend(nf1);
public_input.extend(mac1);
public_input.extend(nf2);
public_input.extend(mac2);
public_input.extend(cm1);
public_input.extend(cm2);
public_input.extend(vpub_old);
public_input.extend(vpub_new);
let public_input = multipack::bytes_to_bits(&public_input);
multipack::compute_multipacking(&public_input)
}
fn proof(&self) -> &Groth16Proof {
&self.0.zkproof
}
}
pub struct DescriptionWrapper<T>(pub T);
impl<T> TryFrom<DescriptionWrapper<&T>> for Item
where
T: Description,
{
type Error = TransactionError;
fn try_from(input: DescriptionWrapper<&T>) -> Result<Self, Self::Error> {
Ok(Item::from((
bellman::groth16::Proof::read(&input.0.proof().0[..])
.map_err(|e| TransactionError::MalformedGroth16(e.to_string()))?,
input.0.primary_inputs(),
)))
}
}
pub struct Verifier {
tx: Sender,
}
impl Verifier {
async fn verify_single_spawning(
item: Item,
pvk: &'static ItemVerifyingKey,
) -> Result<(), BoxError> {
spawn_fifo_and_convert(move || item.verify_single(pvk)).await
}
}
impl fmt::Debug for Verifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = "Verifier";
f.debug_struct(name)
.field("batch", &"..")
.field("tx", &self.tx)
.finish()
}
}