495e3be47dff5880de73ab1b78447e5d0a2015ed
[oweals/gnunet.git] / src / psyc / test_psyc.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 psyc/test_psyc.c
23  * @brief Tests for the PSYC API.
24  * @author Gabor X Toth
25  * @author Christian Grothoff
26  */
27
28 #include <inttypes.h>
29
30 #include "platform.h"
31 #include "gnunet_crypto_lib.h"
32 #include "gnunet_common.h"
33 #include "gnunet_util_lib.h"
34 #include "gnunet_testing_lib.h"
35 #include "gnunet_env_lib.h"
36 #include "gnunet_psyc_util_lib.h"
37 #include "gnunet_psyc_service.h"
38
39 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
40
41 #define DEBUG_SERVICE 0
42
43
44 /**
45  * Return value from 'main'.
46  */
47 static int res;
48
49 static const struct GNUNET_CONFIGURATION_Handle *cfg;
50
51 /**
52  * Handle for task for timeout termination.
53  */
54 static GNUNET_SCHEDULER_TaskIdentifier end_badly_task;
55
56 static struct GNUNET_PSYC_Master *mst;
57 static struct GNUNET_PSYC_Slave *slv;
58
59 static struct GNUNET_CRYPTO_EddsaPrivateKey *channel_key;
60 static struct GNUNET_CRYPTO_EcdsaPrivateKey *slave_key;
61
62 static struct GNUNET_CRYPTO_EddsaPublicKey channel_pub_key;
63 static struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
64
65 struct TransmitClosure
66 {
67   struct GNUNET_PSYC_MasterTransmitHandle *mst_tmit;
68   struct GNUNET_PSYC_SlaveTransmitHandle *slv_tmit;
69   struct GNUNET_ENV_Environment *env;
70   struct GNUNET_ENV_Modifier *mod;
71   char *data[16];
72   const char *mod_value;
73   size_t mod_value_size;
74   uint8_t data_delay[16];
75   uint8_t data_count;
76   uint8_t paused;
77   uint8_t n;
78 };
79
80 struct TransmitClosure *tmit;
81
82 static uint8_t join_req_count;
83
84 enum
85 {
86   TEST_NONE,
87   TEST_SLAVE_TRANSMIT,
88   TEST_MASTER_TRANSMIT,
89 } test;
90
91
92 static void
93 master_transmit ();
94
95
96 void master_stopped (void *cls)
97 {
98   if (NULL != tmit)
99   {
100     GNUNET_ENV_environment_destroy (tmit->env);
101     GNUNET_free (tmit);
102     tmit = NULL;
103   }
104   GNUNET_SCHEDULER_shutdown ();
105 }
106
107 void slave_parted (void *cls)
108 {
109   if (NULL != mst)
110   {
111     GNUNET_PSYC_master_stop (mst, GNUNET_NO, &master_stopped, NULL);
112     mst = NULL;
113   }
114   else
115     master_stopped (NULL);
116 }
117
118 /**
119  * Clean up all resources used.
120  */
121 static void
122 cleanup ()
123 {
124   if (NULL != slv)
125   {
126     GNUNET_PSYC_slave_part (slv, GNUNET_NO, &slave_parted, NULL);
127     slv = NULL;
128   }
129   else
130     slave_parted (NULL);
131 }
132
133
134 /**
135  * Terminate the test case (failure).
136  *
137  * @param cls NULL
138  * @param tc scheduler context
139  */
140 static void
141 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
142 {
143   res = 1;
144   cleanup ();
145   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Test FAILED.\n");
146 }
147
148
149 /**
150  * Terminate the test case (success).
151  *
152  * @param cls NULL
153  * @param tc scheduler context
154  */
155 static void
156 end_normally (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
157 {
158   res = 0;
159   cleanup ();
160   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Test PASSED.\n");
161 }
162
163
164 /**
165  * Finish the test case (successfully).
166  */
167 static void
168 end ()
169 {
170   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending tests.\n");
171
172   if (end_badly_task != GNUNET_SCHEDULER_NO_TASK)
173   {
174     GNUNET_SCHEDULER_cancel (end_badly_task);
175     end_badly_task = GNUNET_SCHEDULER_NO_TASK;
176   }
177   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
178                                 &end_normally, NULL);
179 }
180
181
182 static void
183 master_message_cb (void *cls, uint64_t message_id, uint32_t flags,
184                    const struct GNUNET_PSYC_MessageHeader *msg)
185 {
186   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
187               "Master got PSYC message fragment of size %u "
188               "belonging to message ID %llu with flags %x\n",
189               ntohs (msg->header.size), message_id, flags);
190   // FIXME
191 }
192
193
194 static void
195 master_message_part_cb (void *cls, uint64_t message_id,
196                         uint64_t data_offset, uint32_t flags,
197                         const struct GNUNET_MessageHeader *msg)
198 {
199   if (NULL == msg)
200   {
201     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
202                 "Error while receiving message %llu\n", message_id);
203     return;
204   }
205
206   uint16_t type = ntohs (msg->type);
207   uint16_t size = ntohs (msg->size);
208
209   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
210               "Master got message part of type %u and size %u "
211               "belonging to message ID %llu with flags %x\n",
212               type, size, message_id, flags);
213
214   switch (test)
215   {
216   case TEST_SLAVE_TRANSMIT:
217     if (GNUNET_PSYC_MESSAGE_REQUEST != flags)
218     {
219       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
220                   "Unexpected request flags: %x" PRIu32 "\n", flags);
221       GNUNET_assert (0);
222       return;
223     }
224     // FIXME: check rest of message
225
226     if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END == type)
227       master_transmit ();
228     break;
229
230   case TEST_MASTER_TRANSMIT:
231     break;
232
233   default:
234     GNUNET_assert (0);
235   }
236 }
237
238
239 static void
240 slave_message_cb (void *cls, uint64_t message_id, uint32_t flags,
241                   const struct GNUNET_PSYC_MessageHeader *msg)
242 {
243   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
244               "Slave got PSYC message fragment of size %u "
245               "belonging to message ID %llu with flags %x\n",
246               ntohs (msg->header.size), message_id, flags);
247   // FIXME
248 }
249
250
251 static void
252 slave_message_part_cb (void *cls, uint64_t message_id,
253                        uint64_t data_offset, uint32_t flags,
254                        const struct GNUNET_MessageHeader *msg)
255 {
256   if (NULL == msg)
257   {
258     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
259                 "Error while receiving message %llu\n", message_id);
260     return;
261   }
262
263   uint16_t type = ntohs (msg->type);
264   uint16_t size = ntohs (msg->size);
265
266   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
267               "Slave got message part of type %u and size %u "
268               "belonging to message ID %llu with flags %x\n",
269               type, size, message_id, flags);
270
271   switch (test)
272   {
273   case TEST_MASTER_TRANSMIT:
274     if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END == type)
275       end ();
276     break;
277
278   default:
279     GNUNET_assert (0);
280   }
281 }
282
283
284 static void
285 transmit_resume (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
286 {
287   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission resumed.\n");
288   struct TransmitClosure *tmit = cls;
289   if (NULL != tmit->mst_tmit)
290     GNUNET_PSYC_master_transmit_resume (tmit->mst_tmit);
291   else
292     GNUNET_PSYC_slave_transmit_resume (tmit->slv_tmit);
293 }
294
295
296 static int
297 tmit_notify_data (void *cls, uint16_t *data_size, void *data)
298 {
299   struct TransmitClosure *tmit = cls;
300   if (0 == tmit->data_count)
301   {
302     *data_size = 0;
303     return GNUNET_YES;
304   }
305
306   uint16_t size = strlen (tmit->data[tmit->n]);
307   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
308               "Transmit notify data: %u bytes available, "
309               "processing fragment %u/%u (size %u).\n",
310               *data_size, tmit->n + 1, tmit->data_count, size);
311   if (*data_size < size)
312   {
313     *data_size = 0;
314     GNUNET_assert (0);
315     return GNUNET_SYSERR;
316   }
317
318   if (GNUNET_YES != tmit->paused && 0 < tmit->data_delay[tmit->n])
319   {
320     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission paused.\n");
321     tmit->paused = GNUNET_YES;
322     GNUNET_SCHEDULER_add_delayed (
323       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
324                                      tmit->data_delay[tmit->n]),
325       &transmit_resume, tmit);
326     *data_size = 0;
327     return GNUNET_NO;
328   }
329   tmit->paused = GNUNET_NO;
330
331   *data_size = size;
332   memcpy (data, tmit->data[tmit->n], size);
333
334   return ++tmit->n < tmit->data_count ? GNUNET_NO : GNUNET_YES;
335 }
336
337
338 static int
339 tmit_notify_mod (void *cls, uint16_t *data_size, void *data, uint8_t *oper,
340                  uint32_t *full_value_size)
341 {
342   struct TransmitClosure *tmit = cls;
343   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
344               "Transmit notify modifier: %lu bytes available, "
345               "%u modifiers left to process.\n",
346               *data_size, GNUNET_ENV_environment_get_count (tmit->env));
347
348   uint16_t name_size = 0;
349   size_t value_size = 0;
350   const char *value = NULL;
351
352   if (NULL != oper && NULL != tmit->mod)
353   { /* New modifier */
354     tmit->mod = tmit->mod->next;
355     if (NULL == tmit->mod)
356     { /* No more modifiers, continue with data */
357       *data_size = 0;
358       return GNUNET_YES;
359     }
360
361     GNUNET_assert (tmit->mod->value_size < UINT32_MAX);
362     *full_value_size = tmit->mod->value_size;
363     *oper = tmit->mod->oper;
364     name_size = strlen (tmit->mod->name);
365
366     if (name_size + 1 + tmit->mod->value_size <= *data_size)
367     {
368       *data_size = name_size + 1 + tmit->mod->value_size;
369     }
370     else
371     {
372       tmit->mod_value_size = tmit->mod->value_size;
373       value_size = *data_size - name_size - 1;
374       tmit->mod_value_size -= value_size;
375       tmit->mod_value = tmit->mod->value + value_size;
376     }
377
378     memcpy (data, tmit->mod->name, name_size);
379     ((char *)data)[name_size] = '\0';
380     memcpy ((char *)data + name_size + 1, tmit->mod->value, value_size);
381   }
382   else if (NULL != tmit->mod_value && 0 < tmit->mod_value_size)
383   { /* Modifier continuation */
384     value = tmit->mod_value;
385     if (tmit->mod_value_size <= *data_size)
386     {
387       value_size = tmit->mod_value_size;
388       tmit->mod_value = NULL;
389     }
390     else
391     {
392       value_size = *data_size;
393       tmit->mod_value += value_size;
394     }
395     tmit->mod_value_size -= value_size;
396
397     if (*data_size < value_size)
398     {
399       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
400                   "value larger than buffer: %u < %zu\n",
401                   *data_size, value_size);
402       *data_size = 0;
403       return GNUNET_NO;
404     }
405
406     *data_size = value_size;
407     memcpy (data, value, value_size);
408   }
409
410   return GNUNET_NO;
411 }
412
413
414 static void
415 slave_join ();
416
417
418 static void
419 join_decision_cb (void *cls,
420                   const struct GNUNET_PSYC_JoinDecisionMessage *dcsn,
421                   int is_admitted,
422                   const struct GNUNET_PSYC_Message *join_msg)
423 {
424   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
425               "Slave got join decision: %d\n", is_admitted);
426
427   if (GNUNET_YES != is_admitted)
428   { /* First join request is refused, retry. */
429     GNUNET_assert (1 == join_req_count);
430     slave_join ();
431     return;
432   }
433
434   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Slave sending request to master.\n");
435
436   test = TEST_SLAVE_TRANSMIT;
437
438   tmit = GNUNET_new (struct TransmitClosure);
439   tmit->env = GNUNET_ENV_environment_create ();
440   GNUNET_ENV_environment_add (tmit->env, GNUNET_ENV_OP_ASSIGN,
441                               "_abc", "abc def", 7);
442   GNUNET_ENV_environment_add (tmit->env, GNUNET_ENV_OP_ASSIGN,
443                               "_abc_def", "abc def ghi", 11);
444   tmit->mod = GNUNET_ENV_environment_head (tmit->env);
445   tmit->n = 0;
446   tmit->data[0] = "slave test";
447   tmit->data_count = 1;
448   tmit->slv_tmit
449     = GNUNET_PSYC_slave_transmit (slv, "_request_test", tmit_notify_mod,
450                                   tmit_notify_data, tmit,
451                                   GNUNET_PSYC_SLAVE_TRANSMIT_NONE);
452 }
453
454
455 static void
456 join_request_cb (void *cls,
457                  const struct GNUNET_PSYC_JoinRequestMessage *req,
458                  const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
459                  const struct GNUNET_PSYC_Message *join_msg,
460                  struct GNUNET_PSYC_JoinHandle *jh)
461 {
462   struct GNUNET_HashCode slave_key_hash;
463   GNUNET_CRYPTO_hash (slave_key, sizeof (*slave_key), &slave_key_hash);
464   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
465               "Got join request #%u from %s.\n",
466               join_req_count, GNUNET_h2s (&slave_key_hash));
467
468   /* Reject first request */
469   int is_admitted = (0 < join_req_count++) ? GNUNET_YES : GNUNET_NO;
470   GNUNET_PSYC_join_decision (jh, is_admitted, 0, NULL, NULL);
471 }
472
473
474 static void
475 slave_connect_cb (void *cls, uint64_t max_message_id)
476 {
477   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
478               "Slave connected: %lu\n", max_message_id);
479 }
480
481
482 static void
483 slave_join ()
484 {
485   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Joining slave.\n");
486
487   struct GNUNET_PeerIdentity origin; // FIXME: this peer
488   struct GNUNET_ENV_Environment *env = GNUNET_ENV_environment_create ();
489   GNUNET_ENV_environment_add (env, GNUNET_ENV_OP_ASSIGN,
490                               "_foo", "bar baz", 7);
491   GNUNET_ENV_environment_add (env, GNUNET_ENV_OP_ASSIGN,
492                               "_foo_bar", "foo bar baz", 11);
493   struct GNUNET_PSYC_Message *
494     join_msg = GNUNET_PSYC_message_create ("_request_join", env, "some data", 9);
495
496   slv = GNUNET_PSYC_slave_join (cfg, &channel_pub_key, slave_key, &origin, 0, NULL,
497                                 &slave_message_cb, &slave_message_part_cb,
498                                 &slave_connect_cb, &join_decision_cb, NULL,
499                                 join_msg);
500   GNUNET_ENV_environment_destroy (env);
501 }
502
503
504 static void
505 master_transmit ()
506 {
507   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Master sending message to all.\n");
508   test = TEST_MASTER_TRANSMIT;
509   uint32_t i, j;
510
511   char *name_max = "_test_max";
512   uint8_t name_max_size = sizeof ("_test_max");
513   char *val_max = GNUNET_malloc (GNUNET_PSYC_MODIFIER_MAX_PAYLOAD);
514   for (i = 0; i < GNUNET_PSYC_MODIFIER_MAX_PAYLOAD; i++)
515     val_max[i] = (0 == i % 10000) ? '0' + i / 10000 : '.';
516
517   char *name_cont = "_test_cont";
518   uint8_t name_cont_size = sizeof ("_test_cont");
519   char *val_cont = GNUNET_malloc (GNUNET_PSYC_MODIFIER_MAX_PAYLOAD
520                                   + GNUNET_PSYC_MOD_CONT_MAX_PAYLOAD);
521   for (i = 0; i < GNUNET_PSYC_MODIFIER_MAX_PAYLOAD - name_cont_size; i++)
522     val_cont[i] = (0 == i % 10000) ? '0' + i / 10000 : ':';
523   for (j = 0; j < GNUNET_PSYC_MOD_CONT_MAX_PAYLOAD; j++, i++)
524     val_cont[i] = (0 == j % 10000) ? '0' + j / 10000 : '!';
525
526   tmit = GNUNET_new (struct TransmitClosure);
527   tmit->env = GNUNET_ENV_environment_create ();
528   GNUNET_ENV_environment_add (tmit->env, GNUNET_ENV_OP_ASSIGN,
529                               "_foo", "bar baz", 7);
530   GNUNET_ENV_environment_add (tmit->env, GNUNET_ENV_OP_ASSIGN,
531                               name_max, val_max,
532                               GNUNET_PSYC_MODIFIER_MAX_PAYLOAD
533                               - name_max_size);
534   GNUNET_ENV_environment_add (tmit->env, GNUNET_ENV_OP_ASSIGN,
535                               "_foo_bar", "foo bar baz", 11);
536   GNUNET_ENV_environment_add (tmit->env, GNUNET_ENV_OP_ASSIGN,
537                               name_cont, val_cont,
538                               GNUNET_PSYC_MODIFIER_MAX_PAYLOAD - name_cont_size
539                               + GNUNET_PSYC_MOD_CONT_MAX_PAYLOAD);
540   tmit->mod = GNUNET_ENV_environment_head (tmit->env);
541   tmit->data[0] = "foo";
542   tmit->data[1] =  GNUNET_malloc (GNUNET_PSYC_DATA_MAX_PAYLOAD + 1);
543   for (i = 0; i < GNUNET_PSYC_DATA_MAX_PAYLOAD; i++)
544     tmit->data[1][i] = (0 == i % 10000) ? '0' + i / 10000 : '_';
545   tmit->data[2] = "foo bar";
546   tmit->data[3] = "foo bar baz";
547   tmit->data_delay[1] = 3;
548   tmit->data_count = 4;
549   tmit->mst_tmit
550     = GNUNET_PSYC_master_transmit (mst, "_notice_test", tmit_notify_mod,
551                                    tmit_notify_data, tmit,
552                                    GNUNET_PSYC_MASTER_TRANSMIT_INC_GROUP_GEN);
553 }
554
555
556 static void
557 master_start_cb (void *cls, uint64_t max_message_id)
558 {
559   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
560               "Master started: %" PRIu64 "\n", max_message_id);
561   slave_join ();
562 }
563
564
565 /**
566  * Main function of the test, run from scheduler.
567  *
568  * @param cls NULL
569  * @param cfg configuration we use (also to connect to PSYC service)
570  * @param peer handle to access more of the peer (not used)
571  */
572 static void
573 #if DEBUG_SERVICE
574 run (void *cls, char *const *args, const char *cfgfile,
575      const struct GNUNET_CONFIGURATION_Handle *c)
576 #else
577 run (void *cls,
578      const struct GNUNET_CONFIGURATION_Handle *c,
579      struct GNUNET_TESTING_Peer *peer)
580 #endif
581 {
582   cfg = c;
583   end_badly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
584
585   channel_key = GNUNET_CRYPTO_eddsa_key_create ();
586   slave_key = GNUNET_CRYPTO_ecdsa_key_create ();
587
588   GNUNET_CRYPTO_eddsa_key_get_public (channel_key, &channel_pub_key);
589   GNUNET_CRYPTO_ecdsa_key_get_public (slave_key, &slave_pub_key);
590
591   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Starting master.\n");
592   mst = GNUNET_PSYC_master_start (cfg, channel_key, GNUNET_PSYC_CHANNEL_PRIVATE,
593                                   &master_start_cb, &join_request_cb,
594                                   &master_message_cb, &master_message_part_cb,
595                                   NULL);
596 }
597
598
599 int
600 main (int argc, char *argv[])
601 {
602   res = 1;
603 #if DEBUG_SERVICE
604   const struct GNUNET_GETOPT_CommandLineOption opts[] = {
605     GNUNET_GETOPT_OPTION_END
606   };
607   if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, "test-psyc",
608                                        "test-psyc [options]",
609                                        opts, &run, NULL))
610     return 1;
611 #else
612   if (0 != GNUNET_TESTING_peer_run ("test-psyc", "test_psyc.conf", &run, NULL))
613     return 1;
614 #endif
615   return res;
616 }
617
618 /* end of test_psyc.c */