2 This file is part of GNUnet
3 (C) 2012 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
23 #include "gnunet_protocols.h"
24 #include "gnunet_common.h"
25 #include "gnunet_service_lib.h"
26 #include "gnunet_consensus_service.h"
27 #include "gnunet_core_service.h"
28 #include "gnunet_container_lib.h"
29 #include "consensus.h"
32 struct ConsensusClient;
35 send_next (struct ConsensusClient *cli);
39 * An element that is waiting to be transmitted to a client.
44 * Pending elements are kept in a DLL.
46 struct PendingElement *next;
49 * Pending elements are kept in a DLL.
51 struct PendingElement *prev;
56 struct GNUNET_CONSENSUS_Element *element;
61 * A consensus session consists of one or more local clients,
62 * as well as zero or more remote authorities.
64 struct ConsensusSession
67 * Consensus sessions are kept in a DLL.
69 struct ConsensusSession *next;
72 * Consensus sessions are kept in a DLL.
74 struct ConsensusSession *prev;
77 * Consensus clients are kept in a DLL.
79 struct ConsensusClient *clients_head;
82 * Consensus clients are kept in a DLL.
84 struct ConsensusClient *clients_tail;
87 * Local consensus identification, chosen by clients.
89 struct GNUNET_HashCode *local_id;
92 * Global consensus identification, computed
93 * from the local id and participating authorities.
95 struct GNUNET_HashCode *global_id;
98 * Values in the consensus set of this session.
100 struct GNUNET_CONTAINER_MultiHashMap *values;
104 struct ConsensusClient
107 * Consensus clients are kept in a DLL.
109 struct ConsensusClient *next;
111 * Consensus clients are kept in a DLL.
113 struct ConsensusClient *prev;
116 * Corresponding server handle.
118 struct GNUNET_SERVER_Client *client;
121 * Client wants to receive and send updates.
126 * Session this client belongs to
128 struct ConsensusSession *session;
131 * Values in the consensus set of this client.
132 * Includes pending elements.
134 struct GNUNET_CONTAINER_MultiHashMap *values;
137 * Elements that have not been set to the client yet.
139 struct PendingElement *pending_head;
141 * Elements that have not been set to the client yet.
143 struct PendingElement *pending_tail;
146 * Currently active transmit handle for sending to the client
148 struct GNUNET_SERVER_TransmitHandle *th;
151 * Once conclude_requested is GNUNET_YES, the client may not
152 * insert any more values.
154 int conclude_requested;
157 * Client has been informed about the conclusion.
164 * Linked list of sesstions this peer participates in.
166 static struct ConsensusSession *sessions_head;
169 * Linked list of sesstions this peer participates in.
171 static struct ConsensusSession *sessions_tail;
174 * Configuration of the consensus service.
176 static const struct GNUNET_CONFIGURATION_Handle *cfg;
179 * Handle to the server for this service.
181 static struct GNUNET_SERVER_Handle *srv;
184 * Peer that runs this service
186 static struct GNUNET_PeerIdentity *my_peer;
189 struct ConsensusClient *
190 find_client (const struct GNUNET_SERVER_Client *srv_client)
192 struct ConsensusSession *session;
193 struct ConsensusClient *client;
195 session = sessions_head;
196 while (NULL != session)
198 client = session->clients_head;
199 while (NULL != client)
201 if (client->client == srv_client)
205 client = client->next;
207 session = session->next;
213 disconnect_client (struct GNUNET_SERVER_Client *client)
219 compute_global_id (struct GNUNET_HashCode *dst,
220 const struct GNUNET_HashCode *local_id,
221 const struct GNUNET_PeerIdentity *peers,
226 /* FIXME: hash other peers into global id */
232 * Iterator over hash map entries.
234 * @param cls closure, the client
235 * @param key current key code
236 * @param value value in the hash map
237 * @return GNUNET_YES if we should continue to
242 update_pending (void *cls,
243 const struct GNUNET_HashCode *key,
246 struct ConsensusClient *cli;
247 struct GNUNET_CONSENSUS_Element *element;
248 struct PendingElement *pending_element;
250 cli = (struct ConsensusClient *) cls;
251 element = (struct GNUNET_CONSENSUS_Element *) value;
252 pending_element = GNUNET_malloc (sizeof (struct PendingElement));
253 pending_element->element = element;
255 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (cli->values, key))
257 GNUNET_CONTAINER_DLL_insert_tail (cli->pending_head, cli->pending_tail, pending_element);
258 GNUNET_CONTAINER_multihashmap_put (cli->values, key, element, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
268 transmit_pending (void *cls, size_t size, void *buf)
270 struct GNUNET_CONSENSUS_Element *element;
271 struct GNUNET_CONSENSUS_ElementMessage *msg;
272 struct ConsensusClient *cli;
274 cli = (struct ConsensusClient *) cls;
275 msg = (struct GNUNET_CONSENSUS_ElementMessage *) buf;
276 element = cli->pending_head->element;
278 GNUNET_assert (NULL != element);
282 msg->element_type = element->type;
283 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_RECEIVED_ELEMENT);
284 msg->header.size = htons (sizeof (struct GNUNET_CONSENSUS_ElementMessage) + element->size);
285 memcpy (&msg[1], element->data, element->size);
288 cli->pending_head = cli->pending_head->next;
292 return sizeof (struct GNUNET_CONSENSUS_ElementMessage) + element->size;
297 transmit_conclude_done (void *cls, size_t size, void *buf)
299 struct GNUNET_CONSENSUS_ConcludeDoneMessage *msg;
301 msg = (struct GNUNET_CONSENSUS_ConcludeDoneMessage *) buf;
302 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_CONCLUDE);
303 msg->header.size = htons (sizeof (struct GNUNET_CONSENSUS_ConcludeDoneMessage));
304 msg->num_peers = htons (0);
306 return sizeof (struct GNUNET_CONSENSUS_ConcludeDoneMessage);
311 * Schedule sending the next message (if there is any) to a client.
313 * @param cli the client to send the next message to
316 send_next (struct ConsensusClient *cli)
320 GNUNET_assert (NULL != cli);
327 if ((cli->conclude_requested == GNUNET_YES) && (cli->conclude_sent == GNUNET_NO))
329 /* just the conclude message with no other authorities in the dummy */
330 msize = sizeof (struct GNUNET_CONSENSUS_ConcludeMessage);
332 GNUNET_SERVER_notify_transmit_ready (cli->client, msize,
333 GNUNET_TIME_UNIT_FOREVER_REL, &transmit_conclude_done, cli);
334 cli->conclude_sent = GNUNET_YES;
336 else if (NULL != cli->pending_head)
338 msize = cli->pending_head->element->size + sizeof (struct GNUNET_CONSENSUS_ElementMessage);
340 GNUNET_SERVER_notify_transmit_ready (cli->client, msize,
341 GNUNET_TIME_UNIT_FOREVER_REL, &transmit_pending, cli);
347 * Called when a client wants to join a consensus session.
350 client_join (void *cls,
351 struct GNUNET_SERVER_Client *client,
352 const struct GNUNET_MessageHeader *m)
354 struct GNUNET_HashCode global_id;
355 const struct GNUNET_CONSENSUS_JoinMessage *msg;
356 struct ConsensusSession *session;
357 struct ConsensusClient *consensus_client;
359 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "join\n");
361 fprintf(stderr, "foobar\n");
363 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "client joined\n");
365 msg = (struct GNUNET_CONSENSUS_JoinMessage *) m;
367 /* kill the client if it already is in a session */
368 if (NULL != find_client (client))
370 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "client tried to join twice\n");
371 disconnect_client (client);
375 consensus_client = GNUNET_malloc (sizeof (struct ConsensusClient));
376 consensus_client->client = client;
377 consensus_client->begin = GNUNET_NO;
378 consensus_client->values = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO);
380 GNUNET_SERVER_client_keep (client);
382 GNUNET_assert (NULL != consensus_client->values);
384 compute_global_id (&global_id, &msg->session_id, (struct GNUNET_PeerIdentity *) &m[1], msg->num_peers);
386 /* look if we already have a session for this local id */
387 session = sessions_head;
388 while (NULL != session)
390 if (0 == memcmp(&global_id, session->global_id, sizeof (struct GNUNET_HashCode)))
392 GNUNET_CONTAINER_DLL_insert (session->clients_head, session->clients_tail, consensus_client);
393 GNUNET_SERVER_receive_done (client, GNUNET_OK);
396 session = session->next;
399 /* session does not exist yet, create it */
400 session = GNUNET_malloc (sizeof (struct ConsensusSession));
401 session->local_id = GNUNET_memdup (&msg->session_id, sizeof (struct GNUNET_HashCode));
402 session->global_id = GNUNET_memdup (&global_id, sizeof (struct GNUNET_HashCode));
403 session->values = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO);
405 GNUNET_CONTAINER_DLL_insert (sessions_head, sessions_tail, session);
406 GNUNET_CONTAINER_DLL_insert (session->clients_head, session->clients_tail, consensus_client);
408 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "created new session");
410 GNUNET_SERVER_receive_done (client, GNUNET_OK);
415 * Called when a client performs an insert operation.
418 client_insert (void *cls,
419 struct GNUNET_SERVER_Client *client,
420 const struct GNUNET_MessageHeader *m)
422 struct ConsensusClient *consensus_client;
423 struct GNUNET_CONSENSUS_ElementMessage *msg;
424 struct GNUNET_CONSENSUS_Element *element;
425 struct PendingElement *pending_element;
426 struct GNUNET_HashCode key;
429 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "insert\n");
431 consensus_client = find_client (client);
433 if (NULL == consensus_client)
435 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "client tried to insert, but client is not in any session\n");
436 GNUNET_SERVER_client_disconnect (client);
440 msg = (struct GNUNET_CONSENSUS_ElementMessage *) m;
441 element_size = msg->header.size - sizeof (struct GNUNET_CONSENSUS_ElementMessage);
443 element = GNUNET_malloc (sizeof (struct GNUNET_CONSENSUS_Element) + element_size);
445 element->type = msg->element_type;
446 element->size = element_size;
447 memcpy (&element[1], &msg[1], element_size);
448 element->data = &element[1];
450 GNUNET_CRYPTO_hash (element, element_size, &key);
452 GNUNET_CONTAINER_multihashmap_put (consensus_client->session->values, &key, element,
453 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
454 GNUNET_CONTAINER_multihashmap_put (consensus_client->values, &key, element,
455 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
457 /* send the new value to all clients that don't have it */
459 consensus_client = consensus_client->session->clients_head;
460 while (NULL != consensus_client)
462 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (consensus_client->values, &key))
464 pending_element = GNUNET_malloc (sizeof (struct PendingElement));
465 pending_element->element = element;
466 GNUNET_CONTAINER_DLL_insert_tail (consensus_client->pending_head, consensus_client->pending_tail, pending_element);
467 GNUNET_CONTAINER_multihashmap_put (consensus_client->values, &key, element,
468 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
469 send_next (consensus_client);
473 GNUNET_SERVER_receive_done (client, GNUNET_OK);
478 * Called when a client wants to begin
481 client_begin (void *cls,
482 struct GNUNET_SERVER_Client *client,
483 const struct GNUNET_MessageHeader *message)
485 struct ConsensusClient *consensus_client;
487 consensus_client = find_client (client);
489 if (NULL == consensus_client)
491 GNUNET_SERVER_client_disconnect (client);
495 consensus_client->begin = GNUNET_YES;
497 GNUNET_CONTAINER_multihashmap_iterate (consensus_client->session->values, &update_pending, NULL);
498 send_next (consensus_client);
500 GNUNET_SERVER_receive_done (client, GNUNET_OK);
506 * Called when a client performs the conclude operation.
509 client_conclude (void *cls,
510 struct GNUNET_SERVER_Client *client,
511 const struct GNUNET_MessageHeader *message)
513 struct ConsensusClient *consensus_client;
515 consensus_client = find_client (client);
516 if (NULL == consensus_client)
518 GNUNET_SERVER_client_disconnect (client);
521 consensus_client->conclude_requested = GNUNET_YES;
522 send_next (consensus_client);
524 GNUNET_SERVER_receive_done (client, GNUNET_OK);
528 * Task that disconnects from core.
530 * @param cls core handle
531 * @param tc context information (why was this task triggered now)
534 disconnect_core (void *cls,
535 const struct GNUNET_SCHEDULER_TaskContext *tc)
537 struct GNUNET_CORE_Handle *core;
538 core = (struct GNUNET_CORE_Handle *) cls;
539 GNUNET_CORE_disconnect (core);
541 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "disconnected from core\n");
546 core_startup (void *cls,
547 struct GNUNET_CORE_Handle *core,
548 const struct GNUNET_PeerIdentity *peer)
550 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
551 {&client_join, NULL, GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_JOIN, 0},
552 {&client_insert, NULL, GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_INSERT, 0},
553 {&client_begin, NULL, GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_BEGIN,
554 sizeof (struct GNUNET_MessageHeader)},
555 {&client_conclude, NULL, GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_CONCLUDE,
556 sizeof (struct GNUNET_CONSENSUS_ConcludeMessage)},
561 GNUNET_SERVER_add_handlers (srv, handlers);
563 my_peer = GNUNET_memdup(peer, sizeof (struct GNUNET_PeerIdentity));
565 GNUNET_SCHEDULER_add_now (&disconnect_core, core);
567 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "connected to core\n");
572 * Process consensus requests.
575 * @param server the initialized server
576 * @param c configuration to use
579 run (void *cls, struct GNUNET_SERVER_Handle *server, const struct GNUNET_CONFIGURATION_Handle *c)
581 struct GNUNET_CORE_Handle *my_core;
582 static const struct GNUNET_CORE_MessageHandler handlers[] = {
586 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "run\n");
590 my_core = GNUNET_CORE_connect (c, NULL, &core_startup, NULL, NULL, NULL, GNUNET_NO, NULL, GNUNET_NO, handlers);
591 GNUNET_assert (NULL != my_core);
596 * The main function for the statistics service.
598 * @param argc number of arguments from the command line
599 * @param argv command line arguments
600 * @return 0 ok, 1 on error
603 main (int argc, char *const *argv)
606 GNUNET_SERVICE_run (argc, argv, "consensus", GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;