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