]> Git Repo - VerusCoin.git/blob - src/zcash/JoinSplit.cpp
Auto merge of #962 - ebfull:2mb-blocks, r=ebfull
[VerusCoin.git] / src / zcash / JoinSplit.cpp
1 #include "JoinSplit.hpp"
2 #include "prf.h"
3 #include "sodium.h"
4
5 #include "zerocash/utils/util.h"
6 #include "zcash/util.h"
7
8 #include <memory>
9
10 #include <boost/foreach.hpp>
11 #include <boost/format.hpp>
12 #include <boost/optional.hpp>
13 #include <fstream>
14 #include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp"
15 #include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
16 #include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp"
17 #include "libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp"
18
19 #include "sync.h"
20
21 using namespace libsnark;
22
23 namespace libzcash {
24
25 #include "zcash/circuit/gadget.tcc"
26
27 CCriticalSection cs_ParamsIO;
28 CCriticalSection cs_InitializeParams;
29
30 template<typename T>
31 void saveToFile(std::string path, T& obj) {
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
44 template<typename T>
45 void loadFromFile(std::string path, boost::optional<T>& objIn) {
46     LOCK(cs_ParamsIO);
47
48     std::stringstream ss;
49     std::ifstream fh(path, std::ios::binary);
50
51     if(!fh.is_open()) {
52         throw std::runtime_error((boost::format("could not load param file at %s") % path).str());
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
66 template<size_t NumInputs, size_t NumOutputs>
67 class JoinSplitCircuit : public JoinSplit<NumInputs, NumOutputs> {
68 public:
69     typedef default_r1cs_ppzksnark_pp ppzksnark_ppT;
70     typedef Fr<ppzksnark_ppT> FieldT;
71
72     boost::optional<r1cs_ppzksnark_proving_key<ppzksnark_ppT>> pk;
73     boost::optional<r1cs_ppzksnark_verification_key<ppzksnark_ppT>> vk;
74     boost::optional<std::string> pkPath;
75
76     static void initialize() {
77         LOCK(cs_InitializeParams);
78
79         ppzksnark_ppT::init_public_params();
80     }
81
82     void setProvingKeyPath(std::string path) {
83         pkPath = path;
84     }
85
86     void loadProvingKey() {
87         if (!pk) {
88             if (!pkPath) {
89                 throw std::runtime_error("proving key path unknown");
90             }
91             loadFromFile(*pkPath, pk);
92         }
93     }
94
95     void saveProvingKey(std::string path) {
96         if (pk) {
97             saveToFile(path, *pk);
98         } else {
99             throw std::runtime_error("cannot save proving key; key doesn't exist");
100         }
101     }
102     void loadVerifyingKey(std::string path) {
103         loadFromFile(path, vk);
104     }
105     void saveVerifyingKey(std::string path) {
106         if (vk) {
107             saveToFile(path, *vk);
108         } else {
109             throw std::runtime_error("cannot save verifying key; key doesn't exist");
110         }
111     }
112
113     void generate() {
114         protoboard<FieldT> pb;
115
116         joinsplit_gadget<FieldT, NumInputs, NumOutputs> g(pb);
117         g.generate_r1cs_constraints();
118
119         const r1cs_constraint_system<FieldT> constraint_system = pb.get_constraint_system();
120         r1cs_ppzksnark_keypair<ppzksnark_ppT> keypair = r1cs_ppzksnark_generator<ppzksnark_ppT>(constraint_system);
121
122         pk = keypair.pk;
123         vk = keypair.vk;
124     }
125
126     JoinSplitCircuit() {}
127
128     bool verify(
129         const boost::array<unsigned char, ZKSNARK_PROOF_SIZE>& proof,
130         const uint256& pubKeyHash,
131         const uint256& randomSeed,
132         const boost::array<uint256, NumInputs>& macs,
133         const boost::array<uint256, NumInputs>& nullifiers,
134         const boost::array<uint256, NumOutputs>& commitments,
135         uint64_t vpub_old,
136         uint64_t vpub_new,
137         const uint256& rt
138     ) {
139         if (!vk) {
140             throw std::runtime_error("JoinSplit verifying key not loaded");
141         }
142
143         r1cs_ppzksnark_proof<ppzksnark_ppT> r1cs_proof;
144         std::stringstream ss;
145         std::string proof_str(proof.begin(), proof.end());
146         ss.str(proof_str);
147         ss >> r1cs_proof;
148
149         uint256 h_sig = this->h_sig(randomSeed, nullifiers, pubKeyHash);
150
151         auto witness = joinsplit_gadget<FieldT, NumInputs, NumOutputs>::witness_map(
152             rt,
153             h_sig,
154             macs,
155             nullifiers,
156             commitments,
157             vpub_old,
158             vpub_new
159         );
160
161         return r1cs_ppzksnark_verifier_strong_IC<ppzksnark_ppT>(*vk, witness, r1cs_proof);
162     }
163
164     boost::array<unsigned char, ZKSNARK_PROOF_SIZE> prove(
165         const boost::array<JSInput, NumInputs>& inputs,
166         const boost::array<JSOutput, NumOutputs>& outputs,
167         boost::array<Note, NumOutputs>& out_notes,
168         boost::array<ZCNoteEncryption::Ciphertext, NumOutputs>& out_ciphertexts,
169         uint256& out_ephemeralKey,
170         const uint256& pubKeyHash,
171         uint256& out_randomSeed,
172         boost::array<uint256, NumInputs>& out_macs,
173         boost::array<uint256, NumInputs>& out_nullifiers,
174         boost::array<uint256, NumOutputs>& out_commitments,
175         uint64_t vpub_old,
176         uint64_t vpub_new,
177         const uint256& rt
178     ) {
179         if (!pk) {
180             throw std::runtime_error("JoinSplit proving key not loaded");
181         }
182
183         // Compute nullifiers of inputs
184         for (size_t i = 0; i < NumInputs; i++) {
185             out_nullifiers[i] = inputs[i].nullifier();
186         }
187
188         // Sample randomSeed
189         out_randomSeed = random_uint256();
190
191         // Compute h_sig
192         uint256 h_sig = this->h_sig(out_randomSeed, out_nullifiers, pubKeyHash);
193
194         // Sample phi
195         uint252 phi = random_uint252();
196
197         // Compute notes for outputs
198         for (size_t i = 0; i < NumOutputs; i++) {
199             // Sample r
200             uint256 r = random_uint256();
201
202             out_notes[i] = outputs[i].note(phi, r, i, h_sig);
203         }
204
205         // Compute the output commitments
206         for (size_t i = 0; i < NumOutputs; i++) {
207             out_commitments[i] = out_notes[i].cm();
208         }
209
210         // Encrypt the ciphertexts containing the note
211         // plaintexts to the recipients of the value.
212         {
213             ZCNoteEncryption encryptor(h_sig);
214
215             for (size_t i = 0; i < NumOutputs; i++) {
216                 // TODO: expose memo in the public interface
217                 // 0xF6 is invalid UTF8 as per spec
218                 boost::array<unsigned char, ZC_MEMO_SIZE> memo = {{0xF6}};
219
220                 NotePlaintext pt(out_notes[i], memo);
221
222                 out_ciphertexts[i] = pt.encrypt(encryptor, outputs[i].addr.pk_enc);
223             }
224
225             out_ephemeralKey = encryptor.get_epk();
226         }
227
228         // Authenticate h_sig with each of the input
229         // spending keys, producing macs which protect
230         // against malleability.
231         for (size_t i = 0; i < NumInputs; i++) {
232             out_macs[i] = PRF_pk(inputs[i].key, i, h_sig);
233         }
234
235         std::vector<FieldT> primary_input;
236         std::vector<FieldT> aux_input;
237
238         {
239             protoboard<FieldT> pb;
240             {
241                 joinsplit_gadget<FieldT, NumInputs, NumOutputs> g(pb);
242                 g.generate_r1cs_constraints();
243                 g.generate_r1cs_witness(
244                     phi,
245                     rt,
246                     h_sig,
247                     inputs,
248                     out_notes,
249                     vpub_old,
250                     vpub_new
251                 );
252             }
253
254             if (!pb.is_satisfied()) {
255                 throw std::invalid_argument("Constraint system not satisfied by inputs");
256             }
257
258             primary_input = pb.primary_input();
259             aux_input = pb.auxiliary_input();
260         }
261
262         auto proof = r1cs_ppzksnark_prover<ppzksnark_ppT>(
263             *pk,
264             primary_input,
265             aux_input
266         );
267
268         std::stringstream ss;
269         ss << proof;
270         std::string serialized_proof = ss.str();
271
272         boost::array<unsigned char, ZKSNARK_PROOF_SIZE> result_proof;
273         //std::cout << "proof size in bytes when serialized: " << serialized_proof.size() << std::endl;
274         assert(serialized_proof.size() == ZKSNARK_PROOF_SIZE);
275         memcpy(&result_proof[0], &serialized_proof[0], ZKSNARK_PROOF_SIZE);
276
277         return result_proof;
278     }
279 };
280
281 template<size_t NumInputs, size_t NumOutputs>
282 JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Generate()
283 {
284     JoinSplitCircuit<NumInputs, NumOutputs>::initialize();
285     auto js = new JoinSplitCircuit<NumInputs, NumOutputs>();
286     js->generate();
287
288     return js;
289 }
290
291 template<size_t NumInputs, size_t NumOutputs>
292 JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Unopened()
293 {
294     JoinSplitCircuit<NumInputs, NumOutputs>::initialize();
295     return new JoinSplitCircuit<NumInputs, NumOutputs>();
296 }
297
298 template<size_t NumInputs, size_t NumOutputs>
299 uint256 JoinSplit<NumInputs, NumOutputs>::h_sig(
300     const uint256& randomSeed,
301     const boost::array<uint256, NumInputs>& nullifiers,
302     const uint256& pubKeyHash
303 ) {
304     const unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES]
305         = {'Z','c','a','s','h','C','o','m','p','u','t','e','h','S','i','g'};
306
307     std::vector<unsigned char> block(randomSeed.begin(), randomSeed.end());
308
309     for (size_t i = 0; i < NumInputs; i++) {
310         block.insert(block.end(), nullifiers[i].begin(), nullifiers[i].end());
311     }
312
313     block.insert(block.end(), pubKeyHash.begin(), pubKeyHash.end());
314
315     uint256 output;
316
317     if (crypto_generichash_blake2b_salt_personal(output.begin(), 32,
318                                                  &block[0], block.size(),
319                                                  NULL, 0, // No key.
320                                                  NULL,    // No salt.
321                                                  personalization
322                                                 ) != 0)
323     {
324         throw std::logic_error("hash function failure");
325     }
326
327     return output;
328 }
329
330 Note JSOutput::note(const uint252& phi, const uint256& r, size_t i, const uint256& h_sig) const {
331     uint256 rho = PRF_rho(phi, i, h_sig);
332
333     return Note(addr.a_pk, value, rho, r);
334 }
335
336 JSOutput::JSOutput() : addr(uint256(), uint256()), value(0) {
337     SpendingKey a_sk = SpendingKey::random();
338     addr = a_sk.address();
339 }
340
341 JSInput::JSInput() : witness(ZCIncrementalMerkleTree().witness()),
342                      key(SpendingKey::random()) {
343     note = Note(key.address().a_pk, 0, random_uint256(), random_uint256());
344     ZCIncrementalMerkleTree dummy_tree;
345     dummy_tree.append(note.cm());
346     witness = dummy_tree.witness();
347 }
348
349 template class JoinSplit<ZC_NUM_JS_INPUTS,
350                          ZC_NUM_JS_OUTPUTS>;
351
352 }
This page took 0.04361 seconds and 4 git commands to generate.