Zecale - Reconciling Privacy and Scalability on Smart-Contract Chains  0.5
Reference implementation of the Zecale protocol by Clearmatics
aggregator_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 // Read the zecale config, include the appropriate pairing selector and define
6 // the corresponding pairing parameters type.
7 
11 #include "zecale_config.h"
12 
13 #include <boost/program_options.hpp>
14 #include <fstream>
15 #include <grpc/grpc.h>
16 #include <grpcpp/security/server_credentials.h>
17 #include <grpcpp/server.h>
18 #include <grpcpp/server_builder.h>
19 #include <grpcpp/server_context.h>
20 #include <iostream>
21 #include <libsnark/common/data_structures/merkle_tree.hpp>
22 #include <libtool/tool_util.hpp>
23 #include <libzeth/circuits/circuit_types.hpp>
24 #include <libzeth/core/utils.hpp>
25 #include <libzeth/serialization/proto_utils.hpp>
26 #include <libzeth/serialization/r1cs_serialization.hpp>
27 #include <libzeth/zeth_constants.hpp>
28 #include <map>
29 #include <memory>
30 #include <stdio.h>
31 #include <string>
32 #include <zecale/api/aggregator.grpc.pb.h>
33 
34 namespace proto = google::protobuf;
35 namespace po = boost::program_options;
36 
37 // Set the wrapper curve type (wpp) based on the build configuration.
38 #if defined(ZECALE_CURVE_MNT6)
39 #include <libsnark/gadgetlib1/gadgets/pairing/mnt/mnt_pairing_params.hpp>
40 using wpp = libff::mnt6_pp;
41 #elif defined(ZECALE_CURVE_BW6_761)
42 #include <libsnark/gadgetlib1/gadgets/pairing/bw6_761_bls12_377/bw6_761_pairing_params.hpp>
43 using wpp = libff::bw6_761_pp;
44 #else
45 #error "ZECALE_CURVE_* variable not set to supported curve"
46 #endif
47 
48 // The nested curve type (npp)
49 using npp = libsnark::other_curve<wpp>;
50 
51 // Set both wrapper and nested snark schemes based on the build configuration.
52 #if defined(ZECALE_SNARK_PGHR13)
54 #include <libzeth/snarks/pghr13/pghr13_api_handler.hpp>
55 using wsnark = libzeth::pghr13_snark<wpp>;
56 using wapi_handler = libzeth::pghr13_api_handler<wpp>;
58 using napi_handler = libzeth::pghr13_api_handler<npp>;
59 #elif defined(ZECALE_SNARK_GROTH16)
61 #include <libzeth/snarks/groth16/groth16_api_handler.hpp>
62 using wsnark = libzeth::groth16_snark<wpp>;
63 using wapi_handler = libzeth::groth16_api_handler<wpp>;
65 using napi_handler = libzeth::groth16_api_handler<npp>;
66 #else
67 #error "ZECALE_SNARK_* variable not set to supported ZK snark"
68 #endif
69 
70 using nsnark = typename nverifier::snark;
71 
72 static const size_t batch_size = 2;
73 static const size_t num_inputs_per_nested_proof = 1;
74 
75 using aggregator_circuit =
77 
80 class aggregator_server final : public zecale_proto::Aggregator::Service
81 {
82 private:
83  using application_pool =
85 
86  aggregator_circuit &aggregator;
87 
88  // The keypair is the result of the setup for the aggregation circuit
89  const wsnark::keypair &keypair;
90 
91  // The nested verification key is the vk used to verify the nested proofs
92  std::map<std::string, application_pool *> application_pools;
93 
94 public:
96  aggregator_circuit &aggregator, const wsnark::keypair &keypair)
97  : aggregator(aggregator), keypair(keypair)
98  {
99  }
100 
102  {
103  // Release all application_pool objects.
104  for (const auto &entry : application_pools) {
105  delete entry.second;
106  }
107  application_pools.clear();
108  }
109 
110  grpc::Status GetConfiguration(
111  grpc::ServerContext * /*context*/,
112  const proto::Empty * /*request*/,
113  zecale_proto::AggregatorConfiguration *response) override
114  {
115  std::cout << "[INFO] Request for configuration\n";
116  libzecale::aggregator_configuration_to_proto<npp, wpp, nsnark, wsnark>(
117  *response);
118  return grpc::Status::OK;
119  }
120 
121  grpc::Status GetVerificationKey(
122  grpc::ServerContext * /*context*/,
123  const proto::Empty * /*request*/,
124  zeth_proto::VerificationKey *response) override
125  {
126  std::cout << "[ACK] Received the request to get the verification key"
127  << std::endl;
128  std::cout << "[DEBUG] Preparing verification key for response..."
129  << std::endl;
130  try {
131  wapi_handler::verification_key_to_proto(keypair.vk, response);
132  } catch (const std::exception &e) {
133  std::cout << "[ERROR] " << e.what() << std::endl;
134  return grpc::Status(
135  grpc::StatusCode::INVALID_ARGUMENT, grpc::string(e.what()));
136  } catch (...) {
137  std::cout << "[ERROR] In catch all" << std::endl;
138  return grpc::Status(grpc::StatusCode::UNKNOWN, "");
139  }
140 
141  return grpc::Status::OK;
142  }
143 
145  grpc::ServerContext * /*context*/,
146  const zeth_proto::VerificationKey *request,
147  zecale_proto::VerificationKeyHash *response) override
148  {
149  typename nsnark::verification_key vk =
150  napi_handler::verification_key_from_proto(*request);
151  const libff::Fr<wpp> vk_hash =
153  compute_hash(vk, num_inputs_per_nested_proof);
154  const std::string vk_hash_str = libzeth::field_element_to_json(vk_hash);
155  response->set_hash(vk_hash_str);
156 
157  std::cout << "[DEBUG] GetNestedVerificationKeyHash: "
158  << "vk:\n";
159  nsnark::verification_key_write_json(vk, std::cout);
160  std::cout << "\n VK hash: " << vk_hash_str << "\n";
161  return grpc::Status::OK;
162  }
163 
164  grpc::Status RegisterApplication(
165  grpc::ServerContext * /*context*/,
166  const zecale_proto::ApplicationDescription *registration,
167  zecale_proto::VerificationKeyHash *response) override
168  {
169  std::cout << "[ACK] Received 'register application' request"
170  << std::endl;
171  std::cout << "[DEBUG] Registering application..." << std::endl;
172 
173  try {
174  // Ensure an app of the same name has not already been registered.
175  const std::string &name = registration->application_name();
176  if (application_pools.count(name)) {
177  return grpc::Status(
178  grpc::StatusCode::INVALID_ARGUMENT,
179  grpc::string("application already registered"));
180  }
181 
182  // Add the application to the list of supported applications on the
183  // aggregator server.
184  const zeth_proto::VerificationKey &vk_proto = registration->vk();
185  typename nsnark::verification_key vk =
186  napi_handler::verification_key_from_proto(vk_proto);
187  application_pools[name] = new application_pool(name, vk);
188  const libff::Fr<wpp> vk_hash =
190  compute_hash(vk, num_inputs_per_nested_proof);
191  const std::string vk_hash_str =
192  libzeth::field_element_to_json(vk_hash);
193  response->set_hash(vk_hash_str);
194 
195  std::cout << "[DEBUG] Registered application '" << name
196  << " with VK:\n";
197  nsnark::verification_key_write_json(vk, std::cout);
198  std::cout << "\n VK hash: " << vk_hash_str << "\n";
199  } catch (const std::exception &e) {
200  std::cout << "[ERROR] " << e.what() << std::endl;
201  return grpc::Status(
202  grpc::StatusCode::INVALID_ARGUMENT, grpc::string(e.what()));
203  } catch (...) {
204  std::cout << "[ERROR] In catch all" << std::endl;
205  return grpc::Status(grpc::StatusCode::UNKNOWN, "");
206  }
207 
208  return grpc::Status::OK;
209  }
210 
212  grpc::ServerContext * /*context*/,
213  const zecale_proto::NestedTransaction *transaction,
214  proto::Empty * /*response*/) override
215  {
216  try {
217  // Get the application_pool if it exists (otherwise an exception is
218  // thrown, returning an error to the client).
219  const std::string &app_name = transaction->application_name();
220  std::cout << "[ACK] Received nested transaction, app name: "
221  << app_name << std::endl;
222  application_pool *const app_pool = application_pools.at(app_name);
223 
224  // Sanity-check the transaction (number of inputs).
226  libzecale::nested_transaction_from_proto<npp, napi_handler>(
227  *transaction);
228  if (tx.extended_proof().get_primary_inputs().size() !=
229  num_inputs_per_nested_proof) {
230  throw std::invalid_argument("invalid number of inputs");
231  }
232 
233  // Add the proof to the pool for the named application.
234  app_pool->add_tx(tx);
235 
236  std::cout << "[DEBUG] Registered tx with ext proof:\n";
237  tx.extended_proof().write_json(std::cout) << "\n";
238 
239  std::cout << "[DEBUG] " << std::to_string(app_pool->tx_pool_size())
240  << " txs in pool\n";
241  } catch (const std::exception &e) {
242  std::cout << "[ERROR] " << e.what() << std::endl;
243  return grpc::Status(
244  grpc::StatusCode::INVALID_ARGUMENT, grpc::string(e.what()));
245  } catch (...) {
246  std::cout << "[ERROR] In catch all" << std::endl;
247  return grpc::Status(grpc::StatusCode::UNKNOWN, "");
248  }
249 
250  return grpc::Status::OK;
251  }
252 
254  grpc::ServerContext * /*context*/,
255  const zecale_proto::AggregatedTransactionRequest *request,
256  zecale_proto::AggregatedTransaction *response) override
257  {
258  try {
259  // Get the application_pool if it exists (otherwise an exception is
260  // thrown, returning an error to the client).
261  const std::string &app_name = request->application_name();
262  std::cout << "[ACK] Aggregation tx request, app name: " << app_name
263  << std::endl;
264  application_pool *const app_pool = application_pools.at(app_name);
265 
266  // Retrieve a batch from the pool.
267  std::array<libzecale::nested_transaction<npp, nsnark>, batch_size>
268  batch;
269  const size_t num_entries = app_pool->get_next_batch(batch);
270  std::cout << "[DEBUG] Got batch of size "
271  << std::to_string(num_entries) << " from the pool\n";
272  if (num_entries == 0) {
273  throw std::runtime_error("insufficient entries in pool");
274  }
275 
276  // Extract the nested proofs
277  std::array<const libzeth::extended_proof<npp, nsnark> *, batch_size>
278  nested_proofs;
279  for (size_t i = 0; i < batch_size; ++i) {
280  nested_proofs[i] = &batch[i].extended_proof();
281 
282  std::cout << "[DEBUG] got tx " << std::to_string(i)
283  << " with ext proof:\n";
284  nested_proofs[i]->write_json(std::cout);
285  }
286 
287  // Retrieve the nested verification key for this application.
288  const nsnark::verification_key &nested_vk =
289  app_pool->verification_key();
290 
291  std::cout << "[DEBUG] Generating the batched proof...\n";
292  libzeth::extended_proof<wpp, wsnark> wrapping_proof =
293  aggregator.prove(nested_vk, nested_proofs, keypair.pk);
294 
295  std::cout << "[DEBUG] Generated extended proof:\n";
296  wrapping_proof.write_json(std::cout);
297 
298  // Populate the response with name, extended_proof and
299  // nested_parameters.
300  response->set_application_name(app_name);
301  zeth_proto::ExtendedProof *wrapping_proof_proto =
302  new zeth_proto::ExtendedProof();
303  wapi_handler::extended_proof_to_proto(
304  wrapping_proof, wrapping_proof_proto);
305  response->set_allocated_extended_proof(wrapping_proof_proto);
306  for (size_t i = 0; i < batch_size; ++i) {
307  const std::vector<uint8_t> &parameters = batch[i].parameters();
308  response->add_nested_parameters(
309  (const char *)parameters.data(), parameters.size());
310  }
311  std::cout << "[DEBUG] Written to response" << std::endl;
312  } catch (const std::exception &e) {
313  std::cout << "[ERROR] " << e.what() << std::endl;
314  return grpc::Status(
315  grpc::StatusCode::INVALID_ARGUMENT, grpc::string(e.what()));
316  } catch (...) {
317  std::cout << "[ERROR] In catch all" << std::endl;
318  return grpc::Status(grpc::StatusCode::UNKNOWN, "");
319  }
320 
321  return grpc::Status::OK;
322  }
323 };
324 
325 std::string get_server_version()
326 {
327  char buffer[100];
328  int n;
329  // Defined in the zecale_config file
330  n = snprintf(
331  buffer,
332  100,
333  "Version %d.%d",
334  ZECALE_VERSION_MAJOR,
335  ZECALE_VERSION_MINOR);
336  if (n < 0) {
337  return "Version <Not specified>";
338  }
339  std::string version(buffer);
340  return version;
341 }
342 
344 {
345  std::string copyright =
346  "Copyright (c) 2015-2022 Clearmatics Technologies Ltd";
347  std::string license = "SPDX-License-Identifier: LGPL-3.0+";
348  std::string project = "R&D Department: PoC for a privacy preserving "
349  "scalability solution on Ethereum";
350  std::string version = get_server_version();
351  std::string warning = "**WARNING:** This code is a research-quality proof "
352  "of concept, DO NOT use in production!";
353 
354  std::cout << "\n=====================================================\n";
355  std::cout << copyright << "\n";
356  std::cout << license << "\n";
357  std::cout << project << "\n";
358  std::cout << version << "\n";
359  std::cout << warning << "\n";
360  std::cout << "=====================================================\n"
361  << std::endl;
362 }
363 
364 static void RunServer(
365  aggregator_circuit &aggregator, const typename wsnark::keypair &keypair)
366 {
367  // Listen for incoming connections on 0.0.0.0:50052
368  // TODO: Move this in a config file
369  std::string server_address("0.0.0.0:50052");
370 
371  aggregator_server service(aggregator, keypair);
372 
373  grpc::ServerBuilder builder;
374 
375  // Listen on the given address without any authentication mechanism.
376  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
377 
378  // Register "service" as the instance through which we'll communicate with
379  // clients. In this case it corresponds to an *synchronous* service.
380  builder.RegisterService(&service);
381 
382  // Finally assemble the server.
383  std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
384  std::cout << "[DEBUG] Server listening on " << server_address << std::endl;
385 
386  // Wait for the server to shutdown. Note that some other thread must be
387  // responsible for shutting down the server for this call to ever return.
389  server->Wait();
390 }
391 
392 int main(int argc, char **argv)
393 {
394  // Options
395  po::options_description options("");
396  options.add_options()(
397  "keypair,k",
398  po::value<boost::filesystem::path>(),
399  "file to load keypair from");
400 #ifdef DEBUG
401  options.add_options()(
402  "r1cs,r",
403  po::value<boost::filesystem::path>(),
404  "file in which to export the r1cs in json format");
405 #endif
406 
407  auto usage = [&]() {
408  std::cout << "Usage:"
409  << "\n"
410  << " " << argv[0] << " [<options>]\n"
411  << "\n";
412  std::cout << options;
413  std::cout << std::endl;
414  };
415 
416  boost::filesystem::path keypair_file;
417  boost::filesystem::path r1cs_file;
418  try {
419  po::variables_map vm;
420  po::store(
421  po::command_line_parser(argc, argv).options(options).run(), vm);
422  if (vm.count("help")) {
423  usage();
424  return 0;
425  }
426  if (vm.count("keypair")) {
427  keypair_file = vm["keypair"].as<boost::filesystem::path>();
428  }
429  if (vm.count("r1cs")) {
430  r1cs_file = vm["r1cs"].as<boost::filesystem::path>();
431  }
432  } catch (po::error &error) {
433  std::cerr << " ERROR: " << error.what() << std::endl;
434  usage();
435  return 1;
436  }
437 
438  // Default keypair_file if none given
439  if (keypair_file.empty()) {
440  boost::filesystem::path setup_dir =
441  libzeth::get_path_to_setup_directory();
442  if (!setup_dir.empty()) {
443  boost::filesystem::create_directories(setup_dir);
444  }
445  keypair_file = setup_dir / "zecale_keypair.bin";
446  }
447 
448  // Inititalize the curve parameters
449  std::cout << "[INFO] Init params of both curves" << std::endl;
450  npp::init_public_params();
451  wpp::init_public_params();
452 
453  // Set up the aggregator circuit
454  aggregator_circuit aggregator(num_inputs_per_nested_proof);
455 
456  // Load or generate the keypair
457  wsnark::keypair keypair = [&keypair_file, &aggregator]() {
458  if (boost::filesystem::exists(keypair_file)) {
459  std::cout << "[INFO] Loading keypair: " << keypair_file << "\n";
460  wsnark::keypair keypair;
461  std::ifstream in_s =
462  libtool::open_binary_input_file(keypair_file.c_str());
463  wsnark::keypair_read_bytes(keypair, in_s);
464 
465  // Check the VK is for the correct number of inputs.
466  if (keypair.vk.ABC_g1.size() != aggregator.num_primary_inputs()) {
467  throw std::invalid_argument("invalid VK");
468  }
469 
470  return keypair;
471  }
472 
473  std::cout << "[INFO] No keypair file " << keypair_file
474  << ". Generating.\n";
475  const wsnark::keypair keypair = aggregator.generate_trusted_setup();
476 
477  // Check the VK is for the correct number of inputs.
478  if (keypair.vk.ABC_g1.size() != aggregator.num_primary_inputs()) {
479  throw std::invalid_argument("invalid VK");
480  }
481 
482  const size_t num_constraints =
483  aggregator.get_constraint_system().num_constraints();
484  std::cout << "[INFO] Circuit has " << std::to_string(num_constraints)
485  << " constraints\n";
486 
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  wsnark::keypair_write_bytes(keypair, out_s);
491  return keypair;
492  }();
493 
494  // If a file has been given for the JSON representation of the circuit,
495  // write it out.
496  if (!r1cs_file.empty()) {
497  std::cout << "[INFO] Writing R1CS to " << std::endl;
498  std::ofstream r1cs_stream(r1cs_file.c_str());
499  libzeth::r1cs_write_json(
500  aggregator.get_constraint_system(), r1cs_stream);
501  }
502 
503  // Launch the server
504  std::cout << "[INFO] Setup successful, starting the server..." << std::endl;
505  RunServer(aggregator, keypair);
506  return 0;
507 }
main
int main(int argc, char **argv)
Definition: aggregator_server.cpp:392
aggregator_circuit.hpp
application_pool.hpp
display_server_start_message
void display_server_start_message()
Definition: aggregator_server.cpp:343
libzecale::application_pool::get_next_batch
size_t get_next_batch(std::array< nested_transaction< nppT, nsnarkT >, NumProofs > &batch)
libzecale::nested_transaction
Definition: nested_transaction.hpp:17
aggregator_server::RegisterApplication
grpc::Status RegisterApplication(grpc::ServerContext *, const zecale_proto::ApplicationDescription *registration, zecale_proto::VerificationKeyHash *response) override
Definition: aggregator_server.cpp:164
aggregator_server::GetConfiguration
grpc::Status GetConfiguration(grpc::ServerContext *, const proto::Empty *, zecale_proto::AggregatorConfiguration *response) override
Definition: aggregator_server.cpp:110
libzecale::groth16_verifier_parameters
Type definitions to use the groth16 verifier circuit.
Definition: groth16_verifier_parameters.hpp:15
aggregator_server::GetVerificationKey
grpc::Status GetVerificationKey(grpc::ServerContext *, const proto::Empty *, zeth_proto::VerificationKey *response) override
Definition: aggregator_server.cpp:121
aggregator_server::SubmitNestedTransaction
grpc::Status SubmitNestedTransaction(grpc::ServerContext *, const zecale_proto::NestedTransaction *transaction, proto::Empty *) override
Definition: aggregator_server.cpp:211
aggregator_server::GenerateAggregatedTransaction
grpc::Status GenerateAggregatedTransaction(grpc::ServerContext *, const zecale_proto::AggregatedTransactionRequest *request, zecale_proto::AggregatedTransaction *response) override
Definition: aggregator_server.cpp:253
npp
libsnark::other_curve< wpp > npp
Definition: aggregator_server.cpp:49
nsnark
typename nverifier::snark nsnark
Definition: aggregator_server.cpp:70
aggregator_server
Definition: aggregator_server.cpp:80
proto_utils.hpp
libzecale::verification_key_hash_gadget::compute_hash
static FieldT compute_hash(const typename nsnark::verification_key &vk, size_t num_inputs)
libzecale::nested_transaction::extended_proof
const libzeth::extended_proof< nppT, nsnarkT > & extended_proof() const
setup.version
version
Definition: setup.py:19
libzecale::aggregator_circuit
Definition: aggregator_circuit.hpp:33
libzecale::aggregator_circuit::num_primary_inputs
size_t num_primary_inputs() const
pghr13_verifier_parameters.hpp
aggregator_server::~aggregator_server
virtual ~aggregator_server()
Definition: aggregator_server.cpp:101
setup.name
name
Definition: setup.py:18
libzecale::application_pool::add_tx
void add_tx(const nested_transaction< nppT, nsnarkT > &tx)
Add transaction to the pool.
groth16_verifier_parameters.hpp
aggregator_server::GetNestedVerificationKeyHash
grpc::Status GetNestedVerificationKeyHash(grpc::ServerContext *, const zeth_proto::VerificationKey *request, zecale_proto::VerificationKeyHash *response) override
Definition: aggregator_server.cpp:144
aggregator_server::aggregator_server
aggregator_server(aggregator_circuit &aggregator, const wsnark::keypair &keypair)
Definition: aggregator_server.cpp:95
libzecale::application_pool::verification_key
const nsnarkT::verification_key & verification_key() const
libzecale::aggregator_circuit::prove
extended_proof< wppT, wsnarkT > prove(const typename nsnark::verification_key &nested_vk, const std::array< const libzeth::extended_proof< npp, nsnark > *, NumProofs > &extended_proofs, const typename wsnarkT::proving_key &aggregator_proving_key)
Generate a proof and returns an extended proof.
get_server_version
std::string get_server_version()
Definition: aggregator_server.cpp:325
libzecale::application_pool
Definition: application_pool.hpp:22
libzecale::pghr13_verifier_parameters
Definition: pghr13_verifier_parameters.hpp:14
libzecale::application_pool::tx_pool_size
size_t tx_pool_size() const
Returns the number of transactions in the _tx_pool.
libzecale::aggregator_circuit::get_constraint_system
const libsnark::r1cs_constraint_system< libff::Fr< wppT > > & get_constraint_system() const
libzecale::aggregator_circuit::generate_trusted_setup
wsnarkT::keypair generate_trusted_setup() const