commit 22ab4538a2f7a21c0c831599a56d50c147de0ae5
parent 1a4262ba723c6806e2260f6431e273e29c1143b9
Author: Christian Grothoff <christian@grothoff.org>
Date: Fri, 23 Aug 2013 13:39:32 +0000
-starting to rename vectorproduct to scalarproduct, as this is not doing a cross product
Diffstat:
17 files changed, 7557 insertions(+), 7 deletions(-)
diff --git a/configure.ac b/configure.ac
@@ -1291,10 +1291,11 @@ src/gns/Makefile
src/gns/gns.conf
src/gns/nss/Makefile
src/hello/Makefile
-src/include/Makefile
-src/include/gnunet_directories.h
src/identity/Makefile
src/identity/identity.conf
+src/include/Makefile
+src/include/gnunet_directories.h
+src/integration-tests/Makefile
src/hostlist/Makefile
src/lockmanager/Makefile
src/lockmanager/lockmanager.conf
@@ -1313,6 +1314,8 @@ src/postgres/Makefile
src/pt/Makefile
src/regex/Makefile
src/regex/regex.conf
+src/scalarproduct/Makefile
+src/scalarproduct/vectorproduct.conf
src/set/Makefile
src/set/set.conf
src/statistics/Makefile
@@ -1329,9 +1332,6 @@ src/util/Makefile
src/util/resolver.conf
src/vpn/Makefile
src/vpn/vpn.conf
-src/vectorproduct/Makefile
-src/vectorproduct/vectorproduct.conf
-src/integration-tests/Makefile
pkgconfig/Makefile
pkgconfig/gnunetats.pc
pkgconfig/gnunetarm.pc
diff --git a/src/Makefile.am b/src/Makefile.am
@@ -7,12 +7,12 @@ if HAVE_TESTING
TESTBED = testbed
CONSENSUS = consensus
EXPERIMENTATION = experimentation
- VECTORPRODUCT = vectorproduct
+ SCALARPRODUCT = scalarproduct
endif
if HAVE_EXPERIMENTAL
EXP_DIR = dv $(CONSENSUS) $(EXPERIMENTATION)
-#note: vectorproduct is not being listed here yet as the crypto is being reworked at the moment
+#note: scalarproduct is not being listed here yet as the crypto is being reworked at the moment
endif
if HAVE_MYSQL
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
@@ -70,6 +70,7 @@ gnunetinclude_HEADERS = \
gnunet_protocols.h \
gnunet_resolver_service.h \
gnunet_regex_service.h \
+ gnunet_scalarproduct_service.h \
gnunet_scheduler_lib.h \
gnunet_server_lib.h \
gnunet_service_lib.h \
diff --git a/src/include/gnunet_vectorproduct_service.h b/src/include/gnunet_scalarproduct_service.h
diff --git a/src/scalarproduct/Makefile.am b/src/scalarproduct/Makefile.am
@@ -0,0 +1,112 @@
+INCLUDES = -I$(top_srcdir)/src/include
+
+pkgcfgdir= $(pkgdatadir)/config.d/
+
+libexecdir= $(pkglibdir)/libexec/
+
+pkgcfg_DATA = \
+ vectorproduct.conf
+
+if MINGW
+ WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+endif
+
+if USE_COVERAGE
+ AM_CFLAGS = -fprofile-arcs -ftest-coverage
+endif
+
+bin_PROGRAMS = \
+ gnunet-vectorproduct
+
+libexec_PROGRAMS = \
+ gnunet-service-vectorproduct
+
+# FIXME: add option "configure --with-evil"?
+#if HAVE_EXPERIMENTAL
+#libexec_PROGRAMS += \
+# gnunet-service-evil-consensus
+#endif
+
+lib_LTLIBRARIES = \
+ libgnunetvectorproduct.la
+
+gnunet_vectorproduct_SOURCES = \
+ gnunet-vectorproduct.c
+gnunet_vectorproduct_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/vectorproduct/libgnunetvectorproduct.la \
+ -lgcrypt \
+ $(GN_LIBINTL)
+gnunet_vectorproduct_DEPENDENCIES = \
+ libgnunetvectorproduct.la
+
+gnunet_service_vectorproduct_SOURCES = \
+ gnunet-service-vectorproduct.c
+gnunet_service_vectorproduct_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/core/libgnunetcore.la \
+ $(top_builddir)/src/mesh/libgnunetmesh.la \
+ $(top_builddir)/src/set/libgnunetset.la \
+ -lgcrypt \
+ $(GN_LIBINTL)
+
+libgnunetvectorproduct_la_SOURCES = \
+ vectorproduct_api.c
+libgnunetvectorproduct_la_LIBADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/statistics/libgnunetstatistics.la \
+ -lgcrypt \
+ $(LTLIBINTL)
+libgnunetvectorproduct_la_LDFLAGS = \
+ $(GN_LIB_LDFLAGS)
+
+check_PROGRAMS = \
+ test_vectorproduct_api_regression \
+ test_vectorproduct_api \
+ test_vectorproduct_api_4peers
+#FIXME unfinished
+#test_vectorproduct_api_regression2
+
+if ENABLE_TEST_RUN
+ TESTS = $(check_PROGRAMS)
+endif
+
+test_consensus_api_SOURCES = \
+ test_consensus_api.c
+test_consensus_api_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/testing/libgnunettesting.la \
+ $(top_builddir)/src/consensus/libgnunetconsensus.la
+
+test_vectorproduct_api_SOURCES = \
+ test_vectorproduct_api.c
+test_vectorproduct_api_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/vectorproduct/libgnunetvectorproduct.la \
+ -lgcrypt
+
+#FIXME unfinished
+#test_vectorproduct_api_regression2_SOURCES = \
+# test_vectorproduct_api_regression2.c
+#test_vectorproduct_api_regression2_LDADD = \
+# $(top_builddir)/src/vectorproduct/libgnunetvectorproduct.la \
+# $(top_builddir)/src/util/libgnunetutil.la \
+# -lgcrypt
+
+test_vectorproduct_api_regression_SOURCES = \
+ test_vectorproduct_api_regression.c
+test_vectorproduct_api_regression_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/vectorproduct/libgnunetvectorproduct.la \
+ -lgcrypt
+
+test_vectorproduct_api_4peers_SOURCES = \
+ test_vectorproduct_api_4peers.c
+test_vectorproduct_api_4peers_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/vectorproduct/libgnunetvectorproduct.la \
+ -lgcrypt
+
+EXTRA_DIST = \
+ test_vectorproduct.conf
+
diff --git a/src/scalarproduct/gnunet-service-vectorproduct.c b/src/scalarproduct/gnunet-service-vectorproduct.c
@@ -0,0 +1,2067 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file vectorproduct/gnunet-service-vectorproduct.c
+ * @brief vectorproduct service implementation
+ * @author Christian M. Fuchs
+ */
+#include <limits.h>
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_core_service.h"
+#include "gnunet_mesh_service.h"
+#include "gnunet_applications.h"
+#include "gnunet_protocols.h"
+#include "gnunet_vectorproduct_service.h"
+#include "gnunet_vectorproduct.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Global Variables
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Handle to the core service (NULL until we've connected to it).
+ */
+static struct GNUNET_CORE_Handle *my_core;
+
+/**
+ * Handle to the core service (NULL until we've connected to it).
+ */
+static struct GNUNET_MESH_Handle *my_mesh;
+
+/**
+ * The identity of this host.
+ */
+static struct GNUNET_PeerIdentity me;
+
+/**
+ * Service's own public key represented as string
+ */
+static unsigned char * my_pubkey_external;
+
+/**
+ * Service's own public key represented as string
+ */
+static uint16_t my_pubkey_external_length = 0;
+
+/**
+ * Service's own n
+ */
+static gcry_mpi_t my_n;
+
+/**
+ * Service's own n^2 (kept for performance)
+ */
+static gcry_mpi_t my_nsquare;
+
+/**
+ * Service's own public exponent
+ */
+static gcry_mpi_t my_g;
+
+/**
+ * Service's own private multiplier
+ */
+static gcry_mpi_t my_mu;
+
+/**
+ * Service's own private exponent
+ */
+static gcry_mpi_t my_lambda;
+
+/**
+ * Head of our double linked list for client-requests sent to us.
+ * for all of these elements we calculate a vector product with a remote peer
+ * split between service->service and client->service for simplicity
+ */
+static struct ServiceSession * from_client_head;
+/**
+ * Tail of our double linked list for client-requests sent to us.
+ * for all of these elements we calculate a vector product with a remote peer
+ * split between service->service and client->service for simplicity
+ */
+static struct ServiceSession * from_client_tail;
+
+/**
+ * Head of our double linked list for service-requests sent to us.
+ * for all of these elements we help the requesting service in calculating a vector product
+ * split between service->service and client->service for simplicity
+ */
+static struct ServiceSession * from_service_head;
+
+/**
+ * Tail of our double linked list for service-requests sent to us.
+ * for all of these elements we help the requesting service in calculating a vector product
+ * split between service->service and client->service for simplicity
+ */
+static struct ServiceSession * from_service_tail;
+
+/**
+ * Certain events (callbacks for server & mesh operations) must not be queued after shutdown.
+ */
+static int do_shutdown;
+
+///////////////////////////////////////////////////////////////////////////////
+// Helper Functions
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Generates an Paillier private/public keyset and extracts the values using libgrcypt only
+ */
+static void
+generate_keyset ()
+{
+ gcry_sexp_t gen_parms;
+ gcry_sexp_t key;
+ gcry_sexp_t tmp_sexp;
+ gcry_mpi_t p;
+ gcry_mpi_t q;
+ gcry_mpi_t tmp1;
+ gcry_mpi_t tmp2;
+ gcry_mpi_t gcd;
+
+ size_t erroff = 0;
+
+ // we can still use the RSA keygen for generating p,q,n, but using e is pointless.
+ GNUNET_assert (0 == gcry_sexp_build (&gen_parms, &erroff,
+ "(genkey(rsa(nbits %d)(rsa-use-e 3:257)))",
+ KEYBITS));
+
+ GNUNET_assert (0 == gcry_pk_genkey (&key, gen_parms));
+ gcry_sexp_release (gen_parms);
+
+ // get n and d of our publickey as MPI
+ tmp_sexp = gcry_sexp_find_token (key, "n", 0);
+ GNUNET_assert (tmp_sexp);
+ my_n = gcry_sexp_nth_mpi (tmp_sexp, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release (tmp_sexp);
+ tmp_sexp = gcry_sexp_find_token (key, "p", 0);
+ GNUNET_assert (tmp_sexp);
+ p = gcry_sexp_nth_mpi (tmp_sexp, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release (tmp_sexp);
+ tmp_sexp = gcry_sexp_find_token (key, "q", 0);
+ GNUNET_assert (tmp_sexp);
+ q = gcry_sexp_nth_mpi (tmp_sexp, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release (key);
+
+ tmp1 = gcry_mpi_new (0);
+ tmp2 = gcry_mpi_new (0);
+ gcd = gcry_mpi_new (0);
+ my_g = gcry_mpi_new (0);
+ my_mu = gcry_mpi_new (0);
+ my_nsquare = gcry_mpi_new (0);
+ my_lambda = gcry_mpi_new (0);
+
+ // calculate lambda
+ // lambda = \frac{(p-1)*(q-1)}{gcd(p-1,q-1)}
+ gcry_mpi_sub_ui (tmp1, p, 1);
+ gcry_mpi_sub_ui (tmp2, q, 1);
+ gcry_mpi_gcd (gcd, tmp1, tmp2);
+ gcry_mpi_set (my_lambda, tmp1);
+ gcry_mpi_mul (my_lambda, my_lambda, tmp2);
+ gcry_mpi_div (my_lambda, NULL, my_lambda, gcd, 0);
+
+ // generate a g
+ gcry_mpi_mul (my_nsquare, my_n, my_n);
+ do
+ {
+ // find a matching g
+ do
+ {
+ gcry_mpi_randomize (my_g, KEYBITS * 2, GCRY_WEAK_RANDOM);
+ // g must be smaller than n^2
+ if (0 >= gcry_mpi_cmp (my_g, my_nsquare))
+ continue;
+
+ // g must have gcd == 1 with n^2
+ gcry_mpi_gcd (gcd, my_g, my_nsquare);
+ }
+ while (gcry_mpi_cmp_ui (gcd, 1));
+
+ // is this a valid g?
+ // if so, gcd(((g^lambda mod n^2)-1 )/n, n) = 1
+ gcry_mpi_powm (tmp1, my_g, my_lambda, my_nsquare);
+ gcry_mpi_sub_ui (tmp1, tmp1, 1);
+ gcry_mpi_div (tmp1, NULL, tmp1, my_n, 0);
+ gcry_mpi_gcd (gcd, tmp1, my_n);
+ }
+ while (gcry_mpi_cmp_ui (gcd, 1));
+
+ // calculate our mu based on g and n.
+ // mu = (((g^lambda mod n^2)-1 )/n)^-1 mod n
+ gcry_mpi_invm (my_mu, tmp1, my_n);
+
+ GNUNET_assert (0 == gcry_sexp_build (&key, &erroff,
+ "(public-key (paillier (n %M)(g %M)))",
+ my_n, my_g));
+
+ // get the length of this sexpression
+ my_pubkey_external_length = gcry_sexp_sprint (key,
+ GCRYSEXP_FMT_CANON,
+ NULL,
+ UINT16_MAX);
+
+ GNUNET_assert (my_pubkey_external_length > 0);
+ my_pubkey_external = GNUNET_malloc (my_pubkey_external_length);
+
+ // convert the sexpression to canonical format
+ gcry_sexp_sprint (key,
+ GCRYSEXP_FMT_CANON,
+ my_pubkey_external,
+ my_pubkey_external_length);
+
+ gcry_sexp_release (key);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Generated key set with key length %d bits.\n"), KEYBITS);
+}
+
+
+/**
+ * If target != size, move target bytes to the
+ * end of the size-sized buffer and zero out the
+ * first target-size bytes.
+ *
+ * @param buf original buffer
+ * @param size number of bytes in the buffer
+ * @param target target size of the buffer
+ */
+static void
+adjust (unsigned char *buf, size_t size, size_t target)
+{
+ if (size < target)
+ {
+ memmove (&buf[target - size], buf, size);
+ memset (buf, 0, target - size);
+ }
+}
+
+
+/**
+ * encrypts an element using the paillier crypto system
+ *
+ * @param c ciphertext (output)
+ * @param m plaintext
+ * @param g the public base
+ * @param r random base (optional) gets generated and if not NULL but uninitialized
+ * @param n the module from which which r is chosen (Z*_n)
+ * @param n_square the module for encryption, for performance reasons.
+ */
+static void
+encrypt_element (gcry_mpi_t c, gcry_mpi_t m, gcry_mpi_t g, gcry_mpi_t r, gcry_mpi_t n, gcry_mpi_t n_square)
+{
+#ifndef DISABLE_CRYPTO
+ gcry_mpi_t tmp;
+ int release_r = GNUNET_NO;
+
+ GNUNET_assert (tmp = gcry_mpi_new (0));
+ if (NULL == r)
+ {
+ GNUNET_assert (r = gcry_mpi_new (0));
+ release_r = GNUNET_YES;
+
+ while (0 <= gcry_mpi_cmp (r, n) || 0 >= gcry_mpi_cmp_ui (r, 1))
+ {
+ gcry_mpi_randomize (r, KEYBITS, GCRY_WEAK_RANDOM);
+ // r must be 1 < r < n
+ }
+ }
+
+
+ gcry_mpi_powm (c, g, m, n_square);
+ gcry_mpi_powm (tmp, r, n, n_square);
+ gcry_mpi_mulm (c, tmp, c, n_square);
+
+ gcry_mpi_release (tmp);
+ if (GNUNET_YES == release_r)
+ gcry_mpi_release (r);
+#else
+ gcry_mpi_set (c, m);
+#endif
+}
+
+
+/**
+ * decrypts an element using the paillier crypto system
+ *
+ * @param m plaintext (output)
+ * @param c the ciphertext
+ * @param mu the modifier to correct encryption
+ * @param lambda the private exponent
+ * @param n the outer module for decryption
+ * @param n_square the inner module for decryption
+ */
+static void
+decrypt_element (gcry_mpi_t m, gcry_mpi_t c, gcry_mpi_t mu, gcry_mpi_t lambda, gcry_mpi_t n, gcry_mpi_t n_square)
+{
+#ifndef DISABLE_CRYPTO
+ gcry_mpi_powm (m, c, lambda, n_square);
+ gcry_mpi_sub_ui (m, m, 1);
+ gcry_mpi_div (m, NULL, m, n, 0);
+ gcry_mpi_mulm (m, m, mu, n);
+#else
+ gcry_mpi_set (m, c);
+#endif
+}
+
+
+/**
+ * computes the square sum over a vector of a given length.
+ *
+ * @param vector the vector to encrypt
+ * @param length the length of the vector
+ * @return an MPI value containing the calculated sum, never NULL
+ */
+static gcry_mpi_t
+compute_square_sum (gcry_mpi_t * vector, uint16_t length)
+{
+ gcry_mpi_t elem;
+ gcry_mpi_t sum;
+ int32_t i;
+
+ GNUNET_assert (sum = gcry_mpi_new (0));
+ GNUNET_assert (elem = gcry_mpi_new (0));
+
+ // calculare E(sum (ai ^ 2), publickey)
+ for (i = 0; i < length; i++)
+ {
+ gcry_mpi_mul (elem, vector[i], vector[i]);
+ gcry_mpi_add (sum, sum, elem);
+ }
+ gcry_mpi_release (elem);
+
+ return sum;
+}
+
+
+/**
+ * Primitive callback for copying over a message, as they
+ * usually are too complex to be handled in the callback itself.
+ * clears a session-callback, if a session was handed over and the transmit handle was stored
+ *
+ * @param cls the message object
+ * @param size the size of the buffer we got
+ * @param buf the buffer to copy the message to
+ * @return 0 if we couldn't copy, else the size copied over
+ */
+static size_t
+do_send_message (void *cls, size_t size, void *buf)
+{
+ struct MessageObject * info = cls;
+ struct GNUNET_MessageHeader * msg;
+ size_t written = 0;
+
+ GNUNET_assert (info);
+ msg = info->msg;
+ GNUNET_assert (msg);
+ GNUNET_assert (buf);
+
+ if (ntohs (msg->size) == size)
+ {
+ memcpy (buf, msg, size);
+ written = size;
+ }
+
+ // reset the transmit handle, if necessary
+ if (info->transmit_handle)
+ *info->transmit_handle = NULL;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Sent a message of type %hu.\n"), ntohs (msg->type));
+ GNUNET_free(msg);
+ GNUNET_free(info);
+ return written;
+}
+
+
+/**
+ * initializes a new vector with fresh MPI values (=0) of a given length
+ *
+ * @param length of the vector to create
+ * @return the initialized vector, never NULL
+ */
+static gcry_mpi_t *
+initialize_mpi_vector (uint16_t length)
+{
+ uint32_t i;
+ gcry_mpi_t * output = GNUNET_malloc (sizeof (gcry_mpi_t) * length);
+
+ for (i = 0; i < length; i++)
+ GNUNET_assert (NULL != (output[i] = gcry_mpi_new (0)));
+ return output;
+}
+
+
+/**
+ * permutes an MPI vector according to the given permutation vector
+ *
+ * @param vector the vector to permuted
+ * @param perm the permutation to use
+ * @param length the length of the vectors
+ * @return the permuted vector (same as input), never NULL
+ */
+static gcry_mpi_t *
+permute_vector (gcry_mpi_t * vector,
+ unsigned int * perm,
+ uint32_t length)
+{
+ gcry_mpi_t tmp[length];
+ uint32_t i;
+
+ GNUNET_assert (length > 0);
+
+ // backup old layout
+ memcpy (tmp, vector, length * sizeof (gcry_mpi_t));
+
+ // permute vector according to given
+ for (i = 0; i < length; i++)
+ vector[i] = tmp[perm[i]];
+
+ return vector;
+}
+
+
+/**
+ * Populate a vector with random integer values and convert them to
+ *
+ * @param length the length of the vector we must generate
+ * @return an array of MPI values with random values
+ */
+static gcry_mpi_t *
+generate_random_vector (uint16_t length)
+{
+ gcry_mpi_t * random_vector;
+ int32_t value;
+ uint32_t i;
+
+ random_vector = initialize_mpi_vector (length);
+ for (i = 0; i < length; i++)
+ {
+ value = (int32_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX);
+
+ // long to gcry_mpi_t
+ if (value < 0)
+ gcry_mpi_sub_ui (random_vector[i],
+ random_vector[i],
+ -value);
+ else
+ random_vector[i] = gcry_mpi_set_ui (random_vector[i], value);
+ }
+
+ return random_vector;
+}
+
+
+/**
+ * Finds a not terminated client/service session in the
+ * given DLL based on session key, element count and state.
+ *
+ * @param tail - the tail of the DLL
+ * @param my - the session to compare it to
+ * @return a pointer to a matching session,
+ * else NULL
+ */
+static struct ServiceSession *
+find_matching_session (struct ServiceSession * tail,
+ struct GNUNET_HashCode * key,
+ uint16_t element_count,
+ enum SessionState * state,
+ const struct GNUNET_PeerIdentity * peerid)
+{
+ struct ServiceSession * curr;
+
+ for (curr = tail; NULL != curr; curr = curr->prev)
+ {
+ // if the key matches, and the element_count is same
+ if ((!memcmp (&curr->key, key, sizeof (struct GNUNET_HashCode)))
+ && (curr->element_count == element_count))
+ {
+ // if incoming state is NULL OR is same as state of the queued request
+ if ((NULL == state) || (curr->state == *state))
+ {
+ // if peerid is NULL OR same as the peer Id in the queued request
+ if ((NULL == peerid)
+ || (!memcmp (&curr->peer, peerid, sizeof (struct GNUNET_PeerIdentity))))
+ // matches and is not an already terminated session
+ return curr;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+static void
+destroy_tunnel (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct ServiceSession * session = cls;
+
+ if (session->tunnel)
+ {
+ GNUNET_MESH_tunnel_destroy (session->tunnel);
+ session->tunnel = NULL;
+ }
+ session->service_transmit_handle = NULL;
+ // we need to set this to NULL so there is no problem with double-cancel later on.
+}
+
+
+static void
+free_session (struct ServiceSession * session)
+{
+ int i;
+
+ if (FINALIZED != session->state)
+ {
+ if (session->a)
+ {
+ for (i = 0; i < session->used_element_count; i++)
+ gcry_mpi_release (session->a[i]);
+
+ GNUNET_free (session->a);
+ }
+ if (session->product)
+ gcry_mpi_release (session->product);
+
+ if (session->remote_pubkey)
+ gcry_sexp_release (session->remote_pubkey);
+
+ GNUNET_free_non_null (session->vector);
+ }
+
+ GNUNET_free (session);
+}
+///////////////////////////////////////////////////////////////////////////////
+// Event and Message Handlers
+///////////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * A client disconnected.
+ *
+ * Remove the associated session(s), release datastructures
+ * and cancel pending outgoing transmissions to the client.
+ * if the session has not yet completed, we also cancel Alice's request to Bob.
+ *
+ * @param cls closure, NULL
+ * @param client identification of the client
+ */
+static void
+handle_client_disconnect (void *cls,
+ struct GNUNET_SERVER_Client
+ * client)
+{
+ struct ServiceSession * elem;
+ struct ServiceSession * next;
+
+ // start from the tail, old stuff will be there...
+ for (elem = from_client_head; NULL != elem; elem = next)
+ {
+ next = elem->next;
+ if (elem->client != client)
+ continue;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Client (%p) disconnected from us.\n"), client);
+ GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, elem);
+
+ if (!(elem->role == BOB && elem->state == FINALIZED))
+ {
+ //we MUST terminate any client message underway
+ if (elem->service_transmit_handle && elem->tunnel)
+ GNUNET_MESH_notify_transmit_ready_cancel (elem->service_transmit_handle);
+ if (elem->tunnel && elem->state == WAITING_FOR_RESPONSE_FROM_SERVICE)
+ destroy_tunnel (elem, NULL);
+ }
+ free_session (elem);
+ }
+}
+
+
+/**
+ * Notify the client that the session has succeeded or failed completely.
+ * This message gets sent to
+ * * alice's client if bob disconnected or to
+ * * bob's client if the operation completed or alice disconnected
+ *
+ * @param client_session the associated client session
+ * @return GNUNET_NO, if we could not notify the client
+ * GNUNET_YES if we notified it.
+ */
+static void
+prepare_client_end_notification (void * cls,
+ const struct GNUNET_SCHEDULER_TaskContext * tc)
+{
+ struct ServiceSession * session = cls;
+ struct GNUNET_VECTORPRODUCT_client_response * msg;
+ struct MessageObject * msg_obj;
+
+ GNUNET_assert (NULL != session);
+
+ msg = GNUNET_new (struct GNUNET_VECTORPRODUCT_client_response);
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_VECTORPRODUCT_SERVICE_TO_CLIENT);
+ memcpy (&msg->key, &session->key, sizeof (struct GNUNET_HashCode));
+ memcpy (&msg->peer, &session->peer, sizeof ( struct GNUNET_PeerIdentity));
+ msg->header.size = htons (sizeof (struct GNUNET_VECTORPRODUCT_client_response));
+ // 0 size and the first char in the product is 0, which should never be zero if encoding is used.
+ msg->product_length = htonl (0);
+
+ msg_obj = GNUNET_malloc (sizeof (struct MessageObject));
+ msg_obj->msg = (struct GNUNET_MessageHeader *) msg;
+ msg_obj->transmit_handle = NULL; // do not reset the transmit handle, please
+
+ //transmit this message to our client
+ session->client_transmit_handle =
+ GNUNET_SERVER_notify_transmit_ready (session->client,
+ sizeof (struct GNUNET_VECTORPRODUCT_client_response),
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &do_send_message,
+ msg_obj);
+
+
+ // if we could not even queue our request, something is wrong
+ if ( ! session->client_transmit_handle)
+ {
+
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not send message to client (%p)! This is OK if it was disconnected beforehand already.\n"), session->client);
+ // usually gets freed by do_send_message
+ GNUNET_free (msg_obj);
+ GNUNET_free (msg);
+ }
+ else
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Sending session-end notification to client (%p) for session %s\n"), &session->client, GNUNET_h2s (&session->key));
+
+ free_session(session);
+}
+
+
+/**
+ * Bob executes:
+ * generates the response message to be sent to alice after computing
+ * the values (1), (2), S and S'
+ * (1)[]: $E_A(a_{pi(i)}) times E_A(- r_{pi(i)} - b_{pi(i)}) &= E_A(a_{pi(i)} - r_{pi(i)} - b_{pi(i)})$
+ * (2)[]: $E_A(a_{pi'(i)}) times E_A(- r_{pi'(i)}) &= E_A(a_{pi'(i)} - r_{pi'(i)})$
+ * S: $S := E_A(sum (r_i + b_i)^2)$
+ * S': $S' := E_A(sum r_i^2)$
+ *
+ * @param kp (1)[]: $E_A(a_{pi(i)}) times E_A(- r_{pi(i)} - b_{pi(i)}) &= E_A(a_{pi(i)} - r_{pi(i)} - b_{pi(i)})$
+ * @param kq (2)[]: $E_A(a_{pi'(i)}) times E_A(- r_{pi'(i)}) &= E_A(a_{pi'(i)} - r_{pi'(i)})$
+ * @param s S: $S := E_A(sum (r_i + b_i)^2)$
+ * @param stick S': $S' := E_A(sum r_i^2)$
+ * @param request the associated requesting session with alice
+ * @param response the associated responder session with bob's client
+ * @return GNUNET_SYSERR if the function was called with NULL parameters or if there was an error
+ * GNUNET_NO if we could not send our message
+ * GNUNET_OK if the operation succeeded
+ */
+static int
+prepare_service_response (gcry_mpi_t * kp,
+ gcry_mpi_t * kq,
+ gcry_mpi_t s,
+ gcry_mpi_t stick,
+ struct ServiceSession * request,
+ struct ServiceSession * response)
+{
+ struct GNUNET_VECTORPRODUCT_service_response * msg;
+ uint16_t msg_length = 0;
+ unsigned char * current = NULL;
+ unsigned char * element_exported = NULL;
+ size_t element_length = 0;
+ int i;
+
+ GNUNET_assert (request);
+
+ msg_length = sizeof (struct GNUNET_VECTORPRODUCT_service_response)
+ + 2 * request->used_element_count * PAILLIER_ELEMENT_LENGTH // kp, kq
+ + 2 * PAILLIER_ELEMENT_LENGTH; // s, stick
+
+ msg = GNUNET_malloc (msg_length);
+ GNUNET_assert (msg);
+
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_VECTORPRODUCT_BOB_TO_ALICE);
+ msg->header.size = htons (msg_length);
+ msg->element_count = htons (request->element_count);
+ msg->used_element_count = htons (request->used_element_count);
+ memcpy (&msg->key, &request->key, sizeof (struct GNUNET_HashCode));
+ current = (unsigned char *) &msg[1];
+
+ // 4 times the same logics with slight variations.
+ // doesn't really justify having 2 functions for that
+ // so i put it into blocks to enhance readability
+ // convert s
+ {
+ element_exported = GNUNET_malloc (PAILLIER_ELEMENT_LENGTH);
+ GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG,
+ element_exported, PAILLIER_ELEMENT_LENGTH,
+ &element_length,
+ s));
+ adjust (element_exported, element_length, PAILLIER_ELEMENT_LENGTH);
+ memcpy (current, element_exported, PAILLIER_ELEMENT_LENGTH);
+ GNUNET_free (element_exported);
+ current += PAILLIER_ELEMENT_LENGTH;
+ }
+
+ // convert stick
+ {
+ element_exported = GNUNET_malloc (PAILLIER_ELEMENT_LENGTH);
+ GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG,
+ element_exported, PAILLIER_ELEMENT_LENGTH,
+ &element_length,
+ stick));
+ adjust (element_exported, element_length, PAILLIER_ELEMENT_LENGTH);
+ memcpy (current, element_exported, PAILLIER_ELEMENT_LENGTH);
+ GNUNET_free (element_exported);
+ current += PAILLIER_ELEMENT_LENGTH;
+ }
+
+ // convert kp[]
+ for (i = 0; i < request->used_element_count; i++)
+ {
+ element_exported = GNUNET_malloc (PAILLIER_ELEMENT_LENGTH);
+ GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG,
+ element_exported, PAILLIER_ELEMENT_LENGTH,
+ &element_length,
+ kp[i]));
+ adjust (element_exported, element_length, PAILLIER_ELEMENT_LENGTH);
+ memcpy (current, element_exported, PAILLIER_ELEMENT_LENGTH);
+ GNUNET_free (element_exported);
+ current += PAILLIER_ELEMENT_LENGTH;
+ }
+
+
+ // convert kq[]
+ for (i = 0; i < request->used_element_count; i++)
+ {
+ element_exported = GNUNET_malloc (PAILLIER_ELEMENT_LENGTH);
+ GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG,
+ element_exported, PAILLIER_ELEMENT_LENGTH,
+ &element_length,
+ kq[i]));
+ adjust (element_exported, element_length, PAILLIER_ELEMENT_LENGTH);
+ memcpy (current, element_exported, PAILLIER_ELEMENT_LENGTH);
+ GNUNET_free (element_exported);
+ current += PAILLIER_ELEMENT_LENGTH;
+ }
+
+ if (GNUNET_SERVER_MAX_MESSAGE_SIZE >= msg_length)
+ {
+ struct MessageObject * msg_obj;
+
+ msg_obj = GNUNET_new (struct MessageObject);
+ msg_obj->msg = (struct GNUNET_MessageHeader *) msg;
+ msg_obj->transmit_handle = (void *) &request->service_transmit_handle; //and reset the transmit handle
+ request->service_transmit_handle =
+ GNUNET_MESH_notify_transmit_ready (request->tunnel,
+ GNUNET_YES,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &request->peer, //must be specified, we are a slave/participant/non-owner
+ msg_length,
+ &do_send_message,
+ msg_obj);
+ // we don't care if it could be send or not. either way, the session is over for us.
+ request->state = FINALIZED;
+ response->state = FINALIZED;
+ }
+ else
+ {
+ // TODO FEATURE: fallback to fragmentation, in case the message is too long
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Message too large, fragmentation is currently not supported!)\n"));
+ }
+
+ //disconnect our client
+ if ( ! request->service_transmit_handle)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Could not send service-response message via mesh!)\n"));
+ GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, response);
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &prepare_client_end_notification,
+ response);
+ return GNUNET_NO;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * executed by bob:
+ * compute the values
+ * (1)[]: $E_A(a_{pi(i)}) times E_A(- r_{pi(i)} - b_{pi(i)}) &= E_A(a_{pi(i)} - r_{pi(i)} - b_{pi(i)})$
+ * (2)[]: $E_A(a_{pi'(i)}) times E_A(- r_{pi'(i)}) &= E_A(a_{pi'(i)} - r_{pi'(i)})$
+ * S: $S := E_A(sum (r_i + b_i)^2)$
+ * S': $S' := E_A(sum r_i^2)$
+ *
+ * @param request the requesting session + bob's requesting peer
+ * @param response the responding session + bob's client handle
+ * @return GNUNET_SYSERR if the computation failed
+ * GNUNET_OK if everything went well.
+ */
+static int
+compute_service_response (struct ServiceSession * request,
+ struct ServiceSession * response)
+{
+ int i, j, ret = GNUNET_SYSERR;
+ unsigned int * p;
+ unsigned int * q;
+ uint16_t count;
+ gcry_mpi_t * r = NULL;
+ gcry_mpi_t * kp = NULL;
+ gcry_mpi_t * kq = NULL;
+ gcry_mpi_t * b;
+ gcry_mpi_t * ap;
+ gcry_mpi_t * aq;
+ gcry_mpi_t * bp;
+ gcry_mpi_t * bq;
+ gcry_mpi_t * rp;
+ gcry_mpi_t * rq;
+ gcry_mpi_t s = NULL;
+ gcry_mpi_t stick = NULL;
+ gcry_mpi_t remote_n = NULL;
+ gcry_mpi_t remote_nsquare;
+ gcry_mpi_t remote_g = NULL;
+ gcry_sexp_t tmp_exp;
+ uint32_t value;
+
+ GNUNET_assert (request != NULL && response != NULL);
+ count = request->used_element_count;
+
+ b = GNUNET_malloc (sizeof (gcry_mpi_t) * count);
+ ap = GNUNET_malloc (sizeof (gcry_mpi_t) * count);
+ bp = GNUNET_malloc (sizeof (gcry_mpi_t) * count);
+ aq = GNUNET_malloc (sizeof (gcry_mpi_t) * count);
+ bq = GNUNET_malloc (sizeof (gcry_mpi_t) * count);
+ rp = GNUNET_malloc (sizeof (gcry_mpi_t) * count);
+ rq = GNUNET_malloc (sizeof (gcry_mpi_t) * count);
+
+ // convert responder session to from long to mpi
+ for (i = 0, j = 0; i < response->element_count && j < count; i++)
+ {
+ if (request->mask[i / 8] & (1 << (i % 8)))
+ {
+ value = response->vector[i] >= 0 ? response->vector[i] : -response->vector[i];
+ // long to gcry_mpi_t
+ if (0 > response->vector[i])
+ {
+ b[j] = gcry_mpi_new (0);
+ gcry_mpi_sub_ui (b[j], b[j], value);
+ }
+ else
+ {
+ b[j] = gcry_mpi_set_ui (NULL, value);
+ }
+ j++;
+ }
+ }
+ GNUNET_free (response->vector);
+ response->vector = NULL;
+
+ tmp_exp = gcry_sexp_find_token (request->remote_pubkey, "n", 0);
+ if ( ! tmp_exp)
+ {
+ GNUNET_break_op (0);
+ gcry_sexp_release (request->remote_pubkey);
+ request->remote_pubkey = NULL;
+ goto except;
+ }
+ remote_n = gcry_sexp_nth_mpi (tmp_exp, 1, GCRYMPI_FMT_USG);
+ if ( ! remote_n)
+ {
+ GNUNET_break (0);
+ gcry_sexp_release (tmp_exp);
+ goto except;
+ }
+ remote_nsquare = gcry_mpi_new (KEYBITS + 1);
+ gcry_mpi_mul (remote_nsquare, remote_n, remote_n);
+ gcry_sexp_release (tmp_exp);
+ tmp_exp = gcry_sexp_find_token (request->remote_pubkey, "g", 0);
+ gcry_sexp_release (request->remote_pubkey);
+ request->remote_pubkey = NULL;
+ if ( ! tmp_exp)
+ {
+ GNUNET_break_op (0);
+ gcry_mpi_release (remote_n);
+ goto except;
+ }
+ remote_g = gcry_sexp_nth_mpi (tmp_exp, 1, GCRYMPI_FMT_USG);
+ if ( ! remote_g)
+ {
+ GNUNET_break (0);
+ gcry_mpi_release (remote_n);
+ gcry_sexp_release (tmp_exp);
+ goto except;
+ }
+ gcry_sexp_release (tmp_exp);
+
+ // generate r, p and q
+ r = generate_random_vector (count);
+ p = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, count);
+ q = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, count);
+ //initialize the result vectors
+ kp = initialize_mpi_vector (count);
+ kq = initialize_mpi_vector (count);
+
+ // copy the REFERNCES of a, b and r into aq and bq. we will not change
+ // those values, thus we can work with the references
+ memcpy (ap, request->a, sizeof (gcry_mpi_t) * count);
+ memcpy (aq, request->a, sizeof (gcry_mpi_t) * count);
+ memcpy (bp, b, sizeof (gcry_mpi_t) * count);
+ memcpy (bq, b, sizeof (gcry_mpi_t) * count);
+ memcpy (rp, r, sizeof (gcry_mpi_t) * count);
+ memcpy (rq, r, sizeof (gcry_mpi_t) * count);
+
+ // generate p and q permutations for a, b and r
+ GNUNET_assert (permute_vector (ap, p, count));
+ GNUNET_assert (permute_vector (bp, p, count));
+ GNUNET_assert (permute_vector (rp, p, count));
+ GNUNET_assert (permute_vector (aq, q, count));
+ GNUNET_assert (permute_vector (bq, q, count));
+ GNUNET_assert (permute_vector (rq, q, count));
+
+ // encrypt the element
+ // for the sake of readability I decided to have dedicated permutation
+ // vectors, which get rid of all the lookups in p/q.
+ // however, ap/aq are not absolutely necessary but are just abstraction
+ // Calculate Kp = E(a_pi) + E(-r_pi - b_pi)
+ for (i = 0; i < count; i++)
+ {
+ // E(-r_pi - b_pi)
+ gcry_mpi_sub (kp[i], kp[i], rp[i]);
+ gcry_mpi_sub (kp[i], kp[i], bp[i]);
+ encrypt_element (kp[i], kp[i], NULL, remote_g, remote_n, remote_nsquare);
+
+ // E(-r_pi - b_pi) * E(a_pi) == E(a + (-r -b))
+ //gcry_mpi_mulm (kp[i], kp[i], ap[i], remote_nsquare);
+ gcry_mpi_add (kp[i], kp[i], ap[i]);
+ }
+ GNUNET_free (ap);
+ GNUNET_free (bp);
+ GNUNET_free (rp);
+
+ // Calculate Kq = E(a_qi) + E( -r_qi)
+ for (i = 0; i < count; i++)
+ {
+ // E(-r_qi)
+ gcry_mpi_sub (kq[i], kq[i], rq[i]);
+ encrypt_element (kq[i], kq[i], NULL, remote_g, remote_n, remote_nsquare);
+
+ // E(-r_qi) * E(a_qi) == E(aqi + (- rqi))
+ //gcry_mpi_mulm (kq[i], kq[i], aq[i], remote_nsquare);
+ gcry_mpi_add (kq[i], kq[i], aq[i]);
+ }
+ GNUNET_free (aq);
+ GNUNET_free (bq);
+ GNUNET_free (rq);
+
+ // Calculate S' = E(SUM( r_i^2 ))
+ stick = compute_square_sum (r, count);
+ encrypt_element (stick, stick, NULL, remote_g, remote_n, remote_nsquare);
+
+ // Calculate S = E(SUM( (r_i + b_i)^2 ))
+ for (i = 0; i < count; i++)
+ {
+ gcry_mpi_add (r[i], r[i], b[i]);
+ }
+ s = compute_square_sum (r, count);
+ encrypt_element (s, s, NULL, remote_g, remote_n, remote_nsquare);
+ gcry_mpi_release (remote_n);
+ gcry_mpi_release (remote_g);
+ gcry_mpi_release (remote_nsquare);
+
+ // release r and tmp
+ for (i = 0; i < count; i++)
+ // rp, rq, aq, ap, bp, bq are released along with a, r, b respectively, (a and b are handled at except:)
+ gcry_mpi_release (r[i]);
+
+ // copy the Kp[], Kq[], S and Stick into a new message
+ if (GNUNET_YES != prepare_service_response (kp, kq, s, stick, request, response))
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Computation of values for alice failed!\n"));
+ else
+ ret = GNUNET_OK;
+
+ for (i = 0; i < count; i++)
+ {
+ gcry_mpi_release (kq[i]);
+ gcry_mpi_release (kp[i]);
+ }
+
+ gcry_mpi_release (s);
+ gcry_mpi_release (stick);
+
+except:
+ for (i = 0; i < count; i++)
+ {
+ gcry_mpi_release (b[i]);
+ gcry_mpi_release (request->a[i]);
+ }
+
+ GNUNET_free (b);
+ GNUNET_free (request->a);
+ request->a = NULL;
+
+ return ret;
+}
+
+
+/**
+ * Executed by Alice, fills in a service-request message and sends it to the given peer
+ *
+ * @param session the session associated with this request, then also holds the CORE-handle
+ * @return GNUNET_SYSERR if we could not send the message
+ * GNUNET_NO if the message was too large
+ * GNUNET_OK if we sent it
+ */
+static void
+prepare_service_request (void *cls,
+ const struct GNUNET_PeerIdentity * peer,
+ const struct GNUNET_ATS_Information * atsi)
+{
+ struct ServiceSession * session = cls;
+ unsigned char * current;
+ struct GNUNET_VECTORPRODUCT_service_request * msg;
+ struct MessageObject * msg_obj;
+ unsigned int i;
+ unsigned int j;
+ uint16_t msg_length;
+ size_t element_length = 0; //gets initialized by gcry_mpi_print, but the compiler doesn't know that
+ gcry_mpi_t a;
+ gcry_mpi_t r;
+ uint32_t value;
+
+ GNUNET_assert (NULL != cls);
+ GNUNET_assert (NULL != peer);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Successfully created new tunnel to peer (%s)!\n"), GNUNET_i2s (peer));
+
+ msg_length = sizeof (struct GNUNET_VECTORPRODUCT_service_request)
+ + session->used_element_count * PAILLIER_ELEMENT_LENGTH
+ + session->mask_length
+ + my_pubkey_external_length;
+
+ if (GNUNET_SERVER_MAX_MESSAGE_SIZE < sizeof (struct GNUNET_VECTORPRODUCT_service_request)
+ + session->used_element_count * PAILLIER_ELEMENT_LENGTH
+ + session->mask_length
+ + my_pubkey_external_length)
+ {
+ // TODO FEATURE: fallback to fragmentation, in case the message is too long
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Message too large, fragmentation is currently not supported!\n"));
+ GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, session);
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &prepare_client_end_notification,
+ session);
+ return;
+ }
+ msg = GNUNET_malloc (msg_length);
+
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_VECTORPRODUCT_ALICE_TO_BOB);
+ memcpy (&msg->key, &session->key, sizeof (struct GNUNET_HashCode));
+ msg->mask_length = htons (session->mask_length);
+ msg->pk_length = htons (my_pubkey_external_length);
+ msg->used_element_count = htons (session->used_element_count);
+ msg->element_count = htons (session->element_count);
+ msg->header.size = htons (msg_length);
+
+ // fill in the payload
+ current = (unsigned char *) &msg[1];
+ // copy over the mask
+ memcpy (current, session->mask, session->mask_length);
+ // copy over our public key
+ current += session->mask_length;
+ memcpy (current, my_pubkey_external, my_pubkey_external_length);
+ current += my_pubkey_external_length;
+
+ // now copy over the element vector
+ session->a = GNUNET_malloc (sizeof (gcry_mpi_t) * session->used_element_count);
+ a = gcry_mpi_new (KEYBITS * 2);
+ r = gcry_mpi_new (KEYBITS * 2);
+ // encrypt our vector and generate string representations
+ for (i = 0, j = 0; i < session->element_count; i++)
+ {
+ // if this is a used element...
+ if (session->mask[i / 8] & 1 << (i % 8))
+ {
+ unsigned char * element_exported = GNUNET_malloc (PAILLIER_ELEMENT_LENGTH);
+ value = session->vector[i] >= 0 ? session->vector[i] : -session->vector[i];
+
+ // long to gcry_mpi_t
+ if (session->vector[i] < 0)
+ {
+ a = gcry_mpi_set_ui (NULL, 0);
+ gcry_mpi_sub_ui (a, a, value);
+ }
+ else
+ a = gcry_mpi_set_ui (NULL, value);
+
+ // multiply with a given factor to avoid disclosing 1
+ session->a[j++] = gcry_mpi_set (NULL, a);
+ encrypt_element (a, a, r, my_g, my_n, my_nsquare);
+
+ // get representation as string
+ // we always supply some value, so gcry_mpi_print fails only if it can't reserve memory
+ GNUNET_assert ( ! gcry_mpi_print (GCRYMPI_FMT_USG,
+ element_exported, PAILLIER_ELEMENT_LENGTH,
+ &element_length,
+ a));
+
+ // move buffer content to the end of the buffer so it can easily be read by libgcrypt. also this now has fixed size
+ adjust (element_exported, element_length, PAILLIER_ELEMENT_LENGTH);
+
+ // copy over to the message
+ memcpy (current, element_exported, PAILLIER_ELEMENT_LENGTH);
+ current += PAILLIER_ELEMENT_LENGTH;
+ }
+ }
+ gcry_mpi_release (a);
+ gcry_mpi_release (r);
+
+ msg_obj = GNUNET_malloc (sizeof (struct MessageObject));
+ msg_obj->msg = (struct GNUNET_MessageHeader *) msg;
+ msg_obj->transmit_handle = (void *) &session->service_transmit_handle; //and reset the transmit handle
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transmitting service request.\n"));
+
+ //transmit via mesh messaging
+ session->state = WAITING_FOR_RESPONSE_FROM_SERVICE;
+ session->service_transmit_handle = GNUNET_MESH_notify_transmit_ready (session->tunnel, GNUNET_YES,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ peer, //multicast to all targets, maybe useful in the future
+ msg_length,
+ &do_send_message,
+ msg_obj);
+ if ( ! session->service_transmit_handle)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Could not send mutlicast message to tunnel!\n"));
+ GNUNET_free (msg_obj);
+ GNUNET_free (msg);
+ GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, session);
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &prepare_client_end_notification,
+ session);
+ return;
+ }
+}
+
+
+/**
+ * Method called whenever a peer has disconnected from the tunnel.
+ * Implementations of this callback must NOT call
+ * GNUNET_MESH_tunnel_destroy immediately, but instead schedule those
+ * to run in some other task later. However, calling
+ * "GNUNET_MESH_notify_transmit_ready_cancel" is allowed.
+ *
+ * @param cls closure
+ * @param peer peer identity the tunnel stopped working with
+ */
+static void
+tunnel_peer_disconnect_handler (void *cls, const struct GNUNET_PeerIdentity * peer)
+{
+ // as we have only one peer connected in each session, just remove the session and say good bye
+ struct ServiceSession * session = cls;
+ struct ServiceSession * curr;
+ GNUNET_assert(cls);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Peer (%s) disconnected from our tunnel!\n"), GNUNET_i2s (peer));
+
+ if ((session->role == ALICE) && (FINALIZED != session->state) && ( ! do_shutdown))
+ {
+ for (curr = from_client_head; NULL != curr; curr = curr->next)
+ if (curr == session)
+ {
+ GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, session);
+ break;
+ }
+ GNUNET_SCHEDULER_add_now (&destroy_tunnel,
+ session);
+ // if this happened before we received the answer, we must terminate the session
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &prepare_client_end_notification,
+ session);
+ }
+}
+
+
+/**
+ * Handler for a client request message.
+ * Can either be type A or B
+ * A: request-initiation to compute a scalar product with a peer
+ * B: response role, keep the values + session and wait for a matching session or process a waiting request
+ *
+ * @param cls closure
+ * @param client identification of the client
+ * @param message the actual message
+ */
+static void
+handle_client_request (void *cls,
+ struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ struct GNUNET_VECTORPRODUCT_client_request * msg = (struct GNUNET_VECTORPRODUCT_client_request *) message;
+ struct ServiceSession * session;
+ uint16_t element_count;
+ uint16_t mask_length;
+ uint16_t msg_type;
+ int32_t * vector;
+ uint32_t i;
+
+ GNUNET_assert (message);
+
+ //we need at least a peer and one message id to compare
+ if (sizeof (struct GNUNET_VECTORPRODUCT_client_request) > ntohs (msg->header.size))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Too short message received from client!\n"));
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+
+ msg_type = ntohs (msg->header.type);
+ element_count = ntohs (msg->element_count);
+ mask_length = ntohs (msg->mask_length);
+
+ //sanity check: is the message as long as the message_count fields suggests?
+ if (( ntohs (msg->header.size) != (sizeof (struct GNUNET_VECTORPRODUCT_client_request) + element_count * sizeof (int32_t) + mask_length))
+ || (0 == element_count))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Invalid message received from client, session information incorrect!\n"));
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+
+ // do we have a duplicate session here already?
+ if (NULL != find_matching_session (from_client_tail,
+ &msg->key,
+ element_count,
+ NULL, NULL))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Duplicate session information received, cannot create new session with key `%s'\n"), GNUNET_h2s (&msg->key));
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+
+ session = GNUNET_malloc (sizeof (struct ServiceSession));
+ session->client = client;
+ session->element_count = element_count;
+ session->mask_length = mask_length;
+ // get our transaction key
+ memcpy (&session->key, &msg->key, sizeof (struct GNUNET_HashCode));
+ //allocate memory for vector and encrypted vector
+ session->vector = GNUNET_malloc (sizeof (int32_t) * element_count);
+ vector = (int32_t *) & msg[1];
+
+ if (GNUNET_MESSAGE_TYPE_VECTORPRODUCT_CLIENT_TO_ALICE == msg_type)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Got client-request-session with key %s, preparing tunnel to remote service.\n"), GNUNET_h2s (&session->key));
+
+ session->role = ALICE;
+ // fill in the mask
+ session->mask = GNUNET_malloc (mask_length);
+ memcpy (session->mask, &vector[element_count], mask_length);
+
+ // copy over the elements
+ session->used_element_count = 0;
+ for (i = 0; i < element_count; i++)
+ {
+ session->vector[i] = ntohl (vector[i]);
+ if (session->vector[i] == 0)
+ session->mask[i / 8] &= ~(1 << (i % 8));
+ if (session->mask[i / 8] & (1 << (i % 8)))
+ session->used_element_count++;
+ }
+
+ if ( ! session->used_element_count)
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (session->vector);
+ GNUNET_free (session->a);
+ GNUNET_free (session);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ //session with ourself makes no sense!
+ if ( ! memcmp (&msg->peer, &me, sizeof (struct GNUNET_PeerIdentity)))
+ {
+ GNUNET_break (0);
+ GNUNET_free (session->vector);
+ GNUNET_free (session->a);
+ GNUNET_free (session);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ // get our peer ID
+ memcpy (&session->peer, &msg->peer, sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Creating new tunnel to for session with key %s.\n"), GNUNET_h2s (&session->key));
+ GNUNET_CONTAINER_DLL_insert (from_client_head, from_client_tail, session);
+ session->tunnel = GNUNET_MESH_tunnel_create (my_mesh, session,
+ prepare_service_request,
+ tunnel_peer_disconnect_handler,
+ session);
+ if ( ! session->tunnel)
+ {
+ GNUNET_break (0);
+ GNUNET_free (session->vector);
+ GNUNET_free (session->a);
+ GNUNET_free (session);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ GNUNET_MESH_peer_request_connect_add (session->tunnel, &session->peer);
+ GNUNET_SERVER_receive_done (client, GNUNET_YES);
+ session->state = WAITING_FOR_BOBS_CONNECT;
+ }
+ else
+ {
+ struct ServiceSession * requesting_session;
+ enum SessionState needed_state = REQUEST_FROM_SERVICE_RECEIVED;
+
+ session->role = BOB;
+ session->mask = NULL;
+ // copy over the elements
+ session->used_element_count = element_count;
+ for (i = 0; i < element_count; i++)
+ session->vector[i] = ntohl (vector[i]);
+ session->state = MESSAGE_FROM_RESPONDING_CLIENT_RECEIVED;
+
+ GNUNET_CONTAINER_DLL_insert (from_client_head, from_client_tail, session);
+ GNUNET_SERVER_receive_done (client, GNUNET_YES);
+ //check if service queue contains a matching request
+ requesting_session = find_matching_session (from_service_tail,
+ &session->key,
+ session->element_count,
+ &needed_state, NULL);
+ if (NULL != requesting_session)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Got client-responder-session with key %s and a matching service-request-session set, processing.\n"), GNUNET_h2s (&session->key));
+ if (GNUNET_OK != compute_service_response (requesting_session, session))
+ {
+ GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, session);
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &prepare_client_end_notification,
+ session);
+ }
+ }
+ else
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Got client-responder-session with key %s but NO matching service-request-session set, queuing element for later use.\n"), GNUNET_h2s (&session->key));
+ // no matching session exists yet, store the response
+ // for later processing by handle_service_request()
+ }
+}
+
+
+/**
+ * Function called for inbound tunnels.
+ *
+ * @param cls closure
+ * @param tunnel new handle to the tunnel
+ * @param initiator peer that started the tunnel
+ * @param atsi performance information for the tunnel
+ * @return initial tunnel context for the tunnel
+ * (can be NULL -- that's not an error)
+ */
+static void *
+tunnel_incoming_handler (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
+ const struct GNUNET_PeerIdentity *initiator,
+ const struct GNUNET_ATS_Information *atsi)
+{
+
+ struct ServiceSession * c = GNUNET_new (struct ServiceSession);
+
+ memcpy (&c->peer, initiator, sizeof (struct GNUNET_PeerIdentity));
+ c->tunnel = tunnel;
+ c->role = BOB;
+ return c;
+}
+
+
+/**
+ * Function called whenever an inbound tunnel is destroyed. Should clean up
+ * any associated state.
+ *
+ * @param cls closure (set from GNUNET_MESH_connect)
+ * @param tunnel connection to the other end (henceforth invalid)
+ * @param tunnel_ctx place where local state associated
+ * with the tunnel is stored (our 'struct TunnelState')
+ */
+static void
+tunnel_destruction_handler (void *cls,
+ const struct GNUNET_MESH_Tunnel *tunnel,
+ void *tunnel_ctx)
+{
+ struct ServiceSession * service_session = tunnel_ctx;
+ struct ServiceSession * client_session;
+ struct ServiceSession * curr;
+
+ GNUNET_assert (service_session);
+ if (!memcmp (&service_session->peer, &me, sizeof (struct GNUNET_PeerIdentity)))
+ return;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Tunnel destroyed, terminating session with peer (%s)\n"), GNUNET_i2s (&service_session->peer));
+ // remove the session, unless it has already been dequeued, but somehow still active
+ // this could bug without the IF in case the queue is empty and the service session was the only one know to the service
+ for (curr = from_service_head; NULL != curr; curr = curr->next)
+ if (curr == service_session)
+ {
+ GNUNET_CONTAINER_DLL_remove (from_service_head, from_service_tail, curr);
+ break;
+ }
+ // there is a client waiting for this service session, terminate it, too!
+ // i assume the tupel of key and element count is unique. if it was not the rest of the code would not work either.
+ client_session = find_matching_session (from_client_tail,
+ &service_session->key,
+ service_session->element_count,
+ NULL, NULL);
+ free_session (service_session);
+
+ // the client has to check if it was waiting for a result
+ // or if it was a responder, no point in adding more statefulness
+ if (client_session && ( ! do_shutdown))
+ {
+ // remove the session, we just found it in the queue, so it must be there
+ GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, client_session);
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &prepare_client_end_notification,
+ client_session);
+ }
+}
+
+
+/**
+ * Compute our scalar product, done by Alice
+ *
+ * @param session - the session associated with this computation
+ * @param kp - (1) from the protocol definition:
+ * $E_A(a_{pi(i)}) times E_A(- r_{pi(i)} - b_{pi(i)}) &= E_A(a_{pi(i)} - r_{pi(i)} - b_{pi(i)})$
+ * @param kq - (2) from the protocol definition:
+ * $E_A(a_{pi'(i)}) times E_A(- r_{pi'(i)}) &= E_A(a_{pi'(i)} - r_{pi'(i)})$
+ * @param s - S from the protocol definition:
+ * $S := E_A(sum (r_i + b_i)^2)$
+ * @param stick - S' from the protocol definition:
+ * $S' := E_A(sum r_i^2)$
+ * @return product as MPI, never NULL
+ */
+static gcry_mpi_t
+compute_scalar_product (struct ServiceSession * session,
+ gcry_mpi_t * kp, gcry_mpi_t * kq, gcry_mpi_t s, gcry_mpi_t stick)
+{
+ uint16_t count;
+ gcry_mpi_t divider;
+ gcry_mpi_t t;
+ gcry_mpi_t u;
+ gcry_mpi_t utick;
+ gcry_mpi_t p;
+ gcry_mpi_t ptick;
+ gcry_mpi_t product;
+ gcry_mpi_t tmp;
+ unsigned int i;
+
+ count = session->used_element_count;
+ tmp = gcry_mpi_new (KEYBITS);
+ for (i = 0; i < count; i++)
+ {
+ decrypt_element (kp[i], kp[i], my_mu, my_lambda, my_n, my_nsquare);
+ decrypt_element (kq[i], kq[i], my_mu, my_lambda, my_n, my_nsquare);
+ }
+
+ // calculate t = E(sum(ai))
+ t = compute_square_sum (session->a, count);
+ encrypt_element (t, t, NULL, my_g, my_n, my_nsquare);
+
+ // calculate U
+ u = gcry_mpi_new (0);
+ tmp = compute_square_sum (kp, count);
+ gcry_mpi_sub (u, u, tmp);
+ encrypt_element (u, u, NULL, my_g, my_n, my_nsquare);
+ gcry_mpi_release (tmp);
+
+ //calculate U'
+ utick = gcry_mpi_new (0);
+ tmp = compute_square_sum (kq, count);
+ gcry_mpi_sub (utick, utick, tmp);
+ encrypt_element (utick, utick, NULL, my_g, my_n, my_nsquare);
+ gcry_mpi_release (tmp);
+
+ GNUNET_assert (p = gcry_mpi_new (0));
+ GNUNET_assert (ptick = gcry_mpi_new (0));
+
+ // compute P
+ gcry_mpi_add (p, s, t);
+ //gcry_mpi_mulm (p, p, u, my_nsquare);
+ gcry_mpi_add (p, p, u);
+ decrypt_element (p, p, my_mu, my_lambda, my_n, my_nsquare);
+
+ // compute P'
+ gcry_mpi_add (ptick, stick, t);
+ //gcry_mpi_mulm (ptick, ptick, utick, my_nsquare);
+ gcry_mpi_add (ptick, ptick, utick);
+ decrypt_element (ptick, ptick, my_mu, my_lambda, my_n, my_nsquare);
+
+ gcry_mpi_release (t);
+ gcry_mpi_release (u);
+ gcry_mpi_release (utick);
+
+ // compute product
+ GNUNET_assert (product = gcry_mpi_new (0));
+ gcry_mpi_sub (product, p, ptick);
+ gcry_mpi_release (p);
+ gcry_mpi_release (ptick);
+ divider = gcry_mpi_set_ui (NULL, 2);
+ gcry_mpi_div (product, NULL, product, divider, 0);
+
+ gcry_mpi_release (divider);
+ for (i = 0; i < count; i++)
+ gcry_mpi_release (session->a[i]);
+ GNUNET_free (session->a);
+ session->a = NULL;
+
+ return product;
+}
+
+
+/**
+ * prepare the response we will send to alice or bobs' clients.
+ * in Bobs case the product will be NULL.
+ *
+ * @param session the session associated with our client.
+ */
+static void
+prepare_client_response (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct ServiceSession * session = cls;
+ struct GNUNET_VECTORPRODUCT_client_response * msg;
+ unsigned char * product_exported = NULL;
+ size_t product_length = 0;
+ uint16_t msg_length = 0;
+ struct MessageObject * msg_obj;
+
+ GNUNET_assert (session);
+
+ if (session->product)
+ {
+ // get representation as string
+ GNUNET_assert ( ! gcry_mpi_aprint (GCRYMPI_FMT_USG,
+ &product_exported,
+ &product_length,
+ session->product));
+ gcry_mpi_release (session->product);
+ session->product = NULL;
+ }
+
+ msg_length = sizeof (struct GNUNET_VECTORPRODUCT_client_response) +product_length;
+ msg = GNUNET_malloc (msg_length);
+ memcpy (&msg[1], product_exported, product_length);
+ GNUNET_free_non_null (product_exported);
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_VECTORPRODUCT_SERVICE_TO_CLIENT);
+ msg->header.size = htons (msg_length);
+ memcpy (&msg->key, &session->key, sizeof (struct GNUNET_HashCode));
+ memcpy (&msg->peer, &session->peer, sizeof ( struct GNUNET_PeerIdentity));
+ msg->product_length = htonl (product_length);
+
+ msg_obj = GNUNET_malloc (sizeof (struct MessageObject));
+ msg_obj->msg = (struct GNUNET_MessageHeader *) msg;
+ msg_obj->transmit_handle = NULL; // don't reset the transmit handle
+
+ //transmit this message to our client
+ session->client_transmit_handle =
+ GNUNET_SERVER_notify_transmit_ready (session->client,
+ msg_length,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &do_send_message,
+ msg_obj);
+ if ( ! session->client_transmit_handle)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not send message to client (%p)! This probably is OK if the client disconnected before us.\n"), session->client);
+ session->client = NULL;
+ // callback was not called!
+ GNUNET_free (msg_obj);
+ GNUNET_free (msg);
+ }
+ else
+ // gracefully sent message, just terminate session structure
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Sent result to client (%p), this session (%s) has ended!\n"), session->client, GNUNET_h2s (&session->key));
+ free_session (session);
+}
+
+
+/**
+ * Handle a request from another service to calculate a vectorproduct with us.
+ *
+ * @param cls closure (set from GNUNET_MESH_connect)
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx place to store local state associated with the tunnel
+ * @param sender who sent the message
+ * @param message the actual message
+ * @param atsi performance data for the connection
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+handle_service_request (void *cls,
+ struct GNUNET_MESH_Tunnel * tunnel,
+ void **tunnel_ctx,
+ const struct GNUNET_PeerIdentity * sender,
+ const struct GNUNET_MessageHeader * message,
+ const struct GNUNET_ATS_Information * atsi)
+{
+ struct ServiceSession * session;
+ struct GNUNET_VECTORPRODUCT_service_request * msg = (struct GNUNET_VECTORPRODUCT_service_request *) message;
+ uint16_t mask_length;
+ uint16_t pk_length;
+ uint16_t used_elements;
+ uint16_t element_count;
+ uint16_t msg_length;
+ unsigned char * current;
+ struct ServiceSession * responder_session;
+ int32_t i = -1;
+ enum SessionState needed_state;
+
+ GNUNET_assert (NULL != message);
+ GNUNET_assert (NULL != sender);
+ GNUNET_assert (NULL != tunnel_ctx);
+ session = (struct ServiceSession *) * tunnel_ctx;
+ // is this tunnel already in use?
+ if ( (session->next) || (from_service_head == session))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Got a service request over a tunnel that is already in use, ignoring!\n"));
+ return GNUNET_SYSERR;
+ }
+ // Check if message was sent by me, which would be bad!
+ if ( ! memcmp (sender, &me, sizeof (struct GNUNET_PeerIdentity)))
+ {
+ GNUNET_break (0);
+ GNUNET_free (session);
+ return GNUNET_SYSERR;
+ }
+ // this protocol can at best be 1:N, but never M:N!
+ // Check if the sender is not the peer, I am connected to, which would be bad!
+ if (memcmp (sender, &session->peer, sizeof (struct GNUNET_PeerIdentity)))
+ {
+ GNUNET_break (0);
+ GNUNET_free (session);
+ return GNUNET_SYSERR;
+ }
+
+ //we need at least a peer and one message id to compare
+ if (ntohs (msg->header.size) < sizeof (struct GNUNET_VECTORPRODUCT_service_request))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Too short message received from peer!\n"));
+ GNUNET_free (session);
+ return GNUNET_SYSERR;
+ }
+ mask_length = ntohs (msg->mask_length);
+ pk_length = ntohs (msg->pk_length);
+ used_elements = ntohs (msg->used_element_count);
+ element_count = ntohs (msg->element_count);
+ msg_length = sizeof (struct GNUNET_VECTORPRODUCT_service_request)
+ + mask_length + pk_length + used_elements * PAILLIER_ELEMENT_LENGTH;
+
+ //sanity check: is the message as long as the message_count fields suggests?
+ if ((ntohs (msg->header.size) != msg_length) || (element_count < used_elements)
+ || (used_elements == 0) || (mask_length != (element_count / 8 + (element_count % 8 ? 1 : 0)))
+ )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Invalid message received from peer, message count does not match message length!\n"));
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Used elements: %hu\nElement Count: %hu\nExpected Mask Length: %hu\nCalculated Masklength: %d\n"), used_elements, element_count, mask_length, (element_count / 8 + (element_count % 8 ? 1 : 0)));
+ GNUNET_free (session);
+ return GNUNET_SYSERR;
+ }
+ if (find_matching_session (from_service_tail,
+ &msg->key,
+ element_count,
+ NULL,
+ sender))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Got message with duplicate session key (`%s'), ignoring service request.\n"), (const char *) &(msg->key));
+ GNUNET_free (session);
+ return GNUNET_SYSERR;
+ }
+
+ memcpy (&session->peer, sender, sizeof (struct GNUNET_PeerIdentity));
+ session->state = REQUEST_FROM_SERVICE_RECEIVED;
+ session->element_count = ntohs (msg->element_count);
+ session->used_element_count = used_elements;
+ session->tunnel = tunnel;
+
+ // session key
+ memcpy (&session->key, &msg->key, sizeof (struct GNUNET_HashCode));
+ current = (unsigned char *) &msg[1];
+ //preserve the mask, we will need that later on
+ session->mask = GNUNET_malloc (mask_length);
+ memcpy (session->mask, current, mask_length);
+ //the public key
+ current += mask_length;
+
+ //convert the publickey to sexp
+ if (gcry_sexp_new (&session->remote_pubkey, current, pk_length, 1))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not translate remote public key to sexpression!\n"));
+ GNUNET_free (session->mask);
+ GNUNET_free (session);
+ return GNUNET_SYSERR;
+ }
+
+ current += pk_length;
+
+ //check if service queue contains a matching request
+ needed_state = MESSAGE_FROM_RESPONDING_CLIENT_RECEIVED;
+ responder_session = find_matching_session (from_client_tail,
+ &session->key,
+ session->element_count,
+ &needed_state, NULL);
+
+ session->a = GNUNET_malloc (sizeof (gcry_mpi_t) * used_elements);
+
+ if (GNUNET_SERVER_MAX_MESSAGE_SIZE >= sizeof (struct GNUNET_VECTORPRODUCT_service_request)
+ +pk_length
+ + mask_length
+ + used_elements * PAILLIER_ELEMENT_LENGTH)
+ {
+ gcry_error_t ret = 0;
+ session->a = GNUNET_malloc (sizeof (gcry_mpi_t) * used_elements);
+ // Convert each vector element to MPI_value
+ for (i = 0; i < used_elements; i++)
+ {
+ size_t read = 0;
+
+ ret = gcry_mpi_scan (&session->a[i],
+ GCRYMPI_FMT_USG,
+ ¤t[i * PAILLIER_ELEMENT_LENGTH],
+ PAILLIER_ELEMENT_LENGTH,
+ &read);
+ if (ret) // read < GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not translate E[a%d] to MPI!\n%s/%s\n"),
+ i, gcry_strsource (ret), gcry_strerror (ret));
+ goto except;
+ }
+ }
+ GNUNET_CONTAINER_DLL_insert (from_service_head, from_service_tail, session);
+ if (responder_session)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Got session with key %s and a matching element set, processing.\n"), GNUNET_h2s (&session->key));
+ if (GNUNET_OK != compute_service_response (session, responder_session))
+ {
+ //something went wrong, remove it again...
+ GNUNET_CONTAINER_DLL_remove (from_service_head, from_service_tail, session);
+ goto except;
+ }
+ }
+ else
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Got session with key %s without a matching element set, queueing.\n"), GNUNET_h2s (&session->key));
+ return GNUNET_OK;
+ }
+ else
+ {
+ // TODO FEATURE: fallback to fragmentation, in case the message is too long
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Message too large, fragmentation is currently not supported!\n"));
+ goto except;
+ }
+except:
+ for (i = 0; i < used_elements; i++)
+ if (session->a[i])
+ gcry_mpi_release (session->a[i]);
+ gcry_sexp_release (session->remote_pubkey);
+ session->remote_pubkey = NULL;
+ GNUNET_free_non_null (session->a);
+ session->a = NULL;
+ free_session (session);
+ // and notify our client-session that we could not complete the session
+ if (responder_session)
+ {
+ // we just found the responder session in this queue
+ GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, responder_session);
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &prepare_client_end_notification,
+ responder_session);
+ }
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Handle a response we got from another service we wanted to calculate a vectorproduct with.
+ *
+ * @param cls closure (set from GNUNET_MESH_connect)
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx place to store local state associated with the tunnel
+ * @param sender who sent the message
+ * @param message the actual message
+ * @param atsi performance data for the connection
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+handle_service_response (void *cls,
+ struct GNUNET_MESH_Tunnel * tunnel,
+ void **tunnel_ctx,
+ const struct GNUNET_PeerIdentity * sender,
+ const struct GNUNET_MessageHeader * message,
+ const struct GNUNET_ATS_Information * atsi)
+{
+
+ struct ServiceSession * session;
+ struct GNUNET_VECTORPRODUCT_service_response * msg = (struct GNUNET_VECTORPRODUCT_service_response *) message;
+ unsigned char * current;
+ uint16_t count;
+ gcry_mpi_t s = NULL;
+ gcry_mpi_t stick = NULL;
+ size_t read;
+ size_t i;
+ uint16_t used_element_count;
+ size_t msg_size;
+ gcry_mpi_t * kp = NULL;
+ gcry_mpi_t * kq = NULL;
+
+ GNUNET_assert (NULL != message);
+ GNUNET_assert (NULL != sender);
+ GNUNET_assert (NULL != tunnel_ctx);
+ session = (struct ServiceSession *) * tunnel_ctx;
+ GNUNET_assert (NULL != session);
+ count = session->used_element_count;
+ session->product = NULL;
+
+ if (memcmp (&session->peer, sender, sizeof (struct GNUNET_PeerIdentity)))
+ {
+ GNUNET_break_op (0);
+ goto invalid_msg;
+ }
+ //we need at least a peer and one message id to compare
+ if (sizeof (struct GNUNET_VECTORPRODUCT_service_response) > ntohs (msg->header.size))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Too short message received from peer!\n"));
+ goto invalid_msg;
+ }
+ used_element_count = ntohs (msg->used_element_count);
+ msg_size = sizeof (struct GNUNET_VECTORPRODUCT_service_response)
+ + 2 * used_element_count * PAILLIER_ELEMENT_LENGTH
+ + 2 * PAILLIER_ELEMENT_LENGTH;
+ //sanity check: is the message as long as the message_count fields suggests?
+ if ((ntohs (msg->header.size) != msg_size) || (count != used_element_count))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Invalid message received from peer!\n"));
+ goto invalid_msg;
+ }
+ if (GNUNET_SERVER_MAX_MESSAGE_SIZE < msg_size)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Message too large, fragmentation is currently not supported!\n"));
+ goto invalid_msg;
+ }
+
+ //convert s
+ current = (unsigned char *) &msg[1];
+ if (gcry_mpi_scan (&s, GCRYMPI_FMT_USG, current,
+ PAILLIER_ELEMENT_LENGTH, &read))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not translate s to an MPI value!\n"));
+ goto invalid_msg;
+ }
+ current += PAILLIER_ELEMENT_LENGTH;
+ //convert stick
+ if (gcry_mpi_scan (&stick, GCRYMPI_FMT_USG, current,
+ PAILLIER_ELEMENT_LENGTH, &read))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not translate s' to an MPI value!\n"));
+ goto invalid_msg;
+ }
+ current += PAILLIER_ELEMENT_LENGTH;
+
+ kp = GNUNET_malloc (sizeof (gcry_mpi_t) * count);
+ // Convert each kp[] to its MPI_value
+ for (i = 0; i < count; i++)
+ {
+ if (gcry_mpi_scan (&kp[i], GCRYMPI_FMT_USG, current,
+ PAILLIER_ELEMENT_LENGTH, &read))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not translate Kp[%d]to an MPI value!\n"), i);
+ goto invalid_msg;
+ }
+ current += PAILLIER_ELEMENT_LENGTH;
+ }
+
+
+ kq = GNUNET_malloc (sizeof (gcry_mpi_t) * count);
+ // Convert each kq[] to its MPI_value
+ for (i = 0; i < count; i++)
+ {
+ if (gcry_mpi_scan (&kq[i], GCRYMPI_FMT_USG, current,
+ PAILLIER_ELEMENT_LENGTH, &read))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not translate Kq[%d]to an MPI value!\n"), i);
+ goto invalid_msg;
+ }
+ current += PAILLIER_ELEMENT_LENGTH;
+ }
+
+ session->product = compute_scalar_product (session, kp, kq, s, stick);
+
+invalid_msg:
+ if (s)
+ gcry_mpi_release (s);
+ if (stick)
+ gcry_mpi_release (stick);
+ for (i = 0; kp && i < count; i++)
+ if (kp[i]) gcry_mpi_release (kp[i]);
+ for (i = 0; kq && i < count; i++)
+ if (kq[i]) gcry_mpi_release (kq[i]);
+ GNUNET_free_non_null (kp);
+ GNUNET_free_non_null (kq);
+
+ session->state = FINALIZED;
+ // the tunnel has done its job, terminate our connection and the tunnel
+ // the peer will be notified that the tunnel was destroyed via tunnel_destruction_handler
+ GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, session);
+ GNUNET_SCHEDULER_add_now (&destroy_tunnel, session);
+ // send message with product to client
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &prepare_client_response, session);
+ return GNUNET_OK;
+ // if success: terminate the session gracefully, else terminate with error
+}
+
+
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ * @param tc unused
+ */
+static void
+shutdown_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct ServiceSession * curr;
+ struct ServiceSession * next;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Shutting down, initiating cleanup.\n"));
+
+ do_shutdown = GNUNET_YES;
+ // terminate all owned open tunnels.
+ for (curr = from_client_head; NULL != curr; curr = next)
+ {
+ next = curr->next;
+ if (FINALIZED != curr->state)
+ {
+ destroy_tunnel (curr, NULL);
+ curr->state = FINALIZED;
+ }
+ }
+
+ if (my_core)
+ {
+ GNUNET_CORE_disconnect (my_core);
+ my_core = NULL;
+ }
+
+ if (my_mesh)
+ {
+ GNUNET_MESH_disconnect (my_mesh);
+ my_mesh = NULL;
+ }
+}
+
+
+/**
+ * To be called on core init/fail.
+ *
+ * @param cls closure, NULL
+ * @param server handle to the server for this service
+ * @param my_identity the public identity of this peer
+ */
+static void
+core_init (void *cls, struct GNUNET_CORE_Handle *server,
+ const struct GNUNET_PeerIdentity *my_identity)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Core initialized\n"));
+ me = *my_identity;
+}
+
+
+/**
+ * Initialization of the program and message handlers
+ *
+ * @param cls closure
+ * @param server the initialized server
+ * @param c configuration to use
+ */
+static void
+run (void *cls,
+ struct GNUNET_SERVER_Handle *server,
+ const struct GNUNET_CONFIGURATION_Handle *c)
+{
+ static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
+ {&handle_client_request, NULL, GNUNET_MESSAGE_TYPE_VECTORPRODUCT_CLIENT_TO_ALICE, 0},
+ {&handle_client_request, NULL, GNUNET_MESSAGE_TYPE_VECTORPRODUCT_CLIENT_TO_BOB, 0},
+ {NULL, NULL, 0, 0}
+ };
+ static const struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
+ { &handle_service_request, GNUNET_MESSAGE_TYPE_VECTORPRODUCT_ALICE_TO_BOB, 0},
+ { &handle_service_response, GNUNET_MESSAGE_TYPE_VECTORPRODUCT_BOB_TO_ALICE, 0},
+ {NULL, 0, 0}
+ };
+ static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
+ {NULL, 0, 0}
+ };
+ static GNUNET_MESH_ApplicationType mesh_types[] = {
+ GNUNET_APPLICATION_TYPE_VECTORPRODUCT,
+ GNUNET_APPLICATION_TYPE_END
+ };
+
+ //generate private/public key set
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Generating rsa-key.\n"));
+ generate_keyset ();
+ // register server callbacks and disconnect handler
+ GNUNET_SERVER_add_handlers (server, server_handlers);
+ GNUNET_SERVER_disconnect_notify (server,
+ &handle_client_disconnect,
+ NULL);
+
+ my_core = GNUNET_CORE_connect (c, NULL, &core_init, NULL, NULL, NULL,
+ GNUNET_NO, NULL, GNUNET_NO, core_handlers);
+ if (!my_core)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Connect to CORE failed\n"));
+ return;
+ }
+ my_mesh = GNUNET_MESH_connect (c, NULL,
+ &tunnel_incoming_handler,
+ &tunnel_destruction_handler,
+ mesh_handlers, mesh_types);
+ if (!my_mesh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Connect to MESH failed\n"));
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Mesh initialized\n"));
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+ &shutdown_task,
+ NULL);
+}
+
+
+/**
+ * The main function for the vectorproduct service.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ return (GNUNET_OK ==
+ GNUNET_SERVICE_run (argc, argv,
+ "vectorproduct",
+ GNUNET_SERVICE_OPTION_NONE,
+ &run, NULL)) ? 0 : 1;
+}
+
+/* end of gnunet-service-ext.c */
diff --git a/src/scalarproduct/gnunet-vectorproduct.c b/src/scalarproduct/gnunet-vectorproduct.c
@@ -0,0 +1,410 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file vectorproduct/gnunet-vectorproduct.c
+ * @brief vectorproduct client
+ * @author Christian M. Fuchs
+ */
+#define GCRYPT_NO_DEPRECATED
+#include <gcrypt.h>
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_vectorproduct_service.h"
+#include "gnunet_protocols.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "gnunet-vectorproduct",__VA_ARGS__)
+/**
+ * Option -p: destination peer identity for checking message-ids with
+ */
+static char *input_peer_id = NULL;
+
+/**
+ * Option -p: destination peer identity for checking message-ids with
+ */
+static char *input_key = NULL;
+
+/**
+ * Option -e: vector to calculate a vectorproduct with
+ */
+static char *input_elements = NULL;
+
+/**
+ * Option -m: message-ids to calculate a vectorproduct with
+ */
+static char *input_mask = NULL;
+
+/**
+ * the count of the messages sent to the service for processing
+ */
+static unsigned short element_count;
+
+/**
+ * the count of the mask bytes
+ */
+unsigned short mask_length = 0;
+
+/**
+ * the count of the number of mask bytes
+ */
+unsigned short mask_bytes;
+
+/**
+ * the array of converted message IDs to send to our service
+ */
+static int32_t * elements = NULL;
+
+/**
+ * the array of converted message IDs to send to our service
+ */
+static unsigned char * mask = NULL;
+
+/**
+ * information about the peer we are comparing with
+ */
+struct GNUNET_PeerIdentity peer;
+
+/**
+ * information about the peer we are comparing with
+ */
+struct GNUNET_HashCode key;
+
+/**
+ * Pointer to the GNUNET_VECTORPRODUCT_Handle
+ */
+struct GNUNET_VECTORPRODUCT_Handle *handle;
+
+/**
+ * Global return value
+ */
+static int ret;
+
+struct GNUNET_VECTORPRODUCT_TestCls
+{
+ struct GNUNET_VECTORPRODUCT_Handle * h;
+};
+
+struct GNUNET_VECTORPRODUCT_TestCls test_cls;
+
+
+/**
+ * Callback called if we are initiating a new computation session
+ *
+ * @param cls unused
+ * @param status if our job was successfully processed
+ */
+static void
+responder_callback (void *cls,
+ const struct GNUNET_HashCode * key,
+ enum GNUNET_VECTORPRODUCT_ResponseStatus status)
+{
+ ret = -1;
+
+ switch (status)
+ {
+ case GNUNET_VECTORPRODUCT_Status_Success:
+ ret = 0;
+ LOG (GNUNET_ERROR_TYPE_INFO, "Session %s concluded.\n", GNUNET_h2s (key));
+ break;
+ case GNUNET_VECTORPRODUCT_Status_InvalidResponse:
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: invalid response\n", GNUNET_h2s (key));
+ break;
+ case GNUNET_VECTORPRODUCT_Status_Timeout:
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: timeout\n", GNUNET_h2s (key));
+ break;
+ case GNUNET_VECTORPRODUCT_Status_Failure:
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: service failure\n", GNUNET_h2s (key));
+ case GNUNET_VECTORPRODUCT_Status_ServiceDisconnected:
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: service disconnect!!\n", GNUNET_h2s (key));
+ break;
+ default:
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: return code %d\n", GNUNET_h2s (key), (int) status);
+ }
+
+ GNUNET_VECTORPRODUCT_disconnect (handle);
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Callback called if we are initiating a new computation session
+ *
+ * @param cls unused
+ * @param key unused
+ * @param peer unused
+ * @param status if our job was successfully processed
+ * @param size size of the msg returned
+ * @param msg the response we got.
+ * @param type of the message received
+ */
+static void
+requester_callback (void *cls,
+ const struct GNUNET_HashCode * key,
+ const struct GNUNET_PeerIdentity * peer,
+ enum GNUNET_VECTORPRODUCT_ResponseStatus status,
+ const struct GNUNET_VECTORPRODUCT_client_response *msg)
+{
+ uint32_t product_len;
+ ret = -1;
+
+ switch (status)
+ {
+ case GNUNET_VECTORPRODUCT_Status_Success:
+ product_len = ntohl (msg->product_length);
+
+ LOG (GNUNET_ERROR_TYPE_INFO, "Session %s concluded.\n", GNUNET_h2s (key));
+
+ if (0 < product_len && NULL != &msg[1])
+ {
+ gcry_mpi_t result;
+ size_t read = 0;
+
+ if (0 != gcry_mpi_scan (&result, GCRYMPI_FMT_USG, &msg[1], product_len, &read))
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Could not convert to mpi to value!\n");
+ else
+ {
+ unsigned char * buf;
+ gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, result);
+
+ printf ("Successfully computed result for session %s: %s\n", GNUNET_h2s (key), buf);
+ ret = 0;
+ }
+ }
+ else
+ { //currently not used, but if we get more info due to MESH we will need this
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Service-side error in session %s, return code: %d\n", GNUNET_h2s (key), product_len);
+ }
+ break;
+ case GNUNET_VECTORPRODUCT_Status_InvalidResponse:
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: invalid response\n", GNUNET_h2s (key));
+ break;
+ case GNUNET_VECTORPRODUCT_Status_Timeout:
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: timeout\n", GNUNET_h2s (key));
+ break;
+ case GNUNET_VECTORPRODUCT_Status_Failure:
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: service failure\n", GNUNET_h2s (key));
+ case GNUNET_VECTORPRODUCT_Status_ServiceDisconnected:
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Disconnected from service.\n", GNUNET_h2s (key));
+ break;
+ default:
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: return code %d\n", GNUNET_h2s (key), (int) status);
+ }
+ GNUNET_VECTORPRODUCT_disconnect (handle);
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param cfg configuration
+ */
+static void
+run (void *cls,
+ char *const *args,
+ const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ char * begin = input_elements;
+ char * end;
+ int32_t element;
+ int i;
+ ret = -1;
+
+ if (NULL == input_elements)
+ {
+ FPRINTF (stderr, "%s", _ ("You must specify at least one message ID to check!\n"));
+ return;
+ }
+
+ if (NULL == input_key)
+ {
+ FPRINTF (stderr, "%s", _ ("This program needs a session identifier for comparing vectors.\n"));
+ return;
+ }
+
+ if (1 > strnlen (input_key, sizeof (struct GNUNET_HashCode)))
+ {
+ FPRINTF (stderr, _ ("Please give a session key for --input_key!\n"));
+ return;
+ }
+ GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key);
+
+ if (input_peer_id && GNUNET_OK != GNUNET_CRYPTO_hash_from_string (input_peer_id,
+ (struct GNUNET_HashCode *) &peer))
+ {
+ FPRINTF (stderr, _ ("Tried to set initiator mode, as peer ID was given. "
+ "However, `%s' is not a valid peer identifier.\n"),
+ input_peer_id);
+ return;
+ }
+
+ int exit_loop = 0;
+ /* Read input_elements_peer1, and put in elements_peer1 array */
+ do
+ {
+ unsigned int mcount = element_count;
+ //ignore empty rows of ,,,,,,
+ while (*begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+ if (*end == ',')
+ *end = '\0';
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ return;
+ }
+
+ GNUNET_array_append (elements, mcount, element);
+ element_count++;
+
+ begin = ++end;
+ }
+ while (!exit_loop);
+
+ GNUNET_assert (elements != NULL);
+ GNUNET_assert (element_count > 1);
+ mask_length = element_count / 8 + (element_count % 8 ? 1 : 0);
+ mask = GNUNET_malloc ((element_count / 8) + 2);
+
+ /* Read input_mask_peer1 and read in mask_peer1 array */
+ if (NULL != input_mask)
+ {
+ begin = input_mask;
+ unsigned short mask_count = 0;
+ int exit_loop = 0;
+
+ do
+ {
+ //ignore empty rows of ,,,,,,
+ while (* begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ // gnunet_ascii-armor uses base32, thus we can use , as separator!
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+ if (*end == ',')
+ *end = '\0';
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ return;
+ }
+
+ GNUNET_assert (mask_count <= element_count);
+
+ if (element)
+ mask[mask_count / 8] = mask[mask_count / 8] | 1 << (mask_count % 8);
+
+ mask_count++;
+ begin = ++end;
+ }
+ while (!exit_loop);
+ // +1 to see if we would have more data, which would indicate malformed/superficial input
+ GNUNET_assert (mask_count == element_count);
+ }
+ else if (input_peer_id)
+ {
+ for (i = 0; i <= mask_length; i++)
+ mask[i] = UCHAR_MAX; // all 1's
+ }
+
+ handle = GNUNET_VECTORPRODUCT_connect (cfg);
+ if (handle == NULL)
+ {
+ FPRINTF (stderr, _ ("Could not connect to the GNUNET Vector Product Service\n"));
+ return;
+ }
+
+ test_cls.h = handle;
+
+ if (input_peer_id && !GNUNET_VECTORPRODUCT_request (handle,
+ &key,
+ &peer,
+ element_count,
+ mask_length,
+ elements, mask,
+ GNUNET_TIME_UNIT_MINUTES,
+ &requester_callback,
+ (void *) &test_cls))
+ return;
+ if ( !input_peer_id && !GNUNET_VECTORPRODUCT_prepare_response (handle,
+ &key,
+ element_count,
+ elements,
+ GNUNET_TIME_UNIT_MINUTES,
+ &responder_callback,
+ (void *) &test_cls))
+ return;
+
+ ret = 0;
+}
+
+
+/**
+ * The main function to the vectorproduct client.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ {'e', "elements", "\"val1,val2,...,valn\"",
+ gettext_noop ("A comma separated list of elements to compare as vector with our remote peer."),
+ 1, &GNUNET_GETOPT_set_string, &input_elements},
+ {'m', "mask", "\"0,1,...,maskn\"",
+ gettext_noop ("A comma separated mask to select which elements should actually be compared."),
+ 1, &GNUNET_GETOPT_set_string, &input_mask},
+ {'p', "peer", "PEERID",
+ gettext_noop ("[Optional] peer to calculate our vectorproduct with. If this parameter is not given, the service will wait for a remote peer to compute the request."),
+ 1, &GNUNET_GETOPT_set_string, &input_peer_id},
+ {'k', "key", "TRANSACTION_ID",
+ gettext_noop ("Transaction ID shared with peer."),
+ 1, &GNUNET_GETOPT_set_string, &input_key},
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ return (GNUNET_OK ==
+ GNUNET_PROGRAM_run (argc,
+ argv,
+ "gnunet-vectorproduct",
+ gettext_noop ("Calculate the Vectorproduct with a GNUnet peer."),
+ options, &run, NULL)) ? ret : 1;
+}
+
diff --git a/src/scalarproduct/gnunet_vectorproduct.h b/src/scalarproduct/gnunet_vectorproduct.h
@@ -0,0 +1,274 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file include/gnunet_vectorproduct.h
+ * @brief API to the vectorproduct service
+ * @author Christian M. Fuchs
+ */
+
+#ifndef GNUNET_VECTORPRODUCT_H
+#define GNUNET_VECTORPRODUCT_H
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+#define DISABLE_CRYPTO
+
+/**
+ * Length of the key used for encryption
+ */
+#define KEYBITS 2048
+
+/**
+ * When performing our crypto, we may add two encrypted values with each
+ * a maximal length of GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH.
+ * thus we can receive a slightly longer element (+1 byte)
+ */
+#define PAILLIER_ELEMENT_LENGTH (2*KEYBITS/8 +1)
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Service Structure Definitions
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Message type passed from requesting service Alice to responding service Bob
+ * to initiate a request and make bob participate in our protocol
+ */
+struct GNUNET_VECTORPRODUCT_service_request {
+ /**
+ * GNUNET message header
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * how many bytes the mask has
+ */
+ uint16_t mask_length GNUNET_PACKED;
+
+ /**
+ * the length of the publickey contained within this message
+ */
+ uint16_t pk_length GNUNET_PACKED;
+
+ /**
+ * the transaction/session key used to identify a session
+ */
+ struct GNUNET_HashCode key;
+
+ /**
+ * how many elements the vector in payload contains
+ */
+ uint16_t element_count GNUNET_PACKED;
+
+ /**
+ * how many elements are actually included after the mask was applied.
+ */
+ uint16_t used_element_count GNUNET_PACKED;
+
+ /**
+ * followed by mask | public_key | vector[used_element_count]
+ */
+};
+
+/**
+ * Message type passed from responding service Bob to responding service Alice
+ * to complete a request and allow Alice to compute the result
+ */
+struct GNUNET_VECTORPRODUCT_service_response {
+ /**
+ * GNUNET message header
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * how many elements the vector in payload contains
+ */
+ uint16_t element_count GNUNET_PACKED;
+
+ /**
+ * how many elements are actually included after the mask was applied.
+ */
+ uint16_t used_element_count GNUNET_PACKED;
+
+ /**
+ * the transaction/session key used to identify a session
+ */
+ struct GNUNET_HashCode key;
+
+ /**
+ * followed by s | s' | kp[] | kq[]
+ */
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Service Structure Definitions
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * state a session can be in
+ */
+enum SessionState
+{
+ WAITING_FOR_BOBS_CONNECT,
+ MESSAGE_FROM_RESPONDING_CLIENT_RECEIVED,
+ WAITING_FOR_RESPONSE_FROM_SERVICE,
+ REQUEST_FROM_SERVICE_RECEIVED,
+ FINALIZED
+};
+
+/**
+ * role a peer in a session can assume
+ */
+enum PeerRole
+{
+ ALICE,
+ BOB
+};
+/**
+ * A vectorproduct session which tracks:
+ *
+ * a request form the client to our final response.
+ * or
+ * a request from a service to us(service).
+ */
+struct ServiceSession
+{
+ /**
+ * the role this peer has
+ */
+ enum PeerRole role;
+
+ /**
+ * session information is kept in a DLL
+ */
+ struct ServiceSession *next;
+
+ /**
+ * session information is kept in a DLL
+ */
+ struct ServiceSession *prev;
+
+ /**
+ * (hopefully) unique transaction ID
+ */
+ struct GNUNET_HashCode key;
+
+ /**
+ * state of the session
+ */
+ enum SessionState state;
+
+ /**
+ * Alice or Bob's peerID
+ */
+ struct GNUNET_PeerIdentity peer;
+
+ /**
+ * the client this request is related to
+ */
+ struct GNUNET_SERVER_Client * client;
+
+ /**
+ * how many elements we were supplied with from the client
+ */
+ uint16_t element_count;
+
+ /**
+ * how many elements actually are used after applying the mask
+ */
+ uint16_t used_element_count;
+
+ /**
+ * how many bytes the mask is long.
+ * just for convenience so we don't have to re-re-re calculate it each time
+ */
+ uint16_t mask_length;
+
+ /**
+ * all the vector elements we received
+ */
+ int32_t * vector;
+
+ /**
+ * mask of which elements to check
+ */
+ unsigned char * mask;
+
+ /**
+ * Public key of the remote service, only used by bob
+ */
+ gcry_sexp_t remote_pubkey;
+
+ /**
+ * E(ai)(Bob) or ai(Alice) after applying the mask
+ */
+ gcry_mpi_t * a;
+
+ /**
+ * The computed scalar
+ */
+ gcry_mpi_t product;
+
+ /**
+ * My transmit handle for the current message to a alice/bob
+ */
+ struct GNUNET_MESH_TransmitHandle * service_transmit_handle;
+
+ /**
+ * My transmit handle for the current message to the client
+ */
+ struct GNUNET_SERVER_TransmitHandle * client_transmit_handle;
+
+ /**
+ * tunnel-handle associated with our mesh handle
+ */
+ struct GNUNET_MESH_Tunnel * tunnel;
+
+};
+
+/**
+ * We need to do a minimum of bookkeeping to maintain track of our transmit handles.
+ * each msg is associated with a session and handle. using this information we can determine which msg was sent.
+ */
+struct MessageObject
+{
+ /**
+ * The handle used to transmit with this request
+ */
+ void ** transmit_handle;
+
+ /**
+ * The message to send
+ */
+ struct GNUNET_MessageHeader * msg;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GNUNET_VECTORPRODUCT_H */
+
diff --git a/src/scalarproduct/test_vectorproduct_api.c b/src/scalarproduct/test_vectorproduct_api.c
@@ -0,0 +1,865 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * Aim of test_vectorproduct_api : This test creates two peers. Peer1 is the
+ * responder peer, Bob and Peer2 is the initiator peer, Alice. Both peers
+ * connect to VectorProduct Service, and use the API to issue requests to
+ * service. Test passes, when the expected scalar product is received from the
+ * service.
+ */
+
+/**
+ * @file vectorproduct/testbed_vectorproduct_api.c
+ * @brief VectorProduct API testing between 4 peers using testing API
+ * @author Gaurav Kukreja
+ * @author Christian Fuchs
+ */
+
+#include <string.h>
+
+#include <inttypes.h>
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testbed_service.h"
+#include "gnunet_common.h"
+#include "gnunet_vectorproduct_service.h"
+#include "gnunet_protocols.h"
+
+#define NUM_PEERS 2
+
+#define LOG(kind,...) GNUNET_log_from (kind, "test-vectorproduct-api",__VA_ARGS__)
+
+/**
+ * Structure for holding peer's sockets and IO Handles
+ */
+struct PeerData
+{
+ /**
+ * Handle to testbed peer
+ */
+ struct GNUNET_TESTBED_Peer *peer;
+
+ /**
+ * The service connect operation to stream
+ */
+ struct GNUNET_TESTBED_Operation *op;
+
+ /**
+ * Our Peer id
+ */
+ struct GNUNET_PeerIdentity our_id;
+
+ /**
+ * Pointer to Vector Product Handle
+ */
+ struct GNUNET_VECTORPRODUCT_Handle *vh;
+};
+
+/**
+ * Different states in test setup
+ */
+enum SetupState
+{
+ /**
+ * Get the identity of peer 1
+ */
+ PEER1_GET_IDENTITY,
+
+ /**
+ * Get the identity of peer 2
+ */
+ PEER2_GET_IDENTITY,
+
+ /**
+ * Connect to stream service of peer 1
+ */
+ PEER1_VECTORPRODUCT_CONNECT,
+
+ /**
+ * Connect to stream service of peer 2
+ */
+ PEER2_VECTORPRODUCT_CONNECT
+
+};
+
+/******************************************************************************
+ *** Global Variables *****************************
+ ******************************************************************************/
+
+/**
+ * Maximum allowed message-ids we can check in one go (with one GNUNET_message)
+ */
+static unsigned int max_mids;
+
+/**
+ * Session Key used by both the test peers
+ */
+char input_key[103] = "helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhe";
+
+/**
+ * Input elements for peer1
+ */
+char input_elements_peer1[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11";
+//char input_elements_peer1[] = "11,11,11";
+
+/**
+ * Input Mask for peer 1
+ */
+char input_mask_peer1[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1";
+//char input_mask_peer1[] = "1,1,1";
+
+/**
+ * the array of converted message IDs to send to our service
+ */
+static int32_t * elements_peer1 = NULL;
+
+/**
+ * Number of elements
+ */
+uint16_t element_count_peer1 = 0;
+
+/**
+ * Input elements for peer2
+ */
+char input_elements_peer2[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11";
+//char input_elements_peer2[] = "11,11,11";
+
+/**
+ * Input Mask for peer 2
+ */
+char input_mask_peer2[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1";
+//char input_mask_peer2[] = "1,1,1";
+
+/**
+ * the array of converted message IDs to send to our service
+ */
+static int32_t * elements_peer2 = NULL;
+
+/**
+ * the array of converted message IDs to send to our service
+ */
+static unsigned char * mask_peer2 = NULL;
+
+/**
+ * Number of elements
+ */
+uint16_t element_count_peer2 = 0;
+
+/**
+ * Data context for peer 1
+ */
+static struct PeerData peer1;
+
+/**
+ * Data context for peer 2
+ */
+static struct PeerData peer2;
+
+/**
+ * Various states during test setup
+ */
+static enum SetupState setup_state;
+
+/**
+ * Testbed operation handle
+ */
+static struct GNUNET_TESTBED_Operation *op;
+
+static int ok;
+
+static int responder_ok;
+
+static int requester_ok;
+
+static GNUNET_SCHEDULER_TaskIdentifier abort_task;
+/******************************************************************************
+ *** Static Functions *****************************
+ ******************************************************************************/
+
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Close sockets and stop testing deamons nicely
+ */
+static void
+do_close (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+
+ if (peer1.op != NULL)
+ do_shutdown (&peer1, NULL);
+
+ if (peer2.op != NULL)
+ do_shutdown (&peer2, NULL);
+
+ if (GNUNET_SCHEDULER_NO_TASK != abort_task)
+ GNUNET_SCHEDULER_cancel (abort_task);
+
+ GNUNET_SCHEDULER_shutdown (); /* For shutting down testbed */
+}
+
+/**
+ * Shutdown a peer
+ *
+ * @param cls pointer to "struct PeerData" of the peer to be disconnected
+ * @param tc Task Context
+ */
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ static int shutdown;
+ shutdown++;
+ struct PeerData* peer = (struct PeerData*) cls;
+
+ if (peer == &peer1)
+ LOG (GNUNET_ERROR_TYPE_INFO, "Disconnecting Peer1\n\n");
+ else if (peer == &peer2)
+ LOG (GNUNET_ERROR_TYPE_INFO, "Disconnecting Peer2\n\n");
+
+ // peer->op contains handle to the TESTBED_connect_service operation
+ // calling operation done, leads to call to vectorproduct_da
+ if (peer->op != NULL)
+ {
+ GNUNET_TESTBED_operation_done (peer->op);
+ peer->op = NULL;
+ }
+
+ if (shutdown >= 2)
+ GNUNET_SCHEDULER_add_now (&do_close, NULL);
+}
+
+
+/**
+ * Something went wrong and timed out. Kill everything and set error flag
+ */
+static void
+do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: ABORT due to Timeout\n");
+ ok = GNUNET_SYSERR;
+ abort_task = 0;
+ do_close (cls, tc);
+}
+
+
+/**
+ * Controller event callback
+ *
+ * @param cls NULL
+ * @param event the controller event
+ */
+static void
+controller_event_cb (void *cls,
+ const struct GNUNET_TESTBED_EventInformation *event)
+{
+ GNUNET_assert (event->type == GNUNET_TESTBED_ET_OPERATION_FINISHED);
+
+ switch (setup_state)
+ {
+ case PEER1_VECTORPRODUCT_CONNECT:
+ case PEER2_VECTORPRODUCT_CONNECT:
+ GNUNET_assert (NULL == event->details.operation_finished.emsg);
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
+static void
+responder_callback (void *cls,
+ const struct GNUNET_HashCode * key,
+ enum GNUNET_VECTORPRODUCT_ResponseStatus status)
+{
+
+ if (status == GNUNET_VECTORPRODUCT_Status_Failure)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received status failure\n");
+ responder_ok = -1;
+ }
+ else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received status invalid response\n");
+ responder_ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Timeout == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received timeout occured\n");
+ responder_ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received service disconnected!!\n");
+ responder_ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Success == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO, "Responder Client expected response received!\n");
+ responder_ok = 1;
+ }
+ else
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client status = %d!\n", (int) status);
+ responder_ok = -1;
+ }
+ // TODO : Responder Session Complete. Shutdown Test Cleanly!!!
+ //do_shutdown(&peer1, NULL);
+ GNUNET_SCHEDULER_add_now (&do_shutdown, &peer1);
+ return;
+}
+
+
+static void
+requester_callback (void *cls,
+ const struct GNUNET_HashCode * key,
+ const struct GNUNET_PeerIdentity * peer,
+ enum GNUNET_VECTORPRODUCT_ResponseStatus status,
+ const struct GNUNET_VECTORPRODUCT_client_response *msg)
+{
+ uint32_t product_len;
+
+ if (status == GNUNET_VECTORPRODUCT_Status_Failure)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client received status failure\n");
+ requester_ok = -1;
+ }
+ else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client received status invalid response\n");
+ requester_ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Timeout == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client timeout occured\n");
+ requester_ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client service disconnected!!\n");
+ requester_ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Success != status)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client Status = %d\n", (int) status);
+ requester_ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Success == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO, "Requester Client expected response received!\n");
+ product_len = ntohl(msg->product_length);
+
+ if (0 < product_len)
+ {
+ gcry_mpi_t result;
+ gcry_error_t ret = 0;
+ size_t read = 0;
+ ret = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, &msg[1], product_len, &read);
+
+ if (0 != ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not convert to mpi to value!\n");
+ ok = -1;
+ }
+ else
+ {
+ uint16_t i = 0;
+
+ // calculate expected product
+ gcry_mpi_t expected_result;
+ gcry_mpi_t v1;
+ gcry_mpi_t v2;
+ gcry_mpi_t v1_v2_prod;
+
+ expected_result = gcry_mpi_new (0);
+
+ for (i = 0; i < element_count_peer1; i++)
+ {
+ uint32_t value;
+ v1_v2_prod = gcry_mpi_new (0);
+
+ // long to gcry_mpi_t
+ value = elements_peer1[i] >= 0 ? elements_peer1[i] : -elements_peer1[i];
+ if (elements_peer1[i] < 0)
+ {
+ v1 = gcry_mpi_new (0);
+ gcry_mpi_sub_ui (v1, v1, value);
+ }
+ else
+ v1 = gcry_mpi_set_ui (NULL, value);
+
+ // long to gcry_mpi_t
+ value = elements_peer2[i] >= 0 ? elements_peer2[i] : -elements_peer2[i];
+ if (elements_peer2[i] < 0)
+ {
+ v2 = gcry_mpi_new (0);
+ gcry_mpi_sub_ui (v2, v2, value);
+ }
+ else
+ v2 = gcry_mpi_set_ui (NULL, value);
+
+ gcry_mpi_mul (v1_v2_prod, v1, v2);
+ gcry_mpi_add (expected_result, expected_result, v1_v2_prod);
+
+ gcry_mpi_release (v1);
+ gcry_mpi_release (v2);
+ gcry_mpi_release (v1_v2_prod);
+
+ }
+
+ // compare the result
+ if (!gcry_mpi_cmp (expected_result, result))
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO, "Scalar Product matches expected Result!!\n");
+ requester_ok = 1;
+ }
+ else
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Scalar Product DOES NOT match expected Result!!\n");
+ requester_ok = -1;
+ }
+ gcry_mpi_release (result);
+ gcry_mpi_release (expected_result);
+ }
+ }
+ else
+ { //currently not used, but if we get more info due to MESH we will need this
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Error during computation of vector product, return code: %d\n", product_len);
+ requester_ok = -1;
+ }
+ }
+
+ //do_shutdown(&peer2, NULL);
+ GNUNET_SCHEDULER_add_now (&do_shutdown, &peer2);
+ return;
+}
+
+/**
+ * Prepare the message to be sent by peer2 to its vectorproduct service, to
+ * initiate a request to peer1.
+ */
+static struct GNUNET_VECTORPRODUCT_QueueEntry *
+requester_request ()
+{
+ unsigned int i;
+ int exit_loop;
+ uint16_t mask_length = 0;
+ char * begin = input_elements_peer2;
+ char * end;
+ int32_t element;
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe;
+ struct GNUNET_HashCode key;
+
+ GNUNET_assert (peer2.vh != NULL);
+
+ GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key);
+
+ /* Read input_elements_peer2, and put in elements_peer2 array */
+ exit_loop = 0;
+ do
+ {
+ unsigned int mcount = element_count_peer2;
+ //ignore empty rows of ,,,,,,
+ while (*begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ ok = -1;
+ return NULL;
+ }
+
+ GNUNET_array_append (elements_peer2, mcount, element);
+ element_count_peer2++;
+
+ begin = ++end;
+ }
+ while (!exit_loop && element_count_peer2 < max_mids);
+ GNUNET_assert (elements_peer2 != NULL);
+ GNUNET_assert (element_count_peer2 >= 1);
+
+ /* Read input_mask_peer2 and read in mask_peer2 array */
+ mask_length = element_count_peer2 / 8 + (element_count_peer2 % 8 ? 1 : 0);
+ mask_peer2 = GNUNET_malloc ((element_count_peer2 / 8) + 2);
+ GNUNET_assert (NULL != mask_peer2);
+ if (NULL != input_mask_peer2)
+ {
+ begin = input_mask_peer2;
+ unsigned short mask_count = 0;
+ int exit_loop = 0;
+
+ do
+ {
+ //ignore empty rows of ,,,,,,
+ while (* begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ // gnunet_ascii-armor uses base32, thus we can use , as separator!
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ ok = -1;
+ return NULL;
+ }
+
+ GNUNET_assert (mask_count <= element_count_peer2);
+
+ if (element)
+ mask_peer2[mask_count / 8] = mask_peer2[mask_count / 8] | 1 << (mask_count % 8);
+
+ mask_count++;
+ begin = ++end;
+ }
+ while (!exit_loop);
+ // +1 to see if we would have more data, which would indicate malformed/superficial input
+ GNUNET_assert (mask_count == element_count_peer2);
+ }
+ else
+ {
+ for (i = 0; i <= mask_length; i++)
+ mask_peer2[i] = UCHAR_MAX; // all 1's
+ }
+
+ qe = GNUNET_VECTORPRODUCT_request (peer2.vh,
+ &key,
+ &peer1.our_id,
+ element_count_peer2,
+ mask_length,
+ elements_peer2, mask_peer2,
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
+ &requester_callback,
+ NULL);
+
+ if (qe == NULL)
+ {
+ LOG(GNUNET_ERROR_TYPE_ERROR, "Could not send request to vectorproduct service! Exitting!");
+ ok = -1;
+ return NULL;
+ }
+
+ return qe;
+}
+
+
+/**
+ * Function prepares the message to be sent by peer1 to its vectorproduct service
+ * to prepare response, and wait for a request session to be initiated by peer1
+ */
+static struct GNUNET_VECTORPRODUCT_QueueEntry *
+responder_prepare_response ()
+{
+ GNUNET_assert (peer1.vh != NULL);
+
+ char * begin = input_elements_peer1;
+ char * end;
+ int32_t element;
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe;
+ struct GNUNET_HashCode key;
+
+ GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key);
+
+ /* Read input_elements_peer1, and put in elements_peer1 array */
+ int exit_loop = 0;
+ do
+ {
+ unsigned int mcount = element_count_peer1;
+ //ignore empty rows of ,,,,,,
+ while (*begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ ok = -1;
+ return NULL;
+ }
+
+ GNUNET_array_append (elements_peer1, mcount, element);
+ element_count_peer1++;
+
+ begin = ++end;
+ }
+ while (!exit_loop && element_count_peer1 < max_mids);
+ GNUNET_assert (elements_peer1 != NULL);
+ GNUNET_assert (element_count_peer1 >= 1);
+
+ qe = GNUNET_VECTORPRODUCT_prepare_response (peer1.vh,
+ &key,
+ element_count_peer1,
+ elements_peer1,
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
+ &responder_callback,
+ NULL);
+
+ if (qe == NULL)
+ {
+ LOG(GNUNET_ERROR_TYPE_ERROR, "Could not send request to vectorproduct service! Exitting!");
+ ok = -1;
+ return NULL;
+ }
+
+ return qe;
+}
+
+
+/**
+ * Scheduler task to initiate requester client
+ *
+ * @param cls void* to struct PeerData
+ * @param tc Task Context
+ */
+static void
+request_task(void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext
+ * tc)
+{
+ requester_request();
+ return;
+}
+
+/**
+ * Scheduler task to initiate responder client
+ *
+ * @param cls void* to struct PeerData
+ * @param tc Task Context
+ */
+static void
+prepare_response_task(void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext
+ * tc)
+{
+ responder_prepare_response();
+ return;
+}
+
+
+/**
+ * Adapter function called to destroy a connection to
+ * a service. This function is called when GNUNET_TESTBED_operation_done is
+ * called for peer->op, which holds the handle for GNUNET_TESTBED_service_connect
+ * operation.
+ *
+ * @param cls closure
+ * @param op_result service handle returned from the connect adapter
+ */
+static void
+vectorproduct_da (void *cls, void *op_result)
+{
+ struct PeerData* peer = (struct PeerData*) cls;
+
+ GNUNET_VECTORPRODUCT_disconnect (peer->vh);
+ return;
+}
+
+
+/**
+ * Adapter function called to establish a connection to
+ * a service. This function is called to by GNUNET_TESTBED_service_connect.
+ *
+ * @param cls closure
+ * @param cfg configuration of the peer to connect to; will be available until
+ * GNUNET_TESTBED_operation_done() is called on the operation returned
+ * from GNUNET_TESTBED_service_connect()
+ * @return service handle to return in 'op_result', NULL on error
+ */
+static void *
+vectorproduct_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct PeerData *p = cls;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %u (`%s') started\n", (&peer1 == p) ? 1 : 2,
+ GNUNET_i2s (&p->our_id));
+
+ switch (setup_state)
+ {
+ case PEER1_VECTORPRODUCT_CONNECT:
+ /* Connect peer 2 to vectorproduct service */
+ {
+ peer2.op = GNUNET_TESTBED_service_connect (&peer2, peer2.peer, "vectorproduct",
+ NULL, NULL, vectorproduct_ca,
+ vectorproduct_da, &peer2);
+ setup_state = PEER2_VECTORPRODUCT_CONNECT;
+ }
+
+ /* Actually connect peer 1 to vectorproduct service */
+ peer1.vh = GNUNET_VECTORPRODUCT_connect (cfg);
+ return peer1.vh;
+
+ case PEER2_VECTORPRODUCT_CONNECT:
+ /* Actually connect peer 2 to vectorproduct service */
+ peer2.vh = GNUNET_VECTORPRODUCT_connect (cfg);
+
+ /* Schedule tasks to initiate request from peer2 and prepare_response from peer1 */
+ if(peer1.vh != NULL && peer2.vh != NULL)
+ {
+ GNUNET_SCHEDULER_add_now(&prepare_response_task, &peer1);
+ GNUNET_SCHEDULER_add_now(&request_task, &peer2);
+ }
+
+ return peer2.vh;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
+/**
+ * Callback to be called when the requested peer information is available
+ *
+ * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
+ * @param op the operation this callback corresponds to
+ * @param pinfo the result; will be NULL if the operation has failed
+ * @param emsg error message if the operation has failed; will be NULL if the
+ * operation is successfull
+ */
+static void
+peerinfo_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op_,
+ const struct GNUNET_TESTBED_PeerInformation *pinfo,
+ const char *emsg)
+{
+ GNUNET_assert (NULL == emsg);
+ GNUNET_assert (op == op_);
+ switch (setup_state)
+ {
+ case PEER1_GET_IDENTITY:
+ {
+ memcpy (&peer1.our_id, pinfo->result.id,
+ sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_TESTBED_operation_done (op);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 1 id: %s\n", GNUNET_i2s_full
+ (&peer1.our_id));
+
+ /* Request for peer id of peer 2*/
+ op = GNUNET_TESTBED_peer_get_information (peer2.peer,
+ GNUNET_TESTBED_PIT_IDENTITY,
+ &peerinfo_cb, NULL);
+ setup_state = PEER2_GET_IDENTITY;
+ }
+ break;
+ case PEER2_GET_IDENTITY:
+ {
+ memcpy (&peer2.our_id, pinfo->result.id,
+ sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_TESTBED_operation_done (op);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 2 id: %s\n", GNUNET_i2s_full
+ (&peer2.our_id));
+
+ /* Connect peer 1 to vectorproduct service */
+ peer1.op = GNUNET_TESTBED_service_connect (&peer1, peer1.peer, "vectorproduct",
+ NULL, NULL, vectorproduct_ca,
+ vectorproduct_da, &peer1);
+ setup_state = PEER1_VECTORPRODUCT_CONNECT;
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
+/**
+ * Signature of a main function for a testcase.
+ *
+ * @param cls closure
+ * @param num_peers number of peers in 'peers'
+ * @param peers handle to peers run in the testbed
+ */
+static void
+test_master (void *cls, unsigned int num_peers,
+ struct GNUNET_TESTBED_Peer **peers)
+{
+ GNUNET_assert (NULL != peers);
+ GNUNET_assert (NULL != peers[0]);
+ GNUNET_assert (NULL != peers[1]);
+ peer1.peer = peers[0];
+ peer2.peer = peers[1];
+ /* Get the peer identity and configuration of peer 1 */
+ op = GNUNET_TESTBED_peer_get_information (peer1.peer,
+ GNUNET_TESTBED_PIT_IDENTITY,
+ &peerinfo_cb, NULL);
+ setup_state = PEER1_GET_IDENTITY;
+
+ /* Abort task for stopping test on timeout */
+ abort_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort,
+ NULL);
+}
+
+
+/**
+ * Main function
+ */
+int
+main (int argc, char **argv)
+{
+ uint64_t event_mask;
+
+ ok = GNUNET_NO;
+ event_mask = 0;
+ event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
+ max_mids = (GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_MessageHeader))
+ / sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1;
+
+ (void) GNUNET_TESTBED_test_run ("test_vectorproduct_api",
+ "test_vectorproduct_api_data.conf",
+ NUM_PEERS, event_mask, &controller_event_cb,
+ NULL,
+ &test_master, NULL);
+
+ if (GNUNET_SYSERR == ok)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Test failing due to some error before calling API for request or prepare_response\n");
+ return 1;
+ }
+ else if (GNUNET_SYSERR == responder_ok)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Test failing due to some error in responding_client\n");
+ return 1;
+ }
+ else if (GNUNET_SYSERR == requester_ok)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Test failing due to some error in requesting client\n");
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
diff --git a/src/scalarproduct/test_vectorproduct_api_4peers.c b/src/scalarproduct/test_vectorproduct_api_4peers.c
@@ -0,0 +1,1084 @@
+
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * AIM OF THIS TEST
+ *
+ * The aim for the extended test is to verify the queuing functionality in the
+ * service and the API. The API queues requests received from the clients. The
+ * service queues requests that are received from other services.
+ *
+ * To test this, we create 4 peers. peer1 and peer2 are designated responders,
+ * and peer3 and peer4 are designated as requesters. Each peer calls API for the
+ * vectorproduct service accordingly.
+ *
+ * * peer1 tells the service to prepare response for requests with keys
+ * input_key_p1_p3(shared key b/w peer1 and peer3) and input_key_p1_p4.
+ * Similarly peer2 tells service to prepare response for requests with keys
+ * input_key_p2_p3, and input_key_p2_p4.
+ * * Simultaneously, peer3 tells its service to send a request to peer1 with key
+ * input_key_p1_p3, and a request to peer2 with key input_key_p2_p3. Similarly,
+ * peer 4 sends requests with appropriate keys.
+ *
+ * Each peer sends 2 requests to its service, which tests the queuing in API.
+ * Each service receives 2 requests from other service, which tests the queuing
+ * functionality in the service.
+ */
+
+
+/**
+ * @file vectorproduct/test_vectorproduct_api_4peers.c
+ * @brief Vectorproduct API testing between 4 peers using testing API
+ * @author Gaurav Kukreja
+ * @author Christian Fuchs
+ */
+
+#include <string.h>
+
+#include <inttypes.h>
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testbed_service.h"
+#include "gnunet_common.h"
+#include "gnunet_vectorproduct_service.h"
+#include "gnunet_protocols.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "test-vectorproduct-api-4peers",__VA_ARGS__)
+
+#define NUM_PEERS 4
+
+/**
+ * Structure for holding peer's sockets and IO Handles
+ */
+struct PeerData
+{
+ /**
+ * Handle to testbed peer
+ */
+ struct GNUNET_TESTBED_Peer *peer;
+
+ /**
+ * The service connect operation to stream
+ */
+ struct GNUNET_TESTBED_Operation *op;
+
+ /**
+ * Our Peer id
+ */
+ struct GNUNET_PeerIdentity our_id;
+
+ /**
+ * Pointer to Vector Product Handle
+ */
+ struct GNUNET_VECTORPRODUCT_Handle *vh;
+
+ /**
+ * Input elements for peer
+ */
+ char * input_elements;
+
+ /**
+ * Input Mask for peer
+ */
+ char * input_mask;
+
+ /**
+ * 2 Input keys for peer for 2 sessions of each peer
+ */
+ char * input_keys[2];
+
+ /**
+ * Number of requests(or prepare_response) sent by the peer
+ */
+ int request_num;
+
+ /**
+ * Number of callbacks received by the peer
+ */
+ int callback_num;
+
+ /**
+ * PeerData of the peers, this peer will talk to
+ */
+ struct PeerData * peers[2];
+
+
+};
+
+/**
+ * Different states in test setup
+ */
+enum SetupState
+{
+ /**
+ * Get the identity of peer 1
+ */
+ PEER1_GET_IDENTITY,
+
+ /**
+ * Get the identity of peer 2
+ */
+ PEER2_GET_IDENTITY,
+
+ /**
+ * Get the identity of peer 3
+ */
+ PEER3_GET_IDENTITY,
+
+ /**
+ * Get the identity of peer 4
+ */
+ PEER4_GET_IDENTITY,
+
+ /**
+ * Connect to stream service of peer 1
+ */
+ PEER1_VECTORPRODUCT_CONNECT,
+
+ /**
+ * Connect to stream service of peer 2
+ */
+ PEER2_VECTORPRODUCT_CONNECT,
+
+ /**
+ * Connect to stream service of peer 3
+ */
+ PEER3_VECTORPRODUCT_CONNECT,
+
+ /**
+ * Connect to stream service of peer 4
+ */
+ PEER4_VECTORPRODUCT_CONNECT
+
+};
+
+/******************************************************************************
+ *** Global Variables *****************************
+ ******************************************************************************/
+
+/**
+ * Maximum allowed message-ids we can check in one go (with one GNUNET_message)
+ */
+static unsigned int max_mids;
+
+/**
+ * Session Key used by both the test peers
+ */
+char input_key_p1_p3[103] = "111111111111111111111111111111111111111111111111113333333333333333333333333333333333333333333333333333";
+
+/**
+ * Session Key used by both the test peers
+ */
+char input_key_p1_p4[103] = "111111111111111111111111111111111111111111111111114444444444444444444444444444444444444444444444444444";
+
+/**
+ * Session Key used by both the test peers
+ */
+char input_key_p2_p3[103] = "222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333";
+
+/**
+ * Session Key used by both the test peers
+ */
+char input_key_p2_p4[103] = "222222222222222222222222222222222222222222222222224444444444444444444444444444444444444444444444444444";
+
+/**
+ * Input elements for peer1
+ */
+//char input_elements_peer1[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11";
+char input_elements_peer1[] = "11,11,11";
+
+/**
+ * Input Mask for peer 1
+ */
+//char input_mask_peer1[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1";
+char input_mask_peer1[] = "1,1,1";
+
+/**
+ * Input elements for peer2
+ */
+//char input_elements_peer2[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11";
+char input_elements_peer2[] = "11,11,11";
+/**
+ * Input Mask for peer 2
+ */
+//char input_mask_peer2[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1";
+char input_mask_peer2[] = "1,1,1";
+
+/**
+ * Input elements for peer3
+ */
+//char input_elements_peer3[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11";
+char input_elements_peer3[] = "11,11,11";
+
+/**
+ * Input Mask for peer 3
+ */
+//char input_mask_peer3[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1";
+char input_mask_peer3[] = "1,1,1";
+
+/**
+ * Input elements for peer4
+ */
+//char input_elements_peer4[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11";
+char input_elements_peer4[] = "11,11,11";
+/**
+ * Input Mask for peer 4
+ */
+//char input_mask_peer4[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1";
+char input_mask_peer4[] = "1,1,1";
+
+
+/**
+ * Data context for peer 1
+ */
+static struct PeerData peer1;
+
+/**
+ * Data context for peer 2
+ */
+static struct PeerData peer2;
+
+/**
+ * Data context for peer 3
+ */
+static struct PeerData peer3;
+
+/**
+ * Data context for peer 4
+ */
+static struct PeerData peer4;
+
+/**
+ * Various states during test setup
+ */
+static enum SetupState setup_state;
+
+/**
+ * Testbed operation handle
+ */
+static struct GNUNET_TESTBED_Operation *op;
+
+/**
+ * Return value for the test
+ */
+static int ok;
+
+/**
+ * Abort Task for timeout
+ */
+static GNUNET_SCHEDULER_TaskIdentifier abort_task;
+/******************************************************************************
+ *** Static Functions *****************************
+ ******************************************************************************/
+
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Close sockets and stop testing deamons nicely
+ */
+static void
+do_close (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ if (peer1.op != NULL)
+ GNUNET_SCHEDULER_add_now (&do_shutdown, &peer1);
+
+ if (peer2.op != NULL)
+ GNUNET_SCHEDULER_add_now (&do_shutdown, &peer2);
+
+ if (peer3.op != NULL)
+ GNUNET_SCHEDULER_add_now (&do_shutdown, &peer3);
+
+ if (peer4.op != NULL)
+ GNUNET_SCHEDULER_add_now (&do_shutdown, &peer4);
+
+ if (GNUNET_SCHEDULER_NO_TASK != abort_task)
+ GNUNET_SCHEDULER_cancel (abort_task);
+
+ GNUNET_SCHEDULER_shutdown (); /* For shutting down testbed */
+}
+
+
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ static int shutdown;
+ shutdown++;
+ struct PeerData* peer = (struct PeerData*) cls;
+
+ if (peer == &peer1)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down Peer 1!!! \n");
+ else if (peer == &peer2)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down Peer 2!!! \n");
+ else if (peer == &peer3)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down Peer 3!!! \n");
+ else if (peer == &peer4)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down Peer 4!!! \n");
+
+ // peer->op contains handle to the TESTBED_connect_service operation
+ // calling operation done, leads to call to vectorproduct_da
+ GNUNET_TESTBED_operation_done (peer->op);
+ peer->op = NULL;
+
+ if (shutdown == 4)
+ GNUNET_SCHEDULER_add_now (&do_close, NULL);
+}
+
+
+/**
+ * Something went wrong and timed out. Kill everything and set error flag
+ */
+static void
+do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: ABORT due to Timeout\n");
+ ok = GNUNET_SYSERR;
+ abort_task = 0;
+ do_close (cls, tc);
+}
+
+
+/**
+ * Controller event callback
+ *
+ * @param cls NULL
+ * @param event the controller event
+ */
+static void
+controller_event_cb (void *cls,
+ const struct GNUNET_TESTBED_EventInformation *event)
+{
+ switch (event->type)
+ {
+ case GNUNET_TESTBED_ET_OPERATION_FINISHED:
+ switch (setup_state)
+ {
+ case PEER1_VECTORPRODUCT_CONNECT:
+ case PEER2_VECTORPRODUCT_CONNECT:
+ GNUNET_assert (NULL == event->details.operation_finished.emsg);
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
+static void
+responder_callback (void *cls,
+ const struct GNUNET_HashCode * key,
+ enum GNUNET_VECTORPRODUCT_ResponseStatus status)
+{
+ struct PeerData * peer = cls;
+
+ peer->callback_num++;
+
+ if (peer == &peer1)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Peer1 received callback!!!\n");
+ }
+ else if (peer == &peer2)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Peer2 received callback!!!\n");
+ }
+ else
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Requester callback received, but peer is neither peer1 nor peer2!!!\n");
+
+
+ if (status == GNUNET_VECTORPRODUCT_Status_Failure)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Responder Client received status failure\n");
+ ok = -1;
+ }
+ else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Responder Client received status invalid response\n");
+ ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Timeout == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Responder Client received timeout occured\n");
+ ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Responder Client received service disconnected!!\n");
+ ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Success == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Responder Client expected response received!\n");
+ ok = 1;
+ }
+ else
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Responder Client status = %d!\n", (int) status);
+ ok = -1;
+ }
+
+ // TODO : Responder Session Complete. Shutdown Test Cleanly!!!
+ if (peer->callback_num == 2)
+ GNUNET_SCHEDULER_add_now (&do_shutdown, peer);
+}
+
+
+static void
+requester_callback (void *cls,
+ const struct GNUNET_HashCode * key,
+ const struct GNUNET_PeerIdentity * peer,
+ enum GNUNET_VECTORPRODUCT_ResponseStatus status,
+ const struct GNUNET_VECTORPRODUCT_client_response *msg)
+{
+ struct PeerData * peer_ = cls;
+ uint32_t product_len;
+
+ peer_->callback_num++;
+
+ if (peer_ == &peer3)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Peer3 received callback!!!\n");
+ }
+ else if (peer_ == &peer4)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Peer4 received callback!!!\n");
+ }
+ else
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Requester callback received, but peer is neither peer3 nor peer4!!!\n");
+
+
+ if (status == GNUNET_VECTORPRODUCT_Status_Failure)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Requester Client received status failure\n");
+ ok = -1;
+ }
+ else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Requester Client received status invalid response\n");
+ ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Timeout == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Requester Client timeout occured\n");
+ ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Requester Client service disconnected!!\n");
+ ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Success != status)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Requester Client Status = %d\n", (int) status);
+ ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Success == status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Requester client received status successful!\n");
+ product_len = ntohl (msg->product_length);
+
+ if (0 < product_len)
+ {
+ gcry_mpi_t result;
+ gcry_error_t ret = 0;
+ size_t read = 0;
+
+ ret = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, (void*) &(msg[1]), product_len, &read);
+
+ if (0 != ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not convert to mpi to value!\n");
+ }
+ else
+ {
+ gcry_mpi_release (result);
+ }
+ ok = 1;
+ }
+ else
+ {
+ //currently not used, but if we get more info due to MESH we will need this
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Error during computation of vector product, return code: %d\n", product_len);
+ ok = -1;
+ }
+ }
+
+ if (peer_->callback_num == 2)
+ GNUNET_SCHEDULER_add_now (&do_shutdown, peer_);
+}
+
+
+static struct GNUNET_VECTORPRODUCT_QueueEntry *
+requester_request (char * input_elements,
+ char * input_mask,
+ char * input_key,
+ struct PeerData * peer,
+ struct PeerData * to_peer)
+{
+
+
+ unsigned int i;
+ uint16_t element_count = 0;
+ int32_t * elements = NULL;
+ uint16_t mask_length = 0;
+ unsigned char * mask = NULL;
+ int32_t element;
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe;
+ struct GNUNET_HashCode key;
+ int exit_loop;
+ char * begin = input_elements;
+ char * end;
+
+ GNUNET_assert (peer->vh != NULL);
+
+ GNUNET_CRYPTO_hash_from_string (input_key, &key);
+
+ exit_loop = 0;
+ /* Read input_elements, and put in elements array */
+ do
+ {
+ unsigned int mcount = element_count;
+ //ignore empty rows of ,,,,,,
+ while (*begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ ok = -1;
+ return NULL;
+ }
+
+ GNUNET_array_append (elements, mcount, element);
+ element_count++;
+
+ begin = ++end;
+ }
+ while (!exit_loop && element_count < max_mids);
+ GNUNET_assert (elements != NULL);
+ GNUNET_assert (element_count >= 1);
+
+ /* Read input_mask and read in mask array */
+ mask_length = element_count / 8 + (element_count % 8 ? 1 : 0);
+ mask = GNUNET_malloc ((element_count / 8) + 2);
+ GNUNET_assert (NULL != mask);
+ if (NULL != input_mask)
+ {
+ begin = input_mask;
+ unsigned short mask_count = 0;
+ int exit_loop = 0;
+
+ do
+ {
+ //ignore empty rows of ,,,,,,
+ while (* begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ // gnunet_ascii-armor uses base32, thus we can use , as separator!
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ ok = -1;
+ return NULL;
+ }
+
+ GNUNET_assert (mask_count <= element_count);
+
+ if (element)
+ mask[mask_count / 8] = mask[mask_count / 8] | 1 << (mask_count % 8);
+
+ mask_count++;
+ begin = ++end;
+ }
+ while (!exit_loop);
+ // +1 to see if we would have more data, which would indicate malformed/superficial input
+ GNUNET_assert (mask_count == element_count);
+ }
+ else
+ {
+ for (i = 0; i <= mask_length; i++)
+ mask[i] = UCHAR_MAX; // all 1's
+ }
+
+ qe = GNUNET_VECTORPRODUCT_request (peer->vh,
+ &key,
+ &to_peer->our_id,
+ element_count,
+ mask_length,
+ elements, mask,
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60),
+ &requester_callback,
+ peer);
+
+ if (qe == NULL)
+ {
+ LOG(GNUNET_ERROR_TYPE_WARNING, "Could not send request to vectorproduct service! Exitting!");
+ ok = -1;
+ return NULL;
+ }
+
+ return qe;
+}
+
+
+/**
+ * Function prepares the message to be sent by peer1 to its vectorproduct service
+ * to prepare response, and wait for a request session to be initiated by peer1
+ */
+static struct GNUNET_VECTORPRODUCT_QueueEntry *
+responder_prepare_response (char * input_elements,
+ char * input_mask,
+ char * input_key,
+ struct PeerData * peer)
+{
+ GNUNET_assert (peer->vh != NULL);
+
+ unsigned int i;
+ uint16_t element_count = 0;
+ int32_t * elements = NULL;
+ unsigned short mask_length = 0;
+ unsigned char * mask = NULL;
+ int32_t element;
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe;
+ struct GNUNET_HashCode key;
+ int exit_loop;
+ char * begin;
+ char * end;
+
+ GNUNET_CRYPTO_hash_from_string (input_key, &key);
+
+ /* Read input_elements, and put in elements array */
+ exit_loop = 0;
+ begin = input_elements;
+ do
+ {
+ unsigned int mcount = element_count;
+ //ignore empty rows of ,,,,,,
+ while (*begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ ok = -1;
+ return NULL;
+ }
+
+ GNUNET_array_append (elements, mcount, element);
+ element_count++;
+
+ begin = ++end;
+ }
+ while (!exit_loop && element_count < max_mids);
+ GNUNET_assert (elements != NULL);
+ GNUNET_assert (element_count >= 1);
+
+ /* Read input_mask and read in mask array */
+ mask_length = element_count / 8 + (element_count % 8 ? 1 : 0);
+ mask = GNUNET_malloc ((element_count / 8) + 2);
+ GNUNET_assert (NULL != mask);
+ if (NULL != input_mask)
+ {
+ begin = input_mask;
+ unsigned short mask_count = 0;
+ int exit_loop = 0;
+
+ do
+ {
+ //ignore empty rows of ,,,,,,
+ while (* begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ // gnunet_ascii-armor uses base32, thus we can use , as separator!
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ ok = -1;
+ return NULL;
+ }
+
+ GNUNET_assert (mask_count <= element_count);
+
+ if (element)
+ mask[mask_count / 8] = mask[mask_count / 8] | 1 << (mask_count % 8);
+
+ mask_count++;
+ begin = ++end;
+ }
+ while (!exit_loop);
+ // +1 to see if we would have more data, which would indicate malformed/superficial input
+ GNUNET_assert (mask_count == element_count);
+ }
+ else
+ {
+ for (i = 0; i <= mask_length; i++)
+ mask[i] = UCHAR_MAX; // all 1's
+ }
+
+ qe = GNUNET_VECTORPRODUCT_prepare_response (peer->vh,
+ &key,
+ element_count,
+ elements,
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60),
+ &responder_callback,
+ peer);
+
+ if (qe == NULL)
+ {
+ LOG(GNUNET_ERROR_TYPE_ERROR, "Could not send request to vectorproduct service! Exitting!");
+ ok = -1;
+ return NULL;
+ }
+
+ return qe;
+}
+
+
+static void
+request_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext
+ * tc)
+{
+ struct PeerData * peer = cls;
+
+ requester_request (peer->input_elements, peer->input_mask, peer->input_keys[peer->request_num], peer, peer->peers[peer->request_num]);
+ peer->request_num++;
+ return;
+}
+
+
+static void
+prepare_response_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext
+ * tc)
+{
+ struct PeerData * peer = cls;
+
+ responder_prepare_response (peer->input_elements, peer->input_mask, peer->input_keys[peer->request_num], peer);
+ peer->request_num++;
+ return;
+}
+
+
+/**
+ * Adapter function called to destroy a connection to
+ * a service. This function is called when GNUNET_TESTBED_operation_done is
+ * called for peer->op, which holds the handle for GNUNET_TESTBED_service_connect
+ * operation.
+ *
+ * @param cls closure
+ * @param op_result service handle returned from the connect adapter
+ */
+static void
+vectorproduct_da (void *cls, void *op_result)
+{
+ struct PeerData* peer = (struct PeerData*) cls;
+
+ GNUNET_VECTORPRODUCT_disconnect (peer->vh);
+ return;
+
+ GNUNET_assert (0);
+}
+
+
+/**
+ * Adapter function called to establish a connection to
+ * a service. This function is called to by GNUNET_TESTBED_service_connect.
+ *
+ * @param cls closure
+ * @param cfg configuration of the peer to connect to; will be available until
+ * GNUNET_TESTBED_operation_done() is called on the operation returned
+ * from GNUNET_TESTBED_service_connect()
+ * @return service handle to return in 'op_result', NULL on error
+ */
+static void *
+vectorproduct_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct PeerData *p = cls;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %u (`%s') started\n", (&peer1 == p) ? 1 : 2,
+ GNUNET_i2s (&p->our_id));
+
+ switch (setup_state)
+ {
+ case PEER1_VECTORPRODUCT_CONNECT:
+ /* Connect peer 2 to vectorproduct service */
+ {
+ peer2.op = GNUNET_TESTBED_service_connect (&peer2, peer2.peer, "vectorproduct",
+ NULL, NULL, vectorproduct_ca,
+ vectorproduct_da, &peer2);
+ setup_state = PEER2_VECTORPRODUCT_CONNECT;
+ }
+
+ peer1.vh = GNUNET_VECTORPRODUCT_connect (cfg);
+ return peer1.vh;
+
+ case PEER2_VECTORPRODUCT_CONNECT:
+ /* Connect peer 3 to vectorproduct service */
+ {
+ peer3.op = GNUNET_TESTBED_service_connect (&peer3, peer3.peer, "vectorproduct",
+ NULL, NULL, vectorproduct_ca,
+ vectorproduct_da, &peer3);
+ setup_state = PEER3_VECTORPRODUCT_CONNECT;
+ }
+
+ peer2.vh = GNUNET_VECTORPRODUCT_connect (cfg);
+ return peer2.vh;
+
+ case PEER3_VECTORPRODUCT_CONNECT:
+ /* Connect peer 4 to vectorproduct service */
+ {
+ peer4.op = GNUNET_TESTBED_service_connect (&peer4, peer4.peer, "vectorproduct",
+ NULL, NULL, vectorproduct_ca,
+ vectorproduct_da, &peer4);
+ setup_state = PEER4_VECTORPRODUCT_CONNECT;
+ }
+
+ peer3.vh = GNUNET_VECTORPRODUCT_connect (cfg);
+ return peer3.vh;
+
+ case PEER4_VECTORPRODUCT_CONNECT:
+ peer4.vh = GNUNET_VECTORPRODUCT_connect (cfg);
+
+ /* Schedule the tasks to issue prepare_response calls from peer1 and peer2
+ * for peer3 and peer4.
+ */
+ GNUNET_SCHEDULER_add_now (&prepare_response_task, &peer1);
+ GNUNET_SCHEDULER_add_now (&prepare_response_task, &peer1);
+ GNUNET_SCHEDULER_add_now (&prepare_response_task, &peer2);
+ GNUNET_SCHEDULER_add_now (&prepare_response_task, &peer2);
+
+ /*
+ * Schedule the tasks to issue requests calls from peer3 and peer4
+ * to peer1 and peer2
+ */
+ GNUNET_SCHEDULER_add_now (&request_task, &peer3);
+ GNUNET_SCHEDULER_add_now (&request_task, &peer3);
+ GNUNET_SCHEDULER_add_now (&request_task, &peer4);
+ GNUNET_SCHEDULER_add_now (&request_task, &peer4);
+
+ return peer2.vh;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
+/**
+ * Callback to be called when the requested peer information is available
+ *
+ * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
+ * @param op the operation this callback corresponds to
+ * @param pinfo the result; will be NULL if the operation has failed
+ * @param emsg error message if the operation has failed; will be NULL if the
+ * operation is successfull
+ */
+static void
+peerinfo_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op_,
+ const struct GNUNET_TESTBED_PeerInformation *pinfo,
+ const char *emsg)
+{
+ GNUNET_assert (NULL == emsg);
+ GNUNET_assert (op == op_);
+ switch (setup_state)
+ {
+ case PEER1_GET_IDENTITY:
+ {
+ memcpy (&peer1.our_id, pinfo->result.id,
+ sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_TESTBED_operation_done (op);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 1 id: %s\n", GNUNET_i2s_full
+ (&peer1.our_id));
+
+ /* Request for peer id of peer 2*/
+ op = GNUNET_TESTBED_peer_get_information (peer2.peer,
+ GNUNET_TESTBED_PIT_IDENTITY,
+ &peerinfo_cb, NULL);
+ setup_state = PEER2_GET_IDENTITY;
+ }
+ break;
+ case PEER2_GET_IDENTITY:
+ {
+ memcpy (&peer2.our_id, pinfo->result.id,
+ sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_TESTBED_operation_done (op);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 2 id: %s\n", GNUNET_i2s_full
+ (&peer2.our_id));
+
+ /* Request for peer id of peer 3*/
+ op = GNUNET_TESTBED_peer_get_information (peer3.peer,
+ GNUNET_TESTBED_PIT_IDENTITY,
+ &peerinfo_cb, NULL);
+ setup_state = PEER3_GET_IDENTITY;
+ }
+ break;
+ case PEER3_GET_IDENTITY:
+ {
+ memcpy (&peer3.our_id, pinfo->result.id,
+ sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_TESTBED_operation_done (op);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 3 id: %s\n", GNUNET_i2s_full
+ (&peer3.our_id));
+
+ /* Request for peer id of peer 4*/
+ op = GNUNET_TESTBED_peer_get_information (peer4.peer,
+ GNUNET_TESTBED_PIT_IDENTITY,
+ &peerinfo_cb, NULL);
+ setup_state = PEER4_GET_IDENTITY;
+ }
+ break;
+ case PEER4_GET_IDENTITY:
+ {
+ memcpy (&peer4.our_id, pinfo->result.id,
+ sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_TESTBED_operation_done (op);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 2 id: %s\n", GNUNET_i2s_full
+ (&peer2.our_id));
+
+ /* Connect peer 1 to vectorproduct service */
+ peer1.op = GNUNET_TESTBED_service_connect (&peer1, peer1.peer, "vectorproduct",
+ NULL, NULL, vectorproduct_ca,
+ vectorproduct_da, &peer1);
+ setup_state = PEER1_VECTORPRODUCT_CONNECT;
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
+/**
+ * Signature of a main function for a testcase.
+ *
+ * @param cls closure
+ * @param num_peers number of peers in 'peers'
+ * @param peers handle to peers run in the testbed
+ */
+static void
+test_master (void *cls, unsigned int num_peers,
+ struct GNUNET_TESTBED_Peer **peers)
+{
+ GNUNET_assert (NULL != peers);
+ GNUNET_assert (NULL != peers[0]);
+ GNUNET_assert (NULL != peers[1]);
+ GNUNET_assert (NULL != peers[2]);
+ GNUNET_assert (NULL != peers[3]);
+ peer1.peer = peers[0];
+ peer1.input_elements = input_elements_peer1;
+ peer1.input_mask = input_mask_peer1;
+ peer1.request_num = 0;
+ peer1.callback_num = 0;
+ peer1.input_keys[0] = input_key_p1_p3;
+ peer1.input_keys[1] = input_key_p1_p4;
+
+ peer2.peer = peers[1];
+ peer2.input_elements = input_elements_peer2;
+ peer2.input_mask = input_mask_peer2;
+ peer2.request_num = 0;
+ peer2.callback_num = 0;
+ peer2.input_keys[0] = input_key_p2_p3;
+ peer2.input_keys[1] = input_key_p2_p4;
+
+ peer3.peer = peers[2];
+ peer3.input_elements = input_elements_peer3;
+ peer3.input_mask = input_mask_peer3;
+ peer3.request_num = 0;
+ peer3.callback_num = 0;
+ peer3.input_keys[0] = input_key_p1_p3;
+ peer3.input_keys[1] = input_key_p2_p3;
+ peer3.peers[0] = &peer1;
+ peer3.peers[1] = &peer2;
+
+
+ peer4.peer = peers[3];
+ peer4.input_elements = input_elements_peer4;
+ peer4.input_mask = input_mask_peer4;
+ peer4.request_num = 0;
+ peer4.callback_num = 0;
+ peer4.input_keys[0] = input_key_p1_p4;
+ peer4.input_keys[1] = input_key_p2_p4;
+ peer4.peers[0] = &peer1;
+ peer4.peers[1] = &peer2;
+
+ /* Get the peer identity and configuration of peer 1 */
+ op = GNUNET_TESTBED_peer_get_information (peer1.peer,
+ GNUNET_TESTBED_PIT_IDENTITY,
+ &peerinfo_cb, NULL);
+ setup_state = PEER1_GET_IDENTITY;
+ abort_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 120), &do_abort,
+ NULL);
+}
+
+
+/**
+ * Main function
+ */
+int
+main (int argc, char **argv)
+{
+ uint64_t event_mask;
+
+ ok = GNUNET_NO;
+ event_mask = 0;
+ event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
+ max_mids = (GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_MessageHeader))
+ / sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1;
+ (void) GNUNET_TESTBED_test_run ("test_vectorproduct_api_4peers",
+ "test_vectorproduct_api_data.conf",
+ NUM_PEERS, event_mask, &controller_event_cb,
+ NULL,
+ &test_master, NULL);
+ if (GNUNET_SYSERR == ok)
+ return 1;
+ return 0;
+}
diff --git a/src/scalarproduct/test_vectorproduct_api_data.conf b/src/scalarproduct/test_vectorproduct_api_data.conf
@@ -0,0 +1,96 @@
+[arm]
+DEFAULTSERVICES = core transport vectorproduct mesh testbed
+PORT = 12366
+
+[core]
+PORT = 12092
+
+[vectorproduct]
+#AUTOSTART = YES
+BINARY = gnunet-service-vectorproduct
+UNIXPATH = /tmp/gnunet-service-vectorproduct.sock
+HOME = $SERVICEHOME
+HOSTNAME = localhost
+PORT = 2087
+
+[testbed]
+OVERLAY_TOPOLOGY = CLIQUE
+
+[lockmanager]
+AUTOSTART = NO
+ACCEPT_FROM = 127.0.0.1;
+HOSTNAME = localhost
+PORT = 12101
+
+[statistics]
+AUTOSTART = YES
+ACCEPT_FROM = 127.0.0.1;
+PORT = 12102
+
+[fs]
+AUTOSTART = NO
+
+[resolver]
+AUTOSTART = NO
+
+[mesh]
+# AUTOSTART = YES
+ACCEPT_FROM = 127.0.0.1;
+HOSTNAME = localhost
+PORT = 10700
+# PREFIX = valgrind --leak-check=full
+# PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args
+
+[dht]
+AUTOSTART = YES
+ACCEPT_FROM6 = ::1;
+ACCEPT_FROM = 127.0.0.1;
+HOSTNAME = localhost
+PORT = 12100
+
+[block]
+plugins = dht test
+
+[dhtcache]
+QUOTA = 1 MB
+DATABASE = sqlite
+
+[transport]
+PLUGINS = tcp
+ACCEPT_FROM6 = ::1;
+ACCEPT_FROM = 127.0.0.1;
+NEIGHBOUR_LIMIT = 50
+PORT = 12365
+
+[ats]
+WAN_QUOTA_OUT = 3932160
+WAN_QUOTA_IN = 3932160
+
+[transport-tcp]
+TIMEOUT = 300 s
+PORT = 12368
+
+[TESTING]
+WEAKRANDOM = YES
+
+[gnunetd]
+HOSTKEY = $SERVICEHOME/.hostkey
+
+[PATHS]
+SERVICEHOME = /tmp/test-vectorproduct/
+
+[dns]
+AUTOSTART = NO
+
+[nse]
+AUTOSTART = NO
+
+[vpn]
+AUTOSTART = NO
+
+[nat]
+RETURN_LOCAL_ADDRESSES = YES
+
+[consensus]
+AUTOSTART = NO
+
diff --git a/src/scalarproduct/test_vectorproduct_api_regression.c b/src/scalarproduct/test_vectorproduct_api_regression.c
@@ -0,0 +1,852 @@
+
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file vectorproduct/test_vectorproduct_api_regression.c
+ * @brief VectorProduct API regression test
+ * @author Gaurav Kukreja
+ * @author Christian Fuchs
+ */
+
+/**
+ * AIM of the regression test
+ *
+ * This test tries to check whether the service can handle abrupt client disconnect.
+ *
+ * 1. We create a responder peer, and ask the service to prepare_response. After this,
+ * we disconnect responder peer from service.
+ *
+ * 2. Then we create a requester peer, and ask service to request another peer. We
+ * should check that the service on responder peer is still active and receives
+ * request from the requester. We then disconnect requester peer from service. Both
+ * the requester and responder service should handle this cleanly.
+ */
+
+#include <string.h>
+
+#include <inttypes.h>
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testbed_service.h"
+#include "gnunet_common.h"
+#include "gnunet_vectorproduct_service.h"
+#include "gnunet_protocols.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "test-vectorproduct-api-regression",__VA_ARGS__)
+#define NUM_PEERS 2
+
+/**
+ * Structure for holding peer's sockets and IO Handles
+ */
+struct PeerData
+{
+ /**
+ * Handle to testbed peer
+ */
+ struct GNUNET_TESTBED_Peer *peer;
+
+ /**
+ * The service connect operation to stream
+ */
+ struct GNUNET_TESTBED_Operation *op;
+
+ /**
+ * Our Peer id
+ */
+ struct GNUNET_PeerIdentity our_id;
+
+ /**
+ * Pointer to Vector Product Handle
+ */
+ struct GNUNET_VECTORPRODUCT_Handle *vh;
+};
+
+/**
+ * Different states in test setup
+ */
+enum SetupState
+{
+ /**
+ * Get the identity of peer 1
+ */
+ PEER1_GET_IDENTITY,
+
+ /**
+ * Get the identity of peer 2
+ */
+ PEER2_GET_IDENTITY,
+
+ /**
+ * Connect to stream service of peer 1
+ */
+ PEER1_VECTORPRODUCT_CONNECT,
+
+ /**
+ * Connect to stream service of peer 2
+ */
+ PEER2_VECTORPRODUCT_CONNECT
+
+};
+
+/******************************************************************************
+ *** Global Variables *****************************
+ ******************************************************************************/
+
+/**
+ * Maximum allowed message-ids we can check in one go (with one GNUNET_message)
+ */
+static unsigned int max_mids;
+
+/**
+ * Session Key used by both the test peers
+ */
+char input_key[103] = "helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhe";
+
+/**
+ * Input elements for peer1
+ */
+//char input_elements_peer1[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11";
+char input_elements_peer1[] = "11,11,11";
+
+/**
+ * Input Mask for peer 1
+ */
+//char input_mask_peer1[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1";
+char input_mask_peer1[] = "1,1,1";
+
+/**
+ * the array of converted message IDs to send to our service
+ */
+static int32_t * elements_peer1 = NULL;
+
+/**
+ * Input elements for peer2
+ */
+//char input_elements_peer2[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11";
+char input_elements_peer2[] = "11,11,11";
+/**
+ * Input Mask for peer 2
+ */
+//char input_mask_peer2[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1";
+char input_mask_peer2[] = "1,1,1";
+/**
+ * the array of converted message IDs to send to our service
+ */
+static int32_t * elements_peer2 = NULL;
+
+/**
+ * the array of converted message IDs to send to our service
+ */
+static unsigned char * mask_peer2 = NULL;
+
+/**
+ * Data context for peer 1
+ */
+static struct PeerData peer1;
+
+/**
+ * Data context for peer 2
+ */
+static struct PeerData peer2;
+
+/**
+ * Various states during test setup
+ */
+static enum SetupState setup_state;
+
+/**
+ * Testbed operation handle
+ */
+static struct GNUNET_TESTBED_Operation *op;
+
+/**
+ * Return value of the test.
+ */
+static int ok;
+
+/**
+ * Abort Task for timeout
+ */
+static GNUNET_SCHEDULER_TaskIdentifier abort_task;
+/******************************************************************************
+ *** Static Functions *****************************
+ ******************************************************************************/
+
+/**
+ * Helper function to shutdown a test peer
+ *
+ * @param cls void* to struct PeerData of the peer to be disconnected
+ * @param tc Task Context
+ */
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Helper function to connect a test peer
+ *
+ * @param cls void* to struct PeerData of the peer to be connected
+ * @param tc Task Context
+ */
+static void
+connect_peer (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext * tc);
+
+
+/**
+ * Close sockets and stop testing deamons nicely
+ */
+static void
+do_close (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+
+ if (peer1.op != NULL)
+ do_shutdown (&peer1, NULL);
+
+ if (peer2.op != NULL)
+ do_shutdown (&peer2, NULL);
+
+ if (GNUNET_SCHEDULER_NO_TASK != abort_task)
+ GNUNET_SCHEDULER_cancel (abort_task);
+
+ GNUNET_SCHEDULER_shutdown (); /* For shutting down testbed */
+}
+
+
+/**
+ * Helper function to shutdown a test peer
+ *
+ * @param cls void* to struct PeerData of the peer to be disconnected
+ * @param tc Task Context
+ */
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ static int shutdown;
+ shutdown++;
+ struct PeerData* peer = (struct PeerData*) cls;
+
+ if (peer == &peer1)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting Peer1\n\n");
+ else if (peer == &peer2)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting Peer2\n\n");
+
+ // peer->op contains handle to the TESTBED_connect_service operation
+ // calling operation done, leads to call to vectorproduct_da
+ if (peer->op != NULL)
+ {
+ GNUNET_TESTBED_operation_done (peer->op);
+ peer->op = NULL;
+ }
+
+ if (shutdown >= 2)
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 10), &do_close, NULL);
+}
+
+
+/**
+ * Something went wrong and timed out. Kill everything and set error flag
+ */
+static void
+do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: ABORT due to Timeout\n");
+ ok = GNUNET_SYSERR;
+ abort_task = 0;
+ do_close (cls, tc);
+}
+
+
+/**
+ * Controller event callback
+ *
+ * @param cls NULL
+ * @param event the controller event
+ */
+static void
+controller_event_cb (void *cls,
+ const struct GNUNET_TESTBED_EventInformation *event)
+{
+ switch (event->type)
+ {
+ case GNUNET_TESTBED_ET_OPERATION_FINISHED:
+ switch (setup_state)
+ {
+ case PEER1_VECTORPRODUCT_CONNECT:
+ case PEER2_VECTORPRODUCT_CONNECT:
+ GNUNET_assert (NULL == event->details.operation_finished.emsg);
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
+/**
+ * Callback function called for the responder peer i.e. peer1
+ *
+ * @param cls
+ * @param key Session key
+ * @param status Status of the message
+ */
+static void
+responder_callback (void *cls,
+ const struct GNUNET_HashCode * key,
+ enum GNUNET_VECTORPRODUCT_ResponseStatus status)
+{
+ if (status == GNUNET_VECTORPRODUCT_Status_Failure)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received status failure\n");
+ ok = -1;
+ }
+ else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received status invalid response\n");
+ ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Timeout == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received timeout occured\n");
+ ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received service disconnected!!\n");
+ ok = 1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Success == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Responder Client expected response received!\n");
+ ok = -1;
+ }
+ else
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client status = %d!\n", (int) status);
+ ok = -1;
+ }
+
+ // Not shutting down this time, only for this regression test. We have shutdown explicitly earlier.
+ // Shutting down again is causing problems.
+
+ // if(peer1.vh != NULL)
+ // {
+ // GNUNET_SCHEDULER_add_now(&do_shutdown, &peer1);
+ // }
+ return;
+}
+
+
+/**
+ * Callback function called for the requester peer i.e. peer2
+ *
+ * @param cls
+ * @param key Session key
+ * @param status Status of the message
+ */
+static void
+requester_callback (void *cls,
+ const struct GNUNET_HashCode * key,
+ const struct GNUNET_PeerIdentity * peer,
+ enum GNUNET_VECTORPRODUCT_ResponseStatus status,
+ const struct GNUNET_VECTORPRODUCT_client_response *msg)
+{
+ uint32_t product_len;
+
+ if (status == GNUNET_VECTORPRODUCT_Status_Failure)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client received status failure\n");
+ ok = -1;
+ }
+ else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client received status invalid response\n");
+ ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Timeout == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client timeout occured\n");
+ ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client service disconnected!!\n");
+ ok = 1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Success != status)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Requester Client Status = %d\n", (int) status);
+ ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Success == status)
+ {
+ product_len = ntohl (msg->product_length);
+
+ if (0 < product_len)
+ {
+ gcry_mpi_t result;
+ gcry_error_t ret = 0;
+ size_t read = 0;
+
+ ret = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, (void *) &msg[1], product_len, &read);
+
+ if (0 != ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not convert to mpi to value!\n");
+ }
+ else
+ {
+ gcry_mpi_dump (result);
+ gcry_mpi_release (result);
+ }
+ ok = -1;
+ }
+ else
+ { //currently not used, but if we get more info due to MESH we will need this
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Error during computation of vector product, return code: %d\n", product_len);
+ ok = -1;
+ }
+ }
+
+ // Not shutting down this time, only for this regression test. We have shutdown explicitly earlier.
+ // Shutting down again is causing problems.
+
+ // if(peer2.vh != NULL)
+ // {
+ // GNUNET_SCHEDULER_add_now(&do_shutdown, &peer2);
+ // }
+ return;
+}
+
+
+static void
+requester_request (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext * tc)
+{
+ GNUNET_assert (peer2.vh != NULL);
+
+ unsigned int i;
+ uint16_t element_count = 0;
+ uint16_t mask_length = 0;
+ char * begin = input_elements_peer2;
+ char * end;
+ int32_t element;
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe;
+ struct GNUNET_HashCode key;
+ int exit_loop = 0;
+
+ GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key);
+
+ /* Read input_elements_peer2, and put in elements_peer2 array */
+ exit_loop = 0;
+ do
+ {
+ unsigned int mcount = element_count;
+ //ignore empty rows of ,,,,,,
+ while (*begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ ok = -1;
+ return;
+ }
+
+ GNUNET_array_append (elements_peer2, mcount, element);
+ element_count++;
+
+ begin = ++end;
+ }
+ while (!exit_loop && element_count < max_mids);
+ GNUNET_assert (elements_peer2 != NULL);
+ GNUNET_assert (element_count >= 1);
+
+ /* Read input_mask_peer2 and read in mask_peer2 array */
+ mask_length = element_count / 8 + (element_count % 8 ? 1 : 0);
+ mask_peer2 = GNUNET_malloc ((element_count / 8) + 2);
+ GNUNET_assert (NULL != mask_peer2);
+ if (NULL != input_mask_peer2)
+ {
+ begin = input_mask_peer2;
+ unsigned short mask_count = 0;
+ exit_loop = 0;
+
+ do
+ {
+ //ignore empty rows of ,,,,,,
+ while (* begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ // gnunet_ascii-armor uses base32, thus we can use , as separator!
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ ok = -1;
+ return;
+ }
+
+ GNUNET_assert (mask_count <= element_count);
+
+ if (element)
+ mask_peer2[mask_count / 8] = mask_peer2[mask_count / 8] | 1 << (mask_count % 8);
+
+ mask_count++;
+ begin = ++end;
+ }
+ while (!exit_loop);
+ // +1 to see if we would have more data, which would indicate malformed/superficial input
+ GNUNET_assert (mask_count == element_count);
+ }
+ else
+ {
+ for (i = 0; i <= mask_length; i++)
+ mask_peer2[i] = UCHAR_MAX; // all 1's
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Responder peer key %s\n", &peer1.our_id);
+
+ qe = GNUNET_VECTORPRODUCT_request (peer2.vh,
+ &key,
+ &peer1.our_id,
+ element_count,
+ mask_length,
+ elements_peer2, mask_peer2,
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
+ &requester_callback,
+ NULL);
+
+ if (qe == NULL)
+ {
+ FPRINTF (stderr, "%s", _ ("Could not send request to vectorproduct service! Exitting!"));
+ ok = -1;
+ return;
+ }
+
+ /**
+ * For regression, we shutdown the initiator peer, peer2, one second after
+ * issuing a request. Hopefully, peer1 notices that the tunnel has been
+ * been destroyed, and will shutdown cleanly.
+ */
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &do_shutdown, &peer2);
+
+ return;
+}
+
+
+/**
+ * Function prepares the message to be sent by peer1 to its vectorproduct service
+ * to prepare response, and wait for a request session to be initiated by peer1
+ */
+static void
+responder_prepare_response (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext * tc)
+{
+ GNUNET_assert (peer1.vh != NULL);
+
+ uint16_t element_count = 0;
+ char * begin = input_elements_peer1;
+ char * end;
+ int32_t element;
+
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe;
+
+ struct GNUNET_HashCode key;
+ GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key);
+
+ int exit_loop = 0;
+ /* Read input_elements_peer1, and put in elements_peer1 array */
+ do
+ {
+ unsigned int mcount = element_count;
+ //ignore empty rows of ,,,,,,
+ while (*begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ ok = -1;
+ return;
+ }
+
+ GNUNET_array_append (elements_peer1, mcount, element);
+ element_count++;
+
+ begin = ++end;
+ }
+ while (!exit_loop && element_count < max_mids);
+
+ GNUNET_assert (elements_peer1 != NULL);
+ GNUNET_assert (element_count >= 1);
+
+ qe = GNUNET_VECTORPRODUCT_prepare_response (peer1.vh,
+ &key,
+ element_count,
+ elements_peer1,
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
+ &responder_callback,
+ NULL);
+
+ if (qe == NULL)
+ {
+ FPRINTF (stderr, "%s", _ ("Could not send request to vectorproduct service! Exitting!"));
+ ok = -1;
+ return;
+ }
+
+ // connect the second peer
+ setup_state = PEER2_VECTORPRODUCT_CONNECT;
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1), &connect_peer, &peer2);
+
+ // while the service is waiting for a matching request, disconnect the test client
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &do_shutdown, &peer1);
+
+ return;
+}
+
+
+/**
+ * Adapter function called to destroy a connection to
+ * a service. This function is called when GNUNET_TESTBED_operation_done is
+ * called for peer->op, which holds the handle for GNUNET_TESTBED_service_connect
+ * operation.
+ *
+ * @param cls closure
+ * @param op_result service handle returned from the connect adapter
+ */
+static void
+vectorproduct_da (void *cls, void *op_result)
+{
+ struct PeerData* peer = (struct PeerData*) cls;
+
+ GNUNET_VECTORPRODUCT_disconnect (peer->vh);
+ peer->vh = NULL;
+ return;
+}
+
+
+/**
+ * Adapter function called to establish a connection to
+ * a service. This function is called to by GNUNET_TESTBED_service_connect.
+ *
+ * @param cls closure
+ * @param cfg configuration of the peer to connect to; will be available until
+ * GNUNET_TESTBED_operation_done() is called on the operation returned
+ * from GNUNET_TESTBED_service_connect()
+ * @return service handle to return in 'op_result', NULL on error
+ */
+static void *
+vectorproduct_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct PeerData *p = cls;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %u (`%s') started\n", (&peer1 == p) ? 1 : 2,
+ GNUNET_i2s (&p->our_id));
+
+ switch (setup_state)
+ {
+ case PEER1_VECTORPRODUCT_CONNECT:
+ peer1.vh = GNUNET_VECTORPRODUCT_connect (cfg);
+
+ if (peer1.vh != NULL)
+ {
+ /* prepare_response from peer1 */
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, &responder_prepare_response, NULL);
+ }
+ else
+ {
+ ok = -1;
+ return NULL;
+ }
+
+ return peer1.vh;
+
+ case PEER2_VECTORPRODUCT_CONNECT:
+ /* Actually connect peer 2 to vectorproduct service */
+ peer2.vh = GNUNET_VECTORPRODUCT_connect (cfg);
+
+ if (peer2.vh != NULL)
+ {
+ /* initiate request from peer2 */
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, &requester_request, NULL);
+ }
+ else
+ {
+ ok = -1;
+ return NULL;
+ }
+
+ return peer2.vh;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
+/**
+ * Helper function to connect a test peer
+ *
+ * @param cls void* to struct PeerData of the peer to be connected
+ * @param tc Task Context
+ */
+static void
+connect_peer (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext * tc)
+{
+ struct PeerData *peer = cls;
+
+ peer->op = GNUNET_TESTBED_service_connect (peer, peer->peer, "vectorproduct",
+ NULL, NULL, vectorproduct_ca,
+ vectorproduct_da, peer);
+
+}
+
+
+/**
+ * Callback to be called when the requested peer information is available
+ *
+ * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
+ * @param op the operation this callback corresponds to
+ * @param pinfo the result; will be NULL if the operation has failed
+ * @param emsg error message if the operation has failed; will be NULL if the
+ * operation is successfull
+ */
+static void
+peerinfo_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op_,
+ const struct GNUNET_TESTBED_PeerInformation *pinfo,
+ const char *emsg)
+{
+ GNUNET_assert (NULL == emsg);
+ GNUNET_assert (op == op_);
+
+ switch (setup_state)
+ {
+ case PEER1_GET_IDENTITY:
+ {
+ memcpy (&peer1.our_id, pinfo->result.id,
+ sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_TESTBED_operation_done (op);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 1 id: %s\n", GNUNET_i2s_full
+ (&peer1.our_id));
+
+ /* Request for peer id of peer 2*/
+ setup_state = PEER2_GET_IDENTITY;
+ op = GNUNET_TESTBED_peer_get_information (peer2.peer,
+ GNUNET_TESTBED_PIT_IDENTITY,
+ &peerinfo_cb, NULL);
+ }
+ break;
+ case PEER2_GET_IDENTITY:
+ {
+ memcpy (&peer2.our_id, pinfo->result.id,
+ sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_TESTBED_operation_done (op);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 2 id: %s\n", GNUNET_i2s_full
+ (&peer2.our_id));
+
+ /* Connect peer 1 to vectorproduct service */
+ setup_state = PEER1_VECTORPRODUCT_CONNECT;
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, &connect_peer, &peer1);
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
+/**
+ * Signature of a main function for a testcase.
+ *
+ * @param cls closure
+ * @param num_peers number of peers in 'peers'
+ * @param peers handle to peers run in the testbed
+ */
+static void
+test_master (void *cls, unsigned int num_peers,
+ struct GNUNET_TESTBED_Peer **peers)
+{
+ GNUNET_assert (NULL != peers);
+ GNUNET_assert (NULL != peers[0]);
+ GNUNET_assert (NULL != peers[1]);
+ peer1.peer = peers[0];
+ peer2.peer = peers[1];
+
+ /* Get the peer identity and configuration of peer 1 */
+ setup_state = PEER1_GET_IDENTITY;
+ op = GNUNET_TESTBED_peer_get_information (peer1.peer,
+ GNUNET_TESTBED_PIT_IDENTITY,
+ &peerinfo_cb, NULL);
+
+ abort_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort,
+ NULL);
+}
+
+
+/**
+ * Main function
+ */
+int
+main (int argc, char **argv)
+{
+ uint64_t event_mask;
+
+ ok = GNUNET_NO;
+ event_mask = 0;
+ event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
+ max_mids = (GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_MessageHeader))
+ / sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1;
+
+ (void) GNUNET_TESTBED_test_run ("test_vectorproduct_api_regression",
+ "test_vectorproduct_api_data.conf",
+ NUM_PEERS, event_mask, &controller_event_cb,
+ NULL,
+ &test_master, NULL);
+
+ if (GNUNET_SYSERR == ok)
+ return 1;
+ return 0;
+}
+
+
diff --git a/src/scalarproduct/test_vectorproduct_api_regression2.c b/src/scalarproduct/test_vectorproduct_api_regression2.c
@@ -0,0 +1,931 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file vectorproduct/test_vectorproduct_api_regression2.c
+ * @brief Regression test, destroys requester service before receiving response
+ * responder service
+ * @author Gaurav Kukreja
+ * @author Christian Fuchs
+ */
+
+#include <string.h>
+
+#include <inttypes.h>
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testbed_service.h"
+#include "gnunet_common.h"
+#include "gnunet_vectorproduct_service.h"
+#include "gnunet_protocols.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "test-vectorproduct-api-regression2",__VA_ARGS__)
+#define NUM_PEERS 2
+
+/**
+ * Structure for holding peer's sockets and IO Handles
+ */
+struct PeerData
+{
+ /**
+ * Handle to testbed peer
+ */
+ struct GNUNET_TESTBED_Peer *peer;
+
+ /**
+ * The service connect operation to stream
+ */
+ struct GNUNET_TESTBED_Operation *op;
+
+ /**
+ * Our Peer id
+ */
+ struct GNUNET_PeerIdentity our_id;
+
+ /**
+ * Pointer to Vector Product Handle
+ */
+ struct GNUNET_VECTORPRODUCT_Handle *vh;
+};
+
+/**
+ * Different states in test setup
+ */
+enum SetupState
+{
+ /**
+ * Get the identity of peer 1
+ */
+ PEER1_GET_IDENTITY,
+
+ /**
+ * Get the identity of peer 2
+ */
+ PEER2_GET_IDENTITY,
+
+ /**
+ * Connect to stream service of peer 1
+ */
+ PEER1_VECTORPRODUCT_CONNECT,
+
+ /**
+ * Connect to stream service of peer 2
+ */
+ PEER2_VECTORPRODUCT_CONNECT
+
+};
+
+/******************************************************************************
+ *** Global Variables *****************************
+ ******************************************************************************/
+
+/**
+ * Maximum allowed message-ids we can check in one go (with one GNUNET_message)
+ */
+static unsigned int max_mids;
+
+/**
+ * Session Key used by both the test peers
+ */
+char input_key[103] = "helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhe";
+
+/**
+ * Input elements for peer1
+ */
+char input_elements_peer1[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11";
+//char input_elements_peer1[] = "11,11,11";
+
+/**
+ * Input Mask for peer 1
+ */
+char input_mask_peer1[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1";
+//char input_mask_peer1[] = "1,1,1";
+
+/**
+ * the array of converted message IDs to send to our service
+ */
+static int32_t * elements_peer1 = NULL;
+
+/**
+ * the array of converted message IDs to send to our service
+ */
+static unsigned char * mask_peer1 = NULL;
+
+/**
+ * Number of elements
+ */
+uint16_t element_count_peer1 = 0;
+
+/**
+ * Input elements for peer2
+ */
+char input_elements_peer2[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11";
+//char input_elements_peer2[] = "11,11,11";
+
+/**
+ * Input Mask for peer 2
+ */
+char input_mask_peer2[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1";
+//char input_mask_peer2[] = "1,1,1";
+
+/**
+ * the array of converted message IDs to send to our service
+ */
+static int32_t * elements_peer2 = NULL;
+
+/**
+ * the array of converted message IDs to send to our service
+ */
+static unsigned char * mask_peer2 = NULL;
+
+/**
+ * Number of elements
+ */
+uint16_t element_count_peer2 = 0;
+
+/**
+ * Data context for peer 1
+ */
+static struct PeerData peer1;
+
+/**
+ * Data context for peer 2
+ */
+static struct PeerData peer2;
+
+/**
+ * Various states during test setup
+ */
+static enum SetupState setup_state;
+
+/**
+ * Testbed operation handle
+ */
+static struct GNUNET_TESTBED_Operation *op;
+
+static int ok;
+
+static int responder_ok;
+
+static int requester_ok;
+
+static GNUNET_SCHEDULER_TaskIdentifier abort_task;
+/******************************************************************************
+ *** Static Functions *****************************
+ ******************************************************************************/
+
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Close sockets and stop testing deamons nicely
+ */
+static void
+do_close (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ static int closed;
+
+ if (peer1.op != NULL)
+ {
+ do_shutdown (&peer1, NULL);
+ }
+
+ if (peer2.op != NULL)
+ {
+ do_shutdown (&peer2, NULL);
+ }
+
+ if (GNUNET_SCHEDULER_NO_TASK != abort_task)
+ {
+ GNUNET_SCHEDULER_cancel (abort_task);
+ abort_task = GNUNET_SCHEDULER_NO_TASK;
+ GNUNET_SCHEDULER_shutdown (); /* For shutting down testbed */
+ }
+
+ if (!closed)
+ {
+ closed++;
+ GNUNET_SCHEDULER_shutdown (); /* For shutting down testbed */
+ }
+}
+
+/**
+ * Shutdown a peer
+ *
+ * @param cls closure is a pointer to the struct PeerData of the peer to be disconnected
+ * @param tc Task Context
+ */
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ static int shutdown;
+ shutdown++;
+ struct PeerData* peer = (struct PeerData*) cls;
+
+ // peer->op contains handle to the TESTBED_connect_service operation
+ // calling operation done, leads to call to vectorproduct_da
+ if (peer->op != NULL)
+ {
+ if (peer == &peer1)
+ LOG (GNUNET_ERROR_TYPE_INFO, "Disconnecting Peer1\n\n");
+ else if (peer == &peer2)
+ LOG (GNUNET_ERROR_TYPE_INFO, "Disconnecting Peer2\n\n");
+
+ GNUNET_TESTBED_operation_done (peer->op);
+ peer->op = NULL;
+ }
+
+ if (peer1.op == NULL && peer2.op == NULL)
+ GNUNET_SCHEDULER_add_now (&do_close, NULL);
+}
+
+
+/**
+ * Something went wrong and timed out. Kill everything and set error flag
+ */
+static void
+do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: ABORT due to Timeout\n");
+ ok = GNUNET_SYSERR;
+ abort_task = GNUNET_SCHEDULER_NO_TASK;
+ GNUNET_SCHEDULER_add_now (&do_shutdown, &peer1);
+ GNUNET_SCHEDULER_add_now (&do_shutdown, &peer2);
+}
+
+
+/**
+ * Controller event callback
+ *
+ * @param cls NULL
+ * @param event the controller event
+ */
+static void
+controller_event_cb (void *cls,
+ const struct GNUNET_TESTBED_EventInformation *event)
+{
+ switch (event->type)
+ {
+ case GNUNET_TESTBED_ET_OPERATION_FINISHED:
+ switch (setup_state)
+ {
+ case PEER1_VECTORPRODUCT_CONNECT:
+ case PEER2_VECTORPRODUCT_CONNECT:
+ GNUNET_assert (NULL == event->details.operation_finished.emsg);
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
+static void
+responder_callback (void *cls,
+ const struct GNUNET_HashCode * key,
+ enum GNUNET_VECTORPRODUCT_ResponseStatus status)
+{
+ if (status == GNUNET_VECTORPRODUCT_Status_Failure)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received status failure\n");
+ responder_ok = -1;
+ }
+ else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received status invalid response\n");
+ responder_ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Timeout == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received timeout occured\n");
+ // In this regression test, requester is supposed to fail due to timeout
+ // therefore responder_ok is set to 1, to make regression test pass
+ responder_ok = 1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received service disconnected!!\n");
+ responder_ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Success == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Responder Client expected response received!\n");
+ responder_ok = 1;
+ }
+ else
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client status = %d!\n", (int) status);
+ responder_ok = -1;
+ }
+ // TODO : Responder Session Complete. Shutdown Test Cleanly!!!
+ //do_shutdown(&peer1, NULL);
+ GNUNET_SCHEDULER_add_now (&do_shutdown, &peer1);
+ return;
+}
+
+
+static void
+requester_callback (void *cls,
+ const struct GNUNET_HashCode * key,
+ const struct GNUNET_PeerIdentity * peer,
+ enum GNUNET_VECTORPRODUCT_ResponseStatus status,
+ uint16_t size, struct GNUNET_VECTORPRODUCT_client_response *msg,
+ uint16_t type)
+{
+ uint32_t product_len;
+
+ if (status == GNUNET_VECTORPRODUCT_Status_Failure)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client received status failure\n");
+ // In this regression test, requester is supposed to receive status failure
+ // therefore requester_ok is set to 1, to make regression test pass
+ requester_ok = 1;
+ }
+ else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client received status invalid response\n");
+ requester_ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Timeout == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client timeout occured\n");
+ requester_ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client service disconnected!!\n");
+ requester_ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Success != status)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client Status = %d\n", (int) status);
+ requester_ok = -1;
+ }
+ else if (GNUNET_VECTORPRODUCT_Status_Success == status)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Requester Client expected response received!\n");
+
+ product_len = ntohl (msg->product_length);
+
+ if (0 < product_len)
+ {
+ gcry_mpi_t result;
+ gcry_error_t ret = 0;
+ size_t read = 0;
+
+ ret = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, (void *) &msg[1], product_len, &read);
+
+ if (0 != ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not convert to mpi to value!\n");
+ ok = -1;
+ }
+ else
+ {
+ uint16_t i = 0;
+
+ // calculate expected product
+ gcry_mpi_t expected_result;
+ gcry_mpi_t v1;
+ gcry_mpi_t v2;
+ gcry_mpi_t v1_v2_prod;
+
+ expected_result = gcry_mpi_new (0);
+
+ for (i = 0; i < element_count_peer1; i++)
+ {
+ uint32_t value;
+ v1_v2_prod = gcry_mpi_new (0);
+
+ // long to gcry_mpi_t
+ value = elements_peer1[i] >= 0 ? elements_peer1[i] : -elements_peer1[i];
+ if (elements_peer1[i] < 0)
+ {
+ v1 = gcry_mpi_new (0);
+ gcry_mpi_sub_ui (v1, v1, value);
+ }
+ else
+ v1 = gcry_mpi_set_ui (NULL, value);
+
+ // long to gcry_mpi_t
+ value = elements_peer2[i] >= 0 ? elements_peer2[i] : -elements_peer2[i];
+ if (elements_peer2[i] < 0)
+ {
+ v2 = gcry_mpi_new (0);
+ gcry_mpi_sub_ui (v2, v2, value);
+ }
+ else
+ v2 = gcry_mpi_set_ui (NULL, value);
+
+ gcry_mpi_mul (v1_v2_prod, v1, v2);
+ gcry_mpi_add (expected_result, expected_result, v1_v2_prod);
+
+ gcry_mpi_release (v1);
+ gcry_mpi_release (v2);
+ gcry_mpi_release (v1_v2_prod);
+
+ }
+
+ // compare the result
+ if (!gcry_mpi_cmp (expected_result, result))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Scalar Product matches expected Result!!\n");
+ requester_ok = 1;
+ }
+ else
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Scalar Product DOES NOT match expected Result!!\n");
+ requester_ok = -1;
+ }
+ gcry_mpi_release (result);
+ gcry_mpi_release (expected_result);
+ }
+ }
+ else
+ { //currently not used, but if we get more info due to MESH we will need this
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Error during computation of vector product, return code: %d\n", product_len);
+ requester_ok = -1;
+ }
+ }
+
+ //do_shutdown(&peer2, NULL);
+ GNUNET_SCHEDULER_add_now (&do_shutdown, &peer2);
+ return;
+}
+
+
+static struct GNUNET_VECTORPRODUCT_QueueEntry *
+requester_request ()
+{
+ GNUNET_assert (peer2.vh != NULL);
+
+ unsigned int i;
+ //uint16_t element_count_peer2 = 0;
+ uint16_t mask_length = 0;
+ char * begin = input_elements_peer2;
+ char * end;
+ int32_t element;
+
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe;
+
+ struct GNUNET_HashCode key;
+ GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key);
+
+ int exit_loop = 0;
+ /* Read input_elements_peer2, and put in elements_peer2 array */
+ do
+ {
+ unsigned int mcount = element_count_peer2;
+ //ignore empty rows of ,,,,,,
+ while (*begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ ok = -1;
+ return NULL;
+ }
+
+ GNUNET_array_append (elements_peer2, mcount, element);
+ element_count_peer2++;
+
+ begin = ++end;
+ }
+ while (!exit_loop && element_count_peer2 < max_mids);
+
+ GNUNET_assert (elements_peer2 != NULL);
+ GNUNET_assert (element_count_peer2 >= 1);
+ mask_length = element_count_peer2 / 8 + (element_count_peer2 % 8 ? 1 : 0);
+ mask_peer2 = GNUNET_malloc ((element_count_peer2 / 8) + 2);
+ GNUNET_assert (NULL != mask_peer2);
+
+ /* Read input_mask_peer2 and read in mask_peer2 array */
+ if (NULL != input_mask_peer2)
+ {
+ begin = input_mask_peer2;
+ unsigned short mask_count = 0;
+ int exit_loop = 0;
+
+ do
+ {
+ //ignore empty rows of ,,,,,,
+ while (* begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ // gnunet_ascii-armor uses base32, thus we can use , as separator!
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ ok = -1;
+ return NULL;
+ }
+
+ GNUNET_assert (mask_count <= element_count_peer2);
+
+ if (element)
+ mask_peer2[mask_count / 8] = mask_peer2[mask_count / 8] | 1 << (mask_count % 8);
+
+ mask_count++;
+ begin = ++end;
+ }
+ while (!exit_loop);
+ // +1 to see if we would have more data, which would indicate malformed/superficial input
+ GNUNET_assert (mask_count == element_count_peer2);
+ }
+ else
+ {
+ for (i = 0; i <= mask_length; i++)
+ mask_peer2[i] = UCHAR_MAX; // all 1's
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Responder peer key %s\n", &peer1.our_id);
+
+ // TODO : Create the mask array
+ qe = GNUNET_VECTORPRODUCT_request (peer2.vh,
+ &key,
+ &peer1.our_id,
+ element_count_peer2,
+ mask_length,
+ elements_peer2, mask_peer2,
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
+ &requester_callback,
+ NULL);
+
+ if (qe == NULL)
+ {
+ FPRINTF (stderr, "%s", _ ("Could not send request to vectorproduct service! Exitting!"));
+ ok = -1;
+ return NULL;
+ }
+
+ return qe;
+}
+
+
+/**
+ * Function prepares the message to be sent by peer1 to its vectorproduct service
+ * to prepare response, and wait for a request session to be initiated by peer1
+ */
+static struct GNUNET_VECTORPRODUCT_QueueEntry *
+responder_prepare_response ()
+{
+ GNUNET_assert (peer1.vh != NULL);
+
+ unsigned int i;
+ //uint16_t element_count_peer1 = 0;
+ char * begin = input_elements_peer1;
+ char * end;
+ int32_t element;
+
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe;
+
+ struct GNUNET_HashCode key;
+ GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key);
+
+ int exit_loop = 0;
+ /* Read input_elements_peer1, and put in elements_peer1 array */
+ do
+ {
+ unsigned int mcount = element_count_peer1;
+ //ignore empty rows of ,,,,,,
+ while (*begin == ',')
+ begin++;
+ // get the length of the current element and replace , with null
+ for (end = begin; *end && *end != ','; end++);
+
+ if (*end == '\0')
+ exit_loop = 1;
+
+ if (*end == ',')
+ *end = '\0';
+
+ if (1 != sscanf (begin, "%" SCNd32, &element))
+ {
+ FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+ ok = -1;
+ return NULL;
+ }
+
+ GNUNET_array_append (elements_peer1, mcount, element);
+ element_count_peer1++;
+
+ begin = ++end;
+ }
+ while (!exit_loop && element_count_peer1 < max_mids);
+
+ GNUNET_assert (elements_peer1 != NULL);
+ GNUNET_assert (element_count_peer1 >= 1);
+
+ qe = GNUNET_VECTORPRODUCT_prepare_response (peer1.vh,
+ &key,
+ element_count_peer1,
+ elements_peer1,
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
+ &responder_callback,
+ NULL);
+
+ if (qe == NULL)
+ {
+ FPRINTF (stderr, "%s", _ ("Could not send request to vectorproduct service! Exitting!"));
+ ok = -1;
+ return NULL;
+ }
+ return qe;
+}
+
+
+/**
+ * Scheduler task to initiate requester client
+ *
+ * @param cls void* to struct PeerData
+ * @param tc Task Context
+ */
+static void
+request_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext
+ * tc)
+{
+ struct PeerData * peer = cls;
+
+ requester_request ();
+ return;
+}
+
+
+/**
+ * Scheduler task to initiate responder client
+ *
+ * @param cls void* to struct PeerData
+ * @param tc Task Context
+ */
+static void
+prepare_response_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext
+ * tc)
+{
+ struct PeerData * peer = cls;
+
+ responder_prepare_response ();
+ return;
+}
+
+
+static void
+peer_stop_callback (void *cls,
+ const char *emsg)
+{
+ GNUNET_TESTBED_peer_destroy (peer2.peer);
+}
+
+/**
+ * Destroys Peer2 i.e. the initiator peer (Alice) This function is scheduled to
+ * run a few milliseconds after the request has been sent to the Responding Peer (Bob).
+ * This function tries to emulate a crash of Peer2.
+ *
+ * @param cls Not used
+ * @param tc Task Context - Not used
+ */
+static void
+destroy_server (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ LOG (GNUNET_ERROR_TYPE_INFO, "\n***\nKilling the Requesting Client, hopefully before it receives response\n***\n");
+ do_shutdown (&peer2, NULL);
+ GNUNET_TESTBED_peer_stop (peer2.peer, &peer_stop_callback, NULL);
+}
+
+
+/**
+ * Adapter function called to destroy a connection to
+ * a service. This function is called when GNUNET_TESTBED_operation_done is
+ * called for peer->op, which holds the handle for GNUNET_TESTBED_service_connect
+ * operation.
+ *
+ * @param cls closure
+ * @param op_result service handle returned from the connect adapter
+ */
+static void
+vectorproduct_da (void *cls, void *op_result)
+{
+ struct PeerData* peer = (struct PeerData*) cls;
+
+ GNUNET_VECTORPRODUCT_disconnect (peer->vh);
+ return;
+}
+
+
+/**
+ * Adapter function called to establish a connection to
+ * a service. This function is called to by GNUNET_TESTBED_service_connect.
+ *
+ * @param cls closure
+ * @param cfg configuration of the peer to connect to; will be available until
+ * GNUNET_TESTBED_operation_done() is called on the operation returned
+ * from GNUNET_TESTBED_service_connect()
+ * @return service handle to return in 'op_result', NULL on error
+ */
+static void *
+vectorproduct_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct PeerData *p = cls;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %u (`%s') started\n", (&peer1 == p) ? 1 : 2,
+ GNUNET_i2s (&p->our_id));
+
+ switch (setup_state)
+ {
+ case PEER1_VECTORPRODUCT_CONNECT:
+ /* Connect peer 2 to vectorproduct service */
+ /* The connect adapter vectorproduct_ca will be called to perform the actual connection */
+ {
+ peer2.op = GNUNET_TESTBED_service_connect (&peer2, peer2.peer, "vectorproduct",
+ NULL, NULL, vectorproduct_ca,
+ vectorproduct_da, &peer2);
+ setup_state = PEER2_VECTORPRODUCT_CONNECT;
+ }
+
+ /* Actually connect peer 1 to vectorproduct service */
+ peer1.vh = GNUNET_VECTORPRODUCT_connect (cfg);
+ return peer1.vh;
+
+ case PEER2_VECTORPRODUCT_CONNECT:
+ /* Actually connect peer 2 to vectorproduct service */
+ peer2.vh = GNUNET_VECTORPRODUCT_connect (cfg);
+
+
+ if (peer1.vh != NULL && peer2.vh != NULL)
+ {
+ GNUNET_SCHEDULER_add_now (&prepare_response_task, &peer1);
+ GNUNET_SCHEDULER_add_now (&request_task, &peer2);
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 6),
+ &destroy_server, NULL);
+ }
+ else
+ {
+ // TODO : Handle error. One of the peers is not connected. Cleanly shutdown
+ ok = -1;
+ return NULL;
+ }
+ return peer2.vh;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
+/**
+ * Callback to be called when the requested peer information is available
+ *
+ * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
+ * @param op the operation this callback corresponds to
+ * @param pinfo the result; will be NULL if the operation has failed
+ * @param emsg error message if the operation has failed; will be NULL if the
+ * operation is successfull
+ */
+static void
+peerinfo_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op_,
+ const struct GNUNET_TESTBED_PeerInformation *pinfo,
+ const char *emsg)
+{
+ GNUNET_assert (NULL == emsg);
+ GNUNET_assert (op == op_);
+ switch (setup_state)
+ {
+ case PEER1_GET_IDENTITY:
+ {
+ memcpy (&peer1.our_id, pinfo->result.id,
+ sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_TESTBED_operation_done (op);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 1 id: %s\n", GNUNET_i2s_full
+ (&peer1.our_id));
+
+ /* Request for peer id of peer 2*/
+ op = GNUNET_TESTBED_peer_get_information (peer2.peer,
+ GNUNET_TESTBED_PIT_IDENTITY,
+ &peerinfo_cb, NULL);
+ setup_state = PEER2_GET_IDENTITY;
+ }
+ break;
+ case PEER2_GET_IDENTITY:
+ {
+ memcpy (&peer2.our_id, pinfo->result.id,
+ sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_TESTBED_operation_done (op);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 2 id: %s\n", GNUNET_i2s_full
+ (&peer2.our_id));
+
+ /* Connect peer 1 to vectorproduct service */
+ /* The connect adapter vectorproduct_ca will be called to perform the actual connection */
+ peer1.op = GNUNET_TESTBED_service_connect (&peer1, peer1.peer, "vectorproduct",
+ NULL, NULL, vectorproduct_ca,
+ vectorproduct_da, &peer1);
+ setup_state = PEER1_VECTORPRODUCT_CONNECT;
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
+/**
+ * Signature of a main function for a testcase.
+ *
+ * @param cls closure
+ * @param num_peers number of peers in 'peers'
+ * @param peers handle to peers run in the testbed
+ */
+static void
+test_master (void *cls, unsigned int num_peers,
+ struct GNUNET_TESTBED_Peer **peers)
+{
+ GNUNET_assert (NULL != peers);
+ GNUNET_assert (NULL != peers[0]);
+ GNUNET_assert (NULL != peers[1]);
+ peer1.peer = peers[0];
+ peer2.peer = peers[1];
+ /* Get the peer identity and configuration of peer 1 */
+ op = GNUNET_TESTBED_peer_get_information (peer1.peer,
+ GNUNET_TESTBED_PIT_IDENTITY,
+ &peerinfo_cb, NULL);
+ setup_state = PEER1_GET_IDENTITY;
+ abort_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort,
+ NULL);
+}
+
+
+/**
+ * Main function
+ */
+int
+main (int argc, char **argv)
+{
+ uint64_t event_mask;
+
+ ok = GNUNET_NO;
+ event_mask = 0;
+ event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
+ max_mids = (GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_MessageHeader))
+ / sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1;
+
+ (void) GNUNET_TESTBED_test_run ("test_vectorproduct_api_regression2",
+ "test_vectorproduct_api_data.conf",
+ NUM_PEERS, event_mask, &controller_event_cb,
+ NULL,
+ &test_master, NULL);
+ if (GNUNET_SYSERR == ok)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Test failing due to some error before calling API for request or prepare_response\n");
+ return 1;
+ }
+ else if (GNUNET_SYSERR == responder_ok)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Test failing due to some error in response for responding_client\n");
+ return 1;
+ }
+ else if (GNUNET_SYSERR == requester_ok)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Test failing due to some error in response for requesting client\n");
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
diff --git a/src/scalarproduct/vectorproduct.conf b/src/scalarproduct/vectorproduct.conf
@@ -0,0 +1,7 @@
+[vectorproduct]
+BINARY = gnunet-service-vectorproduct
+UNIXPATH = /tmp/gnunet-service-vectorproduct.sock
+HOME = $SERVICEHOME
+# PORT = 2106
+# PORT = 2087
+
diff --git a/src/scalarproduct/vectorproduct.conf.in b/src/scalarproduct/vectorproduct.conf.in
@@ -0,0 +1,7 @@
+[vectorproduct]
+BINARY = gnunet-service-vectorproduct
+UNIXPATH = /tmp/gnunet-service-vectorproduct.sock
+HOME = $SERVICEHOME
+# PORT = 2106
+@UNIXONLY@ PORT = 2087
+
diff --git a/src/scalarproduct/vectorproduct_api.c b/src/scalarproduct/vectorproduct_api.c
@@ -0,0 +1,715 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file vectorproduct/vectorproduct_api.c
+ * @brief API for the vectorproduct
+ * @author Christian Fuchs
+ * @author Gaurav Kukreja
+ *
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_statistics_service.h"
+#include "gnunet_vectorproduct_service.h"
+#include "gnunet_protocols.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "vectorproduct-api",__VA_ARGS__)
+
+/**************************************************************
+ *** Datatype Declarations **********
+ **************************************************************/
+
+/**
+ * Entry in the request queue per client
+ */
+struct GNUNET_VECTORPRODUCT_QueueEntry
+{
+ /**
+ * This is a linked list.
+ */
+ struct GNUNET_VECTORPRODUCT_QueueEntry *next;
+
+ /**
+ * This is a linked list.
+ */
+ struct GNUNET_VECTORPRODUCT_QueueEntry *prev;
+
+ /**
+ * Handle to the master context.
+ */
+ struct GNUNET_VECTORPRODUCT_Handle *h;
+
+ /**
+ * Size of the message
+ */
+ uint16_t message_size;
+
+ /**
+ * Message to be sent to the vectorproduct service
+ */
+ struct GNUNET_VECTORPRODUCT_client_request* msg;
+
+ union
+ {
+ /**
+ * Function to call after transmission of the request.
+ */
+ GNUNET_VECTORPRODUCT_ContinuationWithStatus cont_status;
+
+ /**
+ * Function to call after transmission of the request.
+ */
+ GNUNET_VECTORPRODUCT_DatumProcessor cont_datum;
+ };
+
+ /**
+ * Closure for 'cont'.
+ */
+ void *cont_cls;
+
+ /**
+ * Has this message been transmitted to the service?
+ * Only ever GNUNET_YES for the head of the queue.
+ * Note that the overall struct should end at a
+ * multiple of 64 bits.
+ */
+ int16_t was_transmitted;
+
+ /**
+ * Timeout for the current operation.
+ */
+ struct GNUNET_TIME_Absolute timeout;
+
+ /**
+ * Task for timeout signaling.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier timeout_task;
+
+ /**
+ * Response Processor for response from the service. This function calls the
+ * continuation function provided by the client.
+ */
+ GNUNET_VECTORPRODUCT_ResponseMessageHandler response_proc;
+};
+
+/**************************************************************
+ *** Function Declarations **********
+ **************************************************************/
+
+/**
+ * Creates a new entry at the tail of the DLL
+ *
+ * @param h handle to the master context
+ *
+ * @return pointer to the entry
+ */
+static struct GNUNET_VECTORPRODUCT_QueueEntry *
+make_queue_entry (struct GNUNET_VECTORPRODUCT_Handle *h);
+
+/**
+ * Removes the head entry from the queue
+ *
+ * @param h Handle to the master context
+ */
+static struct GNUNET_VECTORPRODUCT_QueueEntry *
+free_queue_head_entry (struct GNUNET_VECTORPRODUCT_Handle * h);
+
+/**
+ * Triggered when timeout occurs for a request in queue
+ *
+ * @param cls The pointer to the QueueEntry
+ * @param tc Task Context
+ */
+static void
+timeout_queue_entry (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+/**
+ * Called when a response is received from the service. After basic check
+ * handler in qe->response_proc is called. This functions handles the response
+ * to the client which used the API.
+ *
+ * @param cls Pointer to the Master Context
+ * @param msg Pointer to the data received in response
+ */
+static void
+receive_cb (void *cls, const struct GNUNET_MessageHeader *msg);
+
+/**
+ * Transmits the request to the VectorProduct Sevice
+ *
+ * @param cls Closure
+ * @param size Size of the buffer
+ * @param buf Pointer to the buffer
+ *
+ * @return Size of the message sent
+ */
+static size_t transmit_request (void *cls, size_t size,
+ void *buf);
+
+/**
+ * Issues transmit request for the new entries in the queue
+ *
+ * @param h handle to the master context
+ */
+static void
+process_queue (struct GNUNET_VECTORPRODUCT_Handle *h);
+
+/**************************************************************
+ *** Static Function Declarations **********
+ **************************************************************/
+
+
+/**
+ * Creates a new entry at the tail of the DLL
+ *
+ * @param h handle to the master context
+ *
+ * @return pointer to the entry
+ */
+static struct GNUNET_VECTORPRODUCT_QueueEntry *
+make_queue_entry (struct GNUNET_VECTORPRODUCT_Handle *h)
+{
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe;
+
+ qe = GNUNET_new (struct GNUNET_VECTORPRODUCT_QueueEntry);
+
+ // if queue empty
+ if (NULL == h->queue_head && NULL == h->queue_tail)
+ {
+ qe->next = NULL;
+ qe->prev = NULL;
+ h->queue_head = qe;
+ h->queue_tail = qe;
+ }
+ else
+ {
+ qe->prev = h->queue_tail;
+ h->queue_tail->next = qe;
+ h->queue_tail = qe;
+ }
+
+ return qe;
+}
+
+
+/**
+ * Removes the head entry from the queue
+ *
+ * @param h Handle to the master context
+ */
+static struct GNUNET_VECTORPRODUCT_QueueEntry *
+free_queue_head_entry (struct GNUNET_VECTORPRODUCT_Handle * h)
+{
+ struct GNUNET_VECTORPRODUCT_QueueEntry * qe = NULL;
+
+ GNUNET_assert (NULL != h);
+ if (NULL == h->queue_head && NULL == h->queue_tail)
+ {
+ // The queue is empty. Just return.
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Queue was empty when free_queue_head_entry was called.\n");
+ }
+ else if (h->queue_head == h->queue_tail) //only one entry
+ {
+ qe = h->queue_head;
+ qe->next = NULL;
+ qe->prev = NULL;
+ h->queue_head = NULL;
+ h->queue_tail = NULL;
+ }
+ else
+ {
+ qe = h->queue_head;
+ h->queue_head = h->queue_head->next;
+ h->queue_head->prev = NULL;
+ qe->next = NULL;
+ qe->prev = NULL;
+ }
+ return qe;
+}
+
+
+/**
+ * Triggered when timeout occurs for a request in queue
+ *
+ * @param cls The pointer to the QueueEntry
+ * @param tc Task Context
+ */
+static void
+timeout_queue_entry (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_VECTORPRODUCT_QueueEntry * qe = cls;
+
+ // Update Statistics
+ GNUNET_STATISTICS_update (qe->h->stats,
+ gettext_noop ("# queue entry timeouts"), 1,
+ GNUNET_NO);
+
+ // Clear the timeout_task
+ qe->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+
+ // transmit_request is supposed to cancel timeout task.
+ // If message was not transmitted, there is definitely an error.
+ GNUNET_assert (GNUNET_NO == qe->was_transmitted);
+
+ LOG (GNUNET_ERROR_TYPE_INFO, "Timeout of request in datastore queue\n");
+
+ // remove the queue_entry for the queue
+ GNUNET_CONTAINER_DLL_remove (qe->h->queue_head, qe->h->queue_tail, qe);
+ qe->response_proc (qe, NULL, GNUNET_VECTORPRODUCT_Status_Timeout);
+}
+
+
+/**
+ * Handles the RESULT received in reply of prepare_response from the
+ * service
+ *
+ * @param cls Handle to the Master Context
+ * @param msg Pointer to the response received
+ */
+static void
+process_status_message (void *cls,
+ const struct GNUNET_MessageHeader *msg,
+ enum GNUNET_VECTORPRODUCT_ResponseStatus status)
+{
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe = cls;
+
+ GNUNET_assert (qe != NULL);
+
+ if (qe->cont_status != NULL)
+ qe->cont_status (qe->cont_cls, &qe->msg->key, status);
+}
+
+
+/**
+ * Handles the RESULT received in reply of prepare_response from the
+ * service
+ *
+ * @param cls Handle to the Master Context
+ * @param msg Pointer to the response received
+ */
+static void
+process_result_message (void *cls,
+ const struct GNUNET_MessageHeader *msg,
+ enum GNUNET_VECTORPRODUCT_ResponseStatus status)
+{
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe = cls;
+
+ GNUNET_assert (qe != NULL);
+
+ if (msg == NULL && qe->cont_datum != NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Timeout reached or session terminated.\n");
+ }
+ if (qe->cont_datum != NULL)
+ {
+ qe->cont_datum (qe->cont_cls, &qe->msg->key, &qe->msg->peer, status, (struct GNUNET_VECTORPRODUCT_client_response *) msg);
+ }
+}
+
+
+/**
+ * Called when a response is received from the service. After basic check
+ * handler in qe->response_proc is called. This functions handles the response
+ * to the client which used the API.
+ *
+ * @param cls Pointer to the Master Context
+ * @param msg Pointer to the data received in response
+ */
+static void
+receive_cb (void *cls, const struct GNUNET_MessageHeader *msg)
+{
+ struct GNUNET_VECTORPRODUCT_Handle *h = cls;
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe;
+ int16_t was_transmitted;
+ struct GNUNET_VECTORPRODUCT_client_response *message =
+ (struct GNUNET_VECTORPRODUCT_client_response *) msg;
+
+ h->in_receive = GNUNET_NO;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Received reply from VectorProduct\n");
+
+ if (NULL == (qe = free_queue_head_entry (h)))
+ {
+ /**
+ * The queue head will be NULL if the client disconnected,
+ * * In case of Alice, client disconnected after sending request, before receiving response
+ * * In case of Bob, client disconnected after preparing response, before getting request from Alice.
+ */
+ process_queue (h);
+ return;
+ }
+
+ if (h->client == NULL)
+ {
+ // GKUKREJA : handle this correctly
+ /**
+ * The queue head will be NULL if the client disconnected,
+ * * In case of Alice, client disconnected after sending request, before receiving response
+ * * In case of Bob, client disconnected after preparing response, before getting request from Alice.
+ */
+ process_queue (h);
+ return;
+ }
+
+ was_transmitted = qe->was_transmitted;
+ // Control will only come here, when the request was transmitted to service,
+ // and service responded.
+ GNUNET_assert (was_transmitted == GNUNET_YES);
+
+ if (msg == NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Service responded with NULL!\n");
+ qe->response_proc (qe, NULL, GNUNET_VECTORPRODUCT_Status_Failure);
+ }
+ else if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_VECTORPRODUCT_SERVICE_TO_CLIENT))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid Message Received\n");
+ qe->response_proc (qe, msg, GNUNET_VECTORPRODUCT_Status_InvalidResponse);
+ }
+ else if (ntohl (message->product_length) == 0)
+ {
+ // response for the responder client, successful
+ GNUNET_STATISTICS_update (h->stats,
+ gettext_noop ("# SUC responder result messages received"), 1,
+ GNUNET_NO);
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Received message from service without product attached.\n");
+ qe->response_proc (qe, msg, GNUNET_VECTORPRODUCT_Status_Success);
+ }
+ else if (ntohl (message->product_length) > 0)
+ {
+ // response for the requester client, successful
+ GNUNET_STATISTICS_update (h->stats,
+ gettext_noop ("# SUC requester result messages received"), 1,
+ GNUNET_NO);
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Received message from requester service for requester client.\n");
+ qe->response_proc (qe, msg, GNUNET_VECTORPRODUCT_Status_Success);
+ }
+
+ GNUNET_free (qe);
+ process_queue (h);
+}
+
+
+/**
+ * Transmits the request to the VectorProduct Sevice
+ *
+ * @param cls Closure
+ * @param size Size of the buffer
+ * @param buf Pointer to the buffer
+ *
+ * @return Size of the message sent
+ */
+static size_t
+transmit_request (void *cls, size_t size,
+ void *buf)
+{
+ struct GNUNET_VECTORPRODUCT_Handle *h = cls;
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe;
+ size_t msize;
+
+ if (NULL == (qe = h->queue_head))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Queue head is NULL!\n\n");
+ return 0;
+ }
+
+ GNUNET_SCHEDULER_cancel (qe->timeout_task);
+ qe->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+
+ h->th = NULL;
+ if (NULL == (qe = h->queue_head))
+ return 0; /* no entry in queue */
+ if (buf == NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Failed to transmit request to VECTORPRODUCT.\n");
+ GNUNET_STATISTICS_update (h->stats,
+ gettext_noop ("# transmission request failures"),
+ 1, GNUNET_NO);
+ GNUNET_VECTORPRODUCT_disconnect (h);
+ return 0;
+ }
+ if (size < (msize = qe->message_size))
+ {
+ process_queue (h);
+ return 0;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmitting %u byte request to VECTORPRODUCT\n",
+ msize);
+
+ memcpy (buf, qe->msg, size);
+ GNUNET_free (qe->msg);
+ qe->was_transmitted = GNUNET_YES;
+
+ GNUNET_assert (GNUNET_NO == h->in_receive);
+ h->in_receive = GNUNET_YES;
+
+ GNUNET_CLIENT_receive (h->client, &receive_cb, h,
+ GNUNET_TIME_UNIT_FOREVER_REL);
+
+#if INSANE_STATISTICS
+ GNUNET_STATISTICS_update (h->stats,
+ gettext_noop ("# bytes sent to vectorproduct"), 1,
+ GNUNET_NO);
+#endif
+ return size;
+}
+
+
+/**
+ * Issues transmit request for the new entries in the queue
+ *
+ * @param h handle to the master context
+ */
+static void
+process_queue (struct GNUNET_VECTORPRODUCT_Handle *h)
+{
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe;
+
+ if (NULL == (qe = h->queue_head))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Queue empty\n");
+ return; /* no entry in queue */
+ }
+ if (qe->was_transmitted == GNUNET_YES)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Head request already transmitted\n");
+ return; /* waiting for replies */
+ }
+ if (h->th != NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Pending transmission request\n");
+ return; /* request pending */
+ }
+ if (h->client == NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Not connected\n");
+ return; /* waiting for reconnect */
+ }
+ if (GNUNET_YES == h->in_receive)
+ {
+ /* wait for response to previous query */
+ return;
+ }
+
+ h->th =
+ GNUNET_CLIENT_notify_transmit_ready (h->client, qe->message_size,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_YES,
+ &transmit_request, h);
+
+ if (h->th == NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _ ("Failed to send a message to the vectorproduct service\n"));
+ return;
+ }
+
+ GNUNET_assert (GNUNET_NO == h->in_receive);
+ GNUNET_break (NULL != h->th);
+}
+
+
+
+/**************************************************************
+ *** API **********
+ **************************************************************/
+
+
+/**
+ * Called by the responder client to prepare response
+ *
+ * @param h handle to the master context
+ * @param key Session key - unique to the requesting client
+ * @param element_count Number of elements in the vector
+ * @param mask_length number of bytes in the mask
+ * @param elements Array of elements of the vector
+ * @param mask Array of the mask
+ * @param timeout Relative timeout for the operation
+ * @param cont Callback function
+ * @param cont_cls Closure for the callback function
+ */
+struct GNUNET_VECTORPRODUCT_QueueEntry *
+GNUNET_VECTORPRODUCT_prepare_response (struct GNUNET_VECTORPRODUCT_Handle *h,
+ const struct GNUNET_HashCode * key,
+ uint16_t element_count,
+ int32_t * elements,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_VECTORPRODUCT_ContinuationWithStatus cont,
+ void *cont_cls)
+{
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe = make_queue_entry (h);
+ int32_t * vector;
+ uint16_t size;
+ unsigned int i;
+
+ GNUNET_assert (GNUNET_SERVER_MAX_MESSAGE_SIZE >= sizeof (struct GNUNET_VECTORPRODUCT_client_request)
+ +element_count * sizeof (int32_t));
+ size = sizeof (struct GNUNET_VECTORPRODUCT_client_request) +element_count * sizeof (int32_t);
+
+ qe->message_size = size;
+ qe->msg = GNUNET_malloc (size);
+ qe->msg->header.size = htons (size);
+ qe->msg->header.type = htons (GNUNET_MESSAGE_TYPE_VECTORPRODUCT_CLIENT_TO_BOB);
+ qe->msg->element_count = htons (element_count);
+ qe->msg->mask_length = htons (0);
+ memcpy (&qe->msg->key, key, sizeof (struct GNUNET_HashCode));
+ qe->cont_status = cont;
+ qe->cont_cls = cont_cls;
+ qe->was_transmitted = GNUNET_NO;
+ qe->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout, &timeout_queue_entry, qe);
+ qe->response_proc = &process_status_message;
+ qe->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+
+ vector = (int32_t *) & qe->msg[1];
+ // copy each element over to the message
+ for (i = 0; i < element_count; i++)
+ vector[i] = htonl (elements[i]);
+
+ process_queue (h);
+ return qe;
+}
+
+
+/**
+ * Request the Scalar Product Evaluation
+ *
+ * @param h handle to the master context
+ * @param key Session key - unique to the requesting client
+ * @param peer PeerID of the other peer
+ * @param element_count Number of elements in the vector
+ * @param mask_length number of bytes in the mask
+ * @param elements Array of elements of the vector
+ * @param mask Array of the mask
+ * @param timeout Relative timeout for the operation
+ * @param cont Callback function
+ * @param cont_cls Closure for the callback function
+ */
+struct GNUNET_VECTORPRODUCT_QueueEntry *
+GNUNET_VECTORPRODUCT_request (struct GNUNET_VECTORPRODUCT_Handle *h,
+ const struct GNUNET_HashCode * key,
+ const struct GNUNET_PeerIdentity * peer,
+ uint16_t element_count,
+ uint16_t mask_length,
+ int32_t * elements,
+ const unsigned char * mask,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_VECTORPRODUCT_DatumProcessor cont,
+ void *cont_cls)
+{
+ struct GNUNET_VECTORPRODUCT_QueueEntry *qe = make_queue_entry (h);
+ int32_t * vector;
+ uint16_t size;
+ unsigned int i;
+
+ GNUNET_assert (GNUNET_SERVER_MAX_MESSAGE_SIZE >= sizeof (struct GNUNET_VECTORPRODUCT_client_request)
+ +element_count * sizeof (int32_t)
+ + mask_length);
+ size = sizeof (struct GNUNET_VECTORPRODUCT_client_request) +element_count * sizeof (int32_t) + mask_length;
+
+ qe->message_size = size;
+ qe->msg = GNUNET_malloc (size);
+ qe->msg->header.size = htons (size);
+ qe->msg->header.type = htons (GNUNET_MESSAGE_TYPE_VECTORPRODUCT_CLIENT_TO_ALICE);
+ memcpy (&qe->msg->peer, peer, sizeof (struct GNUNET_PeerIdentity));
+ qe->msg->element_count = htons (element_count);
+ qe->msg->mask_length = htons (mask_length);
+ memcpy (&qe->msg->key, key, sizeof (struct GNUNET_HashCode));
+ qe->cont_datum = cont;
+ qe->cont_cls = cont_cls;
+ qe->was_transmitted = GNUNET_NO;
+ qe->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout, &timeout_queue_entry, qe);
+ qe->response_proc = &process_result_message;
+ qe->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+
+ vector = (int32_t*) & qe->msg[1];
+ // copy each element over to the message
+ for (i = 0; i < element_count; i++)
+ vector[i] = htonl (elements[i]);
+
+ // fill in the mask
+ memcpy (&vector[element_count], mask, mask_length);
+
+ process_queue (h);
+ return qe;
+}
+
+
+/**
+ * Connect to the vectorproduct service.
+ *
+ * @param cfg configuration to use
+ * @return handle to use to access the service
+ */
+struct GNUNET_VECTORPRODUCT_Handle *
+GNUNET_VECTORPRODUCT_connect (const struct GNUNET_CONFIGURATION_Handle * cfg)
+{
+ struct GNUNET_CLIENT_Connection *client;
+ struct GNUNET_VECTORPRODUCT_Handle *h;
+
+ client = GNUNET_CLIENT_connect ("vectorproduct", cfg);
+
+ if (NULL == client)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _ ("Failed to connect to the vectorproduct service\n"));
+ return NULL;
+ }
+
+ h = GNUNET_malloc (sizeof (struct GNUNET_VECTORPRODUCT_Handle) +
+ GNUNET_SERVER_MAX_MESSAGE_SIZE - 1);
+ h->client = client;
+ h->cfg = cfg;
+ h->stats = GNUNET_STATISTICS_create ("vectorproduct-api", cfg);
+ return h;
+}
+
+
+/**
+ * Disconnect from the vectorproduct service.
+ *
+ * @param h handle to the vectorproduct
+ */
+void
+GNUNET_VECTORPRODUCT_disconnect (struct GNUNET_VECTORPRODUCT_Handle * h)
+{
+ struct GNUNET_VECTORPRODUCT_QueueEntry * qe;
+
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ "Disconnecting from VectorProduct\n");
+
+ while (NULL != h->queue_head)
+ {
+ GNUNET_assert (NULL != (qe = free_queue_head_entry (h)));
+ qe->response_proc (qe, NULL, GNUNET_VECTORPRODUCT_Status_ServiceDisconnected);
+ }
+
+ if (h->client != NULL)
+ {
+ GNUNET_CLIENT_disconnect (h->client);
+ h->client = NULL;
+ }
+
+ GNUNET_STATISTICS_destroy (h->stats, GNUNET_NO);
+ h->stats = NULL;
+}
+
+/* end of ext_api.c */
diff --git a/src/scalarproduct/vectorproduct_testing.h b/src/scalarproduct/vectorproduct_testing.h
@@ -0,0 +1,129 @@
+/*
+ * File: vectorproduct_testing.h
+ * Author: gnunet
+ *
+ * Created on June 29, 2013, 7:39 PM
+ */
+
+#ifndef VECTORPRODUCT_TESTING_H
+#define VECTORPRODUCT_TESTING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct GNUNET_VECTORPRODUCT_TESTING_handle
+{
+ /**
+ * Testing library system handle
+ */
+ struct GNUNET_TESTING_System *tl_system;
+
+ /**
+ * head DLL of peers
+ */
+ struct PeerContext *p_head;
+
+ /**
+ * tail DLL of peers
+ */
+ struct PeerContext *p_tail;
+};
+
+struct PeerContext
+{
+ /**
+ * Next element in the DLL
+ */
+ struct PeerContext *next;
+
+ /**
+ * Previous element in the DLL
+ */
+ struct PeerContext *prev;
+
+ /**
+ * Peer's testing handle
+ */
+ struct GNUNET_TESTING_Peer *peer;
+
+ /**
+ * Peer identity
+ */
+ struct GNUNET_PeerIdentity id;
+
+ /**
+ * Handle for the peer's ARM process
+ */
+ struct GNUNET_OS_Process *arm_proc;
+
+ /**
+ * Pointer to Vector Product Handle
+ */
+ struct GNUNET_VECTORPRODUCT_Handle *vh;
+
+ /**
+ * Closure for the callbacks
+ */
+ void *cb_cls;
+
+ /**
+ * An unique number to identify the peer
+ */
+ unsigned int no;
+
+ /**
+ * Peer's configuration
+ */
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Pointer to the master testing handle
+ */
+ struct GNUNET_VECTORPRODUCT_TESTING_handle * vth;
+
+ /**
+ * Callback when two peers are connected and both have called the connect callback
+ * to notify clients about a new peer
+ */
+ void (*start_cb) (struct PeerContext * p, void *cls);
+
+// /**
+// * Pointer to function where the test occurs
+// */
+// GNUNET_VECTORPRODUCT_TESTING_start_cb start_cb;
+};
+
+/**
+ * Callback when two peers are connected and both have called the connect callback
+ * to notify clients about a new peer
+ */
+typedef void (*GNUNET_VECTORPRODUCT_TESTING_start_cb) (struct PeerContext * p,
+ void *cls);
+
+struct GNUNET_VECTORPRODUCT_TESTING_handle *
+GNUNET_VECTORPRODUCT_TESTING_init();
+
+static void
+GNUNET_VECTORPRODUCT_TESTING_done(struct GNUNET_VECTORPRODUCT_TESTING_handle * vth);
+
+struct PeerContext *
+GNUNET_VECTORPRODUCT_TESTING_start_peer (struct GNUNET_VECTORPRODUCT_TESTING_handle * vth,
+ const char *cfgname, int peer_id,
+ GNUNET_VECTORPRODUCT_TESTING_start_cb start_cb,
+ void *cb_cls);
+
+static void
+GNUNET_VECTORPRODUCT_TESTING_stop_peer
+ (struct GNUNET_VECTORPRODUCT_TESTING_handle * vth,
+ struct PeerContext *p);
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* VECTORPRODUCT_TESTING_H */
+