]> Git Repo - VerusCoin.git/blame - src/zcash/JoinSplit.cpp
ViewingKey -> ReceivingKey per zcash/zips#117
[VerusCoin.git] / src / zcash / JoinSplit.cpp
CommitLineData
369df065
SB
1#include "JoinSplit.hpp"
2#include "prf.h"
3#include "sodium.h"
4
81469bbb 5#include "zcash/util.h"
074eb3a2
SB
6
7#include <memory>
8
5e61a78f 9#include <boost/foreach.hpp>
369df065
SB
10#include <boost/format.hpp>
11#include <boost/optional.hpp>
12#include <fstream>
fee88353
JG
13#include <libsnark/common/default_types/r1cs_ppzksnark_pp.hpp>
14#include <libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp>
15#include <libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp>
16#include <libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp>
4305a562 17#include "tinyformat.h"
369df065 18#include "sync.h"
c4643bd9 19#include "amount.h"
369df065
SB
20
21using namespace libsnark;
22
23namespace libzcash {
24
25#include "zcash/circuit/gadget.tcc"
26
27CCriticalSection cs_ParamsIO;
78df9f02 28CCriticalSection cs_LoadKeys;
369df065
SB
29
30template<typename T>
1a9543d0 31void saveToFile(const std::string path, T& obj) {
369df065
SB
32 LOCK(cs_ParamsIO);
33
34 std::stringstream ss;
35 ss << obj;
36 std::ofstream fh;
37 fh.open(path, std::ios::binary);
38 ss.rdbuf()->pubseekpos(0, std::ios_base::out);
39 fh << ss.rdbuf();
40 fh.flush();
41 fh.close();
42}
43
44template<typename T>
1a9543d0 45void loadFromFile(const std::string path, T& objIn) {
369df065
SB
46 LOCK(cs_ParamsIO);
47
48 std::stringstream ss;
49 std::ifstream fh(path, std::ios::binary);
50
51 if(!fh.is_open()) {
4305a562 52 throw std::runtime_error(strprintf("could not load param file at %s", path));
369df065
SB
53 }
54
55 ss << fh.rdbuf();
56 fh.close();
57
58 ss.rdbuf()->pubseekpos(0, std::ios_base::in);
59
60 T obj;
61 ss >> obj;
62
63 objIn = std::move(obj);
64}
65
66template<size_t NumInputs, size_t NumOutputs>
67class JoinSplitCircuit : public JoinSplit<NumInputs, NumOutputs> {
68public:
69 typedef default_r1cs_ppzksnark_pp ppzksnark_ppT;
70 typedef Fr<ppzksnark_ppT> FieldT;
71
1a9543d0
SB
72 r1cs_ppzksnark_verification_key<ppzksnark_ppT> vk;
73 r1cs_ppzksnark_processed_verification_key<ppzksnark_ppT> vk_precomp;
74 std::string pkPath;
369df065 75
1a9543d0
SB
76 JoinSplitCircuit(const std::string vkPath, const std::string pkPath) : pkPath(pkPath) {
77 loadFromFile(vkPath, vk);
78 vk_precomp = r1cs_ppzksnark_verifier_process_vk(vk);
0a958ae7 79 }
1a9543d0 80 ~JoinSplitCircuit() {}
0a958ae7 81
1a9543d0
SB
82 static void generate(const std::string r1csPath,
83 const std::string vkPath,
84 const std::string pkPath)
85 {
369df065
SB
86 protoboard<FieldT> pb;
87
88 joinsplit_gadget<FieldT, NumInputs, NumOutputs> g(pb);
89 g.generate_r1cs_constraints();
90
1a9543d0 91 auto r1cs = pb.get_constraint_system();
0a958ae7 92
1a9543d0 93 saveToFile(r1csPath, r1cs);
78df9f02 94
1a9543d0 95 r1cs_ppzksnark_keypair<ppzksnark_ppT> keypair = r1cs_ppzksnark_generator<ppzksnark_ppT>(r1cs);
369df065 96
1a9543d0
SB
97 saveToFile(vkPath, keypair.vk);
98 saveToFile(pkPath, keypair.pk);
369df065
SB
99 }
100
369df065 101 bool verify(
f0dab51c 102 const ZCProof& proof,
bc59f537 103 ProofVerifier& verifier,
369df065
SB
104 const uint256& pubKeyHash,
105 const uint256& randomSeed,
032164d5 106 const boost::array<uint256, NumInputs>& macs,
369df065
SB
107 const boost::array<uint256, NumInputs>& nullifiers,
108 const boost::array<uint256, NumOutputs>& commitments,
109 uint64_t vpub_old,
110 uint64_t vpub_new,
111 const uint256& rt
112 ) {
25d21970 113 try {
f0dab51c 114 auto r1cs_proof = proof.to_libsnark_proof<r1cs_ppzksnark_proof<ppzksnark_ppT>>();
d81c31f5
S
115
116 uint256 h_sig = this->h_sig(randomSeed, nullifiers, pubKeyHash);
117
118 auto witness = joinsplit_gadget<FieldT, NumInputs, NumOutputs>::witness_map(
119 rt,
120 h_sig,
121 macs,
122 nullifiers,
123 commitments,
124 vpub_old,
125 vpub_new
126 );
127
bc59f537 128 return verifier.check(
1a9543d0
SB
129 vk,
130 vk_precomp,
bc59f537
SB
131 witness,
132 r1cs_proof
133 );
25d21970
SB
134 } catch (...) {
135 return false;
136 }
369df065
SB
137 }
138
f0dab51c 139 ZCProof prove(
369df065
SB
140 const boost::array<JSInput, NumInputs>& inputs,
141 const boost::array<JSOutput, NumOutputs>& outputs,
142 boost::array<Note, NumOutputs>& out_notes,
143 boost::array<ZCNoteEncryption::Ciphertext, NumOutputs>& out_ciphertexts,
144 uint256& out_ephemeralKey,
145 const uint256& pubKeyHash,
146 uint256& out_randomSeed,
147 boost::array<uint256, NumInputs>& out_macs,
148 boost::array<uint256, NumInputs>& out_nullifiers,
149 boost::array<uint256, NumOutputs>& out_commitments,
150 uint64_t vpub_old,
151 uint64_t vpub_new,
5db5e42e 152 const uint256& rt,
45232b19
S
153 bool computeProof,
154 uint256 *out_esk // Payment disclosure
369df065 155 ) {
c4643bd9
SB
156 if (vpub_old > MAX_MONEY) {
157 throw std::invalid_argument("nonsensical vpub_old value");
158 }
159
160 if (vpub_new > MAX_MONEY) {
161 throw std::invalid_argument("nonsensical vpub_new value");
162 }
163
164 uint64_t lhs_value = vpub_old;
165 uint64_t rhs_value = vpub_new;
166
369df065 167 for (size_t i = 0; i < NumInputs; i++) {
c4643bd9
SB
168 // Sanity checks of input
169 {
5f0a73ce
SB
170 // If note has nonzero value
171 if (inputs[i].note.value != 0) {
172 // The witness root must equal the input root.
173 if (inputs[i].witness.root() != rt) {
174 throw std::invalid_argument("joinsplit not anchored to the correct root");
175 }
176
177 // The tree must witness the correct element
178 if (inputs[i].note.cm() != inputs[i].witness.element()) {
179 throw std::invalid_argument("witness of wrong element for joinsplit input");
180 }
c4643bd9
SB
181 }
182
183 // Ensure we have the key to this note.
184 if (inputs[i].note.a_pk != inputs[i].key.address().a_pk) {
185 throw std::invalid_argument("input note not authorized to spend with given key");
186 }
187
188 // Balance must be sensical
189 if (inputs[i].note.value > MAX_MONEY) {
190 throw std::invalid_argument("nonsensical input note value");
191 }
192
193 lhs_value += inputs[i].note.value;
194
195 if (lhs_value > MAX_MONEY) {
196 throw std::invalid_argument("nonsensical left hand size of joinsplit balance");
197 }
198 }
199
200 // Compute nullifier of input
369df065
SB
201 out_nullifiers[i] = inputs[i].nullifier();
202 }
203
204 // Sample randomSeed
205 out_randomSeed = random_uint256();
206
207 // Compute h_sig
208 uint256 h_sig = this->h_sig(out_randomSeed, out_nullifiers, pubKeyHash);
209
210 // Sample phi
defe37a6 211 uint252 phi = random_uint252();
369df065
SB
212
213 // Compute notes for outputs
214 for (size_t i = 0; i < NumOutputs; i++) {
c4643bd9
SB
215 // Sanity checks of output
216 {
217 if (outputs[i].value > MAX_MONEY) {
218 throw std::invalid_argument("nonsensical output value");
219 }
220
221 rhs_value += outputs[i].value;
222
223 if (rhs_value > MAX_MONEY) {
224 throw std::invalid_argument("nonsensical right hand side of joinsplit balance");
225 }
226 }
227
369df065
SB
228 // Sample r
229 uint256 r = random_uint256();
230
231 out_notes[i] = outputs[i].note(phi, r, i, h_sig);
232 }
233
c4643bd9
SB
234 if (lhs_value != rhs_value) {
235 throw std::invalid_argument("invalid joinsplit balance");
236 }
237
369df065
SB
238 // Compute the output commitments
239 for (size_t i = 0; i < NumOutputs; i++) {
240 out_commitments[i] = out_notes[i].cm();
241 }
242
243 // Encrypt the ciphertexts containing the note
244 // plaintexts to the recipients of the value.
245 {
246 ZCNoteEncryption encryptor(h_sig);
247
248 for (size_t i = 0; i < NumOutputs; i++) {
4eb1a96f 249 NotePlaintext pt(out_notes[i], outputs[i].memo);
369df065
SB
250
251 out_ciphertexts[i] = pt.encrypt(encryptor, outputs[i].addr.pk_enc);
252 }
253
254 out_ephemeralKey = encryptor.get_epk();
45232b19
S
255
256 // !!! Payment disclosure START
257 if (out_esk != nullptr) {
258 *out_esk = encryptor.get_esk();
259 }
260 // !!! Payment disclosure END
369df065
SB
261 }
262
263 // Authenticate h_sig with each of the input
264 // spending keys, producing macs which protect
265 // against malleability.
266 for (size_t i = 0; i < NumInputs; i++) {
267 out_macs[i] = PRF_pk(inputs[i].key, i, h_sig);
268 }
269
5db5e42e
JG
270 if (!computeProof) {
271 return ZCProof();
272 }
273
bf76024e 274 protoboard<FieldT> pb;
369df065 275 {
bf76024e
SB
276 joinsplit_gadget<FieldT, NumInputs, NumOutputs> g(pb);
277 g.generate_r1cs_constraints();
278 g.generate_r1cs_witness(
279 phi,
280 rt,
281 h_sig,
282 inputs,
283 out_notes,
284 vpub_old,
285 vpub_new
286 );
287 }
369df065 288
c4643bd9
SB
289 // The constraint system must be satisfied or there is an unimplemented
290 // or incorrect sanity check above. Or the constraint system is broken!
291 assert(pb.is_satisfied());
369df065 292
bf76024e
SB
293 // TODO: These are copies, which is not strictly necessary.
294 std::vector<FieldT> primary_input = pb.primary_input();
295 std::vector<FieldT> aux_input = pb.auxiliary_input();
296
297 // Swap A and B if it's beneficial (less arithmetic in G2)
298 // In our circuit, we already know that it's beneficial
299 // to swap, but it takes so little time to perform this
300 // estimate that it doesn't matter if we check every time.
301 pb.constraint_system.swap_AB_if_beneficial();
302
394f4185 303 std::ifstream fh(pkPath, std::ios::binary);
1a9543d0 304
394f4185 305 if(!fh.is_open()) {
4305a562 306 throw std::runtime_error(strprintf("could not load param file at %s", pkPath));
394f4185
SB
307 }
308
309 return ZCProof(r1cs_ppzksnark_prover_streaming<ppzksnark_ppT>(
310 fh,
369df065 311 primary_input,
bf76024e
SB
312 aux_input,
313 pb.constraint_system
f0dab51c 314 ));
369df065
SB
315 }
316};
317
318template<size_t NumInputs, size_t NumOutputs>
1a9543d0
SB
319void JoinSplit<NumInputs, NumOutputs>::Generate(const std::string r1csPath,
320 const std::string vkPath,
321 const std::string pkPath)
369df065 322{
bc59f537 323 initialize_curve_params();
1a9543d0 324 JoinSplitCircuit<NumInputs, NumOutputs>::generate(r1csPath, vkPath, pkPath);
369df065
SB
325}
326
327template<size_t NumInputs, size_t NumOutputs>
1a9543d0
SB
328JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Prepared(const std::string vkPath,
329 const std::string pkPath)
369df065 330{
bc59f537 331 initialize_curve_params();
1a9543d0 332 return new JoinSplitCircuit<NumInputs, NumOutputs>(vkPath, pkPath);
369df065
SB
333}
334
335template<size_t NumInputs, size_t NumOutputs>
336uint256 JoinSplit<NumInputs, NumOutputs>::h_sig(
337 const uint256& randomSeed,
338 const boost::array<uint256, NumInputs>& nullifiers,
339 const uint256& pubKeyHash
340) {
6aae9d1a 341 const unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES]
369df065
SB
342 = {'Z','c','a','s','h','C','o','m','p','u','t','e','h','S','i','g'};
343
344 std::vector<unsigned char> block(randomSeed.begin(), randomSeed.end());
345
346 for (size_t i = 0; i < NumInputs; i++) {
347 block.insert(block.end(), nullifiers[i].begin(), nullifiers[i].end());
348 }
349
350 block.insert(block.end(), pubKeyHash.begin(), pubKeyHash.end());
351
352 uint256 output;
353
354 if (crypto_generichash_blake2b_salt_personal(output.begin(), 32,
355 &block[0], block.size(),
356 NULL, 0, // No key.
357 NULL, // No salt.
358 personalization
359 ) != 0)
360 {
361 throw std::logic_error("hash function failure");
362 }
363
364 return output;
365}
366
defe37a6 367Note JSOutput::note(const uint252& phi, const uint256& r, size_t i, const uint256& h_sig) const {
369df065
SB
368 uint256 rho = PRF_rho(phi, i, h_sig);
369
370 return Note(addr.a_pk, value, rho, r);
371}
372
373JSOutput::JSOutput() : addr(uint256(), uint256()), value(0) {
defe37a6 374 SpendingKey a_sk = SpendingKey::random();
369df065
SB
375 addr = a_sk.address();
376}
377
378JSInput::JSInput() : witness(ZCIncrementalMerkleTree().witness()),
defe37a6 379 key(SpendingKey::random()) {
369df065
SB
380 note = Note(key.address().a_pk, 0, random_uint256(), random_uint256());
381 ZCIncrementalMerkleTree dummy_tree;
382 dummy_tree.append(note.cm());
383 witness = dummy_tree.witness();
384}
385
386template class JoinSplit<ZC_NUM_JS_INPUTS,
387 ZC_NUM_JS_OUTPUTS>;
388
6aae9d1a 389}
This page took 0.198957 seconds and 4 git commands to generate.