use bellman::{
gadgets::multipack,
groth16::{verify_proof, PreparedVerifyingKey, Proof},
};
use ff::Field;
use pairing::bls12_381::{Bls12, Fr};
use zcash_primitives::jubjub::{edwards, FixedGenerators, JubjubBls12, Unknown};
use zcash_primitives::{
redjubjub::{PublicKey, Signature},
transaction::components::Amount,
};
use super::compute_value_balance;
fn is_small_order<Order>(p: &edwards::Point<Bls12, Order>, params: &JubjubBls12) -> bool {
p.double(params).double(params).double(params) == edwards::Point::zero()
}
pub struct SaplingVerificationContext {
cv_sum: edwards::Point<Bls12, Unknown>,
}
impl SaplingVerificationContext {
pub fn new() -> Self {
SaplingVerificationContext {
cv_sum: edwards::Point::zero(),
}
}
pub fn check_spend(
&mut self,
cv: edwards::Point<Bls12, Unknown>,
anchor: Fr,
nullifier: &[u8; 32],
rk: PublicKey<Bls12>,
sighash_value: &[u8; 32],
spend_auth_sig: Signature,
zkproof: Proof<Bls12>,
verifying_key: &PreparedVerifyingKey<Bls12>,
params: &JubjubBls12,
) -> bool {
if is_small_order(&cv, params) {
return false;
}
if is_small_order(&rk.0, params) {
return false;
}
{
let mut tmp = cv.clone();
tmp = tmp.add(&self.cv_sum, params);
self.cv_sum = tmp;
}
let nullifier = &nullifier[..];
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[..]);
if !rk.verify(
&data_to_be_signed,
&spend_auth_sig,
FixedGenerators::SpendingKeyGenerator,
params,
) {
return false;
}
let mut public_input = [Fr::zero(); 7];
{
let (x, y) = rk.0.to_xy();
public_input[0] = x;
public_input[1] = y;
}
{
let (x, y) = cv.to_xy();
public_input[2] = x;
public_input[3] = y;
}
public_input[4] = anchor;
{
let nullifier = multipack::bytes_to_bits_le(nullifier);
let nullifier = multipack::compute_multipacking::<Bls12>(&nullifier);
assert_eq!(nullifier.len(), 2);
public_input[5] = nullifier[0];
public_input[6] = nullifier[1];
}
match verify_proof(verifying_key, &zkproof, &public_input[..]) {
Ok(true) => true,
_ => false,
}
}
pub fn check_output(
&mut self,
cv: edwards::Point<Bls12, Unknown>,
cm: Fr,
epk: edwards::Point<Bls12, Unknown>,
zkproof: Proof<Bls12>,
verifying_key: &PreparedVerifyingKey<Bls12>,
params: &JubjubBls12,
) -> bool {
if is_small_order(&cv, params) {
return false;
}
if is_small_order(&epk, params) {
return false;
}
{
let mut tmp = cv.clone();
tmp = tmp.negate(); tmp = tmp.add(&self.cv_sum, params);
self.cv_sum = tmp;
}
let mut public_input = [Fr::zero(); 5];
{
let (x, y) = cv.to_xy();
public_input[0] = x;
public_input[1] = y;
}
{
let (x, y) = epk.to_xy();
public_input[2] = x;
public_input[3] = y;
}
public_input[4] = cm;
match verify_proof(verifying_key, &zkproof, &public_input[..]) {
Ok(true) => true,
_ => false,
}
}
pub fn final_check(
&self,
value_balance: Amount,
sighash_value: &[u8; 32],
binding_sig: Signature,
params: &JubjubBls12,
) -> bool {
let mut bvk = PublicKey(self.cv_sum.clone());
let mut value_balance = match compute_value_balance(value_balance, params) {
Some(a) => a,
None => return false,
};
value_balance = value_balance.negate();
bvk.0 = bvk.0.add(&value_balance, params);
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[..]);
bvk.verify(
&data_to_be_signed,
&binding_sig,
FixedGenerators::ValueCommitmentRandomness,
params,
)
}
}