2 This file is part of GNUnet
3 (C) 2013, 2014 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 3, 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.
21 * @file set/gnunet-service-set_intersection.c
22 * @brief two-peer set intersection
23 * @author Christian Fuchs
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
28 #include "gnunet-service-set.h"
29 #include "gnunet_block_lib.h"
30 #include "gnunet-service-set_protocol.h"
35 * Current phase we are in for a intersection operation.
37 enum IntersectionOperationPhase
40 * We are just starting.
45 * We have send the number of our elements to the other
46 * peer, but did not setup our element set yet.
51 * We have initialized our set and are now reducing it by exchanging
52 * Bloom filters until one party notices the their element hashes
58 * The protocol is over. Results may still have to be sent to the
66 * State of an evaluate operation with another peer.
71 * The bf we currently receive
73 struct GNUNET_CONTAINER_BloomFilter *remote_bf;
76 * BF of the set's element.
78 struct GNUNET_CONTAINER_BloomFilter *local_bf;
81 * Remaining elements in the intersection operation.
82 * Maps element-id-hashes to 'elements in our set'.
84 struct GNUNET_CONTAINER_MultiHashMap *my_elements;
87 * Iterator for sending the final set of @e my_elements to the client.
89 struct GNUNET_CONTAINER_MultiHashMapIterator *full_result_iter;
92 * Evaluate operations are held in a linked list.
94 struct OperationState *next;
97 * Evaluate operations are held in a linked list.
99 struct OperationState *prev;
102 * For multipart BF transmissions, we have to store the
103 * bloomfilter-data until we fully received it.
108 * XOR of the keys of all of the elements (remaining) in my set.
109 * Always updated when elements are added or removed to
112 struct GNUNET_HashCode my_xor;
115 * XOR of the keys of all of the elements (remaining) in
116 * the other peer's set. Updated when we receive the
117 * other peer's Bloom filter.
119 struct GNUNET_HashCode other_xor;
122 * How many bytes of @e bf_data are valid?
124 uint32_t bf_data_offset;
127 * Current element count contained within @e my_elements.
128 * (May differ briefly during initialization.)
130 uint32_t my_element_count;
133 * size of the bloomfilter in @e bf_data.
135 uint32_t bf_data_size;
138 * size of the bloomfilter
140 uint32_t bf_bits_per_element;
143 * Salt currently used for BF construction (by us or the other peer,
144 * depending on where we are in the code).
149 * Current state of the operation.
151 enum IntersectionOperationPhase phase;
154 * Generation in which the operation handle
157 unsigned int generation_created;
160 * Did we send the client that we are done?
162 int client_done_sent;
167 * Extra state required for efficient set intersection.
168 * Merely tracks the total number of elements.
173 * Number of currently valid elements in the set which have not been
176 uint32_t current_set_element_count;
181 * If applicable in the current operation mode, send a result message
182 * to the client indicating we removed an element.
184 * @param op intersection operation
185 * @param element element to send
188 send_client_removed_element (struct Operation *op,
189 struct GNUNET_SET_Element *element)
191 struct GNUNET_MQ_Envelope *ev;
192 struct GNUNET_SET_ResultMessage *rm;
194 if (GNUNET_SET_RESULT_REMOVED != op->spec->result_mode)
195 return; /* Wrong mode for transmitting removed elements */
196 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
197 "Sending removed element (size %u) to client\n",
199 GNUNET_assert (0 != op->spec->client_request_id);
200 ev = GNUNET_MQ_msg_extra (rm,
202 GNUNET_MESSAGE_TYPE_SET_RESULT);
208 rm->result_status = htons (GNUNET_SET_STATUS_OK);
209 rm->request_id = htonl (op->spec->client_request_id);
210 rm->element_type = element->element_type;
214 GNUNET_MQ_send (op->spec->set->client_mq,
220 * Fills the "my_elements" hashmap with all relevant elements.
222 * @param cls the `struct Operation *` we are performing
223 * @param key current key code
224 * @param value the `struct ElementEntry *` from the hash map
225 * @return #GNUNET_YES (we should continue to iterate)
228 filtered_map_initialization (void *cls,
229 const struct GNUNET_HashCode *key,
232 struct Operation *op = cls;
233 struct ElementEntry *ee = value;
234 struct GNUNET_HashCode mutated_hash;
237 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
238 "FIMA called for %s:%u\n",
239 GNUNET_h2s (&ee->element_hash),
242 if ( (op->generation_created < ee->generation_removed) &&
243 (op->generation_created >= ee->generation_added) )
245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
246 "Reduced initialization, not starting with %s:%u (wrong generation)\n",
247 GNUNET_h2s (&ee->element_hash),
249 return GNUNET_YES; /* element not valid in our operation's generation */
252 /* Test if element is in other peer's bloomfilter */
253 GNUNET_BLOCK_mingle_hash (&ee->element_hash,
256 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
257 "Testing mingled hash %s with salt %u\n",
258 GNUNET_h2s (&mutated_hash),
261 GNUNET_CONTAINER_bloomfilter_test (op->state->remote_bf,
264 /* remove this element */
265 send_client_removed_element (op,
267 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
268 "Reduced initialization, not starting with %s:%u\n",
269 GNUNET_h2s (&ee->element_hash),
273 op->state->my_element_count++;
274 GNUNET_CRYPTO_hash_xor (&op->state->my_xor,
277 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
278 "Filtered initialization of my_elements, adding %s:%u\n",
279 GNUNET_h2s (&ee->element_hash),
281 GNUNET_break (GNUNET_YES ==
282 GNUNET_CONTAINER_multihashmap_put (op->state->my_elements,
285 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
292 * Removes elements from our hashmap if they are not contained within the
293 * provided remote bloomfilter.
295 * @param cls closure with the `struct Operation *`
296 * @param key current key code
297 * @param value value in the hash map
298 * @return #GNUNET_YES (we should continue to iterate)
301 iterator_bf_reduce (void *cls,
302 const struct GNUNET_HashCode *key,
305 struct Operation *op = cls;
306 struct ElementEntry *ee = value;
307 struct GNUNET_HashCode mutated_hash;
309 GNUNET_BLOCK_mingle_hash (&ee->element_hash,
312 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
313 "Testing mingled hash %s with salt %u\n",
314 GNUNET_h2s (&mutated_hash),
317 GNUNET_CONTAINER_bloomfilter_test (op->state->remote_bf,
320 GNUNET_break (0 < op->state->my_element_count);
321 op->state->my_element_count--;
322 GNUNET_CRYPTO_hash_xor (&op->state->my_xor,
325 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
326 "Bloom filter reduction of my_elements, removing %s:%u\n",
327 GNUNET_h2s (&ee->element_hash),
329 GNUNET_assert (GNUNET_YES ==
330 GNUNET_CONTAINER_multihashmap_remove (op->state->my_elements,
333 send_client_removed_element (op,
338 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
339 "Bloom filter reduction of my_elements, keeping %s:%u\n",
340 GNUNET_h2s (&ee->element_hash),
348 * Create initial bloomfilter based on all the elements given.
350 * @param cls the `struct Operation *`
351 * @param key current key code
352 * @param value the `struct ElementEntry` to process
353 * @return #GNUNET_YES (we should continue to iterate)
356 iterator_bf_create (void *cls,
357 const struct GNUNET_HashCode *key,
360 struct Operation *op = cls;
361 struct ElementEntry *ee = value;
362 struct GNUNET_HashCode mutated_hash;
364 GNUNET_BLOCK_mingle_hash (&ee->element_hash,
367 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
368 "Initializing BF with hash %s with salt %u\n",
369 GNUNET_h2s (&mutated_hash),
371 GNUNET_CONTAINER_bloomfilter_add (op->state->local_bf,
378 * Inform the client that the intersection operation has failed,
379 * and proceed to destroy the evaluate operation.
381 * @param op the intersection operation to fail
384 fail_intersection_operation (struct Operation *op)
386 struct GNUNET_MQ_Envelope *ev;
387 struct GNUNET_SET_ResultMessage *msg;
389 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
390 "Intersection operation failed\n");
391 if (NULL != op->state->my_elements)
393 GNUNET_CONTAINER_multihashmap_destroy (op->state->my_elements);
394 op->state->my_elements = NULL;
396 ev = GNUNET_MQ_msg (msg,
397 GNUNET_MESSAGE_TYPE_SET_RESULT);
398 msg->result_status = htons (GNUNET_SET_STATUS_FAILURE);
399 msg->request_id = htonl (op->spec->client_request_id);
400 msg->element_type = htons (0);
401 GNUNET_MQ_send (op->spec->set->client_mq,
403 _GSS_operation_destroy (op,
409 * Send a bloomfilter to our peer. After the result done message has
410 * been sent to the client, destroy the evaluate operation.
412 * @param op intersection operation
415 send_bloomfilter (struct Operation *op)
417 struct GNUNET_MQ_Envelope *ev;
418 struct BFMessage *msg;
420 uint32_t bf_elementbits;
425 /* We consider the ratio of the set sizes to determine
426 the number of bits per element, as the smaller set
427 should use more bits to maximize its set reduction
428 potential and minimize overall bandwidth consumption. */
429 bf_elementbits = 2 + ceil (log2((double)
430 (op->spec->remote_element_count /
431 (double) op->state->my_element_count)));
432 if (bf_elementbits < 1)
433 bf_elementbits = 1; /* make sure k is not 0 */
434 /* optimize BF-size to ~50% of bits set */
435 bf_size = ceil ((double) (op->state->my_element_count
436 * bf_elementbits / log(2)));
437 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
438 "Sending Bloom filter (%u) of size %u bytes\n",
439 (unsigned int) bf_elementbits,
440 (unsigned int) bf_size);
441 op->state->local_bf = GNUNET_CONTAINER_bloomfilter_init (NULL,
444 op->state->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
446 GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements,
450 /* send our Bloom filter */
451 chunk_size = 60 * 1024 - sizeof (struct BFMessage);
452 if (bf_size <= chunk_size)
455 chunk_size = bf_size;
456 ev = GNUNET_MQ_msg_extra (msg,
458 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF);
459 GNUNET_assert (GNUNET_SYSERR !=
460 GNUNET_CONTAINER_bloomfilter_get_raw_data (op->state->local_bf,
463 msg->sender_element_count = htonl (op->state->my_element_count);
464 msg->bloomfilter_total_length = htonl (bf_size);
465 msg->bits_per_element = htonl (bf_elementbits);
466 msg->sender_mutator = htonl (op->state->salt);
467 msg->element_xor_hash = op->state->my_xor;
468 GNUNET_MQ_send (op->mq, ev);
473 bf_data = GNUNET_malloc (bf_size);
474 GNUNET_assert (GNUNET_SYSERR !=
475 GNUNET_CONTAINER_bloomfilter_get_raw_data (op->state->local_bf,
479 while (offset < bf_size)
481 if (bf_size - chunk_size < offset)
482 chunk_size = bf_size - offset;
483 ev = GNUNET_MQ_msg_extra (msg,
485 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF);
489 offset += chunk_size;
490 msg->sender_element_count = htonl (op->state->my_element_count);
491 msg->bloomfilter_total_length = htonl (bf_size);
492 msg->bits_per_element = htonl (bf_elementbits);
493 msg->sender_mutator = htonl (op->state->salt);
494 msg->element_xor_hash = op->state->my_xor;
495 GNUNET_MQ_send (op->mq, ev);
497 GNUNET_free (bf_data);
499 GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf);
500 op->state->local_bf = NULL;
505 * Signal to the client that the operation has finished and
506 * destroy the operation.
508 * @param cls operation to destroy
511 send_client_done_and_destroy (void *cls)
513 struct Operation *op = cls;
514 struct GNUNET_MQ_Envelope *ev;
515 struct GNUNET_SET_ResultMessage *rm;
517 ev = GNUNET_MQ_msg (rm,
518 GNUNET_MESSAGE_TYPE_SET_RESULT);
519 rm->request_id = htonl (op->spec->client_request_id);
520 rm->result_status = htons (GNUNET_SET_STATUS_DONE);
521 rm->element_type = htons (0);
522 GNUNET_MQ_send (op->spec->set->client_mq,
524 _GSS_operation_destroy (op,
530 * Send all elements in the full result iterator.
532 * @param cls the `struct Operation *`
535 send_remaining_elements (void *cls)
537 struct Operation *op = cls;
539 const struct ElementEntry *ee;
540 struct GNUNET_MQ_Envelope *ev;
541 struct GNUNET_SET_ResultMessage *rm;
542 const struct GNUNET_SET_Element *element;
545 res = GNUNET_CONTAINER_multihashmap_iterator_next (op->state->full_result_iter,
548 if (GNUNET_NO == res)
550 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
551 "Sending done and destroy because iterator ran out\n");
552 send_client_done_and_destroy (op);
556 element = &ee->element;
557 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
558 "Sending element (size %u) to client (full set)\n",
560 GNUNET_assert (0 != op->spec->client_request_id);
561 ev = GNUNET_MQ_msg_extra (rm,
563 GNUNET_MESSAGE_TYPE_SET_RESULT);
564 GNUNET_assert (NULL != ev);
565 rm->result_status = htons (GNUNET_SET_STATUS_OK);
566 rm->request_id = htonl (op->spec->client_request_id);
567 rm->element_type = element->element_type;
571 GNUNET_MQ_notify_sent (ev,
572 &send_remaining_elements,
574 GNUNET_MQ_send (op->spec->set->client_mq,
580 * Inform the peer that this operation is complete.
582 * @param op the intersection operation to fail
585 send_peer_done (struct Operation *op)
587 struct GNUNET_MQ_Envelope *ev;
588 struct IntersectionDoneMessage *idm;
590 op->state->phase = PHASE_FINISHED;
591 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
592 "Intersection succeeded, sending DONE\n");
593 GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf);
594 op->state->local_bf = NULL;
596 ev = GNUNET_MQ_msg (idm,
597 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE);
598 idm->final_element_count = htonl (op->state->my_element_count);
599 idm->element_xor_hash = op->state->my_xor;
600 GNUNET_MQ_send (op->mq,
606 * Process a Bloomfilter once we got all the chunks.
608 * @param op the intersection operation
611 process_bf (struct Operation *op)
613 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
614 "Received BF in phase %u, foreign count is %u, my element count is %u/%u\n",
616 op->spec->remote_element_count,
617 op->state->my_element_count,
618 GNUNET_CONTAINER_multihashmap_size (op->spec->set->elements));
619 switch (op->state->phase)
623 fail_intersection_operation(op);
625 case PHASE_COUNT_SENT:
626 /* This is the first BF being sent, build our initial map with
627 filtering in place */
628 op->state->my_elements
629 = GNUNET_CONTAINER_multihashmap_create (op->spec->remote_element_count,
631 op->state->my_element_count = 0;
632 GNUNET_CONTAINER_multihashmap_iterate (op->spec->set->elements,
633 &filtered_map_initialization,
636 case PHASE_BF_EXCHANGE:
637 /* Update our set by reduction */
638 GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements,
644 fail_intersection_operation(op);
647 GNUNET_CONTAINER_bloomfilter_free (op->state->remote_bf);
648 op->state->remote_bf = NULL;
650 if ( (0 == op->state->my_element_count) || /* fully disjoint */
651 ( (op->state->my_element_count == op->spec->remote_element_count) &&
652 (0 == memcmp (&op->state->my_xor,
653 &op->state->other_xor,
654 sizeof (struct GNUNET_HashCode))) ) )
660 op->state->phase = PHASE_BF_EXCHANGE;
661 send_bloomfilter (op);
666 * Handle an BF message from a remote peer.
668 * @param cls the intersection operation
669 * @param mh the header of the message
672 handle_p2p_bf (void *cls,
673 const struct GNUNET_MessageHeader *mh)
675 struct Operation *op = cls;
676 const struct BFMessage *msg;
679 uint32_t bf_bits_per_element;
682 msize = htons (mh->size);
683 if (msize < sizeof (struct BFMessage))
686 fail_intersection_operation (op);
689 msg = (const struct BFMessage *) mh;
690 switch (op->state->phase)
694 fail_intersection_operation (op);
696 case PHASE_COUNT_SENT:
697 case PHASE_BF_EXCHANGE:
698 bf_size = ntohl (msg->bloomfilter_total_length);
699 bf_bits_per_element = ntohl (msg->bits_per_element);
700 chunk_size = msize - sizeof (struct BFMessage);
701 op->state->other_xor = msg->element_xor_hash;
702 if (bf_size == chunk_size)
704 if (NULL != op->state->bf_data)
707 fail_intersection_operation (op);
710 /* single part, done here immediately */
712 = GNUNET_CONTAINER_bloomfilter_init ((const char*) &msg[1],
714 bf_bits_per_element);
715 op->state->salt = ntohl (msg->sender_mutator);
716 op->spec->remote_element_count = ntohl (msg->sender_element_count);
720 /* multipart chunk */
721 if (NULL == op->state->bf_data)
723 /* first chunk, initialize */
724 op->state->bf_data = GNUNET_malloc (bf_size);
725 op->state->bf_data_size = bf_size;
726 op->state->bf_bits_per_element = bf_bits_per_element;
727 op->state->bf_data_offset = 0;
728 op->state->salt = ntohl (msg->sender_mutator);
729 op->spec->remote_element_count = ntohl (msg->sender_element_count);
734 if ( (op->state->bf_data_size != bf_size) ||
735 (op->state->bf_bits_per_element != bf_bits_per_element) ||
736 (op->state->bf_data_offset + chunk_size > bf_size) ||
737 (op->state->salt != ntohl (msg->sender_mutator)) ||
738 (op->spec->remote_element_count != ntohl (msg->sender_element_count)) )
741 fail_intersection_operation (op);
745 memcpy (&op->state->bf_data[op->state->bf_data_offset],
746 (const char*) &msg[1],
748 op->state->bf_data_offset += chunk_size;
749 if (op->state->bf_data_offset == bf_size)
751 /* last chunk, run! */
753 = GNUNET_CONTAINER_bloomfilter_init (op->state->bf_data,
755 bf_bits_per_element);
756 GNUNET_free (op->state->bf_data);
757 op->state->bf_data = NULL;
758 op->state->bf_data_size = 0;
764 fail_intersection_operation (op);
771 * Fills the "my_elements" hashmap with the initial set of
772 * (non-deleted) elements from the set of the specification.
774 * @param cls closure with the `struct Operation *`
775 * @param key current key code for the element
776 * @param value value in the hash map with the `struct ElementEntry *`
777 * @return #GNUNET_YES (we should continue to iterate)
780 initialize_map_unfiltered (void *cls,
781 const struct GNUNET_HashCode *key,
784 struct ElementEntry *ee = value;
785 struct Operation *op = cls;
787 if ( (op->generation_created < ee->generation_removed) &&
788 (op->generation_created >= ee->generation_added) )
789 return GNUNET_YES; /* element not live in operation's generation */
790 GNUNET_CRYPTO_hash_xor (&op->state->my_xor,
793 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
794 "Initial full initialization of my_elements, adding %s:%u\n",
795 GNUNET_h2s (&ee->element_hash),
797 GNUNET_break (GNUNET_YES ==
798 GNUNET_CONTAINER_multihashmap_put (op->state->my_elements,
801 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
807 * Send our element count to the peer, in case our element count is
810 * @param op intersection operation
813 send_element_count (struct Operation *op)
815 struct GNUNET_MQ_Envelope *ev;
816 struct IntersectionElementInfoMessage *msg;
818 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
819 "Sending our element count (%u)\n",
820 op->state->my_element_count);
821 ev = GNUNET_MQ_msg (msg,
822 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO);
823 msg->sender_element_count = htonl (op->state->my_element_count);
824 GNUNET_MQ_send (op->mq, ev);
829 * We go first, initialize our map with all elements and
830 * send the first Bloom filter.
832 * @param op operation to start exchange for
835 begin_bf_exchange (struct Operation *op)
837 op->state->phase = PHASE_BF_EXCHANGE;
838 op->state->my_elements
839 = GNUNET_CONTAINER_multihashmap_create (op->state->my_element_count,
841 GNUNET_CONTAINER_multihashmap_iterate (op->spec->set->elements,
842 &initialize_map_unfiltered,
844 send_bloomfilter (op);
849 * Handle the initial `struct IntersectionElementInfoMessage` from a
852 * @param cls the intersection operation
853 * @param mh the header of the message
856 handle_p2p_element_info (void *cls,
857 const struct GNUNET_MessageHeader *mh)
859 struct Operation *op = cls;
860 const struct IntersectionElementInfoMessage *msg;
862 if (ntohs (mh->size) != sizeof (struct IntersectionElementInfoMessage))
865 fail_intersection_operation(op);
868 msg = (const struct IntersectionElementInfoMessage *) mh;
869 op->spec->remote_element_count = ntohl (msg->sender_element_count);
870 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
871 "Received remote element count (%u), I have %u\n",
872 op->spec->remote_element_count,
873 op->state->my_element_count);
874 if ( ( (PHASE_INITIAL != op->state->phase) &&
875 (PHASE_COUNT_SENT != op->state->phase) ) ||
876 (op->state->my_element_count > op->spec->remote_element_count) ||
877 (0 == op->state->my_element_count) ||
878 (0 == op->spec->remote_element_count) )
881 fail_intersection_operation(op);
884 GNUNET_break (NULL == op->state->remote_bf);
885 begin_bf_exchange (op);
890 * Send a result message to the client indicating that the operation
891 * is over. After the result done message has been sent to the
892 * client, destroy the evaluate operation.
894 * @param op intersection operation
897 finish_and_destroy (struct Operation *op)
899 GNUNET_assert (GNUNET_NO == op->state->client_done_sent);
901 if (GNUNET_SET_RESULT_FULL == op->spec->result_mode)
903 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
904 "Sending full result set\n");
905 op->state->full_result_iter
906 = GNUNET_CONTAINER_multihashmap_iterator_create (op->state->my_elements);
907 send_remaining_elements (op);
910 send_client_done_and_destroy (op);
915 * Remove all elements from our hashmap.
917 * @param cls closure with the `struct Operation *`
918 * @param key current key code
919 * @param value value in the hash map
920 * @return #GNUNET_YES (we should continue to iterate)
923 filter_all (void *cls,
924 const struct GNUNET_HashCode *key,
927 struct Operation *op = cls;
928 struct ElementEntry *ee = value;
930 GNUNET_break (0 < op->state->my_element_count);
931 op->state->my_element_count--;
932 GNUNET_CRYPTO_hash_xor (&op->state->my_xor,
935 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
936 "Final reduction of my_elements, removing %s:%u\n",
937 GNUNET_h2s (&ee->element_hash),
939 GNUNET_assert (GNUNET_YES ==
940 GNUNET_CONTAINER_multihashmap_remove (op->state->my_elements,
943 send_client_removed_element (op,
950 * Handle a done message from a remote peer
952 * @param cls the intersection operation
953 * @param mh the message
956 handle_p2p_done (void *cls,
957 const struct GNUNET_MessageHeader *mh)
959 struct Operation *op = cls;
960 const struct IntersectionDoneMessage *idm;
962 if (PHASE_BF_EXCHANGE != op->state->phase)
964 /* wrong phase to conclude? FIXME: Or should we allow this
965 if the other peer has _initially_ already an empty set? */
967 fail_intersection_operation (op);
970 if (ntohs (mh->size) != sizeof (struct IntersectionDoneMessage))
973 fail_intersection_operation (op);
976 idm = (const struct IntersectionDoneMessage *) mh;
977 if (0 == ntohl (idm->final_element_count))
979 /* other peer determined empty set is the intersection,
980 remove all elements */
981 GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements,
985 if ( (op->state->my_element_count != ntohl (idm->final_element_count)) ||
986 (0 != memcmp (&op->state->my_xor,
987 &idm->element_xor_hash,
988 sizeof (struct GNUNET_HashCode))) )
990 /* Other peer thinks we are done, but we disagree on the result! */
992 fail_intersection_operation (op);
995 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
997 op->state->phase = PHASE_FINISHED;
998 finish_and_destroy (op);
1003 * Initiate a set intersection operation with a remote peer.
1005 * @param op operation that is created, should be initialized to
1006 * begin the evaluation
1007 * @param opaque_context message to be transmitted to the listener
1008 * to convince him to accept, may be NULL
1011 intersection_evaluate (struct Operation *op,
1012 const struct GNUNET_MessageHeader *opaque_context)
1014 struct GNUNET_MQ_Envelope *ev;
1015 struct OperationRequestMessage *msg;
1017 op->state = GNUNET_new (struct OperationState);
1018 /* we started the operation, thus we have to send the operation request */
1019 op->state->phase = PHASE_INITIAL;
1020 op->state->my_element_count = op->spec->set->state->current_set_element_count;
1022 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1023 "Initiating intersection operation evaluation\n");
1024 ev = GNUNET_MQ_msg_nested_mh (msg,
1025 GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST,
1029 /* the context message is too large!? */
1031 GNUNET_SERVER_client_disconnect (op->spec->set->client);
1034 msg->operation = htonl (GNUNET_SET_OPERATION_INTERSECTION);
1035 msg->app_id = op->spec->app_id;
1036 msg->element_count = htonl (op->state->my_element_count);
1037 GNUNET_MQ_send (op->mq,
1039 if (NULL != opaque_context)
1040 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1041 "Sent op request with context message\n");
1043 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1044 "Sent op request without context message\n");
1049 * Accept an intersection operation request from a remote peer. Only
1050 * initializes the private operation state.
1052 * @param op operation that will be accepted as an intersection operation
1055 intersection_accept (struct Operation *op)
1057 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1058 "Accepting set intersection operation\n");
1059 op->state = GNUNET_new (struct OperationState);
1060 op->state->phase = PHASE_INITIAL;
1061 op->state->my_element_count
1062 = op->spec->set->state->current_set_element_count;
1063 op->state->my_elements
1064 = GNUNET_CONTAINER_multihashmap_create
1065 (GNUNET_MIN (op->state->my_element_count,
1066 op->spec->remote_element_count),
1068 if (op->spec->remote_element_count < op->state->my_element_count)
1070 /* If the other peer (Alice) has fewer elements than us (Bob),
1071 we just send the count as Alice should send the first BF */
1072 send_element_count (op);
1073 op->state->phase = PHASE_COUNT_SENT;
1076 /* We have fewer elements, so we start with the BF */
1077 begin_bf_exchange (op);
1082 * Dispatch messages for a intersection operation.
1084 * @param op the state of the intersection evaluate operation
1085 * @param mh the received message
1086 * @return #GNUNET_SYSERR if the tunnel should be disconnected,
1087 * #GNUNET_OK otherwise
1090 intersection_handle_p2p_message (struct Operation *op,
1091 const struct GNUNET_MessageHeader *mh)
1093 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1094 "Received p2p message (t: %u, s: %u)\n",
1095 ntohs (mh->type), ntohs (mh->size));
1096 switch (ntohs (mh->type))
1098 /* this message handler is not active until after we received an
1099 * operation request message, thus the ops request is not handled here
1101 case GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO:
1102 handle_p2p_element_info (op, mh);
1104 case GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF:
1105 handle_p2p_bf (op, mh);
1107 case GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE:
1108 handle_p2p_done (op, mh);
1111 /* something wrong with cadet's message handlers? */
1119 * Handler for peer-disconnects, notifies the client about the aborted
1120 * operation. If we did not expect anything from the other peer, we
1121 * gracefully terminate the operation.
1123 * @param op the destroyed operation
1126 intersection_peer_disconnect (struct Operation *op)
1128 if (PHASE_FINISHED != op->state->phase)
1130 fail_intersection_operation (op);
1133 /* the session has already been concluded */
1134 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1135 "Other peer disconnected (finished)\n");
1136 if (GNUNET_NO == op->state->client_done_sent)
1137 finish_and_destroy (op);
1142 * Destroy the intersection operation. Only things specific to the
1143 * intersection operation are destroyed.
1145 * @param op intersection operation to destroy
1148 intersection_op_cancel (struct Operation *op)
1150 /* check if the op was canceled twice */
1151 GNUNET_assert (NULL != op->state);
1152 if (NULL != op->state->remote_bf)
1154 GNUNET_CONTAINER_bloomfilter_free (op->state->remote_bf);
1155 op->state->remote_bf = NULL;
1157 if (NULL != op->state->local_bf)
1159 GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf);
1160 op->state->local_bf = NULL;
1162 if (NULL != op->state->my_elements)
1164 GNUNET_CONTAINER_multihashmap_destroy (op->state->my_elements);
1165 op->state->my_elements = NULL;
1167 GNUNET_free (op->state);
1169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1170 "Destroying intersection op state done\n");
1175 * Create a new set supporting the intersection operation.
1177 * @return the newly created set
1179 static struct SetState *
1180 intersection_set_create ()
1182 struct SetState *set_state;
1184 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1185 "Intersection set created\n");
1186 set_state = GNUNET_new (struct SetState);
1187 set_state->current_set_element_count = 0;
1194 * Add the element from the given element message to the set.
1196 * @param set_state state of the set want to add to
1197 * @param ee the element to add to the set
1200 intersection_add (struct SetState *set_state,
1201 struct ElementEntry *ee)
1203 set_state->current_set_element_count++;
1208 * Destroy a set that supports the intersection operation
1210 * @param set_state the set to destroy
1213 intersection_set_destroy (struct SetState *set_state)
1215 GNUNET_free (set_state);
1220 * Remove the element given in the element message from the set.
1222 * @param set_state state of the set to remove from
1223 * @param element set element to remove
1226 intersection_remove (struct SetState *set_state,
1227 struct ElementEntry *element)
1229 GNUNET_assert (0 < set_state->current_set_element_count);
1230 set_state->current_set_element_count--;
1235 * Get the table with implementing functions for set intersection.
1237 * @return the operation specific VTable
1239 const struct SetVT *
1240 _GSS_intersection_vt ()
1242 static const struct SetVT intersection_vt = {
1243 .create = &intersection_set_create,
1244 .msg_handler = &intersection_handle_p2p_message,
1245 .add = &intersection_add,
1246 .remove = &intersection_remove,
1247 .destroy_set = &intersection_set_destroy,
1248 .evaluate = &intersection_evaluate,
1249 .accept = &intersection_accept,
1250 .peer_disconnect = &intersection_peer_disconnect,
1251 .cancel = &intersection_op_cancel,
1254 return &intersection_vt;