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