1 #include "JoinSplit.hpp"
5 #include "zerocash/utils/util.h"
6 #include "zcash/util.h"
10 #include <boost/foreach.hpp>
11 #include <boost/format.hpp>
12 #include <boost/optional.hpp>
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"
21 using namespace libsnark;
25 #include "zcash/circuit/gadget.tcc"
27 CCriticalSection cs_ParamsIO;
28 CCriticalSection cs_InitializeParams;
31 void saveToFile(std::string path, T& obj) {
37 fh.open(path, std::ios::binary);
38 ss.rdbuf()->pubseekpos(0, std::ios_base::out);
45 void loadFromFile(std::string path, boost::optional<T>& objIn) {
49 std::ifstream fh(path, std::ios::binary);
52 throw std::runtime_error((boost::format("could not load param file at %s") % path).str());
58 ss.rdbuf()->pubseekpos(0, std::ios_base::in);
63 objIn = std::move(obj);
66 template<size_t NumInputs, size_t NumOutputs>
67 class JoinSplitCircuit : public JoinSplit<NumInputs, NumOutputs> {
69 typedef default_r1cs_ppzksnark_pp ppzksnark_ppT;
70 typedef Fr<ppzksnark_ppT> FieldT;
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;
76 static void initialize() {
77 LOCK(cs_InitializeParams);
79 ppzksnark_ppT::init_public_params();
82 void setProvingKeyPath(std::string path) {
86 void loadProvingKey() {
89 throw std::runtime_error("proving key path unknown");
91 loadFromFile(*pkPath, pk);
95 void saveProvingKey(std::string path) {
97 saveToFile(path, *pk);
99 throw std::runtime_error("cannot save proving key; key doesn't exist");
102 void loadVerifyingKey(std::string path) {
103 loadFromFile(path, vk);
105 void saveVerifyingKey(std::string path) {
107 saveToFile(path, *vk);
109 throw std::runtime_error("cannot save verifying key; key doesn't exist");
114 protoboard<FieldT> pb;
116 joinsplit_gadget<FieldT, NumInputs, NumOutputs> g(pb);
117 g.generate_r1cs_constraints();
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);
126 JoinSplitCircuit() {}
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,
140 throw std::runtime_error("JoinSplit verifying key not loaded");
143 r1cs_ppzksnark_proof<ppzksnark_ppT> r1cs_proof;
144 std::stringstream ss;
145 std::string proof_str(proof.begin(), proof.end());
149 uint256 h_sig = this->h_sig(randomSeed, nullifiers, pubKeyHash);
151 auto witness = joinsplit_gadget<FieldT, NumInputs, NumOutputs>::witness_map(
161 return r1cs_ppzksnark_verifier_strong_IC<ppzksnark_ppT>(*vk, witness, r1cs_proof);
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,
180 throw std::runtime_error("JoinSplit proving key not loaded");
183 // Compute nullifiers of inputs
184 for (size_t i = 0; i < NumInputs; i++) {
185 out_nullifiers[i] = inputs[i].nullifier();
189 out_randomSeed = random_uint256();
192 uint256 h_sig = this->h_sig(out_randomSeed, out_nullifiers, pubKeyHash);
195 uint252 phi = random_uint252();
197 // Compute notes for outputs
198 for (size_t i = 0; i < NumOutputs; i++) {
200 uint256 r = random_uint256();
202 out_notes[i] = outputs[i].note(phi, r, i, h_sig);
205 // Compute the output commitments
206 for (size_t i = 0; i < NumOutputs; i++) {
207 out_commitments[i] = out_notes[i].cm();
210 // Encrypt the ciphertexts containing the note
211 // plaintexts to the recipients of the value.
213 ZCNoteEncryption encryptor(h_sig);
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}};
220 NotePlaintext pt(out_notes[i], memo);
222 out_ciphertexts[i] = pt.encrypt(encryptor, outputs[i].addr.pk_enc);
225 out_ephemeralKey = encryptor.get_epk();
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);
235 std::vector<FieldT> primary_input;
236 std::vector<FieldT> aux_input;
239 protoboard<FieldT> pb;
241 joinsplit_gadget<FieldT, NumInputs, NumOutputs> g(pb);
242 g.generate_r1cs_constraints();
243 g.generate_r1cs_witness(
254 if (!pb.is_satisfied()) {
255 throw std::invalid_argument("Constraint system not satisfied by inputs");
258 primary_input = pb.primary_input();
259 aux_input = pb.auxiliary_input();
262 auto proof = r1cs_ppzksnark_prover<ppzksnark_ppT>(
268 std::stringstream ss;
270 std::string serialized_proof = ss.str();
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);
281 template<size_t NumInputs, size_t NumOutputs>
282 JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Generate()
284 JoinSplitCircuit<NumInputs, NumOutputs>::initialize();
285 auto js = new JoinSplitCircuit<NumInputs, NumOutputs>();
291 template<size_t NumInputs, size_t NumOutputs>
292 JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Unopened()
294 JoinSplitCircuit<NumInputs, NumOutputs>::initialize();
295 return new JoinSplitCircuit<NumInputs, NumOutputs>();
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
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'};
307 std::vector<unsigned char> block(randomSeed.begin(), randomSeed.end());
309 for (size_t i = 0; i < NumInputs; i++) {
310 block.insert(block.end(), nullifiers[i].begin(), nullifiers[i].end());
313 block.insert(block.end(), pubKeyHash.begin(), pubKeyHash.end());
317 if (crypto_generichash_blake2b_salt_personal(output.begin(), 32,
318 &block[0], block.size(),
324 throw std::logic_error("hash function failure");
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);
333 return Note(addr.a_pk, value, rho, r);
336 JSOutput::JSOutput() : addr(uint256(), uint256()), value(0) {
337 SpendingKey a_sk = SpendingKey::random();
338 addr = a_sk.address();
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();
349 template class JoinSplit<ZC_NUM_JS_INPUTS,