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