zcash_proofs/
sprout.rs

1//! APIs for creating and verifying Sprout proofs.
2
3use bellman::{
4    gadgets::multipack,
5    groth16::{self, create_random_proof, Parameters, PreparedVerifyingKey, Proof},
6};
7use bls12_381::Bls12;
8use rand_core::OsRng;
9
10use crate::circuit::sprout::*;
11
12const GROTH_PROOF_SIZE: usize = 48 // π_A
13    + 96 // π_B
14    + 48; // π_C
15pub const WITNESS_PATH_SIZE: usize = 1 + 33 * TREE_DEPTH + 8;
16
17/// Sprout JoinSplit proof generation.
18#[allow(clippy::too_many_arguments)]
19pub fn create_proof(
20    phi: [u8; 32],
21    rt: [u8; 32],
22    h_sig: [u8; 32],
23
24    // First input
25    in_sk1: [u8; 32],
26    in_value1: u64,
27    in_rho1: [u8; 32],
28    in_r1: [u8; 32],
29    in_auth1: &[u8; WITNESS_PATH_SIZE],
30
31    // Second input
32    in_sk2: [u8; 32],
33    in_value2: u64,
34    in_rho2: [u8; 32],
35    in_r2: [u8; 32],
36    in_auth2: &[u8; WITNESS_PATH_SIZE],
37
38    // First output
39    out_pk1: [u8; 32],
40    out_value1: u64,
41    out_r1: [u8; 32],
42
43    // Second output
44    out_pk2: [u8; 32],
45    out_value2: u64,
46    out_r2: [u8; 32],
47
48    // Public value
49    vpub_old: u64,
50    vpub_new: u64,
51
52    proving_key: &Parameters<Bls12>,
53) -> Proof<Bls12> {
54    let mut inputs = Vec::with_capacity(2);
55    {
56        let mut handle_input = |sk, value, rho, r, mut auth: &[u8]| {
57            let value = Some(value);
58            let rho = Some(UniqueRandomness(rho));
59            let r = Some(CommitmentRandomness(r));
60            let a_sk = Some(SpendingKey(sk));
61
62            // skip the first byte
63            assert_eq!(auth[0], TREE_DEPTH as u8);
64            auth = &auth[1..];
65
66            let mut auth_path = [None; TREE_DEPTH];
67            for i in (0..TREE_DEPTH).rev() {
68                // skip length of inner vector
69                assert_eq!(auth[0], 32);
70                auth = &auth[1..];
71
72                let mut sibling = [0u8; 32];
73                sibling.copy_from_slice(&auth[0..32]);
74                auth = &auth[32..];
75
76                auth_path[i] = Some((sibling, false));
77            }
78
79            let mut position = {
80                let mut bytes = [0; 8];
81                bytes.copy_from_slice(&auth[0..8]);
82                u64::from_le_bytes(bytes)
83            };
84
85            for entry in auth_path.iter_mut() {
86                if let Some(p) = entry {
87                    p.1 = (position & 1) == 1;
88                }
89
90                position >>= 1;
91            }
92
93            inputs.push(JsInput {
94                value,
95                a_sk,
96                rho,
97                r,
98                auth_path,
99            });
100        };
101
102        handle_input(in_sk1, in_value1, in_rho1, in_r1, &in_auth1[..]);
103        handle_input(in_sk2, in_value2, in_rho2, in_r2, &in_auth2[..]);
104    }
105
106    let mut outputs = Vec::with_capacity(2);
107    {
108        let mut handle_output = |a_pk, value, r| {
109            outputs.push(JsOutput {
110                value: Some(value),
111                a_pk: Some(PayingKey(a_pk)),
112                r: Some(CommitmentRandomness(r)),
113            });
114        };
115
116        handle_output(out_pk1, out_value1, out_r1);
117        handle_output(out_pk2, out_value2, out_r2);
118    }
119
120    let js = JoinSplit {
121        vpub_old: Some(vpub_old),
122        vpub_new: Some(vpub_new),
123        h_sig: Some(h_sig),
124        phi: Some(phi),
125        inputs,
126        outputs,
127        rt: Some(rt),
128    };
129
130    // Initialize secure RNG
131    let mut rng = OsRng;
132
133    create_random_proof(js, proving_key, &mut rng).expect("proving should not fail")
134}
135
136/// Sprout JoinSplit proof verification.
137#[allow(clippy::too_many_arguments)]
138pub fn verify_proof(
139    proof: &[u8; GROTH_PROOF_SIZE],
140    rt: &[u8; 32],
141    h_sig: &[u8; 32],
142    mac1: &[u8; 32],
143    mac2: &[u8; 32],
144    nf1: &[u8; 32],
145    nf2: &[u8; 32],
146    cm1: &[u8; 32],
147    cm2: &[u8; 32],
148    vpub_old: u64,
149    vpub_new: u64,
150    verifying_key: &PreparedVerifyingKey<Bls12>,
151) -> bool {
152    // Prepare the public input for the verifier
153    let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2));
154    public_input.extend(rt);
155    public_input.extend(h_sig);
156    public_input.extend(nf1);
157    public_input.extend(mac1);
158    public_input.extend(nf2);
159    public_input.extend(mac2);
160    public_input.extend(cm1);
161    public_input.extend(cm2);
162    public_input.extend(vpub_old.to_le_bytes());
163    public_input.extend(vpub_new.to_le_bytes());
164
165    let public_input = multipack::bytes_to_bits(&public_input);
166    let public_input = multipack::compute_multipacking(&public_input);
167
168    let proof = match Proof::read(&proof[..]) {
169        Ok(p) => p,
170        Err(_) => return false,
171    };
172
173    // Verify the proof
174    groth16::verify_proof(verifying_key, &proof, &public_input[..]).is_ok()
175}