dokumentation monster session part 1
[oweals/gnunet.git] / src / set / gnunet-service-set_intersection.c
1 /*
2       This file is part of GNUnet
3       (C) 2013 Christian Grothoff (and other contributing authors)
4
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.
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       General Public License for more details.
14
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.
19 */
20
21 /**
22  * @file set/gnunet-service-set_intersection.c
23  * @brief two-peer set intersection
24  * @author Christian Fuchs
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet-service-set.h"
29 #include "strata_estimator.h"
30 #include "set_protocol.h"
31 #include <gcrypt.h>
32
33
34 /**
35  * Number of IBFs in a strata estimator.
36  */
37 #define SE_STRATA_COUNT 32
38 /**
39  * Size of the IBFs in the strata estimator.
40  */
41 #define SE_IBF_SIZE 80
42 /**
43  * hash num parameter for the difference digests and strata estimators
44  */
45 #define SE_IBF_HASH_NUM 4
46
47 /**
48  * Number of buckets that can be transmitted in one message.
49  */
50 #define MAX_BUCKETS_PER_MESSAGE ((1<<15) / IBF_BUCKET_SIZE)
51
52 /**
53  * The maximum size of an ibf we use is 2^(MAX_IBF_ORDER).
54  * Choose this value so that computing the IBF is still cheaper
55  * than transmitting all values.
56  */
57 #define MAX_IBF_ORDER (16)
58
59 /**
60  * Number of buckets used in the ibf per estimated
61  * difference.
62  */
63 #define IBF_ALPHA 4
64
65
66 /**
67  * Current phase we are in for a intersection operation.
68  */
69 enum IntersectionOperationPhase
70 {
71   /**
72    * We sent the request message, and expect a BF
73    */
74   PHASE_EXPECT_INITIAL,
75   /**
76    * We sent the request message, and expect a BF
77    */
78   PHASE_BF_EXCHANGE,
79   /**
80    * The protocol is over.
81    * Results may still have to be sent to the client.
82    */
83   PHASE_FINISHED
84 };
85
86
87 /**
88  * State of an evaluate operation
89  * with another peer.
90  */
91 struct OperationState
92 {
93   /**
94    * Tunnel to the remote peer.
95    */
96   struct GNUNET_MESH_Tunnel *tunnel;
97
98   /**
99    * Detail information about the set operation,
100    * including the set to use.
101    */
102   struct OperationSpecification *spec;
103
104   /**
105    * Message queue for the peer.
106    */
107   struct GNUNET_MQ_Handle *mq;
108
109   /**
110    * The bf we currently receive
111    */
112   struct BloomFilter *remote_bf;
113
114   /**
115    * BF of the set's element.
116    */
117   struct BloomFilter *local_bf;
118
119   /**
120    * Current state of the operation.
121    */
122   enum IntersectionOperationPhase phase;
123
124   /**
125    * Generation in which the operation handle
126    * was created.
127    */
128   unsigned int generation_created;
129
130   /**
131    * Set state of the set that this operation
132    * belongs to.
133    */
134   struct Set *set;
135
136   /**
137    * Evaluate operations are held in
138    * a linked list.
139    */
140   struct OperationState *next;
141
142    /**
143     * Evaluate operations are held in
144     * a linked list.
145     */
146   struct OperationState *prev;
147
148   /**
149    * Did we send the client that we are done?
150    */
151   int client_done_sent;
152 };
153
154
155 /**
156  * The key entry is used to associate an ibf key with
157  * an element.
158  */
159 struct KeyEntry
160 {
161   /**
162    * IBF key for the entry, derived from the current salt.
163    */
164   struct IBF_Key ibf_key;
165
166   /**
167    * The actual element associated with the key
168    */
169   struct ElementEntry *element;
170
171   /**
172    * Element that collides with this element
173    * on the ibf key
174    */
175   struct KeyEntry *next_colliding;
176 };
177
178
179 /**
180  * Used as a closure for sending elements
181  * with a specific IBF key.
182  */
183 struct SendElementClosure
184 {
185   /**
186    * The IBF key whose matching elements should be
187    * sent.
188    */
189   struct IBF_Key ibf_key;
190
191   /**
192    * Operation for which the elements
193    * should be sent.
194    */
195   struct OperationState *eo;
196 };
197
198
199 /**
200  * Extra state required for efficient set intersection.
201  */
202 struct SetState
203 {
204   /**
205    * The strata estimator is only generated once for
206    * each set.
207    * The IBF keys are derived from the element hashes with
208    * salt=0.
209    */
210   struct StrataEstimator *se;
211
212   /**
213    * Evaluate operations are held in
214    * a linked list.
215    */
216   struct OperationState *ops_head;
217
218   /**
219    * Evaluate operations are held in
220    * a linked list.
221    */
222   struct OperationState *ops_tail;
223 };
224
225
226 /**
227  * Iterator over hash map entries.
228  *
229  * @param cls closure
230  * @param key current key code
231  * @param value value in the hash map
232  * @return GNUNET_YES if we should continue to
233  *         iterate,
234  *         GNUNET_NO if not.
235  */
236 static int
237 destroy_key_to_element_iter (void *cls,
238                              uint32_t key,
239                              void *value)
240 {
241   struct KeyEntry *k = value;
242
243   while (NULL != k)
244   {
245     struct KeyEntry *k_tmp = k;
246     k = k->next_colliding;
247     if (GNUNET_YES == k_tmp->element->remote)
248     {
249       GNUNET_free (k_tmp->element);
250       k_tmp->element = NULL;
251     }
252     GNUNET_free (k_tmp);
253   }
254   return GNUNET_YES;
255 }
256
257
258 /**
259  * Destroy a intersection operation, and free all resources
260  * associated with it.
261  *
262  * @param eo the intersection operation to destroy
263  */
264 static void
265 intersection_operation_destroy (struct OperationState *eo)
266 {
267   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "destroying intersection op\n");
268   GNUNET_CONTAINER_DLL_remove (eo->set->state->ops_head,
269                                eo->set->state->ops_tail,
270                                eo);
271   if (NULL != eo->mq)
272   {
273     GNUNET_MQ_destroy (eo->mq);
274     eo->mq = NULL;
275   }
276   if (NULL != eo->tunnel)
277   {
278     struct GNUNET_MESH_Tunnel *t = eo->tunnel;
279     eo->tunnel = NULL;
280     GNUNET_MESH_tunnel_destroy (t);
281   }
282   // TODO: destroy set elements?
283   if (NULL != eo->spec)
284   {
285     if (NULL != eo->spec->context_msg)
286     {
287       GNUNET_free (eo->spec->context_msg);
288       eo->spec->context_msg = NULL;
289     }
290     GNUNET_free (eo->spec);
291     eo->spec = NULL;
292   }
293   GNUNET_free (eo);
294
295   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "destroying intersection op done\n");
296
297   /* FIXME: do a garbage collection of the set generations */
298 }
299
300
301 /**
302  * Inform the client that the intersection operation has failed,
303  * and proceed to destroy the evaluate operation.
304  *
305  * @param eo the intersection operation to fail
306  */
307 static void
308 fail_intersection_operation (struct OperationState *eo)
309 {
310   struct GNUNET_MQ_Envelope *ev;
311   struct GNUNET_SET_ResultMessage *msg;
312
313   ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_RESULT);
314   msg->result_status = htons (GNUNET_SET_STATUS_FAILURE);
315   msg->request_id = htonl (eo->spec->client_request_id);
316   msg->element_type = htons (0);
317   GNUNET_MQ_send (eo->spec->set->client_mq, ev);
318   intersection_operation_destroy (eo);
319 }
320
321
322 /**
323  * Derive the IBF key from a hash code and
324  * a salt.
325  *
326  * @param src the hash code
327  * @param salt salt to use
328  * @return the derived IBF key
329  */
330 static struct IBF_Key
331 get_ibf_key (struct GNUNET_HashCode *src, uint16_t salt)
332 {
333   struct IBF_Key key;
334
335   GNUNET_CRYPTO_hkdf (&key, sizeof (key),
336                       GCRY_MD_SHA512, GCRY_MD_SHA256,
337                       src, sizeof *src,
338                       &salt, sizeof (salt),
339                       NULL, 0);
340   return key;
341 }
342
343
344 /**
345  * Send a request for the evaluate operation to a remote peer
346  *
347  * @param eo operation with the other peer
348  */
349 static void
350 send_operation_request (struct OperationState *eo)
351 {
352   struct GNUNET_MQ_Envelope *ev;
353   struct OperationRequestMessage *msg;
354
355   ev = GNUNET_MQ_msg_nested_mh (msg, GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST,
356                                 eo->spec->context_msg);
357
358   if (NULL == ev)
359   {
360     /* the context message is too large */
361     GNUNET_break (0);
362     GNUNET_SERVER_client_disconnect (eo->spec->set->client);
363     return;
364   }
365   msg->operation = htonl (GNUNET_SET_OPERATION_UNION);
366   msg->app_id = eo->spec->app_id;
367   msg->salt = htonl (eo->spec->salt);
368   GNUNET_MQ_send (eo->mq, ev);
369
370   if (NULL != eo->spec->context_msg)
371     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sent op request with context message\n");
372   else
373     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sent op request without context message\n");
374
375   if (NULL != eo->spec->context_msg)
376   {
377     GNUNET_free (eo->spec->context_msg);
378     eo->spec->context_msg = NULL;
379   }
380
381 }
382
383
384 /**
385  * Iterator to create the mapping between ibf keys
386  * and element entries.
387  *
388  * @param cls closure
389  * @param key current key code
390  * @param value value in the hash map
391  * @return GNUNET_YES if we should continue to
392  *         iterate,
393  *         GNUNET_NO if not.
394  */
395 static int
396 op_register_element_iterator (void *cls,
397                          uint32_t key,
398                          void *value)
399 {
400   struct KeyEntry *const new_k = cls;
401   struct KeyEntry *old_k = value;
402
403   GNUNET_assert (NULL != old_k);
404   do
405   {
406     if (old_k->ibf_key.key_val == new_k->ibf_key.key_val)
407     {
408       new_k->next_colliding = old_k->next_colliding;
409       old_k->next_colliding = new_k;
410       return GNUNET_NO;
411     }
412     old_k = old_k->next_colliding;
413   } while (NULL != old_k);
414   return GNUNET_YES;
415 }
416
417
418 /**
419  * Insert an element into the intersection operation's
420  * key-to-element mapping. Takes ownership of 'ee'.
421  * Note that this does not insert the element in the set,
422  * only in the operation's key-element mapping.
423  * This is done to speed up re-tried operations, if some elements
424  * were transmitted, and then the IBF fails to decode.
425  *
426  * @param eo the intersection operation
427  * @param ee the element entry
428  */
429 static void
430 op_register_element (struct OperationState *eo, struct ElementEntry *ee)
431 {
432   int ret;
433   struct IBF_Key ibf_key;
434   struct KeyEntry *k;
435
436   ibf_key = get_ibf_key (&ee->element_hash, eo->spec->salt);
437   k = GNUNET_new (struct KeyEntry);
438   k->element = ee;
439   k->ibf_key = ibf_key;
440   ret = GNUNET_CONTAINER_multihashmap32_get_multiple (eo->key_to_element,
441                                                       (uint32_t) ibf_key.key_val,
442                                                       op_register_element_iterator, k);
443
444   /* was the element inserted into a colliding bucket? */
445   if (GNUNET_SYSERR == ret)
446     return;
447
448   GNUNET_CONTAINER_multihashmap32_put (eo->key_to_element, (uint32_t) ibf_key.key_val, k,
449                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
450 }
451
452
453
454 /**
455  * Iterator for initializing the
456  * key-to-element mapping of a intersection operation
457  *
458  * @param cls the intersection operation
459  * @param key unised
460  * @param value the element entry to insert
461  *        into the key-to-element mapping
462  * @return GNUNET_YES to continue iterating,
463  *         GNUNET_NO to stop
464  */
465 static int
466 init_key_to_element_iterator (void *cls,
467                               const struct GNUNET_HashCode *key,
468                               void *value)
469 {
470   struct OperationState *eo = cls;
471   struct ElementEntry *e = value;
472
473   /* make sure that the element belongs to the set at the time
474    * of creating the operation */
475   if ( (e->generation_added > eo->generation_created) ||
476        ( (GNUNET_YES == e->removed) &&
477          (e->generation_removed < eo->generation_created)))
478     return GNUNET_YES;
479
480   GNUNET_assert (GNUNET_NO == e->remote);
481
482   op_register_element (eo, e);
483   return GNUNET_YES;
484 }
485
486 /**
487  * Handle an IBF message from a remote peer.
488  *
489  * @param cls the intersection operation
490  * @param mh the header of the message
491  */
492 static void
493 handle_p2p_bf (void *cls, const struct GNUNET_MessageHeader *mh)
494 {
495   struct OperationState *eo = cls;
496   struct BFMessage *msg = (struct BFMessage *) mh;
497   unsigned int buckets_in_message;
498
499   if (eo->phase == PHASE_EXPECT_INITIAL )
500   {
501     eo->phase = PHASE_BF_EXCHANGE;
502     
503     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "creating new bf of size %u\n", 1<<msg->order);
504
505     // if (the remote peer has less elements than us)
506     //    run our elements through his bloomfilter
507     // else if (we have the same elements)
508     //    done;
509     // 
510     // evict elements we can exclude through the bloomfilter
511     //
512     // create a new bloomfilter over our remaining elements
513     // 
514     // send our new count and the bloomfilter back
515   }
516   else if (eo->phase == PHASE_BF_EXCHANGE)
517   {
518
519   }
520
521 }
522
523
524 /**
525  * Send a result message to the client indicating
526  * that there is a new element.
527  *
528  * @param eo intersection operation
529  * @param element element to send
530  */
531 static void
532 send_client_element (struct OperationState *eo,
533                      struct GNUNET_SET_Element *element)
534 {
535   struct GNUNET_MQ_Envelope *ev;
536   struct GNUNET_SET_ResultMessage *rm;
537
538   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sending element (size %u) to client\n", element->size);
539   GNUNET_assert (0 != eo->spec->client_request_id);
540   ev = GNUNET_MQ_msg_extra (rm, element->size, GNUNET_MESSAGE_TYPE_SET_RESULT);
541   if (NULL == ev)
542   {
543     GNUNET_MQ_discard (ev);
544     GNUNET_break (0);
545     return;
546   }
547   rm->result_status = htons (GNUNET_SET_STATUS_OK);
548   rm->request_id = htonl (eo->spec->client_request_id);
549   rm->element_type = element->type;
550   memcpy (&rm[1], element->data, element->size);
551   GNUNET_MQ_send (eo->spec->set->client_mq, ev);
552 }
553
554
555 /**
556  * Send a result message to the client indicating
557  * that the operation is over.
558  * After the result done message has been sent to the client,
559  * destroy the evaluate operation.
560  *
561  * @param eo intersection operation
562  */
563 static void
564 send_client_done_and_destroy (struct OperationState *eo)
565 {
566   struct GNUNET_MQ_Envelope *ev;
567   struct GNUNET_SET_ResultMessage *rm;
568
569   GNUNET_assert (GNUNET_NO == eo->client_done_sent);
570
571   eo->client_done_sent = GNUNET_YES;
572
573   ev = GNUNET_MQ_msg (rm, GNUNET_MESSAGE_TYPE_SET_RESULT);
574   rm->request_id = htonl (eo->spec->client_request_id);
575   rm->result_status = htons (GNUNET_SET_STATUS_DONE);
576   rm->element_type = htons (0);
577   GNUNET_MQ_send (eo->spec->set->client_mq, ev);
578
579   intersection_operation_destroy (eo);
580 }
581
582
583 /**
584  * Handle a done message from a remote peer
585  *
586  * @param cls the intersection operation
587  * @param mh the message
588  */
589 static void
590 handle_p2p_done (void *cls, const struct GNUNET_MessageHeader *mh)
591 {
592   struct OperationState *eo = cls;
593   struct GNUNET_MQ_Envelope *ev;
594
595   if (eo->phase == PHASE_EXPECT_ELEMENTS_AND_REQUESTS)
596   {
597     /* we got all requests, but still have to send our elements as response */
598
599     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got DONE, sending final DONE after elements\n");
600     eo->phase = PHASE_FINISHED;
601     ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_P2P_DONE);
602     GNUNET_MQ_send (eo->mq, ev);
603     return;
604   }
605   if (eo->phase == PHASE_EXPECT_ELEMENTS)
606   {
607     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got final DONE\n");
608     eo->phase = PHASE_FINISHED;
609     send_client_done_and_destroy (eo);
610     return;
611   }
612   GNUNET_break (0);
613   fail_intersection_operation (eo);
614 }
615
616
617 /**
618  * Evaluate a intersection operation with
619  * a remote peer.
620  *
621  * @param spec specification of the operation the evaluate
622  * @param tunnel tunnel already connected to the partner peer
623  * @param tc tunnel context, passed here so all new incoming
624  *        messages are directly going to the intersection operations
625  * @return a handle to the operation
626  */
627 static void
628 intersection_evaluate (struct OperationSpecification *spec,
629                 struct GNUNET_MESH_Tunnel *tunnel,
630                 struct TunnelContext *tc)
631 {
632   struct OperationState *eo;
633
634   eo = GNUNET_new (struct OperationState);
635   tc->vt = _GSS_intersection_vt ();
636   tc->op = eo;
637   eo->generation_created = spec->set->current_generation++;
638   eo->set = spec->set;
639   eo->spec = spec;
640   eo->tunnel = tunnel;
641   eo->mq = GNUNET_MESH_mq_create (tunnel);
642
643   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
644               "evaluating intersection operation, (app %s)\n",
645               GNUNET_h2s (&eo->spec->app_id));
646
647   /* we started the operation, thus we have to send the operation request */
648   eo->phase = PHASE_EXPECT_SE;
649
650   GNUNET_CONTAINER_DLL_insert (eo->set->state->ops_head,
651                                eo->set->state->ops_tail,
652                                eo);
653
654   send_initial_bloomfilter (eo);
655 }
656
657
658 /**
659  * Accept an intersection operation request from a remote peer
660  *
661  * @param spec all necessary information about the operation
662  * @param tunnel open tunnel to the partner's peer
663  * @param tc tunnel context, passed here so all new incoming
664  *        messages are directly going to the intersection operations
665  * @return operation
666  */
667 static void
668 intersection_accept (struct OperationSpecification *spec,
669               struct GNUNET_MESH_Tunnel *tunnel,
670               struct TunnelContext *tc)
671 {
672   struct OperationState *eo;
673
674   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "accepting set intersection operation\n");
675
676   eo = GNUNET_new (struct OperationState);
677   tc->vt = _GSS_intersection_vt ();
678   tc->op = eo;
679   eo->set = spec->set;
680   eo->generation_created = eo->set->current_generation++;
681   eo->spec = spec;
682   eo->tunnel = tunnel;
683   eo->mq = GNUNET_MESH_mq_create (tunnel);
684   /* transfer ownership of mq and socket from incoming to eo */
685   GNUNET_CONTAINER_DLL_insert (eo->set->state->ops_head,
686                                eo->set->state->ops_tail,
687                                eo);
688   /* kick off the operation */
689   send_bloomfilter (eo);
690 }
691
692
693 /**
694  * Create a new set supporting the intersection operation
695  *
696  * @return the newly created set
697  */
698 static struct SetState *
699 intersection_set_create (void)
700 {
701   struct SetState *set_state;
702
703   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "intersection set created\n");
704
705   set_state = GNUNET_new (struct SetState);
706
707   //TODO: actually create that thing
708   
709   return set_state;
710 }
711
712
713 /**
714  * Add the element from the given element message to the set.
715  *
716  * @param set_state state of the set want to add to
717  * @param ee the element to add to the set
718  */
719 static void
720 intersection_add (struct SetState *set_state, struct ElementEntry *ee)
721 {
722   //TODO
723 }
724
725
726 /**
727  * Destroy a set that supports the intersection operation
728  *
729  * @param set_state the set to destroy
730  */
731 static void
732 intersection_set_destroy (struct SetState *set_state)
733 {
734   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "destroying intersection set\n");
735   /* important to destroy operations before the rest of the set */
736   while (NULL != set_state->ops_head)
737     intersection_operation_destroy (set_state->ops_head);
738   if (NULL != set_state->se)
739   {
740     //TODO: actually destroy that thing
741     set_state->se = NULL;
742   }
743   GNUNET_free (set_state);
744 }
745
746
747 /**
748  * Remove the element given in the element message from the set.
749  *
750  * @param set_state state of the set to remove from
751  * @param element set element to remove
752  */
753 static void
754 intersection_remove (struct SetState *set_state, struct ElementEntry *element)
755 {
756   //TODO
757 }
758
759
760 /**
761  * Dispatch messages for a intersection operation.
762  *
763  * @param eo the state of the intersection evaluate operation
764  * @param mh the received message
765  * @return GNUNET_SYSERR if the tunnel should be disconnected,
766  *         GNUNET_OK otherwise
767  */
768 int
769 intersection_handle_p2p_message (struct OperationState *eo,
770                           const struct GNUNET_MessageHeader *mh)
771 {
772   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "received p2p message (t: %u, s: %u)\n",
773               ntohs (mh->type), ntohs (mh->size));
774   switch (ntohs (mh->type))
775   {
776     case GNUNET_MESSAGE_TYPE_SET_P2P_BF:
777       handle_p2p_bf (eo, mh);
778       break;
779     case GNUNET_MESSAGE_TYPE_SET_P2P_DONE:
780       handle_p2p_done (eo, mh);
781       break;
782     default:
783       /* something wrong with mesh's message handlers? */
784       GNUNET_assert (0);
785   }
786   return GNUNET_OK;
787 }
788
789
790 static void
791 intersection_peer_disconnect (struct OperationState *op)
792 {
793   /* Are we already disconnected? */
794   if (NULL == op->tunnel)
795     return;
796   op->tunnel = NULL;
797   if (NULL != op->mq)
798   {
799     GNUNET_MQ_destroy (op->mq);
800     op->mq = NULL;
801   }
802   if (PHASE_FINISHED != op->phase)
803   {
804     struct GNUNET_MQ_Envelope *ev;
805     struct GNUNET_SET_ResultMessage *msg;
806
807     ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_RESULT);
808     msg->request_id = htonl (op->spec->client_request_id);
809     msg->result_status = htons (GNUNET_SET_STATUS_FAILURE);
810     msg->element_type = htons (0);
811     GNUNET_MQ_send (op->spec->set->client_mq, ev);
812     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "other peer disconnected prematurely\n");
813     intersection_operation_destroy (op);
814     return;
815   }
816   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "other peer disconnected (finished)\n");
817   if (GNUNET_NO == op->client_done_sent)
818     send_client_done_and_destroy (op);
819 }
820
821
822 static void
823 intersection_op_cancel (struct SetState *set_state, uint32_t op_id)
824 {
825   /* FIXME: implement */
826 }
827
828
829 const struct SetVT *
830 _GSS_intersection_vt ()
831 {
832   static const struct SetVT intersection_vt = {
833     .create = &intersection_set_create,
834     .msg_handler = &intersection_handle_p2p_message,
835     .add = &intersection_add,
836     .remove = &intersection_remove,
837     .destroy_set = &intersection_set_destroy,
838     .evaluate = &intersection_evaluate,
839     .accept = &intersection_accept,
840     .peer_disconnect = &intersection_peer_disconnect,
841     .cancel = &intersection_op_cancel,
842   };
843
844   return &intersection_vt;
845 }