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