Zeth - Zerocash on Ethereum  0.8
Reference implementation of the Zeth protocol by Clearmatics
prover_server.cpp
Go to the documentation of this file.
1 // Copyright (c) 2015-2022 Clearmatics Technologies Ltd
2 //
3 // SPDX-License-Identifier: LGPL-3.0+
4 
5 #include "libtool/tool_util.hpp"
8 #include "libzeth/core/utils.hpp"
13 #include "zeth_config.h"
14 
15 #include <boost/program_options.hpp>
16 #include <fstream>
17 #include <grpc/grpc.h>
18 #include <grpcpp/security/server_credentials.h>
19 #include <grpcpp/server.h>
20 #include <grpcpp/server_builder.h>
21 #include <grpcpp/server_context.h>
22 #include <libsnark/common/data_structures/merkle_tree.hpp>
23 #include <memory>
24 #include <stdio.h>
25 #include <string>
26 #include <zeth/api/prover.grpc.pb.h>
27 
33 
34 namespace proto = google::protobuf;
35 namespace po = boost::program_options;
36 
37 static void prover_configuration_to_proto(
38  zeth_proto::ProverConfiguration &prover_config_proto)
39 {
40  prover_config_proto.set_zksnark(snark::name);
41  libzeth::pairing_parameters_to_proto<pp>(
42  *prover_config_proto.mutable_pairing_parameters());
43 }
44 
48 class prover_server final : public zeth_proto::Prover::Service
49 {
50 private:
51  circuit_wrapper &prover;
52 
53  // The keypair is the result of the setup. Store a copy internally.
54  snark::keypair keypair;
55 
56  // Optional file to write proofs into (for debugging).
57  boost::filesystem::path extproof_json_output_file;
58 
59  // Optional file to write proofs into (for debugging).
60  boost::filesystem::path proof_output_file;
61 
62  // Optional file to write primary input data into (for debugging).
63  boost::filesystem::path primary_output_file;
64 
65  // Optional file to write full assignments into (for debugging).
66  boost::filesystem::path assignment_output_file;
67 
68 public:
69  explicit prover_server(
70  circuit_wrapper &prover,
71  const snark::keypair &keypair,
72  const boost::filesystem::path &extproof_json_output_file,
73  const boost::filesystem::path &proof_output_file,
74  const boost::filesystem::path &primary_output_file,
75  const boost::filesystem::path &assignment_output_file)
76  : prover(prover)
77  , keypair(keypair)
78  , extproof_json_output_file(extproof_json_output_file)
79  , proof_output_file(proof_output_file)
80  , primary_output_file(primary_output_file)
81  , assignment_output_file(assignment_output_file)
82  {
83  }
84 
85  grpc::Status GetConfiguration(
86  grpc::ServerContext *,
87  const proto::Empty *,
88  zeth_proto::ProverConfiguration *response) override
89  {
90  std::cout << "[ACK] Received the request for configuration\n";
91  prover_configuration_to_proto(*response);
92  return grpc::Status::OK;
93  }
94 
95  grpc::Status GetVerificationKey(
96  grpc::ServerContext *,
97  const proto::Empty *,
98  zeth_proto::VerificationKey *response) override
99  {
100  std::cout << "[ACK] Received the request to get the verification key"
101  << std::endl;
102  std::cout << "[DEBUG] Preparing verification key for response..."
103  << std::endl;
104  try {
105  api_handler::verification_key_to_proto(this->keypair.vk, response);
106  } catch (const std::exception &e) {
107  std::cout << "[ERROR] " << e.what() << std::endl;
108  return grpc::Status(
109  grpc::StatusCode::INVALID_ARGUMENT, grpc::string(e.what()));
110  } catch (...) {
111  std::cout << "[ERROR] In catch all" << std::endl;
112  return grpc::Status(grpc::StatusCode::UNKNOWN, "");
113  }
114 
115  return grpc::Status::OK;
116  }
117 
118  grpc::Status Prove(
119  grpc::ServerContext *,
120  const zeth_proto::ProofInputs *proof_inputs,
121  zeth_proto::ExtendedProofAndPublicData *proof_and_public_data) override
122  {
123  std::cout << "[ACK] Received the request to generate a proof"
124  << std::endl;
125  std::cout << "[DEBUG] Parse received message to compute proof..."
126  << std::endl;
127 
128  // Parse received message to feed to the prover
129  try {
130  // TODO: Factor this into more maintainable smaller functions
131 
132  Field root = libzeth::base_field_element_from_hex<Field>(
133  proof_inputs->mk_root());
134  libzeth::bits64 vpub_in =
135  libzeth::bits64::from_hex(proof_inputs->pub_in_value());
136  libzeth::bits64 vpub_out =
137  libzeth::bits64::from_hex(proof_inputs->pub_out_value());
138  libzeth::bits256 h_sig_in =
139  libzeth::bits256::from_hex(proof_inputs->h_sig());
140  libzeth::bits256 phi_in =
141  libzeth::bits256::from_hex(proof_inputs->phi());
142 
143  if (libzeth::ZETH_NUM_JS_INPUTS != proof_inputs->js_inputs_size()) {
144  std::cout << "[INFO] Request with "
145  << proof_inputs->js_inputs_size()
146  << " inputs. Expecting "
147  << libzeth::ZETH_NUM_JS_INPUTS << "\n";
148  throw std::invalid_argument("Invalid number of JS inputs");
149  }
150  if (libzeth::ZETH_NUM_JS_OUTPUTS !=
151  proof_inputs->js_outputs_size()) {
152  throw std::invalid_argument("Invalid number of JS outputs");
153  }
154 
155  std::cout << "[DEBUG] Process all inputs of the JoinSplit"
156  << std::endl;
157  std::array<
158  libzeth::
159  joinsplit_input<Field, libzeth::ZETH_MERKLE_TREE_DEPTH>,
160  libzeth::ZETH_NUM_JS_INPUTS>
161  joinsplit_inputs;
162  for (size_t i = 0; i < libzeth::ZETH_NUM_JS_INPUTS; i++) {
163  printf(
164  "\r input (%zu / %zu)\n", i, libzeth::ZETH_NUM_JS_INPUTS);
165  const zeth_proto::JoinsplitInput &received_input =
166  proof_inputs->js_inputs(i);
167  joinsplit_inputs[i] = libzeth::joinsplit_input_from_proto<
168  Field,
169  libzeth::ZETH_MERKLE_TREE_DEPTH>(received_input);
170  }
171 
172  std::cout << "[DEBUG] Process all outputs of the JoinSplit"
173  << std::endl;
174  std::array<libzeth::zeth_note, libzeth::ZETH_NUM_JS_OUTPUTS>
175  joinsplit_outputs;
176  for (size_t i = 0; i < libzeth::ZETH_NUM_JS_OUTPUTS; i++) {
177  printf(
178  "\r output (%zu / %zu)\n",
179  i,
180  libzeth::ZETH_NUM_JS_OUTPUTS);
181  const zeth_proto::ZethNote &received_output =
182  proof_inputs->js_outputs(i);
183  libzeth::zeth_note parsed_output =
184  libzeth::zeth_note_from_proto(received_output);
185  joinsplit_outputs[i] = parsed_output;
186  }
187 
188  std::cout << "[DEBUG] Data parsed successfully" << std::endl;
189  std::cout << "[DEBUG] Generating the proof..." << std::endl;
190 
191  std::vector<Field> public_data;
192  libzeth::extended_proof<pp, snark> ext_proof = this->prover.prove(
193  root,
194  joinsplit_inputs,
195  joinsplit_outputs,
196  vpub_in,
197  vpub_out,
198  h_sig_in,
199  phi_in,
200  this->keypair.pk,
201  public_data);
202 
203  std::cout << "[DEBUG] Displaying extended proof and public data\n";
204  ext_proof.write_json(std::cout);
205  for (const Field &f : public_data) {
206  std::cout << libzeth::base_field_element_to_hex(f) << "\n";
207  }
208 
209  // Write a copy of the proof for debugging.
210  if (!extproof_json_output_file.empty()) {
211  std::cout << "[DEBUG] Writing extended proof (JSON) to "
212  << extproof_json_output_file << "\n";
213  std::ofstream out_s(extproof_json_output_file.c_str());
214  ext_proof.write_json(out_s);
215  }
216  if (!proof_output_file.empty()) {
217  std::cout << "[DEBUG] Writing proof to " << proof_output_file
218  << "\n";
219  std::ofstream out_s =
220  libtool::open_binary_output_file(proof_output_file.c_str());
221  snark::proof_write_bytes(ext_proof.get_proof(), out_s);
222  }
223  if (!primary_output_file.empty()) {
224  std::cout << "[DEBUG] Writing primary input to "
225  << primary_output_file << "\n";
226  std::ofstream out_s = libtool::open_binary_output_file(
227  primary_output_file.c_str());
229  ext_proof.get_primary_inputs(), out_s);
230  }
231  if (!assignment_output_file.empty()) {
232  std::cout << "[DEBUG] WARNING! Writing assignment to "
233  << assignment_output_file << "\n";
234  std::ofstream out_s = libtool::open_binary_output_file(
235  assignment_output_file.c_str());
237  prover.get_last_assignment(), out_s);
238  }
239 
240  std::cout << "[DEBUG] Preparing response..." << std::endl;
241  api_handler::extended_proof_to_proto(
242  ext_proof, proof_and_public_data->mutable_extended_proof());
243  for (size_t i = 0; i < public_data.size(); ++i) {
244  proof_and_public_data->add_public_data(
245  libzeth::base_field_element_to_hex(public_data[i]));
246  }
247 
248  } catch (const std::exception &e) {
249  std::cout << "[ERROR] " << e.what() << std::endl;
250  return grpc::Status(
251  grpc::StatusCode::INVALID_ARGUMENT, grpc::string(e.what()));
252  } catch (...) {
253  std::cout << "[ERROR] In catch all" << std::endl;
254  return grpc::Status(grpc::StatusCode::UNKNOWN, "");
255  }
256 
257  return grpc::Status::OK;
258  }
259 };
260 
261 std::string get_server_version()
262 {
263  char buffer[100];
264  int n;
265  // Defined in the zethConfig file
266  n = snprintf(
267  buffer, 100, "Version %d.%d", ZETH_VERSION_MAJOR, ZETH_VERSION_MINOR);
268  if (n < 0) {
269  return "Version <Not specified>";
270  }
271  std::string version(buffer);
272  return version;
273 }
274 
276 {
277  std::string copyright =
278  "Copyright (c) 2015-2022 Clearmatics Technologies Ltd";
279  std::string license = "SPDX-License-Identifier: LGPL-3.0+";
280  std::string project =
281  "R&D Department: PoC for Zerocash on Ethereum/Autonity";
282  std::string version = get_server_version();
283  std::string warning = "**WARNING:** This code is a research-quality proof "
284  "of concept, DO NOT use in production!";
285 
286  std::cout << "\n=====================================================\n";
287  std::cout << copyright << "\n";
288  std::cout << license << "\n";
289  std::cout << project << "\n";
290  std::cout << version << "\n";
291  std::cout << warning << "\n";
292  std::cout << "=====================================================\n"
293  << std::endl;
294 }
295 
296 static void RunServer(
297  circuit_wrapper &prover,
298  const typename snark::keypair &keypair,
299  const boost::filesystem::path &extproof_json_output_file,
300  const boost::filesystem::path &proof_output_file,
301  const boost::filesystem::path &primary_output_file,
302  const boost::filesystem::path &assignment_output_file)
303 {
304  // Listen for incoming connections on 0.0.0.0:50051
305  std::string server_address("0.0.0.0:50051");
306 
307  prover_server service(
308  prover,
309  keypair,
310  extproof_json_output_file,
311  proof_output_file,
312  primary_output_file,
313  assignment_output_file);
314 
315  grpc::ServerBuilder builder;
316 
317  // Listen on the given address without any authentication mechanism.
318  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
319 
320  // Register "service" as the instance through which we'll communicate with
321  // clients. In this case it corresponds to an *synchronous* service.
322  builder.RegisterService(&service);
323 
324  // Finally assemble the server.
325  std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
326  std::cout << "[INFO] Server listening on " << server_address << "\n";
327 
328  // Wait for the server to shutdown. Note that some other thread must be
329  // responsible for shutting down the server for this call to ever return.
331  server->Wait();
332 }
333 
334 int main(int argc, char **argv)
335 {
336  // Options
337  po::options_description options("Options");
338  options.add_options()("help,h", "This help");
339  options.add_options()(
340  "keypair,k",
341  po::value<boost::filesystem::path>(),
342  "file to load keypair from. If it doesn't exist, a new keypair will be "
343  "generated and written to this file. (default: "
344  "~/zeth_setup/keypair.bin)");
345  options.add_options()(
346  "r1cs,r",
347  po::value<boost::filesystem::path>(),
348  "file in which to export the r1cs (in json format)");
349  options.add_options()(
350  "proving-key-output",
351  po::value<boost::filesystem::path>(),
352  "write proving key to file (if generated)");
353  options.add_options()(
354  "verification-key-output",
355  po::value<boost::filesystem::path>(),
356  "write verification key to file (if generated)");
357  options.add_options()(
358  "extproof-json-output",
359  po::value<boost::filesystem::path>(),
360  "(DEBUG) write generated extended proofs (JSON) to file");
361  options.add_options()(
362  "proof-output",
363  po::value<boost::filesystem::path>(),
364  "(DEBUG) write generated proofs to file");
365  options.add_options()(
366  "primary-output",
367  po::value<boost::filesystem::path>(),
368  "(DEBUG) write primary input to file");
369  options.add_options()(
370  "assignment-output",
371  po::value<boost::filesystem::path>(),
372  "(DEBUG) write full assignment to file (INSECURE!)");
373 
374  auto usage = [&]() {
375  std::cout << "Usage:"
376  << "\n"
377  << " " << argv[0] << " [<options>]\n"
378  << "\n";
379  std::cout << options;
380  std::cout << std::endl;
381  };
382 
383  boost::filesystem::path keypair_file;
384  boost::filesystem::path r1cs_file;
385  boost::filesystem::path proving_key_output_file;
386  boost::filesystem::path verification_key_output_file;
387  boost::filesystem::path extproof_json_output_file;
388  boost::filesystem::path proof_output_file;
389  boost::filesystem::path primary_output_file;
390  boost::filesystem::path assignment_output_file;
391  try {
392  po::variables_map vm;
393  po::store(
394  po::command_line_parser(argc, argv).options(options).run(), vm);
395  if (vm.count("help")) {
396  usage();
397  return 0;
398  }
399  if (vm.count("keypair")) {
400  keypair_file = vm["keypair"].as<boost::filesystem::path>();
401  }
402  if (vm.count("r1cs")) {
403  r1cs_file = vm["r1cs"].as<boost::filesystem::path>();
404  }
405  if (vm.count("proving-key-output")) {
406  proving_key_output_file =
407  vm["proving-key-output"].as<boost::filesystem::path>();
408  }
409  if (vm.count("verification-key-output")) {
410  verification_key_output_file =
411  vm["verification-key-output"].as<boost::filesystem::path>();
412  }
413  if (vm.count("extproof-json-output")) {
414  extproof_json_output_file =
415  vm["extproof-json-output"].as<boost::filesystem::path>();
416  }
417  if (vm.count("proof-output")) {
418  proof_output_file =
419  vm["proof-output"].as<boost::filesystem::path>();
420  }
421  if (vm.count("primary-output")) {
422  primary_output_file =
423  vm["primary-output"].as<boost::filesystem::path>();
424  }
425  if (vm.count("assignment-output")) {
426  assignment_output_file =
427  vm["assignment-output"].as<boost::filesystem::path>();
428  }
429  } catch (po::error &error) {
430  std::cerr << " ERROR: " << error.what() << std::endl;
431  usage();
432  return 1;
433  }
434 
435  // Default keypair_file if none given
436  if (keypair_file.empty()) {
437  boost::filesystem::path setup_dir =
439  if (!setup_dir.empty()) {
440  boost::filesystem::create_directories(setup_dir);
441  }
442  keypair_file = setup_dir / "keypair.bin";
443  }
444 
445  // Inititalize the curve parameters
446  std::cout << "[INFO] Init params (" << libzeth::pp_name<pp>() << ")\n";
447  pp::init_public_params();
448 
449  // If the keypair file exists, load and use it, otherwise generate a new
450  // keypair and write it to the file.
451  circuit_wrapper prover;
452  snark::keypair keypair = [&keypair_file,
453  &proving_key_output_file,
454  &verification_key_output_file,
455  &prover]() {
456  if (boost::filesystem::exists(keypair_file)) {
457  std::cout << "[INFO] Loading keypair: " << keypair_file << "\n";
458 
459  snark::keypair keypair;
460  std::ifstream in_s =
461  libtool::open_binary_input_file(keypair_file.c_str());
462  snark::keypair_read_bytes(keypair, in_s);
463  return keypair;
464  }
465 
466  std::cout << "[INFO] No keypair file " << keypair_file
467  << ". Generating.\n";
468  const snark::keypair keypair = prover.generate_trusted_setup();
469 
470  if (!proving_key_output_file.empty()) {
471  std::cout << "[DEBUG] Writing separate proving key to "
472  << proving_key_output_file << "\n";
473  std::ofstream out_s = libtool::open_binary_output_file(
474  proving_key_output_file.c_str());
475  snark::proving_key_write_bytes(keypair.pk, out_s);
476  }
477  if (!verification_key_output_file.empty()) {
478  std::cout << "[DEBUG] Writing separate verification key to "
479  << verification_key_output_file << "\n";
480  std::ofstream out_s = libtool::open_binary_output_file(
481  verification_key_output_file.c_str());
482  snark::verification_key_write_bytes(keypair.vk, out_s);
483  }
484 
485  // Write the keypair last. If something above fails, this same
486  // code-path will be executed again on the next invocation.
487  std::cout << "[INFO] Writing new keypair to " << keypair_file << "\n";
488  std::ofstream out_s =
489  libtool::open_binary_output_file(keypair_file.c_str());
490  snark::keypair_write_bytes(keypair, out_s);
491 
492  return keypair;
493  }();
494 
495  // If a file is given, export the JSON representation of the constraint
496  // system.
497  if (!r1cs_file.empty()) {
498  std::cout << "[INFO] Writing R1CS to " << r1cs_file << "\n";
499  std::ofstream r1cs_stream(r1cs_file.c_str());
500  libzeth::r1cs_write_json(prover.get_constraint_system(), r1cs_stream);
501  }
502 
503  std::cout << "[INFO] Setup successful, starting the server..." << std::endl;
504  RunServer(
505  prover,
506  keypair,
507  extproof_json_output_file,
508  proof_output_file,
509  primary_output_file,
510  assignment_output_file);
511  return 0;
512 }
libzeth::circuit_wrapper
Definition: circuit_wrapper.hpp:27
Field
libzeth::defaults::Field Field
Definition: prover_server.cpp:29
r1cs_serialization.hpp
utils.hpp
libzeth::extended_proof
Definition: extended_proof.hpp:17
libzeth::circuit_wrapper::get_constraint_system
const libsnark::r1cs_constraint_system< Field > & get_constraint_system() const
tool_util.hpp
prover_server::Prove
grpc::Status Prove(grpc::ServerContext *, const zeth_proto::ProofInputs *proof_inputs, zeth_proto::ExtendedProofAndPublicData *proof_and_public_data) override
Definition: prover_server.cpp:118
Field
libzeth::defaults::Field Field
Definition: mpc_subcommand.hpp:11
prover_server::prover_server
prover_server(circuit_wrapper &prover, const snark::keypair &keypair, const boost::filesystem::path &extproof_json_output_file, const boost::filesystem::path &proof_output_file, const boost::filesystem::path &primary_output_file, const boost::filesystem::path &assignment_output_file)
Definition: prover_server.cpp:69
get_server_version
std::string get_server_version()
Definition: prover_server.cpp:261
libzeth::extended_proof::get_proof
const snarkT::proof & get_proof() const
prover_server::GetConfiguration
grpc::Status GetConfiguration(grpc::ServerContext *, const proto::Empty *, zeth_proto::ProverConfiguration *response) override
Definition: prover_server.cpp:85
prover_server::GetVerificationKey
grpc::Status GetVerificationKey(grpc::ServerContext *, const proto::Empty *, zeth_proto::VerificationKey *response) override
Definition: prover_server.cpp:95
libzeth::bits< 64 >
display_server_start_message
void display_server_start_message()
Definition: prover_server.cpp:275
extended_proof.hpp
libtool::open_binary_output_file
std::ofstream open_binary_output_file(const std::string &filename)
Utility function to open a binary file for writing, with appropriate flags.
Definition: tool_util.cpp:19
libzeth::joinsplit_input_from_proto
joinsplit_input< FieldT, TreeDepth > joinsplit_input_from_proto(const zeth_proto::JoinsplitInput &input)
proto_utils.hpp
api_handler
libzeth::defaults::api_handler api_handler
Definition: prover_server.cpp:31
pp
libzeth::defaults::pp pp
Definition: prover_server.cpp:28
libzeth::r1cs_variable_assignment_write_bytes
void r1cs_variable_assignment_write_bytes(const libsnark::r1cs_variable_assignment< FieldT > &assignment, std::ostream &out_s)
setup.version
version
Definition: setup.py:21
libzeth::zeth_note_from_proto
zeth_note zeth_note_from_proto(const zeth_proto::ZethNote &note)
Definition: proto_utils.cpp:32
zeth_constants.hpp
libzeth::r1cs_write_json
std::ostream & r1cs_write_json(const libsnark::r1cs_constraint_system< FieldT > &r1cs, std::ostream &out_s)
main
int main(int argc, char **argv)
Definition: prover_server.cpp:334
libzeth::extended_proof::get_primary_inputs
const libsnark::r1cs_primary_input< libff::Fr< ppT > > & get_primary_inputs() const
setup.name
name
Definition: setup.py:20
pp
defaults::pp pp
Definition: mpc_create_keypair.cpp:14
libzeth::extended_proof::write_json
std::ostream & write_json(std::ostream &) const
libzeth::circuit_wrapper::prove
extended_proof< ppT, snarkT > prove(const Field &root, const std::array< joinsplit_input< Field, TreeDepth >, NumInputs > &inputs, const std::array< zeth_note, NumOutputs > &outputs, const bits64 &vpub_in, const bits64 &vpub_out, const bits256 &h_sig_in, const bits256 &phi_in, const typename snarkT::proving_key &proving_key, std::vector< Field > &out_public_data) const
libtool::open_binary_input_file
std::ifstream open_binary_input_file(const std::string &filename)
Definition: tool_util.cpp:10
prover_server
Definition: prover_server.cpp:48
circuit_types.hpp
libzeth::circuit_wrapper::get_last_assignment
const std::vector< Field > & get_last_assignment() const
libzeth::zeth_note
Definition: note.hpp:15
libzeth::base_field_element_to_hex
std::string base_field_element_to_hex(const FieldT &field_el)
libzeth::get_path_to_setup_directory
boost::filesystem::path get_path_to_setup_directory()
Definition: filesystem_util.cpp:10
libzeth::circuit_wrapper::generate_trusted_setup
snarkT::keypair generate_trusted_setup() const
coordinator.crypto.VerificationKey
VerificationKey
Definition: crypto.py:15
libzeth::bits< 64 >::from_hex
static bits from_hex(const std::string &hex)
snark
libzeth::defaults::snark snark
Definition: prover_server.cpp:30
r1cs_variable_assignment_serialization.hpp