PSYCstore service and API implementation
[oweals/gnunet.git] / src / psycstore / test_plugin_psycstore.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 psycstore/test_plugin_psycstore.c
23  * @brief Test for the PSYCstore plugins.
24  * @author Gabor X Toth
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_testing_lib.h"
30 #include "gnunet_psycstore_plugin.h"
31 #include "gnunet_psycstore_service.h"
32 #include "gnunet_multicast_service.h"
33
34 #define DEBUG_PSYCSTORE GNUNET_EXTRA_LOGGING
35 #if DEBUG_PSYCSTORE
36 # define LOG_LEVEL "DEBUG"
37 #else
38 # define LOG_LEVEL "WARNING"
39 #endif
40
41 #define C2ARG(str) str, (sizeof (str) - 1)
42
43 #define LOG(kind,...)                                                          \
44   GNUNET_log_from (kind, "test-plugin-psycstore", __VA_ARGS__)
45
46 #define ASSERT(x)                                                              \
47   do {                                                                         \
48     if (! (x))                                                                 \
49     {                                                                          \
50       LOG (GNUNET_ERROR_TYPE_ERROR, "Error at %s:%d\n", __FILE__, __LINE__);   \
51       goto FAILURE;                                                            \
52     }                                                                          \
53   } while (0)
54
55 static int ok;
56
57 /**
58  * Name of plugin under test.
59  */
60 static const char *plugin_name;
61
62 static struct GNUNET_CRYPTO_EccPrivateKey *channel_key;
63 static struct GNUNET_CRYPTO_EccPrivateKey *slave_key;
64
65 static struct GNUNET_CRYPTO_EccPublicSignKey channel_pub_key;
66 static struct GNUNET_CRYPTO_EccPublicSignKey slave_pub_key;
67
68 /**
69  * Function called when the service shuts down.  Unloads our psycstore
70  * plugin.
71  *
72  * @param api api to unload
73  */
74 static void
75 unload_plugin (struct GNUNET_PSYCSTORE_PluginFunctions *api)
76 {
77   char *libname;
78
79   GNUNET_asprintf (&libname, "libgnunet_plugin_psycstore_%s", plugin_name);
80   GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
81   GNUNET_free (libname);
82 }
83
84
85 /**
86  * Load the psycstore plugin.
87  *
88  * @param cfg configuration to pass
89  * @return NULL on error
90  */
91 static struct GNUNET_PSYCSTORE_PluginFunctions *
92 load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
93 {
94   struct GNUNET_PSYCSTORE_PluginFunctions *ret;
95   char *libname;
96
97   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading `%s' psycstore plugin\n"),
98               plugin_name);
99   GNUNET_asprintf (&libname, "libgnunet_plugin_psycstore_%s", plugin_name);
100   if (NULL == (ret = GNUNET_PLUGIN_load (libname, (void*) cfg)))
101   {
102     FPRINTF (stderr, "Failed to load plugin `%s'!\n", plugin_name);
103     return NULL;
104   }
105   GNUNET_free (libname);
106   return ret;
107 }
108
109
110 struct FragmentClosure
111 {
112   uint8_t n;
113   uint64_t flags[16];
114   struct GNUNET_MULTICAST_MessageHeader *msg[16];
115 };
116
117 static int
118 fragment_cb (void *cls, struct GNUNET_MULTICAST_MessageHeader *msg2,
119              enum GNUNET_PSYCSTORE_MessageFlags flags)
120 {
121   struct FragmentClosure *fcls = cls;
122   struct GNUNET_MULTICAST_MessageHeader *msg1 = fcls->msg[fcls->n];
123   uint64_t flags1 = fcls->flags[fcls->n++];
124   int ret;
125
126   if (flags1 == flags && msg1->header.size == msg2->header.size
127       && 0 == memcmp (msg1, msg2, ntohs (msg1->header.size)))
128   {
129     LOG (GNUNET_ERROR_TYPE_DEBUG, "Fragment %llu matches\n",
130          GNUNET_ntohll (msg1->fragment_id));
131     ret = GNUNET_YES;
132   }
133   else
134   {
135     LOG (GNUNET_ERROR_TYPE_ERROR, "Fragment %llu differs\n",
136          GNUNET_ntohll (msg1->fragment_id));
137     ret = GNUNET_SYSERR;
138   }
139
140   GNUNET_free (msg2);
141   return ret;
142 }
143
144
145 struct StateClosure {
146   size_t n;
147   char *name[16];
148   void *value[16];
149   size_t value_size[16];
150 };
151
152 static int
153 state_cb (void *cls, const char *name, const void *value, size_t value_size)
154 {
155   struct StateClosure *scls = cls;
156   const void *val = scls->value[scls->n];
157   size_t val_size = scls->value_size[scls->n++];
158
159   /* FIXME: check name */
160
161   return value_size == val_size && 0 == memcmp (value, val, val_size)
162     ? GNUNET_YES
163     : GNUNET_SYSERR;
164 }
165
166
167 static void
168 run (void *cls, char *const *args, const char *cfgfile,
169      const struct GNUNET_CONFIGURATION_Handle *cfg)
170 {
171   struct GNUNET_PSYCSTORE_PluginFunctions *db;
172
173   ok = 1;
174   db = load_plugin (cfg);
175   if (NULL == db)
176   {
177     FPRINTF (stderr,
178              "%s",
179              "Failed to initialize PSYCstore.  "
180              "Database likely not setup, skipping test.\n");
181     return;
182   }
183
184   /* Store & test membership */
185
186   channel_key = GNUNET_CRYPTO_ecc_key_create ();
187   slave_key = GNUNET_CRYPTO_ecc_key_create ();
188
189   GNUNET_CRYPTO_ecc_key_get_public_for_signature (channel_key, &channel_pub_key);
190   GNUNET_CRYPTO_ecc_key_get_public_for_signature (slave_key, &slave_pub_key);
191
192   ASSERT (GNUNET_OK == db->membership_store (db->cls, &channel_pub_key,
193                                              &slave_pub_key, GNUNET_YES,
194                                              4, 2, 1));
195
196   ASSERT (GNUNET_YES == db->membership_test (db->cls, &channel_pub_key,
197                                              &slave_pub_key, 4));
198
199   ASSERT (GNUNET_YES == db->membership_test (db->cls, &channel_pub_key,
200                                              &slave_pub_key, 2));
201
202   ASSERT (GNUNET_NO == db->membership_test (db->cls, &channel_pub_key,
203                                             &slave_pub_key, 1));
204
205
206   /* Store & get messages */
207
208   struct GNUNET_MULTICAST_MessageHeader *msg
209     = GNUNET_malloc (sizeof (*msg) + sizeof (channel_pub_key));
210   ASSERT (msg != NULL);
211
212   msg->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
213   msg->header.size = htons (sizeof (*msg) + sizeof (channel_pub_key));
214
215   msg->hop_counter = htonl (9);
216   msg->fragment_id = GNUNET_htonll (INT64_MAX - 1);
217   msg->fragment_offset = GNUNET_htonll (0);
218   msg->message_id = GNUNET_htonll (INT64_MAX - 10);
219   msg->group_generation = GNUNET_htonll (INT64_MAX - 3);
220   msg->flags = htonl (GNUNET_MULTICAST_MESSAGE_LAST_FRAGMENT);
221
222   memcpy (&msg[1], &channel_pub_key, sizeof (channel_pub_key));
223
224   msg->purpose.size = htonl (ntohs (msg->header.size)
225                              - sizeof (msg->header)
226                              - sizeof (msg->hop_counter)
227                              - sizeof (msg->signature));
228   msg->purpose.purpose = htonl (234);
229   GNUNET_CRYPTO_ecc_sign (slave_key, &msg->purpose, &msg->signature);
230
231   struct FragmentClosure fcls = { 0 };
232   fcls.n = 0;
233   fcls.msg[0] = msg;
234   fcls.flags[0] = GNUNET_PSYCSTORE_MESSAGE_STATE;
235
236   ASSERT (GNUNET_OK == db->fragment_store (db->cls, &channel_pub_key, msg,
237                                            fcls.flags[0]));
238
239   ASSERT (GNUNET_OK == db->fragment_get (db->cls, &channel_pub_key,
240                                          GNUNET_ntohll (msg->fragment_id),
241                                          fragment_cb, &fcls));
242   ASSERT (fcls.n == 1);
243
244   fcls.n = 0;
245
246   ASSERT (GNUNET_OK == db->message_get_fragment (db->cls, &channel_pub_key,
247                                                  GNUNET_ntohll (msg->message_id),
248                                                  GNUNET_ntohll (msg->fragment_offset),
249                                                  fragment_cb, &fcls));
250   ASSERT (fcls.n == 1);
251
252   ASSERT (GNUNET_OK == db->message_add_flags (
253             db->cls, &channel_pub_key, GNUNET_ntohll (msg->message_id),
254             GNUNET_PSYCSTORE_MESSAGE_STATE_APPLIED));
255
256   fcls.n = 0;
257   fcls.flags[0] |= GNUNET_PSYCSTORE_MESSAGE_STATE_APPLIED;
258
259   ASSERT (GNUNET_OK == db->fragment_get (db->cls, &channel_pub_key,
260                                          GNUNET_ntohll (msg->fragment_id),
261                                          fragment_cb, &fcls));
262   ASSERT (fcls.n == 1);
263
264   struct GNUNET_MULTICAST_MessageHeader *msg1
265     = GNUNET_malloc (sizeof (*msg1) + sizeof (channel_pub_key));
266
267   memcpy (msg1, msg, sizeof (*msg1) + sizeof (channel_pub_key));
268
269   msg1->fragment_id = GNUNET_htonll (INT64_MAX);
270   msg1->fragment_offset = GNUNET_htonll (32768);
271
272   fcls.n = 0;
273   fcls.msg[1] = msg1;
274   fcls.flags[1] = GNUNET_PSYCSTORE_MESSAGE_STATE_HASH;
275
276   ASSERT (GNUNET_OK == db->fragment_store (db->cls, &channel_pub_key, msg1,
277                                            fcls.flags[1]));
278
279   uint64_t retfrags = 0;
280   ASSERT (GNUNET_OK == db->message_get (db->cls, &channel_pub_key,
281                                         GNUNET_ntohll (msg->message_id),
282                                         &retfrags, fragment_cb, &fcls));
283   ASSERT (fcls.n == 2 && retfrags == 2);
284
285   /* Master counters */
286
287   uint64_t fragment_id = 0, message_id = 0, group_generation = 0;
288   ASSERT (GNUNET_OK == db->counters_get_master (db->cls, &channel_pub_key,
289                                                 &fragment_id, &message_id,
290                                                 &group_generation)
291           && fragment_id == GNUNET_ntohll (msg1->fragment_id)
292           && message_id == GNUNET_ntohll (msg1->message_id)
293           && group_generation == GNUNET_ntohll (msg1->group_generation));
294
295
296   /* Modify state */
297
298   message_id = GNUNET_ntohll (fcls.msg[0]->message_id) + 1;
299   ASSERT (GNUNET_OK == db->state_modify_begin (db->cls, &channel_pub_key,
300                                                message_id, 1));
301
302   ASSERT (GNUNET_OK == db->state_modify_set (db->cls, &channel_pub_key, "_foo",
303                                              C2ARG("one two three")));
304
305   ASSERT (GNUNET_OK == db->state_modify_set (db->cls, &channel_pub_key,
306                                              "_foo_bar", slave_key,
307                                              sizeof (*slave_key)));
308
309   ASSERT (GNUNET_OK == db->state_modify_end (db->cls, &channel_pub_key,
310                                              message_id));
311
312   struct StateClosure scls = { 0 };
313   scls.n = 0;
314   scls.value[0] = "one two three";
315   scls.value_size[0] = strlen ("one two three");
316
317   ASSERT (GNUNET_OK == db->state_get (db->cls, &channel_pub_key, "_foo",
318                                       state_cb, &scls));
319   ASSERT (scls.n == 1);
320
321   scls.n = 0;
322   scls.value[1] = slave_key;
323   scls.value_size[1] = sizeof (*slave_key);
324
325   ASSERT (GNUNET_OK == db->state_get_prefix (db->cls, &channel_pub_key, "_foo",
326                                              state_cb, &scls));
327   ASSERT (scls.n == 2);
328
329   scls.n = 0;
330   ASSERT (GNUNET_NO == db->state_get_signed (db->cls, &channel_pub_key,
331                                              state_cb, &scls));
332   ASSERT (scls.n == 0);
333
334   ASSERT (GNUNET_OK == db->state_update_signed (db->cls, &channel_pub_key));
335
336   scls.n = 0;
337   ASSERT (GNUNET_YES == db->state_get_signed (db->cls, &channel_pub_key,
338                                               state_cb, &scls));
339   ASSERT (scls.n == 2);
340
341   /* Slave counters */
342
343   uint64_t max_state_msg_id = 0;
344   ASSERT (GNUNET_OK == db->counters_get_slave (db->cls, &channel_pub_key,
345                                                &max_state_msg_id)
346           && max_state_msg_id == message_id);
347
348   /* State sync */
349
350   scls.n = 0;
351   scls.value[0] = channel_key;
352   scls.value_size[0] = sizeof (*channel_key);
353   scls.value[1] = "three two one";
354   scls.value_size[1] = strlen ("three two one");
355
356   ASSERT (GNUNET_OK == db->state_sync_begin (db->cls, &channel_pub_key));
357
358   ASSERT (GNUNET_OK == db->state_sync_set (db->cls, &channel_pub_key,
359                                            "_sync_bar",
360                                            scls.value[0], scls.value_size[0]));
361
362   ASSERT (GNUNET_OK == db->state_sync_set (db->cls, &channel_pub_key,
363                                            "_sync_foo",
364                                            scls.value[1], scls.value_size[1]));
365
366   ASSERT (GNUNET_OK == db->state_sync_end (db->cls, &channel_pub_key, INT64_MAX - 5));
367
368   ASSERT (GNUNET_NO == db->state_get_prefix (db->cls, &channel_pub_key, "_foo",
369                                              state_cb, &scls));
370   ASSERT (scls.n == 0);
371
372   ASSERT (GNUNET_OK == db->state_get_prefix (db->cls, &channel_pub_key, "_sync",
373                                              state_cb, &scls));
374   ASSERT (scls.n == 2);
375
376   scls.n = 0;
377   ASSERT (GNUNET_OK == db->state_get_signed (db->cls, &channel_pub_key,
378                                              state_cb, &scls));
379   ASSERT (scls.n == 2);
380
381   /* Modify state after sync */
382
383   message_id = GNUNET_ntohll (fcls.msg[0]->message_id) + 6;
384   ASSERT (GNUNET_OK == db->state_modify_begin (db->cls, &channel_pub_key,
385                                                message_id, 3));
386
387   ASSERT (GNUNET_OK == db->state_modify_set (db->cls, &channel_pub_key, "_sync_foo",
388                                              C2ARG("five six seven")));
389
390   ASSERT (GNUNET_OK == db->state_modify_end (db->cls, &channel_pub_key,
391                                              message_id));
392
393   /* Reset state */
394
395   scls.n = 0;
396   ASSERT (GNUNET_OK == db->state_reset (db->cls, &channel_pub_key));
397   ASSERT (scls.n == 0);
398
399   ok = 0;
400
401 FAILURE:
402
403   if (NULL != channel_key)
404   {
405     GNUNET_free (channel_key);
406     channel_key = NULL;
407   }
408   if (NULL != slave_key)
409   {
410     GNUNET_free (slave_key);
411     slave_key = NULL;
412   }
413
414   unload_plugin (db);
415 }
416
417
418 int
419 main (int argc, char *argv[])
420 {
421   char cfg_name[128];
422   char *const xargv[] = {
423     "test-plugin-psycstore",
424     "-c", cfg_name,
425     "-L", LOG_LEVEL,
426     NULL
427   };
428   struct GNUNET_GETOPT_CommandLineOption options[] = {
429     GNUNET_GETOPT_OPTION_END
430   };
431   GNUNET_DISK_directory_remove ("/tmp/gnunet-test-plugin-psycstore-sqlite");
432   GNUNET_log_setup ("test-plugin-psycstore", LOG_LEVEL, NULL);
433   plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
434   GNUNET_snprintf (cfg_name, sizeof (cfg_name), "test_plugin_psycstore_%s.conf",
435                    plugin_name);
436   GNUNET_PROGRAM_run ((sizeof (xargv) / sizeof (char *)) - 1, xargv,
437                       "test-plugin-psycstore", "nohelp", options, &run, NULL);
438
439   if (ok != 0)
440     FPRINTF (stderr, "Missed some testcases: %d\n", ok);
441
442 #if ! DEBUG_PSYCSTORE
443   GNUNET_DISK_directory_remove ("/tmp/gnunet-test-plugin-psycstore-sqlite");
444 #endif
445
446   return ok;
447 }
448
449 /* end of test_plugin_psycstore.c */