1ab00aaf5e4dc3477ff56b5fff962d20d5a60802
[oweals/gnunet.git] / src / rps / gnunet-service-rps_sampler.c
1 /*
2      This file is part of GNUnet.
3      (C)
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 rps/gnunet-service-rps_sampler.c
23  * @brief sampler implementation
24  * @author Julius Bünger
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "rps.h"
29
30 #include "gnunet-service-rps_sampler.h"
31
32 #include <math.h>
33 #include <inttypes.h>
34
35 #define LOG(kind, ...) GNUNET_log(kind, __VA_ARGS__)
36
37 // multiple 'clients'?
38
39 // TODO check for overflows
40
41 // TODO align message structs
42
43 // hist_size_init, hist_size_max
44
45 /***********************************************************************
46  * WARNING: This section needs to be reviewed regarding the use of
47  * functions providing (pseudo)randomness!
48 ***********************************************************************/
49
50 // TODO care about invalid input of the caller (size 0 or less...)
51
52 enum RPS_SamplerEmpty
53 {
54   NOT_EMPTY = 0x0,
55       EMPTY = 0x1
56 };
57
58 /**
59  * A sampler element sampling one PeerID at a time.
60  */
61 struct RPS_SamplerElement
62 {
63   /**
64    * Min-wise linear permutation used by this sampler.
65    *
66    * This is an key later used by a hmac.
67    */
68   struct GNUNET_CRYPTO_AuthKey auth_key;
69
70   /**
71    * The PeerID this sampler currently samples.
72    */
73   struct GNUNET_PeerIdentity peer_id;
74
75   /**
76    * The according hash value of this PeerID.
77    */
78   struct GNUNET_HashCode peer_id_hash;
79
80   /**
81    * Time of last request.
82    */
83   struct GNUNET_TIME_Absolute last_request;
84   
85   /**
86    * Flag that indicates that we are not holding a valid PeerID right now.
87    */
88   enum RPS_SamplerEmpty is_empty;
89 };
90
91 /**
92  * Sampler with its own array of SamplerElements
93  */
94 struct RPS_Sampler
95 {
96   /**
97    * Number of sampler elements we hold.
98    */
99   unsigned int sampler_size;
100   //size_t size;
101
102   /**
103    * All Samplers in one array.
104    */
105   struct RPS_SamplerElement **sampler_elements;
106
107   /**
108    * Index to a sampler element.
109    *
110    * Gets cycled on every hist_request.
111    */
112   uint64_t sampler_elem_index;
113
114   /**
115    * Callback to be called when a peer gets inserted into a sampler.
116    */
117   RPS_sampler_insert_cb insert_cb;
118
119   /**
120    * Closure to the insert_cb.
121    */
122   void *insert_cls;
123
124   /**
125    * Callback to be called when a peer gets inserted into a sampler.
126    */
127   RPS_sampler_remove_cb remove_cb;
128
129   /**
130    * Closure to the remove_cb.
131    */
132   void *remove_cls;
133 };
134
135 /**
136  * Closure to _get_n_rand_peers_ready_cb()
137  */
138 struct RPS_GetNRandPeersReadyCls
139 {
140   /**
141    * Number of peers we are waiting for.
142    */
143   uint64_t num_peers;
144
145   /**
146    * Number of peers we currently have.
147    */
148   uint64_t cur_num_peers;
149
150   /**
151    * Pointer to the array holding the ids.
152    */
153   struct GNUNET_PeerIdentity *ids;
154
155   /**
156    * Callback to be called when all ids are available.
157    */
158   RPS_sampler_n_rand_peers_ready_cb callback;
159
160   /**
161    * Closure given to the callback
162    */
163   void *cls;
164 };
165
166 /**
167  * Callback that is called from _get_rand_peer() when the PeerID is ready.
168  *
169  * @param cls the closure given alongside this function.
170  * @param id the PeerID that was returned
171  */
172 typedef void
173 (*RPS_sampler_rand_peer_ready_cb) (void *cls,
174         const struct GNUNET_PeerIdentity *id);
175
176 /**
177  * Global sampler variable.
178  */
179 struct RPS_Sampler *sampler;
180
181
182 /**
183  * The minimal size for the extended sampler elements.
184  */
185 static size_t min_size;
186
187 /**
188  * The maximal size the extended sampler elements should grow to.
189  */
190 static size_t max_size;
191
192 /**
193  * The size the extended sampler elements currently have.
194  */
195 //static size_t extra_size;
196
197 /**
198  * Inedex to the sampler element that is the next to be returned
199  */
200 static uint64_t client_get_index;
201
202
203 /**
204  * Callback to _get_rand_peer() used by _get_n_rand_peers().
205  *
206  * Checks whether all n peers are available. If they are, 
207  * give those back.
208  */
209   void
210 RPS_sampler_get_n_rand_peers_ready_cb (void *cls,
211     const struct GNUNET_PeerIdentity *id)
212 {
213   struct RPS_GetNRandPeersReadyCls *n_peers_cls;
214
215   n_peers_cls = (struct RPS_GetNRandPeersReadyCls *) cls;
216
217   if (n_peers_cls->num_peers == n_peers_cls->cur_num_peers)
218   {
219     GNUNET_assert (NULL != n_peers_cls->callback);
220
221     n_peers_cls->callback (n_peers_cls->cls, n_peers_cls->ids, n_peers_cls->num_peers);
222     
223     GNUNET_free (n_peers_cls);
224   }
225 }
226
227
228 /**
229  * Reinitialise a previously initialised sampler element.
230  *
231  * @param sampler pointer to the memory that keeps the value.
232  */
233   static void
234 RPS_sampler_elem_reinit (struct RPS_SamplerElement *sampler_el)
235 {
236   sampler_el->is_empty = EMPTY;
237
238   // I guess I don't need to call GNUNET_CRYPTO_hmac_derive_key()...
239   GNUNET_CRYPTO_random_block(GNUNET_CRYPTO_QUALITY_STRONG,
240                              &(sampler_el->auth_key.key),
241                              GNUNET_CRYPTO_HASH_LENGTH);
242
243   sampler_el->last_request = GNUNET_TIME_UNIT_FOREVER_ABS;
244
245   /* We might want to keep the previous peer */
246
247   //GNUNET_CRYPTO_hmac(&sampler_el->auth_key, sampler_el->peer_id,
248   //                   sizeof(struct GNUNET_PeerIdentity),
249   //                   &sampler_el->peer_id_hash);
250 }
251
252
253 /**
254  * (Re)Initialise given Sampler with random min-wise independent function.
255  *
256  * In this implementation this means choosing an auth_key for later use in
257  * a hmac at random.
258  *
259  * @return a newly created RPS_SamplerElement which currently holds no id.
260  */
261   struct RPS_SamplerElement *
262 RPS_sampler_elem_create (void)
263 {
264   struct RPS_SamplerElement *s;
265   
266   s = GNUNET_new (struct RPS_SamplerElement);
267
268   RPS_sampler_elem_reinit (s);
269   LOG (GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: initialised with empty PeerID\n");
270
271   return s;
272 }
273
274
275 /**
276  * Input an PeerID into the given sampler.
277  */
278   static void
279 RPS_sampler_elem_next (struct RPS_SamplerElement *s_elem, const struct GNUNET_PeerIdentity *other,
280     RPS_sampler_insert_cb insert_cb, void *insert_cls,
281     RPS_sampler_remove_cb remove_cb, void *remove_cls)
282 {
283   struct GNUNET_HashCode other_hash;
284
285   if ( 0 == GNUNET_CRYPTO_cmp_peer_identity(other, &(s_elem->peer_id)) )
286   {
287     LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER:          Got PeerID %s\n",
288         GNUNET_i2s(other));
289     LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Have already PeerID %s\n",
290         GNUNET_i2s(&(s_elem->peer_id)));
291   }
292   else
293   {
294     GNUNET_CRYPTO_hmac(&s_elem->auth_key,
295         other,
296         sizeof(struct GNUNET_PeerIdentity),
297         &other_hash);
298
299     if ( EMPTY == s_elem->is_empty )
300     { // Or whatever is a valid way to say
301       // "we have no PeerID at the moment"
302       LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Got PeerID %s; Simply accepting (was empty previously).\n",
303           GNUNET_i2s(other));
304       s_elem->peer_id = *other;
305       //s_elem->peer_id = other;
306       s_elem->peer_id_hash = other_hash;
307       if (NULL != sampler->insert_cb)
308       {
309         sampler->insert_cb(sampler->insert_cls, &(s_elem->peer_id));
310       }
311     }
312     else if ( 0 > GNUNET_CRYPTO_hash_cmp(&other_hash, &s_elem->peer_id_hash) )
313     {
314       LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER:            Got PeerID %s\n",
315           GNUNET_i2s(other));
316       LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Discarding old PeerID %s\n",
317           GNUNET_i2s(&s_elem->peer_id));
318
319       if ( NULL != sampler->remove_cb )
320       {
321         LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Removing old PeerID %s with the remove callback.\n",
322             GNUNET_i2s(&s_elem->peer_id));
323         sampler->remove_cb(sampler->remove_cls, &s_elem->peer_id);
324       }
325
326       memcpy(&s_elem->peer_id, other, sizeof(struct GNUNET_PeerIdentity));
327       //s_elem->peer_id = other;
328       s_elem->peer_id_hash = other_hash;
329
330       if ( NULL != sampler->insert_cb )
331       {
332         LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Inserting new PeerID %s with the insert callback.\n",
333             GNUNET_i2s(&s_elem->peer_id));
334         sampler->insert_cb(sampler->insert_cls, &s_elem->peer_id);
335       }
336     }
337     else
338     {
339       LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER:         Got PeerID %s\n",
340           GNUNET_i2s(other));
341       LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Keeping old PeerID %s\n",
342           GNUNET_i2s(&s_elem->peer_id));
343     }
344   }
345   s_elem->is_empty = NOT_EMPTY;
346 }
347
348
349 /**
350  * Grow or shrink the size of the sampler.
351  *
352  * @param new_size the new size of the sampler
353  */
354   void
355 RPS_sampler_resize (unsigned int new_size)
356 {
357   unsigned int old_size;
358   uint64_t i;
359   struct RPS_SamplerElement **rem_list;
360
361   // TODO check min and max size
362
363   old_size = sampler->sampler_size;
364
365   if (old_size > new_size)
366   { /* Shrinking */
367     /* Temporary store those to properly call the removeCB on those later */
368     rem_list = GNUNET_malloc ((old_size - new_size) * sizeof (struct RPS_SamplerElement *));
369     memcpy (rem_list,
370         &sampler->sampler_elements[new_size],
371         (old_size - new_size) * sizeof (struct RPS_SamplerElement *));
372
373     LOG (GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Shrinking sampler %d -> %d\n", old_size, new_size);
374     GNUNET_array_grow (sampler->sampler_elements, sampler->sampler_size, new_size);
375     LOG (GNUNET_ERROR_TYPE_DEBUG,
376         "SAMPLER: sampler->sampler_elements now points to %p\n",
377         sampler->sampler_elements);
378
379     for (i = 0 ; i < old_size - new_size ; i++)
380     {/* Remove unneeded rest */
381       LOG (GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Removing %" PRIX64 ". sampler\n", i);
382       if (NULL != sampler->remove_cb)
383         sampler->remove_cb (sampler->remove_cls, &rem_list[i]->peer_id);
384       GNUNET_free (rem_list[i]);
385     }
386     GNUNET_free (rem_list);
387   }
388   else if (old_size < new_size)
389   { /* Growing */
390     LOG (GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Growing sampler %d -> %d\n", old_size, new_size);
391     GNUNET_array_grow (sampler->sampler_elements, sampler->sampler_size, new_size);
392     LOG (GNUNET_ERROR_TYPE_DEBUG,
393         "SAMPLER: sampler->sampler_elements now points to %p\n",
394         sampler->sampler_elements);
395
396     for ( i = old_size ; i < new_size ; i++ )
397     { /* Add new sampler elements */
398       sampler->sampler_elements[i] = RPS_sampler_elem_create ();
399       if (NULL != sampler->insert_cb)
400         sampler->insert_cb (sampler->insert_cls, &sampler->sampler_elements[i]->peer_id);
401       LOG (GNUNET_ERROR_TYPE_DEBUG,
402           "SAMPLER: Added %" PRIX64 ". sampler, now pointing to %p, contains %s\n",
403           i, &sampler->sampler_elements[i], GNUNET_i2s (&sampler->sampler_elements[i]->peer_id));
404     }
405   }
406   else
407   {
408     LOG (GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Size remains the same -- nothing to do\n");
409     return;
410   }
411
412   GNUNET_assert(sampler->sampler_size == new_size);
413   LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Finished growing/shrinking.\n"); // remove
414 }
415
416
417 /**
418  * Initialise a tuple of sampler elements.
419  *
420  * @param init_size the size the sampler is initialised with
421  * @param id with which all newly created sampler elements are initialised
422  * @param ins_cb the callback that will be called on every PeerID that is 
423  *               newly inserted into a sampler element
424  * @param ins_cls the closure given to #ins_cb
425  * @param rem_cb the callback that will be called on every PeerID that is
426  *               removed from a sampler element
427  * @param rem_cls the closure given to #rem_cb
428  */
429   void
430 RPS_sampler_init (size_t init_size, const struct GNUNET_PeerIdentity *id,
431     RPS_sampler_insert_cb ins_cb, void *ins_cls,
432     RPS_sampler_remove_cb rem_cb, void *rem_cls)
433 {
434   //struct RPS_Sampler *sampler;
435   //uint64_t i;
436
437   /* Initialise context around extended sampler */
438   min_size = 10; // TODO make input to _samplers_init()
439   max_size = 1000; // TODO make input to _samplers_init()
440
441   sampler = GNUNET_new (struct RPS_Sampler);
442   sampler->sampler_size = 0;
443   sampler->sampler_elements = NULL;
444   sampler->insert_cb = ins_cb;
445   sampler->insert_cls = ins_cls;
446   sampler->remove_cb = rem_cb;
447   sampler->remove_cls = rem_cls;
448   //sampler->sampler_elements = GNUNET_new_array(init_size, struct GNUNET_PeerIdentity);
449   //GNUNET_array_grow (sampler->sampler_elements, sampler->sampler_size, min_size);
450   RPS_sampler_resize (init_size);
451   RPS_sampler_update_list (id); // no super nice desing but ok for the moment
452
453   client_get_index = 0;
454
455   //GNUNET_assert (init_size == sampler->sampler_size);
456 }
457
458
459 /**
460  * A fuction to update every sampler in the given list
461  *
462  * @param id the PeerID that is put in the sampler
463  */
464   void
465 RPS_sampler_update_list (const struct GNUNET_PeerIdentity *id)
466 {
467   uint64_t i;
468
469   for ( i = 0 ; i < sampler->sampler_size ; i++ )
470     RPS_sampler_elem_next (sampler->sampler_elements[i], id,
471         sampler->insert_cb, sampler->insert_cls,
472         sampler->remove_cb, sampler->remove_cls);
473 }
474
475
476 /**
477  * Reinitialise all previously initialised sampler elements with the given value.
478  *
479  * Used to get rid of a PeerID.
480  *
481  * @param id the id of the sampler elements to update.
482  */
483   void
484 RPS_sampler_reinitialise_by_value (const struct GNUNET_PeerIdentity *id)
485 {
486   uint64_t i;
487
488   for ( i = 0 ; i < sampler->sampler_size ; i++ )
489   {
490     if ( 0 == GNUNET_CRYPTO_cmp_peer_identity(id, &(sampler->sampler_elements[i]->peer_id)) )
491     {
492       LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Reinitialising sampler\n");
493       RPS_sampler_elem_reinit (sampler->sampler_elements[i]);
494     }
495   }
496 }
497
498
499 /**
500  * Get one random peer out of the sampled peers.
501  *
502  * We might want to reinitialise this sampler after giving the
503  * corrsponding peer to the client.
504  * Only used internally
505  */
506   const struct GNUNET_PeerIdentity * 
507 RPS_sampler_get_rand_peer_ ()
508 {
509   uint64_t r_index;
510   const struct GNUNET_PeerIdentity *peer; // do we have to malloc that?
511
512   // TODO implement extra logic
513
514   /**;
515    * Choose the r_index of the peer we want to return
516    * at random from the interval of the gossip list
517    */
518   r_index = GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_STRONG,
519       sampler->sampler_size);
520
521   //if ( EMPTY == sampler->sampler_elements[r_index]->is_empty )
522   //  // TODO schedule for later
523   //  peer = NULL;
524   //else
525     peer = &(sampler->sampler_elements[r_index]->peer_id);
526   sampler->sampler_elements[r_index]->last_request = GNUNET_TIME_absolute_get();
527   LOG(GNUNET_ERROR_TYPE_DEBUG, "Sgrp: Returning PeerID %s\n", GNUNET_i2s(peer));
528
529   return peer;
530 }
531
532
533 /**
534  * Get n random peers out of the sampled peers.
535  *
536  * We might want to reinitialise this sampler after giving the
537  * corrsponding peer to the client.
538  * Random with or without consumption?
539  * Only used internally
540  */
541   const struct GNUNET_PeerIdentity *
542 RPS_sampler_get_n_rand_peers_ (uint64_t n)
543 {
544   if ( 0 == sampler->sampler_size )
545   {
546     LOG (GNUNET_ERROR_TYPE_DEBUG,
547         "Sgrp: List empty - Returning NULL\n");
548     return NULL;
549   }
550   else
551   {
552     // TODO check if we have too much (distinct) sampled peers
553     // If we are not ready yet maybe schedule for later
554     struct GNUNET_PeerIdentity *peers;
555     uint64_t i;
556
557     peers = GNUNET_malloc (n * sizeof(struct GNUNET_PeerIdentity));
558
559     for ( i = 0 ; i < n ; i++ ) {
560       //peers[i] = RPS_sampler_get_rand_peer_(sampler->sampler_elements);
561       memcpy (&peers[i], RPS_sampler_get_rand_peer_ (), sizeof (struct GNUNET_PeerIdentity));
562     }
563     return peers;
564   }
565 }
566
567
568 /**
569  * Get one random peer out of the sampled peers.
570  *
571  * We might want to reinitialise this sampler after giving the
572  * corrsponding peer to the client.
573  *
574  * @return a random PeerID of the PeerIDs previously put into the sampler.
575  */
576   void
577 RPS_sampler_get_rand_peer (RPS_sampler_rand_peer_ready_cb cb,
578     void *cls, struct GNUNET_PeerIdentity *id)
579 {
580   do
581   {
582   *id = sampler->sampler_elements[client_get_index]->peer_id;
583
584   RPS_sampler_elem_reinit (sampler->sampler_elements[client_get_index]);
585   if ( client_get_index == sampler->sampler_size )
586     client_get_index = 0;
587   else
588     client_get_index++;
589   } while (NOT_EMPTY == sampler->sampler_elements[client_get_index]->is_empty);
590
591   cb (cls, id);
592 }
593
594
595 /**
596  * Get n random peers out of the sampled peers.
597  *
598  * We might want to reinitialise this sampler after giving the
599  * corrsponding peer to the client.
600  * Random with or without consumption?
601  *
602  * @param cb callback that will be called once the ids are ready.
603  * @param cls closure given to @a cb
604  * @param num_peers the number of peers requested
605  */
606   void
607 RPS_sampler_get_n_rand_peers (RPS_sampler_n_rand_peers_ready_cb cb,
608     void *cls, uint64_t num_peers)
609 {
610   // use _get_rand_peers_ ?
611   if ( 0 == sampler->sampler_size )
612   {
613     LOG (GNUNET_ERROR_TYPE_DEBUG,
614         "Sgrp: List empty - Returning NULL\n");
615   }
616   else
617   {
618     // TODO check if we have too much (distinct) sampled peers
619     // If we are not ready yet maybe schedule for later
620     struct GNUNET_PeerIdentity *peers;
621     uint64_t i;
622     struct RPS_GetNRandPeersReadyCls *cb_cls;
623
624     peers = GNUNET_new_array (num_peers, struct GNUNET_PeerIdentity);
625
626     cb_cls = GNUNET_new (struct RPS_GetNRandPeersReadyCls);
627     cb_cls->num_peers = num_peers;
628     cb_cls->cur_num_peers = 0;
629     cb_cls->callback = NULL;
630     cb_cls->cls = NULL;
631
632     for ( i = 0 ; i < num_peers ; i++ )
633       RPS_sampler_get_rand_peer (RPS_sampler_get_n_rand_peers_ready_cb,
634           cb_cls, &peers[i]);
635   }
636 }
637
638
639 /**
640  * Counts how many Samplers currently hold a given PeerID.
641  *
642  * @param id the PeerID to count.
643  *
644  * @return the number of occurrences of id.
645  */
646   uint64_t
647 RPS_sampler_count_id (const struct GNUNET_PeerIdentity *id)
648 {
649   uint64_t count;
650   uint64_t i;
651
652   count = 0;
653   for ( i = 0 ; i < sampler->sampler_size ; i++ )
654   {
655     if ( 0 == GNUNET_CRYPTO_cmp_peer_identity (&sampler->sampler_elements[i]->peer_id, id) 
656         && EMPTY != sampler->sampler_elements[i]->is_empty)
657       count++;
658   }
659   return count;
660 }
661
662
663 /**
664  * Cleans the sampler.
665  */
666   void
667 RPS_sampler_destroy ()
668 {
669   RPS_sampler_resize (0);
670   GNUNET_array_grow (sampler->sampler_elements, sampler->sampler_size, 0);
671 }
672
673 /* end of gnunet-service-rps.c */