d182894c00e434ea3322e7922b53195df8a95b53
[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  * Global sampler variable.
137  */
138 struct RPS_Sampler *sampler;
139
140
141 /**
142  * The minimal size for the extended sampler elements.
143  */
144 static size_t min_size;
145
146 /**
147  * The maximal size the extended sampler elements should grow to.
148  */
149 static size_t max_size;
150
151 /**
152  * The size the extended sampler elements currently have.
153  */
154 //static size_t extra_size;
155
156 /**
157  * Inedex to the sampler element that is the next to be returned
158  */
159 static uint64_t client_get_index;
160
161
162 /**
163  * Reinitialise a previously initialised sampler element.
164  *
165  * @param sampler pointer to the memory that keeps the value.
166  */
167   static void
168 RPS_sampler_elem_reinit (struct RPS_SamplerElement *sampler_el)
169 {
170   sampler_el->is_empty = EMPTY;
171
172   // I guess I don't need to call GNUNET_CRYPTO_hmac_derive_key()...
173   GNUNET_CRYPTO_random_block(GNUNET_CRYPTO_QUALITY_STRONG,
174                              &(sampler_el->auth_key.key),
175                              GNUNET_CRYPTO_HASH_LENGTH);
176
177   sampler_el->last_request = GNUNET_TIME_UNIT_FOREVER_ABS;
178
179   /* We might want to keep the previous peer */
180
181   //GNUNET_CRYPTO_hmac(&sampler_el->auth_key, sampler_el->peer_id,
182   //                   sizeof(struct GNUNET_PeerIdentity),
183   //                   &sampler_el->peer_id_hash);
184 }
185
186
187 /**
188  * (Re)Initialise given Sampler with random min-wise independent function.
189  *
190  * In this implementation this means choosing an auth_key for later use in
191  * a hmac at random.
192  *
193  * @return a newly created RPS_SamplerElement which currently holds no id.
194  */
195   struct RPS_SamplerElement *
196 RPS_sampler_elem_create (void)
197 {
198   struct RPS_SamplerElement *s;
199   
200   s = GNUNET_new (struct RPS_SamplerElement);
201
202   RPS_sampler_elem_reinit (s);
203   LOG (GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: initialised with empty PeerID\n");
204
205   return s;
206 }
207
208
209 /**
210  * Input an PeerID into the given sampler.
211  */
212   static void
213 RPS_sampler_elem_next (struct RPS_SamplerElement *s_elem, const struct GNUNET_PeerIdentity *other,
214     RPS_sampler_insert_cb insert_cb, void *insert_cls,
215     RPS_sampler_remove_cb remove_cb, void *remove_cls)
216 {
217   struct GNUNET_HashCode other_hash;
218
219   if ( 0 == GNUNET_CRYPTO_cmp_peer_identity(other, &(s_elem->peer_id)) )
220   {
221     LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER:          Got PeerID %s\n",
222         GNUNET_i2s(other));
223     LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Have already PeerID %s\n",
224         GNUNET_i2s(&(s_elem->peer_id)));
225   }
226   else
227   {
228     GNUNET_CRYPTO_hmac(&s_elem->auth_key,
229         other,
230         sizeof(struct GNUNET_PeerIdentity),
231         &other_hash);
232
233     if ( EMPTY == s_elem->is_empty )
234     { // Or whatever is a valid way to say
235       // "we have no PeerID at the moment"
236       LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Got PeerID %s; Simply accepting (was empty previously).\n",
237           GNUNET_i2s(other));
238       s_elem->peer_id = *other;
239       //s_elem->peer_id = other;
240       s_elem->peer_id_hash = other_hash;
241       if (NULL != sampler->insert_cb)
242       {
243         sampler->insert_cb(sampler->insert_cls, &(s_elem->peer_id));
244       }
245     }
246     else if ( 0 > GNUNET_CRYPTO_hash_cmp(&other_hash, &s_elem->peer_id_hash) )
247     {
248       LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER:            Got PeerID %s\n",
249           GNUNET_i2s(other));
250       LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Discarding old PeerID %s\n",
251           GNUNET_i2s(&s_elem->peer_id));
252
253       if ( NULL != sampler->remove_cb )
254       {
255         LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Removing old PeerID %s with the remove callback.\n",
256             GNUNET_i2s(&s_elem->peer_id));
257         sampler->remove_cb(sampler->remove_cls, &s_elem->peer_id);
258       }
259
260       memcpy(&s_elem->peer_id, other, sizeof(struct GNUNET_PeerIdentity));
261       //s_elem->peer_id = other;
262       s_elem->peer_id_hash = other_hash;
263
264       if ( NULL != sampler->insert_cb )
265       {
266         LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Inserting new PeerID %s with the insert callback.\n",
267             GNUNET_i2s(&s_elem->peer_id));
268         sampler->insert_cb(sampler->insert_cls, &s_elem->peer_id);
269       }
270     }
271     else
272     {
273       LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER:         Got PeerID %s\n",
274           GNUNET_i2s(other));
275       LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Keeping old PeerID %s\n",
276           GNUNET_i2s(&s_elem->peer_id));
277     }
278   }
279   s_elem->is_empty = NOT_EMPTY;
280 }
281
282
283 /**
284  * Grow or shrink the size of the sampler.
285  *
286  * @param new_size the new size of the sampler
287  */
288   void
289 RPS_sampler_resize (unsigned int new_size)
290 {
291   unsigned int old_size;
292   uint64_t i;
293   struct RPS_SamplerElement **rem_list;
294
295   // TODO check min and max size
296
297   old_size = sampler->sampler_size;
298
299   if (old_size > new_size)
300   { /* Shrinking */
301     /* Temporary store those to properly call the removeCB on those later */
302     rem_list = GNUNET_malloc ((old_size - new_size) * sizeof (struct RPS_SamplerElement *));
303     memcpy (rem_list,
304         &sampler->sampler_elements[new_size],
305         (old_size - new_size) * sizeof (struct RPS_SamplerElement *));
306
307     LOG (GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Shrinking sampler %d -> %d\n", old_size, new_size);
308     GNUNET_array_grow (sampler->sampler_elements, sampler->sampler_size, new_size);
309     LOG (GNUNET_ERROR_TYPE_DEBUG,
310         "SAMPLER: sampler->sampler_elements now points to %p\n",
311         sampler->sampler_elements);
312
313     for (i = 0 ; i < old_size - new_size ; i++)
314     {/* Remove unneeded rest */
315       LOG (GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Removing %" PRIX64 ". sampler\n", i);
316       if (NULL != sampler->remove_cb)
317         sampler->remove_cb (sampler->remove_cls, &rem_list[i]->peer_id);
318       GNUNET_free (rem_list[i]);
319     }
320   }
321   else if (old_size < new_size)
322   { /* Growing */
323     LOG (GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Growing sampler %d -> %d\n", old_size, new_size);
324     GNUNET_array_grow (sampler->sampler_elements, sampler->sampler_size, new_size);
325     LOG (GNUNET_ERROR_TYPE_DEBUG,
326         "SAMPLER: sampler->sampler_elements now points to %p\n",
327         sampler->sampler_elements);
328
329     for ( i = old_size ; i < new_size ; i++ )
330     { /* Add new sampler elements */
331       sampler->sampler_elements[i] = RPS_sampler_elem_create ();
332       if (NULL != sampler->insert_cb)
333         sampler->insert_cb (sampler->insert_cls, &sampler->sampler_elements[i]->peer_id);
334       LOG (GNUNET_ERROR_TYPE_DEBUG,
335           "SAMPLER: Added %" PRIX64 ". sampler, now pointing to %p, contains %s\n",
336           i, &sampler->sampler_elements[i], GNUNET_i2s (&sampler->sampler_elements[i]->peer_id));
337     }
338   }
339   else
340   {
341     LOG (GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Size remains the same -- nothing to do\n");
342     return;
343   }
344
345   GNUNET_assert(sampler->sampler_size == new_size);
346   LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Finished growing/shrinking.\n"); // remove
347 }
348
349
350 /**
351  * Initialise a tuple of sampler elements.
352  *
353  * @param init_size the size the sampler is initialised with
354  * @param id with which all newly created sampler elements are initialised
355  * @param ins_cb the callback that will be called on every PeerID that is 
356  *               newly inserted into a sampler element
357  * @param ins_cls the closure given to #ins_cb
358  * @param rem_cb the callback that will be called on every PeerID that is
359  *               removed from a sampler element
360  * @param rem_cls the closure given to #rem_cb
361  */
362   void
363 RPS_sampler_init (size_t init_size, const struct GNUNET_PeerIdentity *id,
364     RPS_sampler_insert_cb ins_cb, void *ins_cls,
365     RPS_sampler_remove_cb rem_cb, void *rem_cls)
366 {
367   //struct RPS_Sampler *sampler;
368   //uint64_t i;
369
370   /* Initialise context around extended sampler */
371   min_size = 10; // TODO make input to _samplers_init()
372   max_size = 1000; // TODO make input to _samplers_init()
373   GNUNET_new_array (64, struct GNUNET_TIME_Relative);
374
375   sampler = GNUNET_new (struct RPS_Sampler);
376   sampler->sampler_size = 0;
377   sampler->sampler_elements = NULL;
378   sampler->insert_cb = ins_cb;
379   sampler->insert_cls = ins_cls;
380   sampler->remove_cb = rem_cb;
381   sampler->remove_cls = rem_cls;
382   //sampler->sampler_elements = GNUNET_new_array(init_size, struct GNUNET_PeerIdentity);
383   //GNUNET_array_grow (sampler->sampler_elements, sampler->sampler_size, min_size);
384   RPS_sampler_resize (init_size);
385   RPS_sampler_update_list (id); // no super nice desing but ok for the moment
386
387   client_get_index = 0;
388
389   //GNUNET_assert (init_size == sampler->sampler_size);
390 }
391
392
393 /**
394  * A fuction to update every sampler in the given list
395  *
396  * @param id the PeerID that is put in the sampler
397  */
398   void
399 RPS_sampler_update_list (const struct GNUNET_PeerIdentity *id)
400 {
401   uint64_t i;
402
403   for ( i = 0 ; i < sampler->sampler_size ; i++ )
404     RPS_sampler_elem_next (sampler->sampler_elements[i], id,
405         sampler->insert_cb, sampler->insert_cls,
406         sampler->remove_cb, sampler->remove_cls);
407 }
408
409
410 /**
411  * Reinitialise all previously initialised sampler elements with the given value.
412  *
413  * Used to get rid of a PeerID.
414  *
415  * @param id the id of the sampler elements to update.
416  */
417   void
418 RPS_sampler_reinitialise_by_value (const struct GNUNET_PeerIdentity *id)
419 {
420   uint64_t i;
421
422   for ( i = 0 ; i < sampler->sampler_size ; i++ )
423   {
424     if ( 0 == GNUNET_CRYPTO_cmp_peer_identity(id, &(sampler->sampler_elements[i]->peer_id)) )
425     {
426       LOG(GNUNET_ERROR_TYPE_DEBUG, "SAMPLER: Reinitialising sampler\n");
427       RPS_sampler_elem_reinit (sampler->sampler_elements[i]);
428     }
429   }
430 }
431
432
433 /**
434  * Get one random peer out of the sampled peers.
435  *
436  * We might want to reinitialise this sampler after giving the
437  * corrsponding peer to the client.
438  * Only used internally
439  */
440   const struct GNUNET_PeerIdentity * 
441 RPS_sampler_get_rand_peer_ ()
442 {
443   uint64_t r_index;
444   const struct GNUNET_PeerIdentity *peer; // do we have to malloc that?
445
446   // TODO implement extra logic
447
448   /**;
449    * Choose the r_index of the peer we want to return
450    * at random from the interval of the gossip list
451    */
452   r_index = GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_STRONG,
453       sampler->sampler_size);
454
455   //if ( EMPTY == sampler->sampler_elements[r_index]->is_empty )
456   //  // TODO schedule for later
457   //  peer = NULL;
458   //else
459     peer = &(sampler->sampler_elements[r_index]->peer_id);
460   sampler->sampler_elements[r_index]->last_request = GNUNET_TIME_absolute_get();
461   LOG(GNUNET_ERROR_TYPE_DEBUG, "Sgrp: Returning PeerID %s\n", GNUNET_i2s(peer));
462
463   return peer;
464 }
465
466
467 /**
468  * Get n random peers out of the sampled peers.
469  *
470  * We might want to reinitialise this sampler after giving the
471  * corrsponding peer to the client.
472  * Random with or without consumption?
473  * Only used internally
474  */
475   const struct GNUNET_PeerIdentity *
476 RPS_sampler_get_n_rand_peers_ (uint64_t n)
477 {
478   if ( 0 == sampler->sampler_size )
479   {
480     LOG (GNUNET_ERROR_TYPE_DEBUG,
481         "Sgrp: List empty - Returning NULL\n");
482     return NULL;
483   }
484   else
485   {
486     // TODO check if we have too much (distinct) sampled peers
487     // If we are not ready yet maybe schedule for later
488     struct GNUNET_PeerIdentity *peers;
489     uint64_t i;
490
491     peers = GNUNET_malloc (n * sizeof(struct GNUNET_PeerIdentity));
492
493     for ( i = 0 ; i < n ; i++ ) {
494       //peers[i] = RPS_sampler_get_rand_peer_(sampler->sampler_elements);
495       memcpy (&peers[i], RPS_sampler_get_rand_peer_ (), sizeof (struct GNUNET_PeerIdentity));
496     }
497     return peers;
498   }
499 }
500
501
502 /**
503  * Get one random peer out of the sampled peers.
504  *
505  * We might want to reinitialise this sampler after giving the
506  * corrsponding peer to the client.
507  *
508  * @return a random PeerID of the PeerIDs previously put into the sampler.
509  */
510   const struct GNUNET_PeerIdentity * 
511 RPS_sampler_get_rand_peer ()
512 {
513   struct GNUNET_PeerIdentity *peer;
514
515   // use _get_rand_peer_ ?
516   peer = GNUNET_new (struct GNUNET_PeerIdentity);
517   *peer = sampler->sampler_elements[client_get_index]->peer_id;
518   RPS_sampler_elem_reinit (sampler->sampler_elements[client_get_index]);
519   if ( client_get_index == sampler->sampler_size )
520     client_get_index = 0;
521   else
522     client_get_index++;
523   return peer;
524 }
525
526
527 /**
528  * Get n random peers out of the sampled peers.
529  *
530  * We might want to reinitialise this sampler after giving the
531  * corrsponding peer to the client.
532  * Random with or without consumption?
533  *
534  * @return n random PeerIDs of the PeerIDs previously put into the sampler.
535  */
536   const struct GNUNET_PeerIdentity *
537 RPS_sampler_get_n_rand_peers (uint64_t n)
538 {
539   // use _get_rand_peers_ ?
540   if ( 0 == sampler->sampler_size )
541   {
542     LOG (GNUNET_ERROR_TYPE_DEBUG,
543         "Sgrp: List empty - Returning NULL\n");
544     return NULL;
545   }
546   else
547   {
548     // TODO check if we have too much (distinct) sampled peers
549     // If we are not ready yet maybe schedule for later
550     struct GNUNET_PeerIdentity *peers;
551     const struct GNUNET_PeerIdentity *peer;
552     uint64_t i;
553
554     peers = GNUNET_malloc (n * sizeof (struct GNUNET_PeerIdentity));
555
556     for ( i = 0 ; i < n ; i++ ) {
557       //peers[i] = RPS_sampler_get_rand_peer_(sampler->sampler_elements);
558       peer = RPS_sampler_get_rand_peer ();
559       memcpy (&peers[i], peer, sizeof (struct GNUNET_PeerIdentity));
560       //GNUNET_free (peer);
561     }
562     return peers;
563   }
564 }
565
566
567 /**
568  * Counts how many Samplers currently hold a given PeerID.
569  *
570  * @param id the PeerID to count.
571  *
572  * @return the number of occurrences of id.
573  */
574   uint64_t
575 RPS_sampler_count_id (const struct GNUNET_PeerIdentity *id)
576 {
577   uint64_t count;
578   uint64_t i;
579
580   count = 0;
581   for ( i = 0 ; i < sampler->sampler_size ; i++ )
582   {
583     if ( 0 == GNUNET_CRYPTO_cmp_peer_identity (&sampler->sampler_elements[i]->peer_id, id) 
584         && EMPTY != sampler->sampler_elements[i]->is_empty)
585       count++;
586   }
587   return count;
588 }
589
590
591 /**
592  * Cleans the sampler.
593  */
594   void
595 RPS_sampler_destroy ()
596 {
597   RPS_sampler_resize (0);
598   GNUNET_array_grow (sampler->sampler_elements, sampler->sampler_size, 0);
599 }
600
601 /* end of gnunet-service-rps.c */