ftbfs
[oweals/gnunet.git] / src / scalarproduct / scalarproduct_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2013, 2014, 2016 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 /**
21  * @file scalarproduct/scalarproduct_api.c
22  * @brief API for the scalarproduct
23  * @author Christian Fuchs
24  * @author Gaurav Kukreja
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_statistics_service.h"
30 #include "gnunet_scalarproduct_service.h"
31 #include "gnunet_protocols.h"
32 #include "scalarproduct.h"
33
34 #define LOG(kind, ...) GNUNET_log_from (kind, "scalarproduct-api", __VA_ARGS__)
35
36
37 /**
38  * The abstraction function for our internal callback
39  *
40  * @param h computation handle
41  * @param msg response we got, NULL on errors
42  * @param status processing status code
43  */
44 typedef void
45 (*GNUNET_SCALARPRODUCT_ResponseMessageHandler) (struct
46                                                 GNUNET_SCALARPRODUCT_ComputationHandle
47                                                 *h,
48                                                 const struct
49                                                 ClientResponseMessage *msg,
50                                                 enum
51                                                 GNUNET_SCALARPRODUCT_ResponseStatus
52                                                 status);
53
54
55 /**
56  * A handle returned for each computation
57  */
58 struct GNUNET_SCALARPRODUCT_ComputationHandle
59 {
60   /**
61    * Our configuration.
62    */
63   const struct GNUNET_CONFIGURATION_Handle *cfg;
64
65   /**
66    * Current connection to the scalarproduct service.
67    */
68   struct GNUNET_MQ_Handle *mq;
69
70   /**
71    * Function to call after transmission of the request (Bob).
72    */
73   GNUNET_SCALARPRODUCT_ContinuationWithStatus cont_status;
74
75   /**
76    * Function to call after transmission of the request (Alice).
77    */
78   GNUNET_SCALARPRODUCT_DatumProcessor cont_datum;
79
80   /**
81    * Closure for @e cont_status or @e cont_datum.
82    */
83   void *cont_cls;
84
85   /**
86    * API internal callback for results and failures to be forwarded to
87    * the client.
88    */
89   GNUNET_SCALARPRODUCT_ResponseMessageHandler response_proc;
90
91   /**
92    * The shared session key identifying this computation
93    */
94   struct GNUNET_HashCode key;
95 };
96
97
98 /**
99  * Called when a response is received from the service. Perform basic
100  * check that the message is well-formed.
101  *
102  * @param cls Pointer to the Master Context
103  * @param message Pointer to the data received in response
104  * @return #GNUNET_OK if @a message is well-formed
105  */
106 static int
107 check_response (void *cls,
108                 const struct ClientResponseMessage *message)
109 {
110   if (ntohs (message->header.size) !=
111       ntohl (message->product_length) + sizeof(struct ClientResponseMessage))
112   {
113     GNUNET_break (0);
114     return GNUNET_SYSERR;
115   }
116   return GNUNET_OK;
117 }
118
119
120 /**
121  * Handles the STATUS received from the service for a response, does
122  * not contain a payload.  Called when we participate as "Bob" via
123  * #GNUNET_SCALARPRODUCT_accept_computation().
124  *
125  * @param h our Handle
126  * @param msg the response received
127  * @param status the condition the request was terminated with (eg: disconnect)
128  */
129 static void
130 process_status_message (struct GNUNET_SCALARPRODUCT_ComputationHandle *h,
131                         const struct ClientResponseMessage *msg,
132                         enum GNUNET_SCALARPRODUCT_ResponseStatus status)
133 {
134   if (NULL != h->cont_status)
135     h->cont_status (h->cont_cls,
136                     status);
137   GNUNET_SCALARPRODUCT_cancel (h);
138 }
139
140
141 /**
142  * Called when a response is received from the service. After basic
143  * check, the handler in `h->response_proc` is called. This functions
144  * handles the response to the client which used the API.
145  *
146  * @param cls Pointer to the Master Context
147  * @param msg Pointer to the data received in response
148  */
149 static void
150 handle_response (void *cls,
151                  const struct ClientResponseMessage *message)
152 {
153   struct GNUNET_SCALARPRODUCT_ComputationHandle *h = cls;
154   enum GNUNET_SCALARPRODUCT_ResponseStatus status;
155
156   status = (enum GNUNET_SCALARPRODUCT_ResponseStatus) ntohl (message->status);
157   h->response_proc (h,
158                     message,
159                     status);
160 }
161
162
163 /**
164  * Check if the keys for all given elements are unique.
165  *
166  * @param elements elements to check
167  * @param element_count size of the @a elements array
168  * @return #GNUNET_OK if all keys are unique
169  */
170 static int
171 check_unique (const struct GNUNET_SCALARPRODUCT_Element *elements,
172               uint32_t element_count)
173 {
174   struct GNUNET_CONTAINER_MultiHashMap *map;
175   uint32_t i;
176   int ok;
177
178   ok = GNUNET_OK;
179   map = GNUNET_CONTAINER_multihashmap_create (2 * element_count,
180                                               GNUNET_YES);
181   for (i = 0; i < element_count; i++)
182     if (GNUNET_OK !=
183         GNUNET_CONTAINER_multihashmap_put (map,
184                                            &elements[i].key,
185                                            map,
186                                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
187     {
188       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
189                   _ ("Keys given to SCALARPRODUCT not unique!\n"));
190       ok = GNUNET_SYSERR;
191     }
192   GNUNET_CONTAINER_multihashmap_destroy (map);
193   return ok;
194 }
195
196
197 /**
198  * We encountered an error communicating with the set service while
199  * performing a set operation. Report to the application.
200  *
201  * @param cls the `struct GNUNET_SCALARPRODUCT_ComputationHandle`
202  * @param error error code
203  */
204 static void
205 mq_error_handler (void *cls,
206                   enum GNUNET_MQ_Error error)
207 {
208   struct GNUNET_SCALARPRODUCT_ComputationHandle *h = cls;
209
210   LOG (GNUNET_ERROR_TYPE_INFO,
211        "Disconnected from SCALARPRODUCT service.\n");
212   h->response_proc (h,
213                     NULL,
214                     GNUNET_SCALARPRODUCT_STATUS_DISCONNECTED);
215 }
216
217
218 /**
219  * Used by Bob's client to cooperate with Alice,
220  *
221  * @param cfg the gnunet configuration handle
222  * @param key Session key unique to the requesting client
223  * @param elements Array of elements of the vector
224  * @param element_count Number of elements in the @a elements vector
225  * @param cont Callback function
226  * @param cont_cls Closure for @a cont
227  * @return a new handle for this computation
228  */
229 struct GNUNET_SCALARPRODUCT_ComputationHandle *
230 GNUNET_SCALARPRODUCT_accept_computation (const struct
231                                          GNUNET_CONFIGURATION_Handle *cfg,
232                                          const struct
233                                          GNUNET_HashCode *session_key,
234                                          const struct
235                                          GNUNET_SCALARPRODUCT_Element *elements,
236                                          uint32_t element_count,
237                                          GNUNET_SCALARPRODUCT_ContinuationWithStatus
238                                          cont,
239                                          void *cont_cls)
240 {
241   struct GNUNET_SCALARPRODUCT_ComputationHandle *h
242     = GNUNET_new (struct GNUNET_SCALARPRODUCT_ComputationHandle);
243   struct GNUNET_MQ_MessageHandler handlers[] = {
244     GNUNET_MQ_hd_var_size (response,
245                            GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT,
246                            struct ClientResponseMessage,
247                            h),
248     GNUNET_MQ_handler_end ()
249   };
250   struct GNUNET_MQ_Envelope *env;
251   struct BobComputationMessage *msg;
252   struct ComputationBobCryptodataMultipartMessage *mmsg;
253   uint32_t size;
254   uint16_t possible;
255   uint16_t todo;
256   uint32_t element_count_transfered;
257
258
259   if (GNUNET_SYSERR == check_unique (elements,
260                                      element_count))
261     return NULL;
262   h->cont_status = cont;
263   h->cont_cls = cont_cls;
264   h->response_proc = &process_status_message;
265   h->cfg = cfg;
266   h->key = *session_key;
267   h->mq = GNUNET_CLIENT_connect (cfg,
268                                  "scalarproduct-bob",
269                                  handlers,
270                                  &mq_error_handler,
271                                  h);
272   if (NULL == h->mq)
273   {
274     /* scalarproduct configuration error */
275     GNUNET_break (0);
276     GNUNET_free (h);
277     return NULL;
278   }
279   possible = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(struct
280                                                    BobComputationMessage))
281              / sizeof(struct GNUNET_SCALARPRODUCT_Element);
282   todo = GNUNET_MIN (possible,
283                      element_count);
284   size = todo * sizeof(struct GNUNET_SCALARPRODUCT_Element);
285   env = GNUNET_MQ_msg_extra (msg,
286                              size,
287                              GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_BOB);
288   msg->element_count_total = htonl (element_count);
289   msg->element_count_contained = htonl (todo);
290   msg->session_key = *session_key;
291   GNUNET_memcpy (&msg[1],
292                  elements,
293                  size);
294   element_count_transfered = todo;
295   GNUNET_MQ_send (h->mq,
296                   env);
297   possible = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(*mmsg))
298              / sizeof(struct GNUNET_SCALARPRODUCT_Element);
299   while (element_count_transfered < element_count)
300   {
301     todo = GNUNET_MIN (possible,
302                        element_count - element_count_transfered);
303     size = todo * sizeof(struct GNUNET_SCALARPRODUCT_Element);
304     env = GNUNET_MQ_msg_extra (mmsg,
305                                size,
306                                GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_BOB);
307     mmsg->element_count_contained = htonl (todo);
308     GNUNET_memcpy (&mmsg[1],
309                    &elements[element_count_transfered],
310                    size);
311     element_count_transfered += todo;
312     GNUNET_MQ_send (h->mq,
313                     env);
314   }
315   return h;
316 }
317
318
319 /**
320  * Handles the RESULT received from the service for a request, should
321  * contain a result MPI value.  Called when we participate as "Alice" via
322  * #GNUNET_SCALARPRODUCT_start_computation().
323  *
324  * @param h our Handle
325  * @param msg Pointer to the response received
326  * @param status the condition the request was terminated with (eg: disconnect)
327  */
328 static void
329 process_result_message (struct GNUNET_SCALARPRODUCT_ComputationHandle *h,
330                         const struct ClientResponseMessage *msg,
331                         enum GNUNET_SCALARPRODUCT_ResponseStatus status)
332 {
333   uint32_t product_len;
334   gcry_mpi_t result = NULL;
335   gcry_error_t rc;
336   gcry_mpi_t num;
337   size_t rsize;
338
339   if (GNUNET_SCALARPRODUCT_STATUS_SUCCESS == status)
340   {
341     result = gcry_mpi_new (0);
342
343     product_len = ntohl (msg->product_length);
344     if (0 < product_len)
345     {
346       rsize = 0;
347       if (0 != (rc = gcry_mpi_scan (&num, GCRYMPI_FMT_STD,
348                                     &msg[1],
349                                     product_len,
350                                     &rsize)))
351       {
352         LOG_GCRY (GNUNET_ERROR_TYPE_ERROR,
353                   "gcry_mpi_scan",
354                   rc);
355         gcry_mpi_release (result);
356         result = NULL;
357         status = GNUNET_SCALARPRODUCT_STATUS_INVALID_RESPONSE;
358       }
359       else
360       {
361         if (0 < (int32_t) ntohl (msg->range))
362           gcry_mpi_add (result, result, num);
363         else
364           gcry_mpi_sub (result, result, num);
365         gcry_mpi_release (num);
366       }
367     }
368   }
369   if (NULL != h->cont_datum)
370     h->cont_datum (h->cont_cls,
371                    status,
372                    result);
373   if (NULL != result)
374     gcry_mpi_release (result);
375   GNUNET_SCALARPRODUCT_cancel (h);
376 }
377
378
379 /**
380  * Request by Alice's client for computing a scalar product
381  *
382  * @param cfg the gnunet configuration handle
383  * @param session_key Session key should be unique to the requesting client
384  * @param peer PeerID of the other peer
385  * @param elements Array of elements of the vector
386  * @param element_count Number of elements in the @a elements vector
387  * @param cont Callback function
388  * @param cont_cls Closure for @a cont
389  * @return a new handle for this computation
390  */
391 struct GNUNET_SCALARPRODUCT_ComputationHandle *
392 GNUNET_SCALARPRODUCT_start_computation (const struct
393                                         GNUNET_CONFIGURATION_Handle *cfg,
394                                         const struct
395                                         GNUNET_HashCode *session_key,
396                                         const struct GNUNET_PeerIdentity *peer,
397                                         const struct
398                                         GNUNET_SCALARPRODUCT_Element *elements,
399                                         uint32_t element_count,
400                                         GNUNET_SCALARPRODUCT_DatumProcessor cont,
401                                         void *cont_cls)
402 {
403   struct GNUNET_SCALARPRODUCT_ComputationHandle *h
404     = GNUNET_new (struct GNUNET_SCALARPRODUCT_ComputationHandle);
405   struct GNUNET_MQ_MessageHandler handlers[] = {
406     GNUNET_MQ_hd_var_size (response,
407                            GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT,
408                            struct ClientResponseMessage,
409                            h),
410     GNUNET_MQ_handler_end ()
411   };
412   struct GNUNET_MQ_Envelope *env;
413   struct AliceComputationMessage *msg;
414   struct ComputationBobCryptodataMultipartMessage *mmsg;
415   uint32_t size;
416   uint16_t possible;
417   uint16_t todo;
418   uint32_t element_count_transfered;
419
420   if (GNUNET_SYSERR == check_unique (elements,
421                                      element_count))
422     return NULL;
423   h->mq = GNUNET_CLIENT_connect (cfg,
424                                  "scalarproduct-alice",
425                                  handlers,
426                                  &mq_error_handler,
427                                  h);
428   if (NULL == h->mq)
429   {
430     /* missconfigured scalarproduct service */
431     GNUNET_break (0);
432     GNUNET_free (h);
433     return NULL;
434   }
435   h->cont_datum = cont;
436   h->cont_cls = cont_cls;
437   h->response_proc = &process_result_message;
438   h->cfg = cfg;
439   h->key = *session_key;
440
441   possible = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(struct
442                                                    AliceComputationMessage))
443              / sizeof(struct GNUNET_SCALARPRODUCT_Element);
444   todo = GNUNET_MIN (possible,
445                      element_count);
446   size = todo * sizeof(struct GNUNET_SCALARPRODUCT_Element);
447   env = GNUNET_MQ_msg_extra (msg,
448                              size,
449                              GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE);
450   msg->element_count_total = htonl (element_count);
451   msg->element_count_contained = htonl (todo);
452   msg->reserved = htonl (0);
453   msg->peer = *peer;
454   msg->session_key = *session_key;
455   GNUNET_memcpy (&msg[1],
456                  elements,
457                  size);
458   GNUNET_MQ_send (h->mq,
459                   env);
460   element_count_transfered = todo;
461   possible = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(*mmsg))
462              / sizeof(struct GNUNET_SCALARPRODUCT_Element);
463   while (element_count_transfered < element_count)
464   {
465     todo = GNUNET_MIN (possible,
466                        element_count - element_count_transfered);
467     size = todo * sizeof(struct GNUNET_SCALARPRODUCT_Element);
468     env = GNUNET_MQ_msg_extra (mmsg,
469                                size,
470                                GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_ALICE);
471     mmsg->element_count_contained = htonl (todo);
472     GNUNET_memcpy (&mmsg[1],
473                    &elements[element_count_transfered],
474                    size);
475     element_count_transfered += todo;
476     GNUNET_MQ_send (h->mq,
477                     env);
478   }
479   return h;
480 }
481
482
483 /**
484  * Cancel an ongoing computation or revoke our collaboration offer.
485  * Closes the connection to the service
486  *
487  * @param h computation handle to terminate
488  */
489 void
490 GNUNET_SCALARPRODUCT_cancel (struct GNUNET_SCALARPRODUCT_ComputationHandle *h)
491 {
492   if (NULL != h->mq)
493   {
494     GNUNET_MQ_destroy (h->mq);
495     h->mq = NULL;
496   }
497   GNUNET_free (h);
498 }
499
500
501 /* end of scalarproduct_api.c */