1use 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 + 96 + 48; pub const WITNESS_PATH_SIZE: usize = 1 + 33 * TREE_DEPTH + 8;
16
17#[allow(clippy::too_many_arguments)]
19pub fn create_proof(
20 phi: [u8; 32],
21 rt: [u8; 32],
22 h_sig: [u8; 32],
23
24 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 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 out_pk1: [u8; 32],
40 out_value1: u64,
41 out_r1: [u8; 32],
42
43 out_pk2: [u8; 32],
45 out_value2: u64,
46 out_r2: [u8; 32],
47
48 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 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 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 let mut rng = OsRng;
132
133 create_random_proof(js, proving_key, &mut rng).expect("proving should not fail")
134}
135
136#[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 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 groth16::verify_proof(verifying_key, &proof, &public_input[..]).is_ok()
175}