2 This file is part of GNUnet
3 Copyright (C) 2014 GNUnet e.V.
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file secretsharing/gnunet-secretsharing-profiler.c
23 * @brief profiling tool for distributed key generation and decryption
24 * @author Florian Dold
27 #include "gnunet_util_lib.h"
28 #include "gnunet_secretsharing_service.h"
29 #include "gnunet_testbed_service.h"
32 * How many peers should participate in the key generation?
34 static unsigned int num_peers = 3;
37 * What should the threshold for then key be?
39 static unsigned int threshold = 2;
42 * Should we try to decrypt a value after the key generation?
44 static int decrypt = GNUNET_NO;
47 * When would we like to see the operation finished?
49 static struct GNUNET_TIME_Relative timeout;
52 * When should dkg communication start?
54 static struct GNUNET_TIME_Relative delay;
57 * Handles for secretsharing sessions.
59 static struct GNUNET_SECRETSHARING_Session **session_handles;
61 static struct GNUNET_SECRETSHARING_DecryptionHandle **decrypt_handles;
64 * Shares we got from the distributed key generation.
66 static struct GNUNET_SECRETSHARING_Share **shares;
68 static struct GNUNET_SECRETSHARING_PublicKey common_pubkey;
71 static unsigned int num_connected_sessions;
73 static unsigned int num_connected_decrypt;
76 * Handles to the running peers.
77 * When peers[i] is NULL, the i-th peer has stopped.
79 static struct GNUNET_TESTBED_Peer **peers;
81 static struct GNUNET_PeerIdentity *peer_ids;
83 static unsigned int num_retrieved_peer_ids;
85 static unsigned int num_generated;
87 static unsigned int num_decrypted;
89 static struct GNUNET_HashCode session_id;
91 static unsigned int verbose;
93 static struct GNUNET_SECRETSHARING_Plaintext reference_plaintext;
95 static struct GNUNET_SECRETSHARING_Ciphertext ciphertext;
97 static struct GNUNET_TIME_Absolute dkg_start;
99 static struct GNUNET_TIME_Absolute dkg_deadline;
102 static struct GNUNET_TIME_Absolute decrypt_start;
104 static struct GNUNET_TIME_Absolute decrypt_deadline;
107 * Connect operations, one for every peer.
109 static struct GNUNET_TESTBED_Operation **connect_ops;
112 * Are we performing a shutdown right now?
114 static int in_shutdown;
118 * Signature of the event handler function called by the
119 * respective event controller.
122 * @param event information about the event
125 controller_cb(void *cls,
126 const struct GNUNET_TESTBED_EventInformation *event)
133 * Callback to be called when a service connect operation is completed
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.
142 session_connect_complete(void *cls,
143 struct GNUNET_TESTBED_Operation *op,
149 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
150 "testbed connect emsg: %s\n",
155 num_connected_sessions++;
157 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
158 "dkg: session connect complete\n");
160 if (num_connected_sessions == num_peers)
162 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
163 "dkg: all peers connected\n");
169 * Callback to be called when a service connect operation is completed
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.
178 decrypt_connect_complete(void *cls,
179 struct GNUNET_TESTBED_Operation *op,
185 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
186 "testbed connect emsg: %s\n",
191 num_connected_decrypt++;
193 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
194 "decrypt: session connect complete\n");
196 if (num_connected_decrypt == num_peers)
198 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
199 "decrypt: all peers connected\n");
205 * Called when a decryption has succeeded.
207 * @param cls Plaintext
208 * @param plaintext Plaintext
210 static void decrypt_cb(void *cls,
211 const struct GNUNET_SECRETSHARING_Plaintext *plaintext)
213 struct GNUNET_SECRETSHARING_DecryptionHandle **dhp = cls;
214 unsigned int n = dhp - decrypt_handles;
220 // we should still be connected if this is called
221 GNUNET_assert(NULL != connect_ops[n]);
223 GNUNET_TESTBED_operation_done(connect_ops[n]);
225 if (NULL == plaintext)
227 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "decrypt failed for peer %u\n", n);
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);
233 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "decrypt got wrong result for peer %u\n", n);
235 if (num_decrypted == num_peers)
237 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "every peer decrypted\n");
238 GNUNET_SCHEDULER_shutdown();
247 * Adapter function called to establish a connection to
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
257 decrypt_connect_adapter(void *cls,
258 const struct GNUNET_CONFIGURATION_Handle *cfg)
260 struct GNUNET_SECRETSHARING_DecryptionHandle **hp = cls;
261 unsigned int n = hp - decrypt_handles;
263 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
264 "decrypt connect adapter, %d peers\n",
266 *hp = GNUNET_SECRETSHARING_decrypt(cfg, shares[n], &ciphertext,
267 decrypt_start, decrypt_deadline,
276 * Adapter function called to destroy a connection to
280 * @param op_result service handle returned from the connect adapter
283 decrypt_disconnect_adapter(void *cls, void *op_result)
285 struct GNUNET_SECRETSHARING_DecryptionHandle **dh = cls;
286 unsigned int n = dh - decrypt_handles;
288 GNUNET_assert(*dh == decrypt_handles[n]);
292 GNUNET_SECRETSHARING_decrypt_cancel(*dh);
296 GNUNET_assert(NULL != connect_ops[n]);
297 connect_ops[n] = NULL;
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)
308 struct GNUNET_SECRETSHARING_Session **sp = cls;
309 unsigned int n = sp - session_handles;
310 char pubkey_str[1024];
315 shares[n] = my_share;
316 if (NULL == my_share)
318 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "key generation failed for peer #%u\n", n);
322 ret = GNUNET_STRINGS_data_to_string(public_key, sizeof *public_key, pubkey_str, 1024);
323 GNUNET_assert(NULL != ret);
325 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "key generation successful for peer #%u, pubkey %s\n", n,
328 /* we're the first to get the key -> store it */
329 if (num_generated == 1)
331 common_pubkey = *public_key;
333 else if (0 != GNUNET_memcmp(public_key, &common_pubkey))
335 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "generated public keys do not match\n");
336 GNUNET_SCHEDULER_shutdown();
341 // we should still be connected
342 GNUNET_assert(NULL != connect_ops[n]);
344 // disconnect from the service, will call the disconnect callback
345 GNUNET_TESTBED_operation_done(connect_ops[n]);
350 * Adapter function called to establish a connection to
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
360 session_connect_adapter(void *cls,
361 const struct GNUNET_CONFIGURATION_Handle *cfg)
363 struct GNUNET_SECRETSHARING_Session **sp = cls;
365 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
366 "connect adapter, %d peers\n",
368 *sp = GNUNET_SECRETSHARING_create_session(cfg,
375 &secret_ready_cb, sp);
382 * Adapter function called to destroy a connection to
386 * @param op_result service handle returned from the connect adapter
389 session_disconnect_adapter(void *cls, void *op_result)
391 struct GNUNET_SECRETSHARING_Session **sp = cls;
392 unsigned int n = (sp - session_handles);
394 GNUNET_assert(*sp == session_handles[n]);
398 GNUNET_SECRETSHARING_session_destroy(*sp);
402 GNUNET_assert(NULL != connect_ops[n]);
403 connect_ops[n] = NULL;
405 if (GNUNET_YES == in_shutdown)
408 // all peers received their secret
409 if (num_generated == num_peers)
413 // only do decryption if requested by the user
414 if (GNUNET_NO == decrypt)
416 GNUNET_SCHEDULER_shutdown();
420 decrypt_start = GNUNET_TIME_absolute_add(GNUNET_TIME_absolute_get(), delay);
421 decrypt_deadline = GNUNET_TIME_absolute_add(decrypt_start, timeout);
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);
428 for (i = 0; i < num_peers; i++)
430 GNUNET_TESTBED_service_connect(NULL, peers[i], "secretsharing", &decrypt_connect_complete, NULL,
431 &decrypt_connect_adapter, &decrypt_disconnect_adapter, &decrypt_handles[i]);
437 * Callback to be called when the requested peer information is available
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
446 peer_info_cb(void *cb_cls,
447 struct GNUNET_TESTBED_Operation *op,
448 const struct GNUNET_TESTBED_PeerInformation *pinfo,
451 struct GNUNET_PeerIdentity *p;
454 GNUNET_assert(NULL == emsg);
456 p = (struct GNUNET_PeerIdentity *)cb_cls;
458 if (pinfo->pit == GNUNET_TESTBED_PIT_IDENTITY)
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++)
465 GNUNET_TESTBED_service_connect(NULL, peers[i], "secretsharing", session_connect_complete, NULL,
466 session_connect_adapter, session_disconnect_adapter, &session_handles[i]);
473 GNUNET_TESTBED_operation_done(op);
478 * Signature of the main function of a task.
483 handle_shutdown(void *cls)
485 in_shutdown = GNUNET_YES;
487 if (NULL != connect_ops)
490 for (i = 0; i < num_peers; i++)
491 if (NULL != connect_ops[i])
493 // the disconnect callback will set the op to NULL
494 GNUNET_TESTBED_operation_done(connect_ops[i]);
496 GNUNET_free(connect_ops);
499 // killing the testbed operation will take care of remaining
500 // service handles in the disconnect callback
505 * Signature of a main function for a testcase.
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
514 * @param links_failed the number of overlay link connection attempts that
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)
527 GNUNET_log_setup("gnunet-secretsharing-profiler", "INFO", NULL);
529 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "test master\n");
531 GNUNET_SCHEDULER_add_shutdown(&handle_shutdown, NULL);
533 peers = started_peers;
535 peer_ids = GNUNET_malloc(num_peers * sizeof(struct GNUNET_PeerIdentity));
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 *);
542 for (i = 0; i < num_peers; i++)
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,
555 run(void *cls, char *const *args, const char *cfgfile,
556 const struct GNUNET_CONFIGURATION_Handle *cfg)
558 static char *session_str = "gnunet-secretsharing/test";
560 int topology_cmp_result;
562 dkg_start = GNUNET_TIME_absolute_add(GNUNET_TIME_absolute_get(), delay);
563 dkg_deadline = GNUNET_TIME_absolute_add(dkg_start, timeout);
565 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, "testbed", "OVERLAY_TOPOLOGY", &topology))
568 "'OVERLAY_TOPOLOGY' not found in 'testbed' config section, "
569 "seems like you passed the wrong configuration file\n");
573 topology_cmp_result = strcasecmp(topology, "NONE");
574 GNUNET_free(topology);
576 if (0 == topology_cmp_result)
579 "'OVERLAY_TOPOLOGY' set to 'NONE', "
580 "seems like you passed the wrong configuration file\n");
584 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
585 "running gnunet-secretsharing-profiler\n");
587 GNUNET_CRYPTO_hash(session_str, strlen(session_str), &session_id);
589 (void)GNUNET_TESTBED_test_run("gnunet-secretsharing-profiler",
601 main(int argc, char **argv)
603 struct GNUNET_GETOPT_CommandLineOption options[] = {
604 GNUNET_GETOPT_option_uint('n',
607 gettext_noop("number of peers in consensus"),
610 GNUNET_GETOPT_option_relative_time('D',
613 gettext_noop("dkg start delay"),
616 GNUNET_GETOPT_option_relative_time('t',
619 gettext_noop("dkg timeout"),
622 GNUNET_GETOPT_option_uint('k',
625 gettext_noop("threshold"),
628 GNUNET_GETOPT_option_flag('d',
630 gettext_noop("also profile decryption"),
634 GNUNET_GETOPT_option_verbose(&verbose),
636 GNUNET_GETOPT_OPTION_END
639 delay = GNUNET_TIME_UNIT_ZERO;
640 timeout = GNUNET_TIME_UNIT_MINUTES;
641 GNUNET_PROGRAM_run2(argc, argv, "gnunet-secretsharing-profiler",
643 options, &run, NULL, GNUNET_YES);