uncrustify as demanded.
[oweals/gnunet.git] / src / secretsharing / gnunet-secretsharing-profiler.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2014 GNUnet e.V.
4
5       GNUnet is free software: you can redistribute it and/or modify it
6       under the terms of the GNU Affero General Public License as published
7       by the Free Software Foundation, either version 3 of the License,
8       or (at your option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       Affero General Public License for more details.
14
15       You should have received a copy of the GNU Affero General Public License
16       along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20
21 /**
22  * @file secretsharing/gnunet-secretsharing-profiler.c
23  * @brief profiling tool for distributed key generation and decryption
24  * @author Florian Dold
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_secretsharing_service.h"
29 #include "gnunet_testbed_service.h"
30
31 /**
32  * How many peers should participate in the key generation?
33  */
34 static unsigned int num_peers = 3;
35
36 /**
37  * What should the threshold for then key be?
38  */
39 static unsigned int threshold = 2;
40
41 /**
42  * Should we try to decrypt a value after the key generation?
43  */
44 static int decrypt = GNUNET_NO;
45
46 /**
47  * When would we like to see the operation finished?
48  */
49 static struct GNUNET_TIME_Relative timeout;
50
51 /**
52  * When should dkg communication start?
53  */
54 static struct GNUNET_TIME_Relative delay;
55
56 /**
57  * Handles for secretsharing sessions.
58  */
59 static struct GNUNET_SECRETSHARING_Session **session_handles;
60
61 static struct GNUNET_SECRETSHARING_DecryptionHandle **decrypt_handles;
62
63 /**
64  * Shares we got from the distributed key generation.
65  */
66 static struct GNUNET_SECRETSHARING_Share **shares;
67
68 static struct GNUNET_SECRETSHARING_PublicKey common_pubkey;
69
70
71 static unsigned int num_connected_sessions;
72
73 static unsigned int num_connected_decrypt;
74
75 /**
76  * Handles to the running peers.
77  * When peers[i] is NULL, the i-th peer has stopped.
78  */
79 static struct GNUNET_TESTBED_Peer **peers;
80
81 static struct GNUNET_PeerIdentity *peer_ids;
82
83 static unsigned int num_retrieved_peer_ids;
84
85 static unsigned int num_generated;
86
87 static unsigned int num_decrypted;
88
89 static struct GNUNET_HashCode session_id;
90
91 static unsigned int verbose;
92
93 static struct GNUNET_SECRETSHARING_Plaintext reference_plaintext;
94
95 static struct GNUNET_SECRETSHARING_Ciphertext ciphertext;
96
97 static struct GNUNET_TIME_Absolute dkg_start;
98
99 static struct GNUNET_TIME_Absolute dkg_deadline;
100
101
102 static struct GNUNET_TIME_Absolute decrypt_start;
103
104 static struct GNUNET_TIME_Absolute decrypt_deadline;
105
106 /**
107  * Connect operations, one for every peer.
108  */
109 static struct GNUNET_TESTBED_Operation **connect_ops;
110
111 /**
112  * Are we performing a shutdown right now?
113  */
114 static int in_shutdown;
115
116
117 /**
118  * Signature of the event handler function called by the
119  * respective event controller.
120  *
121  * @param cls closure
122  * @param event information about the event
123  */
124 static void
125 controller_cb(void *cls,
126               const struct GNUNET_TESTBED_EventInformation *event)
127 {
128   GNUNET_assert(0);
129 }
130
131
132 /**
133  * Callback to be called when a service connect operation is completed
134  *
135  * @param cls the callback closure from functions generating an operation
136  * @param op the operation that has been finished
137  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
138  * @param emsg error message in case the operation has failed; will be NULL if
139  *          operation has executed successfully.
140  */
141 static void
142 session_connect_complete(void *cls,
143                          struct GNUNET_TESTBED_Operation *op,
144                          void *ca_result,
145                          const char *emsg)
146 {
147   if (NULL != emsg)
148     {
149       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
150                  "testbed connect emsg: %s\n",
151                  emsg);
152       GNUNET_assert(0);
153     }
154
155   num_connected_sessions++;
156
157   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
158              "dkg: session connect complete\n");
159
160   if (num_connected_sessions == num_peers)
161     {
162       GNUNET_log(GNUNET_ERROR_TYPE_INFO,
163                  "dkg: all peers connected\n");
164     }
165 }
166
167
168 /**
169  * Callback to be called when a service connect operation is completed
170  *
171  * @param cls the callback closure from functions generating an operation
172  * @param op the operation that has been finished
173  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
174  * @param emsg error message in case the operation has failed; will be NULL if
175  *          operation has executed successfully.
176  */
177 static void
178 decrypt_connect_complete(void *cls,
179                          struct GNUNET_TESTBED_Operation *op,
180                          void *ca_result,
181                          const char *emsg)
182 {
183   if (NULL != emsg)
184     {
185       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
186                  "testbed connect emsg: %s\n",
187                  emsg);
188       GNUNET_assert(0);
189     }
190
191   num_connected_decrypt++;
192
193   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
194              "decrypt: session connect complete\n");
195
196   if (num_connected_decrypt == num_peers)
197     {
198       GNUNET_log(GNUNET_ERROR_TYPE_INFO,
199                  "decrypt: all peers connected\n");
200     }
201 }
202
203
204 /**
205  * Called when a decryption has succeeded.
206  *
207  * @param cls Plaintext
208  * @param plaintext Plaintext
209  */
210 static void decrypt_cb(void *cls,
211                        const struct GNUNET_SECRETSHARING_Plaintext *plaintext)
212 {
213   struct GNUNET_SECRETSHARING_DecryptionHandle **dhp = cls;
214   unsigned int n = dhp - decrypt_handles;
215
216   num_decrypted++;
217
218   *dhp = NULL;
219
220   // we should still be connected if this is called
221   GNUNET_assert(NULL != connect_ops[n]);
222
223   GNUNET_TESTBED_operation_done(connect_ops[n]);
224
225   if (NULL == plaintext)
226     {
227       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "decrypt failed for peer %u\n", n);
228       return;
229     }
230   else if (0 == GNUNET_memcmp(&reference_plaintext, plaintext))
231     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "decrypt got correct result for peer %u\n", n);
232   else
233     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "decrypt got wrong result for peer %u\n", n);
234
235   if (num_decrypted == num_peers)
236     {
237       GNUNET_log(GNUNET_ERROR_TYPE_INFO, "every peer decrypted\n");
238       GNUNET_SCHEDULER_shutdown();
239     }
240
241   *dhp = NULL;
242 }
243
244
245
246 /**
247  * Adapter function called to establish a connection to
248  * a service.
249  *
250  * @param cls closure
251  * @param cfg configuration of the peer to connect to; will be available until
252  *          GNUNET_TESTBED_operation_done() is called on the operation returned
253  *          from GNUNET_TESTBED_service_connect()
254  * @return service handle to return in 'op_result', NULL on error
255  */
256 static void *
257 decrypt_connect_adapter(void *cls,
258                         const struct GNUNET_CONFIGURATION_Handle *cfg)
259 {
260   struct GNUNET_SECRETSHARING_DecryptionHandle **hp = cls;
261   unsigned int n = hp - decrypt_handles;
262
263   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
264              "decrypt connect adapter, %d peers\n",
265              num_peers);
266   *hp = GNUNET_SECRETSHARING_decrypt(cfg, shares[n], &ciphertext,
267                                      decrypt_start, decrypt_deadline,
268                                      decrypt_cb,
269                                      hp);
270
271   return *hp;
272 }
273
274
275 /**
276  * Adapter function called to destroy a connection to
277  * a service.
278  *
279  * @param cls closure
280  * @param op_result service handle returned from the connect adapter
281  */
282 static void
283 decrypt_disconnect_adapter(void *cls, void *op_result)
284 {
285   struct GNUNET_SECRETSHARING_DecryptionHandle **dh = cls;
286   unsigned int n = dh - decrypt_handles;
287
288   GNUNET_assert(*dh == decrypt_handles[n]);
289
290   if (NULL != *dh)
291     {
292       GNUNET_SECRETSHARING_decrypt_cancel(*dh);
293       *dh = NULL;
294     }
295
296   GNUNET_assert(NULL != connect_ops[n]);
297   connect_ops[n] = NULL;
298 }
299
300
301 static void
302 secret_ready_cb(void *cls,
303                 struct GNUNET_SECRETSHARING_Share *my_share,
304                 struct GNUNET_SECRETSHARING_PublicKey *public_key,
305                 unsigned int num_ready_peers,
306                 const struct GNUNET_PeerIdentity *ready_peers)
307 {
308   struct GNUNET_SECRETSHARING_Session **sp = cls;
309   unsigned int n = sp - session_handles;
310   char pubkey_str[1024];
311   char *ret;
312
313   num_generated++;
314   *sp = NULL;
315   shares[n] = my_share;
316   if (NULL == my_share)
317     {
318       GNUNET_log(GNUNET_ERROR_TYPE_INFO, "key generation failed for peer #%u\n", n);
319     }
320   else
321     {
322       ret = GNUNET_STRINGS_data_to_string(public_key, sizeof *public_key, pubkey_str, 1024);
323       GNUNET_assert(NULL != ret);
324       *ret = '\0';
325       GNUNET_log(GNUNET_ERROR_TYPE_INFO, "key generation successful for peer #%u, pubkey %s\n", n,
326                  pubkey_str);
327
328       /* we're the first to get the key -> store it */
329       if (num_generated == 1)
330         {
331           common_pubkey = *public_key;
332         }
333       else if (0 != GNUNET_memcmp(public_key, &common_pubkey))
334         {
335           GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "generated public keys do not match\n");
336           GNUNET_SCHEDULER_shutdown();
337           return;
338         }
339     }
340
341   // we should still be connected
342   GNUNET_assert(NULL != connect_ops[n]);
343
344   // disconnect from the service, will call the disconnect callback
345   GNUNET_TESTBED_operation_done(connect_ops[n]);
346 }
347
348
349 /**
350  * Adapter function called to establish a connection to
351  * a service.
352  *
353  * @param cls closure
354  * @param cfg configuration of the peer to connect to; will be available until
355  *          GNUNET_TESTBED_operation_done() is called on the operation returned
356  *          from GNUNET_TESTBED_service_connect()
357  * @return service handle to return in 'op_result', NULL on error
358  */
359 static void *
360 session_connect_adapter(void *cls,
361                         const struct GNUNET_CONFIGURATION_Handle *cfg)
362 {
363   struct GNUNET_SECRETSHARING_Session **sp = cls;
364
365   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
366              "connect adapter, %d peers\n",
367              num_peers);
368   *sp = GNUNET_SECRETSHARING_create_session(cfg,
369                                             num_peers,
370                                             peer_ids,
371                                             &session_id,
372                                             dkg_start,
373                                             dkg_deadline,
374                                             threshold,
375                                             &secret_ready_cb, sp);
376   return *sp;
377 }
378
379
380
381 /**
382  * Adapter function called to destroy a connection to
383  * a service.
384  *
385  * @param cls closure
386  * @param op_result service handle returned from the connect adapter
387  */
388 static void
389 session_disconnect_adapter(void *cls, void *op_result)
390 {
391   struct GNUNET_SECRETSHARING_Session **sp = cls;
392   unsigned int n = (sp - session_handles);
393
394   GNUNET_assert(*sp == session_handles[n]);
395
396   if (NULL != *sp)
397     {
398       GNUNET_SECRETSHARING_session_destroy(*sp);
399       *sp = NULL;
400     }
401
402   GNUNET_assert(NULL != connect_ops[n]);
403   connect_ops[n] = NULL;
404
405   if (GNUNET_YES == in_shutdown)
406     return;
407
408   // all peers received their secret
409   if (num_generated == num_peers)
410     {
411       int i;
412
413       // only do decryption if requested by the user
414       if (GNUNET_NO == decrypt)
415         {
416           GNUNET_SCHEDULER_shutdown();
417           return;
418         }
419
420       decrypt_start = GNUNET_TIME_absolute_add(GNUNET_TIME_absolute_get(), delay);
421       decrypt_deadline = GNUNET_TIME_absolute_add(decrypt_start, timeout);
422
423       // compute g^42 as the plaintext which we will decrypt and then
424       // cooperatively decrypt
425       GNUNET_SECRETSHARING_plaintext_generate_i(&reference_plaintext, 42);
426       GNUNET_SECRETSHARING_encrypt(&common_pubkey, &reference_plaintext, &ciphertext);
427
428       for (i = 0; i < num_peers; i++)
429         connect_ops[i] =
430           GNUNET_TESTBED_service_connect(NULL, peers[i], "secretsharing", &decrypt_connect_complete, NULL,
431                                          &decrypt_connect_adapter, &decrypt_disconnect_adapter, &decrypt_handles[i]);
432     }
433 }
434
435
436 /**
437  * Callback to be called when the requested peer information is available
438  *
439  * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
440  * @param op the operation this callback corresponds to
441  * @param pinfo the result; will be NULL if the operation has failed
442  * @param emsg error message if the operation has failed; will be NULL if the
443  *          operation is successfull
444  */
445 static void
446 peer_info_cb(void *cb_cls,
447              struct GNUNET_TESTBED_Operation *op,
448              const struct GNUNET_TESTBED_PeerInformation *pinfo,
449              const char *emsg)
450 {
451   struct GNUNET_PeerIdentity *p;
452   int i;
453
454   GNUNET_assert(NULL == emsg);
455
456   p = (struct GNUNET_PeerIdentity *)cb_cls;
457
458   if (pinfo->pit == GNUNET_TESTBED_PIT_IDENTITY)
459     {
460       *p = *pinfo->result.id;
461       num_retrieved_peer_ids++;
462       if (num_retrieved_peer_ids == num_peers)
463         for (i = 0; i < num_peers; i++)
464           connect_ops[i] =
465             GNUNET_TESTBED_service_connect(NULL, peers[i], "secretsharing", session_connect_complete, NULL,
466                                            session_connect_adapter, session_disconnect_adapter, &session_handles[i]);
467     }
468   else
469     {
470       GNUNET_assert(0);
471     }
472
473   GNUNET_TESTBED_operation_done(op);
474 }
475
476
477 /**
478  * Signature of the main function of a task.
479  *
480  * @param cls closure
481  */
482 static void
483 handle_shutdown(void *cls)
484 {
485   in_shutdown = GNUNET_YES;
486
487   if (NULL != connect_ops)
488     {
489       unsigned int i;
490       for (i = 0; i < num_peers; i++)
491         if (NULL != connect_ops[i])
492           {
493             // the disconnect callback will set the op to NULL
494             GNUNET_TESTBED_operation_done(connect_ops[i]);
495           }
496       GNUNET_free(connect_ops);
497     }
498
499   // killing the testbed operation will take care of remaining
500   // service handles in the disconnect callback
501 }
502
503
504 /**
505  * Signature of a main function for a testcase.
506  *
507  * @param cls closure
508  * @param h the run handle
509  * @param num_peers number of peers in 'peers'
510  * @param started_peers handle to peers run in the testbed.  NULL upon timeout (see
511  *          GNUNET_TESTBED_test_run()).
512  * @param links_succeeded the number of overlay link connection attempts that
513  *          succeeded
514  * @param links_failed the number of overlay link connection attempts that
515  *          failed
516  */
517 static void
518 test_master(void *cls,
519             struct GNUNET_TESTBED_RunHandle *h,
520             unsigned int num_peers,
521             struct GNUNET_TESTBED_Peer **started_peers,
522             unsigned int links_succeeded,
523             unsigned int links_failed)
524 {
525   int i;
526
527   GNUNET_log_setup("gnunet-secretsharing-profiler", "INFO", NULL);
528
529   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "test master\n");
530
531   GNUNET_SCHEDULER_add_shutdown(&handle_shutdown, NULL);
532
533   peers = started_peers;
534
535   peer_ids = GNUNET_malloc(num_peers * sizeof(struct GNUNET_PeerIdentity));
536
537   session_handles = GNUNET_new_array(num_peers, struct GNUNET_SECRETSHARING_Session *);
538   decrypt_handles = GNUNET_new_array(num_peers, struct GNUNET_SECRETSHARING_DecryptionHandle *);
539   connect_ops = GNUNET_new_array(num_peers, struct GNUNET_TESTBED_Operation *);
540   shares = GNUNET_new_array(num_peers, struct GNUNET_SECRETSHARING_Share *);
541
542   for (i = 0; i < num_peers; i++)
543     {
544       // we do not store the returned operation, as peer_info_cb
545       // will receive it as a parameter and call GNUNET_TESTBED_operation_done.
546       GNUNET_TESTBED_peer_get_information(peers[i],
547                                           GNUNET_TESTBED_PIT_IDENTITY,
548                                           peer_info_cb,
549                                           &peer_ids[i]);
550     }
551 }
552
553
554 static void
555 run(void *cls, char *const *args, const char *cfgfile,
556     const struct GNUNET_CONFIGURATION_Handle *cfg)
557 {
558   static char *session_str = "gnunet-secretsharing/test";
559   char *topology;
560   int topology_cmp_result;
561
562   dkg_start = GNUNET_TIME_absolute_add(GNUNET_TIME_absolute_get(), delay);
563   dkg_deadline = GNUNET_TIME_absolute_add(dkg_start, timeout);
564
565   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, "testbed", "OVERLAY_TOPOLOGY", &topology))
566     {
567       fprintf(stderr,
568               "'OVERLAY_TOPOLOGY' not found in 'testbed' config section, "
569               "seems like you passed the wrong configuration file\n");
570       return;
571     }
572
573   topology_cmp_result = strcasecmp(topology, "NONE");
574   GNUNET_free(topology);
575
576   if (0 == topology_cmp_result)
577     {
578       fprintf(stderr,
579               "'OVERLAY_TOPOLOGY' set to 'NONE', "
580               "seems like you passed the wrong configuration file\n");
581       return;
582     }
583
584   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
585              "running gnunet-secretsharing-profiler\n");
586
587   GNUNET_CRYPTO_hash(session_str, strlen(session_str), &session_id);
588
589   (void)GNUNET_TESTBED_test_run("gnunet-secretsharing-profiler",
590                                 cfgfile,
591                                 num_peers,
592                                 0,
593                                 controller_cb,
594                                 NULL,
595                                 test_master,
596                                 NULL);
597 }
598
599
600 int
601 main(int argc, char **argv)
602 {
603   struct GNUNET_GETOPT_CommandLineOption options[] = {
604     GNUNET_GETOPT_option_uint('n',
605                               "num-peers",
606                               NULL,
607                               gettext_noop("number of peers in consensus"),
608                               &num_peers),
609
610     GNUNET_GETOPT_option_relative_time('D',
611                                        "delay",
612                                        NULL,
613                                        gettext_noop("dkg start delay"),
614                                        &delay),
615
616     GNUNET_GETOPT_option_relative_time('t',
617                                        "timeout",
618                                        NULL,
619                                        gettext_noop("dkg timeout"),
620                                        &timeout),
621
622     GNUNET_GETOPT_option_uint('k',
623                               "threshold",
624                               NULL,
625                               gettext_noop("threshold"),
626                               &threshold),
627
628     GNUNET_GETOPT_option_flag('d',
629                               "descrypt",
630                               gettext_noop("also profile decryption"),
631                               &decrypt),
632
633
634     GNUNET_GETOPT_option_verbose(&verbose),
635
636     GNUNET_GETOPT_OPTION_END
637   };
638
639   delay = GNUNET_TIME_UNIT_ZERO;
640   timeout = GNUNET_TIME_UNIT_MINUTES;
641   GNUNET_PROGRAM_run2(argc, argv, "gnunet-secretsharing-profiler",
642                       "help",
643                       options, &run, NULL, GNUNET_YES);
644   return 0;
645 }