paragraph for gnunet devs that don't know how to use the web
[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
19 /**
20  * @file secretsharing/gnunet-secretsharing-profiler.c
21  * @brief profiling tool for distributed key generation and decryption
22  * @author Florian Dold
23  */
24 #include "platform.h"
25 #include "gnunet_util_lib.h"
26 #include "gnunet_secretsharing_service.h"
27 #include "gnunet_testbed_service.h"
28
29 /**
30  * How many peers should participate in the key generation?
31  */
32 static unsigned int num_peers = 3;
33
34 /**
35  * What should the threshold for then key be?
36  */
37 static unsigned int threshold = 2;
38
39 /**
40  * Should we try to decrypt a value after the key generation?
41  */
42 static int decrypt = GNUNET_NO;
43
44 /**
45  * When would we like to see the operation finished?
46  */
47 static struct GNUNET_TIME_Relative timeout;
48
49 /**
50  * When should dkg communication start?
51  */
52 static struct GNUNET_TIME_Relative delay;
53
54 /**
55  * Handles for secretsharing sessions.
56  */
57 static struct GNUNET_SECRETSHARING_Session **session_handles;
58
59 static struct GNUNET_SECRETSHARING_DecryptionHandle **decrypt_handles;
60
61 /**
62  * Shares we got from the distributed key generation.
63  */
64 static struct GNUNET_SECRETSHARING_Share **shares;
65
66 static struct GNUNET_SECRETSHARING_PublicKey common_pubkey;
67
68
69 static unsigned int num_connected_sessions;
70
71 static unsigned int num_connected_decrypt;
72
73 /**
74  * Handles to the running peers.
75  * When peers[i] is NULL, the i-th peer has stopped.
76  */
77 static struct GNUNET_TESTBED_Peer **peers;
78
79 static struct GNUNET_PeerIdentity *peer_ids;
80
81 static unsigned int num_retrieved_peer_ids;
82
83 static unsigned int num_generated;
84
85 static unsigned int num_decrypted;
86
87 static struct GNUNET_HashCode session_id;
88
89 static unsigned int verbose;
90
91 static struct GNUNET_SECRETSHARING_Plaintext reference_plaintext;
92
93 static struct GNUNET_SECRETSHARING_Ciphertext ciphertext;
94
95 static struct GNUNET_TIME_Absolute dkg_start;
96
97 static struct GNUNET_TIME_Absolute dkg_deadline;
98
99
100 static struct GNUNET_TIME_Absolute decrypt_start;
101
102 static struct GNUNET_TIME_Absolute decrypt_deadline;
103
104 /**
105  * Connect operations, one for every peer.
106  */
107 static struct GNUNET_TESTBED_Operation **connect_ops;
108
109 /**
110  * Are we performing a shutdown right now?
111  */
112 static int in_shutdown;
113
114
115 /**
116  * Signature of the event handler function called by the
117  * respective event controller.
118  *
119  * @param cls closure
120  * @param event information about the event
121  */
122 static void
123 controller_cb (void *cls,
124                const struct GNUNET_TESTBED_EventInformation *event)
125 {
126   GNUNET_assert (0);
127 }
128
129
130 /**
131  * Callback to be called when a service connect operation is completed
132  *
133  * @param cls the callback closure from functions generating an operation
134  * @param op the operation that has been finished
135  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
136  * @param emsg error message in case the operation has failed; will be NULL if
137  *          operation has executed successfully.
138  */
139 static void
140 session_connect_complete (void *cls,
141                           struct GNUNET_TESTBED_Operation *op,
142                           void *ca_result,
143                           const char *emsg)
144 {
145
146   if (NULL != emsg)
147   {
148     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
149                 "testbed connect emsg: %s\n",
150                 emsg);
151     GNUNET_assert (0);
152   }
153
154   num_connected_sessions++;
155
156   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
157               "dkg: session connect complete\n");
158
159   if (num_connected_sessions == num_peers)
160   {
161     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
162                 "dkg: all peers connected\n");
163   }
164 }
165
166
167 /**
168  * Callback to be called when a service connect operation is completed
169  *
170  * @param cls the callback closure from functions generating an operation
171  * @param op the operation that has been finished
172  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
173  * @param emsg error message in case the operation has failed; will be NULL if
174  *          operation has executed successfully.
175  */
176 static void
177 decrypt_connect_complete (void *cls,
178                           struct GNUNET_TESTBED_Operation *op,
179                           void *ca_result,
180                           const char *emsg)
181 {
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   num_decrypted++;
216
217   *dhp = NULL;
218
219   // we should still be connected if this is called
220   GNUNET_assert (NULL != connect_ops[n]);
221
222   GNUNET_TESTBED_operation_done (connect_ops[n]);
223
224   if (NULL == plaintext)
225   {
226     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "decrypt failed for peer %u\n", n);
227     return;
228   }
229   else if (0 == memcmp (&reference_plaintext, plaintext, sizeof (struct GNUNET_SECRETSHARING_Plaintext)))
230     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "decrypt got correct result for peer %u\n", n);
231   else
232     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "decrypt got wrong result for peer %u\n", n);
233
234   if (num_decrypted == num_peers)
235   {
236     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "every peer decrypted\n");
237     GNUNET_SCHEDULER_shutdown ();
238   }
239
240   *dhp = NULL;
241 }
242
243
244
245 /**
246  * Adapter function called to establish a connection to
247  * a service.
248  *
249  * @param cls closure
250  * @param cfg configuration of the peer to connect to; will be available until
251  *          GNUNET_TESTBED_operation_done() is called on the operation returned
252  *          from GNUNET_TESTBED_service_connect()
253  * @return service handle to return in 'op_result', NULL on error
254  */
255 static void *
256 decrypt_connect_adapter (void *cls,
257                  const struct GNUNET_CONFIGURATION_Handle *cfg)
258 {
259   struct GNUNET_SECRETSHARING_DecryptionHandle **hp = cls;
260   unsigned int n = hp - decrypt_handles;
261
262   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
263               "decrypt connect adapter, %d peers\n",
264               num_peers);
265   *hp = GNUNET_SECRETSHARING_decrypt (cfg, shares[n], &ciphertext,
266                                       decrypt_start, decrypt_deadline,
267                                       decrypt_cb,
268                                       hp);
269
270   return *hp;
271 }
272
273
274 /**
275  * Adapter function called to destroy a connection to
276  * a service.
277  *
278  * @param cls closure
279  * @param op_result service handle returned from the connect adapter
280  */
281 static void
282 decrypt_disconnect_adapter(void *cls, void *op_result)
283 {
284   struct GNUNET_SECRETSHARING_DecryptionHandle **dh = cls;
285   unsigned int n = dh - decrypt_handles;
286
287   GNUNET_assert (*dh == decrypt_handles[n]);
288
289   if (NULL != *dh)
290   {
291     GNUNET_SECRETSHARING_decrypt_cancel (*dh);
292     *dh = NULL;
293   }
294
295   GNUNET_assert (NULL != connect_ops[n]);
296   connect_ops[n] = NULL;
297 }
298
299
300 static void
301 secret_ready_cb (void *cls,
302                  struct GNUNET_SECRETSHARING_Share *my_share,
303                  struct GNUNET_SECRETSHARING_PublicKey *public_key,
304                  unsigned int num_ready_peers,
305                  const struct GNUNET_PeerIdentity *ready_peers)
306 {
307   struct GNUNET_SECRETSHARING_Session **sp = cls;
308   unsigned int n = sp - session_handles;
309   char pubkey_str[1024];
310   char *ret;
311
312   num_generated++;
313   *sp = NULL;
314   shares[n] = my_share;
315   if (NULL == my_share)
316   {
317     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "key generation failed for peer #%u\n", n);
318   }
319   else
320   {
321     ret = GNUNET_STRINGS_data_to_string (public_key, sizeof *public_key, pubkey_str, 1024);
322     GNUNET_assert (NULL != ret);
323     *ret = '\0';
324     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "key generation successful for peer #%u, pubkey %s\n", n,
325                 pubkey_str);
326
327     /* we're the first to get the key -> store it */
328     if (num_generated == 1)
329     {
330       common_pubkey = *public_key;
331     }
332     else if (0 != memcmp (public_key, &common_pubkey, sizeof (struct GNUNET_SECRETSHARING_PublicKey)))
333     {
334       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "generated public keys do not match\n");
335       GNUNET_SCHEDULER_shutdown ();
336       return;
337     }
338   }
339
340   // we should still be connected
341   GNUNET_assert (NULL != connect_ops[n]);
342
343   // disconnect from the service, will call the disconnect callback
344   GNUNET_TESTBED_operation_done (connect_ops[n]);
345
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
605     GNUNET_GETOPT_option_uint ('n',
606                                    "num-peers",
607                                    NULL,
608                                    gettext_noop ("number of peers in consensus"),
609                                    &num_peers),
610
611     GNUNET_GETOPT_option_relative_time ('D',
612                                             "delay",
613                                             NULL,
614                                             gettext_noop ("dkg start delay"),
615                                             &delay),
616
617     GNUNET_GETOPT_option_relative_time ('t',
618                                             "timeout",
619                                             NULL,
620                                             gettext_noop ("dkg timeout"),
621                                             &timeout),
622
623     GNUNET_GETOPT_option_uint ('k',
624                                    "threshold",
625                                    NULL,
626                                    gettext_noop ("threshold"),
627                                    &threshold),
628     
629     GNUNET_GETOPT_option_flag ('d',
630                                   "descrypt",
631                                   gettext_noop ("also profile decryption"),
632                                   &decrypt),
633
634
635     GNUNET_GETOPT_option_verbose (&verbose),
636
637     GNUNET_GETOPT_OPTION_END
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 }