1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use bellman::{gadgets::multipack, groth16::Proof};
use bls12_381::Bls12;
use group::{ff::PrimeField, Curve};
use redjubjub::{Binding, SpendAuth};
use crate::{
note::ExtractedNoteCommitment,
value::{CommitmentSum, ValueCommitment},
};
mod single;
pub use single::SaplingVerificationContext;
mod batch;
pub use batch::BatchValidator;
/// A context object for verifying the Sapling components of a Zcash transaction.
struct SaplingVerificationContextInner {
// (sum of the Spend value commitments) - (sum of the Output value commitments)
cv_sum: CommitmentSum,
}
impl SaplingVerificationContextInner {
/// Construct a new context to be used with a single transaction.
fn new() -> Self {
SaplingVerificationContextInner {
cv_sum: CommitmentSum::zero(),
}
}
/// Perform consensus checks on a Sapling SpendDescription, while
/// accumulating its value commitment inside the context for later use.
#[allow(clippy::too_many_arguments)]
fn check_spend<C>(
&mut self,
cv: &ValueCommitment,
anchor: bls12_381::Scalar,
nullifier: &[u8; 32],
rk: &redjubjub::VerificationKey<SpendAuth>,
zkproof: Proof<Bls12>,
verifier_ctx: &mut C,
spend_auth_sig_verifier: impl FnOnce(&mut C, &redjubjub::VerificationKey<SpendAuth>) -> bool,
proof_verifier: impl FnOnce(&mut C, Proof<Bls12>, [bls12_381::Scalar; 7]) -> bool,
) -> bool {
// The "cv is not small order" happens when a SpendDescription is deserialized.
// This happens when transactions or blocks are received over the network, or when
// mined blocks are introduced via the `submitblock` RPC method on full nodes.
let rk_affine = jubjub::AffinePoint::from_bytes((*rk).into()).unwrap();
if rk_affine.is_small_order().into() {
return false;
}
// Accumulate the value commitment in the context
self.cv_sum += cv;
// Grab the nullifier as a sequence of bytes
let nullifier = &nullifier[..];
// Verify the spend_auth_sig
if !spend_auth_sig_verifier(verifier_ctx, rk) {
return false;
}
// Construct public input for circuit
let mut public_input = [bls12_381::Scalar::zero(); 7];
{
let affine = rk_affine;
let (u, v) = (affine.get_u(), affine.get_v());
public_input[0] = u;
public_input[1] = v;
}
{
let affine = cv.as_inner().to_affine();
let (u, v) = (affine.get_u(), affine.get_v());
public_input[2] = u;
public_input[3] = v;
}
public_input[4] = anchor;
// Add the nullifier through multiscalar packing
{
let nullifier = multipack::bytes_to_bits_le(nullifier);
let nullifier = multipack::compute_multipacking(&nullifier);
assert_eq!(nullifier.len(), 2);
public_input[5] = nullifier[0];
public_input[6] = nullifier[1];
}
// Verify the proof
proof_verifier(verifier_ctx, zkproof, public_input)
}
/// Perform consensus checks on a Sapling OutputDescription, while
/// accumulating its value commitment inside the context for later use.
fn check_output(
&mut self,
cv: &ValueCommitment,
cmu: ExtractedNoteCommitment,
epk: jubjub::ExtendedPoint,
zkproof: Proof<Bls12>,
proof_verifier: impl FnOnce(Proof<Bls12>, [bls12_381::Scalar; 5]) -> bool,
) -> bool {
// The "cv is not small order" happens when an OutputDescription is deserialized.
// This happens when transactions or blocks are received over the network, or when
// mined blocks are introduced via the `submitblock` RPC method on full nodes.
if epk.is_small_order().into() {
return false;
}
// Accumulate the value commitment in the context
self.cv_sum -= cv;
// Construct public input for circuit
let mut public_input = [bls12_381::Scalar::zero(); 5];
{
let affine = cv.as_inner().to_affine();
let (u, v) = (affine.get_u(), affine.get_v());
public_input[0] = u;
public_input[1] = v;
}
{
let affine = epk.to_affine();
let (u, v) = (affine.get_u(), affine.get_v());
public_input[2] = u;
public_input[3] = v;
}
public_input[4] = bls12_381::Scalar::from_repr(cmu.to_bytes()).unwrap();
// Verify the proof
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.
fn final_check<V: Into<i64>>(
&self,
value_balance: V,
binding_sig_verifier: impl FnOnce(redjubjub::VerificationKey<Binding>) -> bool,
) -> bool {
// Compute the final bvk.
let bvk = self.cv_sum.into_bvk(value_balance);
// Verify the binding_sig
binding_sig_verifier(bvk)
}
}