From 9aceae8a9f91642665fa28730c961c9f90360bc1 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Sun, 27 Sep 2015 04:32:52 +0000 Subject: SET service: accurate results for symmetric mode --- src/include/gnunet_protocols.h | 23 +- src/set/Makefile.am | 8 +- src/set/gnunet-service-set.c | 6 +- src/set/gnunet-service-set_protocol.h | 8 +- src/set/gnunet-service-set_union.c | 776 +++++++++++++++++------------- src/set/gnunet-set-profiler.c | 51 +- src/set/set_api.c | 63 +-- src/set/test_set.conf | 44 +- src/set/test_set_union_result_full.c | 359 -------------- src/set/test_set_union_result_symmetric.c | 363 ++++++++++++++ 10 files changed, 941 insertions(+), 760 deletions(-) delete mode 100644 src/set/test_set_union_result_full.c create mode 100644 src/set/test_set_union_result_symmetric.c (limited to 'src') diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index 7e36ebfe9..cd0aecd07 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -1801,6 +1801,27 @@ extern "C" * SET message types ******************************************************************************/ +/** + * Demand the whole element from the other + * peer, given only the hash code. + */ +#define GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND 566 + +/** + * Tell the other peer to send us a list of + * hashes that match an IBF key. + */ +#define GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY 567 + +/** + * Tell the other peer which hashes match a + * given IBF key. + */ +#define GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER 568 + +/** + * Reject a set request. + */ #define GNUNET_MESSAGE_TYPE_SET_REJECT 569 /** @@ -1884,7 +1905,7 @@ extern "C" #define GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENT_REQUESTS 585 /** - * Union operation is done. + * Set operation is done. */ #define GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE 586 diff --git a/src/set/Makefile.am b/src/set/Makefile.am index 669a28658..b7617db25 100644 --- a/src/set/Makefile.am +++ b/src/set/Makefile.am @@ -66,7 +66,7 @@ libgnunetset_la_LDFLAGS = \ if HAVE_TESTING check_PROGRAMS = \ test_set_api \ - test_set_union_result_full \ + test_set_union_result_symmetric \ test_set_intersection_result_full \ test_set_union_copy endif @@ -83,9 +83,9 @@ test_set_api_LDADD = \ $(top_builddir)/src/testing/libgnunettesting.la \ libgnunetset.la -test_set_union_result_full_SOURCES = \ - test_set_union_result_full.c -test_set_union_result_full_LDADD = \ +test_set_union_result_symmetric_SOURCES = \ + test_set_union_result_symmetric.c +test_set_union_result_symmetric_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/testing/libgnunettesting.la \ libgnunetset.la diff --git a/src/set/gnunet-service-set.c b/src/set/gnunet-service-set.c index d8e8dfb78..754bc96e0 100644 --- a/src/set/gnunet-service-set.c +++ b/src/set/gnunet-service-set.c @@ -560,6 +560,7 @@ set_destroy (struct Set *set) NULL); GNUNET_CONTAINER_multihashmap_destroy (content->elements); content->elements = NULL; + GNUNET_free (content); } } GNUNET_free_non_null (set->excluded_generations); @@ -1951,8 +1952,11 @@ run (void *cls, { &dispatch_p2p_message, GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST, 0}, { &dispatch_p2p_message, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF, 0}, { &dispatch_p2p_message, GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS, 0}, - { &dispatch_p2p_message, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE, 0}, + { &dispatch_p2p_message, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER, 0}, + { &dispatch_p2p_message, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY, 0}, + { &dispatch_p2p_message, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND, 0}, { &dispatch_p2p_message, GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENT_REQUESTS, 0}, + { &dispatch_p2p_message, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE, 0}, { &dispatch_p2p_message, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE, 0}, { &dispatch_p2p_message, GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO, 0}, { &dispatch_p2p_message, GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF, 0}, diff --git a/src/set/gnunet-service-set_protocol.h b/src/set/gnunet-service-set_protocol.h index c8a3121a6..eee0dc5ae 100644 --- a/src/set/gnunet-service-set_protocol.h +++ b/src/set/gnunet-service-set_protocol.h @@ -58,6 +58,12 @@ struct OperationRequestMessage }; +/** + * Message containing buckets of an invertible bloom filter. + * + * If an IBF has too many buckets for an IBF message, + * it is split into multiple messages. + */ struct IBFMessage { /** @@ -86,7 +92,7 @@ struct IBFMessage */ uint32_t salt GNUNET_PACKED; - /* rest: strata */ + /* rest: buckets */ }; diff --git a/src/set/gnunet-service-set_union.c b/src/set/gnunet-service-set_union.c index 5b452cae1..47abeaac8 100644 --- a/src/set/gnunet-service-set_union.c +++ b/src/set/gnunet-service-set_union.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet - Copyright (C) 2013 Christian Grothoff (and other contributing authors) + Copyright (C) 2013-2015 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 @@ -31,6 +31,11 @@ #include +#define LOG(kind,...) GNUNET_log_from (kind, "set-union",__VA_ARGS__) + +#define LOG_OP(kind,msg,op,...) GNUNET_log_from (kind, "set-union","[OP %x] " msg,((void *)op),__VA_ARGS__) + + /** * Number of IBFs in a strata estimator. */ @@ -40,7 +45,7 @@ */ #define SE_IBF_SIZE 80 /** - * hash num parameter for the difference digests and strata estimators + * The hash num parameter for the difference digests and strata estimators. */ #define SE_IBF_HASH_NUM 4 @@ -69,7 +74,7 @@ enum UnionOperationPhase { /** - * We sent the request message, and expect a strata estimator + * We sent the request message, and expect a strata estimator. */ PHASE_EXPECT_SE, @@ -77,6 +82,8 @@ enum UnionOperationPhase * We sent the strata estimator, and expect an IBF. This phase is entered once * upon initialization and later via #PHASE_EXPECT_ELEMENTS_AND_REQUESTS. * + * XXX: could use better wording. + * * After receiving the complete IBF, we enter #PHASE_EXPECT_ELEMENTS */ PHASE_EXPECT_IBF, @@ -87,33 +94,33 @@ enum UnionOperationPhase PHASE_EXPECT_IBF_CONT, /** - * We are sending request and elements, - * and thus only expect elements from the other peer. - * - * We are currently decoding an IBF until it can no longer be decoded, - * we currently send requests and expect elements - * The remote peer is in #PHASE_EXPECT_ELEMENTS_AND_REQUESTS + * We are decoding an IBF. */ - PHASE_EXPECT_ELEMENTS, + PHASE_INVENTORY_ACTIVE, /** - * We are expecting elements and requests, and send - * requested elements back to the other peer. - * - * We are in this phase if we have SENT an IBF for the remote peer to decode. - * We expect requests, send elements or could receive an new IBF, which takes - * us via #PHASE_EXPECT_IBF to phase #PHASE_EXPECT_ELEMENTS - * - * The remote peer is thus in: - * #PHASE_EXPECT_ELEMENTS + * The other peer is decoding the IBF we just sent. */ - PHASE_EXPECT_ELEMENTS_AND_REQUESTS, + PHASE_INVENTORY_PASSIVE, /** - * The protocol is over. - * Results may still have to be sent to the client. + * The protocol is almost finished, but we still have to flush our message + * queue and/or expect some elements. */ - PHASE_FINISHED + PHASE_FINISH_CLOSING, + + /** + * In the penultimate phase, + * we wait until all our demands + * are satisfied. Then we send a done + * message, and wait for another done message.*/ + PHASE_FINISH_WAITING, + + /** + * In the ultimate phase, we wait until + * our demands are satisfied and then + * quit (sending another DONE message). */ + PHASE_DONE, }; @@ -122,20 +129,19 @@ enum UnionOperationPhase */ struct OperationState { - /** * Copy of the set's strata estimator at the time of - * creation of this operation + * creation of this operation. */ struct StrataEstimator *se; /** - * The ibf we currently receive + * The IBF we currently receive. */ struct InvertibleBloomFilter *remote_ibf; /** - * IBF of the set's element. + * The IBF with the local set's element. */ struct InvertibleBloomFilter *local_ibf; @@ -146,11 +152,6 @@ struct OperationState */ struct GNUNET_CONTAINER_MultiHashMap32 *key_to_element; - /** - * Iterator for sending elements on the key to element mapping to the client. - */ - struct GNUNET_CONTAINER_MultiHashMap32Iterator *full_result_iter; - /** * Current state of the operation. */ @@ -162,10 +163,14 @@ struct OperationState int client_done_sent; /** - * Number of ibf buckets received + * Number of ibf buckets already received into the @a remote_ibf. */ unsigned int ibf_buckets_received; + /** + * Hashes for elements that we have demanded from the other peer. + */ + struct GNUNET_CONTAINER_MultiHashMap *demanded_hashes; }; @@ -181,14 +186,11 @@ struct KeyEntry /** * The actual element associated with the key. + * + * Only owned by the union operation if element->operation + * is #GNUNET_YES. */ struct ElementEntry *element; - - /** - * Element that collides with this element - * on the ibf key. All colliding entries must have the same ibf key. - */ - struct KeyEntry *next_colliding; }; @@ -215,7 +217,7 @@ struct SendElementClosure /** * Extra state required for efficient set union. */ -struct SetState + struct SetState { /** * The strata estimator is only generated once for @@ -244,18 +246,13 @@ destroy_key_to_element_iter (void *cls, { struct KeyEntry *k = value; - while (NULL != k) + GNUNET_assert (NULL != k); + if (GNUNET_YES == k->element->remote) { - struct KeyEntry *k_tmp = k; - - k = k->next_colliding; - if (GNUNET_YES == k_tmp->element->remote) - { - GNUNET_free (k_tmp->element); - k_tmp->element = NULL; - } - GNUNET_free (k_tmp); + GNUNET_free (k->element); + k->element = NULL; } + GNUNET_free (k); return GNUNET_YES; } @@ -269,8 +266,8 @@ destroy_key_to_element_iter (void *cls, static void union_op_cancel (struct Operation *op) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "destroying union op\n"); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "destroying union op\n"); /* check if the op was canceled twice */ GNUNET_assert (NULL != op->state); if (NULL != op->state->remote_ibf) @@ -278,6 +275,11 @@ union_op_cancel (struct Operation *op) ibf_destroy (op->state->remote_ibf); op->state->remote_ibf = NULL; } + if (NULL != op->state->demanded_hashes) + { + GNUNET_CONTAINER_multihashmap_destroy (op->state->demanded_hashes); + op->state->demanded_hashes = NULL; + } if (NULL != op->state->local_ibf) { ibf_destroy (op->state->local_ibf); @@ -298,8 +300,8 @@ union_op_cancel (struct Operation *op) } GNUNET_free (op->state); op->state = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "destroying union op done\n"); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "destroying union op done\n"); } @@ -315,8 +317,8 @@ fail_union_operation (struct Operation *op) struct GNUNET_MQ_Envelope *ev; struct GNUNET_SET_ResultMessage *msg; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "union operation failed\n"); + LOG (GNUNET_ERROR_TYPE_ERROR, + "union operation failed\n"); ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_RESULT); msg->result_status = htons (GNUNET_SET_STATUS_FAILURE); msg->request_id = htonl (op->spec->client_request_id); @@ -340,55 +342,23 @@ get_ibf_key (const struct GNUNET_HashCode *src, { struct IBF_Key key; - GNUNET_CRYPTO_hkdf (&key, sizeof (key), - GCRY_MD_SHA512, GCRY_MD_SHA256, - src, sizeof *src, - &salt, sizeof (salt), - NULL, 0); + GNUNET_CRYPTO_kdf (&key, sizeof (key), + src, sizeof *src, + &salt, sizeof (salt), + NULL, 0); return key; } /** - * Iterator to create the mapping between ibf keys - * and element entries. + * Iterator over the mapping from IBF keys to element entries. Checks if we + * have an element with a given GNUNET_HashCode. * * @param cls closure * @param key current key code * @param value value in the hash map - * @return #GNUNET_YES if we should continue to iterate, - * #GNUNET_NO if not. - */ -static int -op_register_element_iterator (void *cls, - uint32_t key, - void *value) -{ - struct KeyEntry *const new_k = cls; - struct KeyEntry *old_k = value; - - GNUNET_assert (NULL != old_k); - /* check if our ibf key collides with the ibf key in the existing entry */ - if (old_k->ibf_key.key_val == new_k->ibf_key.key_val) - { - /* insert the the new key in the collision chain */ - new_k->next_colliding = old_k->next_colliding; - old_k->next_colliding = new_k; - /* signal to the caller that we were able to insert into a colliding bucket */ - return GNUNET_NO; - } - return GNUNET_YES; -} - - -/** - * Iterator to create the mapping between ibf keys - * and element entries. - * - * @param cls closure - * @param key current key code - * @param value value in the hash map - * @return #GNUNET_YES (we should continue to iterate) + * @return #GNUNET_YES if we should search further, + * #GNUNET_NO if we've found the element. */ static int op_has_element_iterator (void *cls, @@ -399,13 +369,9 @@ op_has_element_iterator (void *cls, struct KeyEntry *k = value; GNUNET_assert (NULL != k); - while (NULL != k) - { - if (0 == GNUNET_CRYPTO_hash_cmp (&k->element->element_hash, - element_hash)) - return GNUNET_NO; - k = k->next_colliding; - } + if (0 == GNUNET_CRYPTO_hash_cmp (&k->element->element_hash, + element_hash)) + return GNUNET_NO; return GNUNET_YES; } @@ -446,6 +412,8 @@ op_has_element (struct Operation *op, * This is done to speed up re-tried operations, if some elements * were transmitted, and then the IBF fails to decode. * + * XXX: clarify ownership, doesn't sound right. + * * @param op the union operation * @param ee the element entry */ @@ -453,7 +421,6 @@ static void op_register_element (struct Operation *op, struct ElementEntry *ee) { - int ret; struct IBF_Key ibf_key; struct KeyEntry *k; @@ -461,18 +428,11 @@ op_register_element (struct Operation *op, k = GNUNET_new (struct KeyEntry); k->element = ee; k->ibf_key = ibf_key; - ret = GNUNET_CONTAINER_multihashmap32_get_multiple (op->state->key_to_element, + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap32_put (op->state->key_to_element, (uint32_t) ibf_key.key_val, - op_register_element_iterator, - k); - - /* was the element inserted into a colliding bucket? */ - if (GNUNET_SYSERR == ret) - return; - GNUNET_CONTAINER_multihashmap32_put (op->state->key_to_element, - (uint32_t) ibf_key.key_val, - k, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + k, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); } @@ -488,13 +448,15 @@ prepare_ibf_iterator (void *cls, uint32_t key, void *value) { - struct InvertibleBloomFilter *ibf = cls; + struct Operation *op = cls; struct KeyEntry *ke = value; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "inserting %x into ibf\n", - ke->ibf_key.key_val); - ibf_insert (ibf, ke->ibf_key); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "[OP %x] inserting %lx (hash %s) into ibf\n", + (void *) op, + (unsigned long) ke->ibf_key.key_val, + GNUNET_h2s (&ke->element->element_hash)); + ibf_insert (op->state->local_ibf, ke->ibf_key); return GNUNET_YES; } @@ -554,13 +516,15 @@ prepare_ibf (struct Operation *op, op->state->local_ibf = ibf_create (size, SE_IBF_HASH_NUM); GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element, &prepare_ibf_iterator, - op->state->local_ibf); + op); } /** * Send an ibf of appropriate size. * + * Fragments the IBF into multiple messages if necessary. + * * @param op the union operation * @param ibf_order order of the ibf to send, size=2^order */ @@ -573,9 +537,9 @@ send_ibf (struct Operation *op, prepare_ibf (op, 1<state->local_ibf; @@ -599,7 +563,7 @@ send_ibf (struct Operation *op, ibf_write_slice (ibf, buckets_sent, buckets_in_message, &msg[1]); buckets_sent += buckets_in_message; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "ibf chunk size %u, %u/%u sent\n", buckets_in_message, buckets_sent, @@ -607,7 +571,9 @@ send_ibf (struct Operation *op, GNUNET_MQ_send (op->mq, ev); } - op->state->phase = PHASE_EXPECT_ELEMENTS_AND_REQUESTS; + /* The other peer must decode the IBF, so + * we're passive. */ + op->state->phase = PHASE_INVENTORY_PASSIVE; } @@ -629,7 +595,7 @@ send_strata_estimator (struct Operation *op) GNUNET_MQ_send (op->mq, ev); op->state->phase = PHASE_EXPECT_IBF; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "sent SE, expecting IBF\n"); } @@ -696,7 +662,7 @@ handle_p2p_strata_estimator (void *cls, strata_estimator_destroy (remote_se); strata_estimator_destroy (op->state->se); op->state->se = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "got se diff=%d, using ibf size %d\n", diff, 1<ibf_key; struct Operation *op = sec->op; struct KeyEntry *ke = value; + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_MessageHeader *mh; - if (ke->ibf_key.key_val != ibf_key.key_val) + /* Detect 32-bit key collision for the 64-bit IBF keys. */ + if (ke->ibf_key.key_val != sec->ibf_key.key_val) return GNUNET_YES; - while (NULL != ke) - { - const struct GNUNET_SET_Element *const element = &ke->element->element; - struct GNUNET_MQ_Envelope *ev; - struct GNUNET_MessageHeader *mh; - GNUNET_assert (ke->ibf_key.key_val == ibf_key.key_val); - ev = GNUNET_MQ_msg_header_extra (mh, - element->size, - GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS); - if (NULL == ev) - { - /* element too large */ - GNUNET_break (0); - continue; - } - memcpy (&mh[1], - element->data, - element->size); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sending element (%s) to peer\n", - GNUNET_h2s (&ke->element->element_hash)); - GNUNET_MQ_send (op->mq, ev); - ke = ke->next_colliding; - } - return GNUNET_NO; + ev = GNUNET_MQ_msg_header_extra (mh, + sizeof (struct GNUNET_HashCode), + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER); + + GNUNET_assert (NULL != ev); + *(struct GNUNET_HashCode *) &mh[1] = ke->element->element_hash; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "[OP %x] sending element offer (%s) to peer\n", + (void *) op, + GNUNET_h2s (&ke->element->element_hash)); + GNUNET_MQ_send (op->mq, ev); + return GNUNET_YES; } /** - * Send all elements that have the specified IBF key - * to the remote peer of the union operation + * Send offers (in the form of GNUNET_Hash-es) to the remote peer for the given IBF key. * * @param op union operation * @param ibf_key IBF key of interest */ static void -send_elements_for_key (struct Operation *op, - struct IBF_Key ibf_key) +send_offers_for_key (struct Operation *op, + struct IBF_Key ibf_key) { struct SendElementClosure send_cls; @@ -771,14 +725,14 @@ send_elements_for_key (struct Operation *op, send_cls.op = op; (void) GNUNET_CONTAINER_multihashmap32_get_multiple (op->state->key_to_element, (uint32_t) ibf_key.key_val, - &send_element_iterator, + &send_offers_iterator, &send_cls); } /** * Decode which elements are missing on each side, and - * send the appropriate elemens and requests + * send the appropriate offers and inquiries. * * @param op union operation */ @@ -791,7 +745,7 @@ decode_and_send (struct Operation *op) unsigned int num_decoded; struct InvertibleBloomFilter *diff_ibf; - GNUNET_assert (PHASE_EXPECT_ELEMENTS == op->state->phase); + GNUNET_assert (PHASE_INVENTORY_ACTIVE == op->state->phase); prepare_ibf (op, op->state->remote_ibf->size); diff_ibf = ibf_dup (op->state->local_ibf); @@ -800,7 +754,7 @@ decode_and_send (struct Operation *op) ibf_destroy (op->state->remote_ibf); op->state->remote_ibf = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "decoding IBF (size=%u)\n", diff_ibf->size); @@ -817,14 +771,14 @@ decode_and_send (struct Operation *op) res = ibf_decode (diff_ibf, &side, &key); if (res == GNUNET_OK) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "decoded ibf key %lx\n", - key.key_val); + (unsigned long) key.key_val); num_decoded += 1; if ( (num_decoded > diff_ibf->size) || (num_decoded > 1 && last_key.key_val == key.key_val) ) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "detected cyclic ibf (decoded %u/%u)\n", num_decoded, diff_ibf->size); @@ -841,15 +795,17 @@ decode_and_send (struct Operation *op) next_order++; if (next_order <= MAX_IBF_ORDER) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "decoding failed, sending larger ibf (size %u)\n", - 1<mq, ev); + /* We now wait until we get a DONE message back + * and then wait for our MQ to be flushed and all our + * demands be delivered. */ break; } if (1 == side) { - send_elements_for_key (op, key); + send_offers_for_key (op, key); } else if (-1 == side) { struct GNUNET_MQ_Envelope *ev; struct GNUNET_MessageHeader *msg; - /* It may be nice to merge multiple requests, but with cadet's corking it is not worth + /* It may be nice to merge multiple requests, but with CADET's corking it is not worth * the effort additional complexity. */ ev = GNUNET_MQ_msg_header_extra (msg, sizeof (struct IBF_Key), - GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENT_REQUESTS); + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY); memcpy (&msg[1], &key, sizeof (struct IBF_Key)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sending element request\n"); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "sending element inquiry for IBF key %lx\n", + (unsigned long) key.key_val); GNUNET_MQ_send (op->mq, ev); } else @@ -897,6 +857,9 @@ decode_and_send (struct Operation *op) /** * Handle an IBF message from a remote peer. * + * Reassemble the IBF from multiple pieces, and + * process the whole IBF once possible. + * * @param cls the union operation * @param mh the header of the message * @return #GNUNET_SYSERR if the tunnel should be disconnected, @@ -917,12 +880,12 @@ handle_p2p_ibf (void *cls, return GNUNET_SYSERR; } msg = (const struct IBFMessage *) mh; - if ( (op->state->phase == PHASE_EXPECT_ELEMENTS_AND_REQUESTS) || + if ( (op->state->phase == PHASE_INVENTORY_PASSIVE) || (op->state->phase == PHASE_EXPECT_IBF) ) { op->state->phase = PHASE_EXPECT_IBF_CONT; GNUNET_assert (NULL == op->state->remote_ibf); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "Creating new ibf of size %u\n", 1 << msg->order); op->state->remote_ibf = ibf_create (1<order, SE_IBF_HASH_NUM); @@ -944,6 +907,13 @@ handle_p2p_ibf (void *cls, return GNUNET_SYSERR; } } + else + { + LOG_OP (GNUNET_ERROR_TYPE_DEBUG, + "wrong phase\n", + op, NULL); + GNUNET_assert (0); + } buckets_in_message = (ntohs (msg->header.size) - sizeof *msg) / IBF_BUCKET_SIZE; @@ -961,6 +931,8 @@ handle_p2p_ibf (void *cls, return GNUNET_SYSERR; } + GNUNET_assert (NULL != op->state->remote_ibf); + ibf_read_slice (&msg[1], op->state->ibf_buckets_received, buckets_in_message, @@ -969,9 +941,9 @@ handle_p2p_ibf (void *cls, if (op->state->ibf_buckets_received == op->state->remote_ibf->size) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "received full ibf\n"); - op->state->phase = PHASE_EXPECT_ELEMENTS; + op->state->phase = PHASE_INVENTORY_ACTIVE; decode_and_send (op); } return GNUNET_OK; @@ -984,15 +956,17 @@ handle_p2p_ibf (void *cls, * * @param op union operation * @param element element to send + * @param status status to send with the new element */ static void send_client_element (struct Operation *op, - struct GNUNET_SET_Element *element) + struct GNUNET_SET_Element *element, + int status) { struct GNUNET_MQ_Envelope *ev; struct GNUNET_SET_ResultMessage *rm; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "sending element (size %u) to client\n", element->size); GNUNET_assert (0 != op->spec->client_request_id); @@ -1003,12 +977,7 @@ send_client_element (struct Operation *op, GNUNET_break (0); return; } - - if (GNUNET_SET_RESULT_ADDED == op->spec->result_mode) - rm->result_status = htons (GNUNET_SET_STATUS_OK); - else if (GNUNET_SET_RESULT_SYMMETRIC == op->spec->result_mode) - rm->result_status = htons (GNUNET_SET_STATUS_ADD_LOCAL); - + rm->result_status = htons (status); rm->request_id = htonl (op->spec->client_request_id); rm->element_type = element->element_type; memcpy (&rm[1], element->data, element->size); @@ -1034,98 +1003,46 @@ send_done_and_destroy (void *cls) rm->result_status = htons (GNUNET_SET_STATUS_DONE); rm->element_type = htons (0); GNUNET_MQ_send (op->spec->set->client_mq, ev); + /* Will also call the union-specific cancel function. */ _GSS_operation_destroy (op, GNUNET_YES); - op->keep--; - if (0 == op->keep) - GNUNET_free (op); } -/** - * Send all remaining elements in the full result iterator. - * - * @param cls operation - */ static void -send_remaining_elements (void *cls) +maybe_finish (struct Operation *op) { - struct Operation *op = cls; - struct KeyEntry *ke; - int res; + unsigned int num_demanded; - res = GNUNET_CONTAINER_multihashmap32_iterator_next (op->state->full_result_iter, - NULL, - (const void **) &ke); - if (GNUNET_NO == res) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sending done and destroy because iterator ran out\n"); - send_done_and_destroy (op); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sending elements from key entry\n"); - while (1) + num_demanded = GNUNET_CONTAINER_multihashmap_size (op->state->demanded_hashes); + + if (PHASE_FINISH_WAITING == op->state->phase) { - struct GNUNET_MQ_Envelope *ev; - struct GNUNET_SET_ResultMessage *rm; - struct GNUNET_SET_Element *element; - - element = &ke->element->element; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sending element (size %u) to client (full set)\n", - element->size); - GNUNET_assert (0 != op->spec->client_request_id); - ev = GNUNET_MQ_msg_extra (rm, - element->size, - GNUNET_MESSAGE_TYPE_SET_RESULT); - if (NULL == ev) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "In PHASE_FINISH_WAITING, pending %u demands\n", + num_demanded); + if (0 == num_demanded) { - GNUNET_MQ_discard (ev); - GNUNET_break (0); - continue; - } - rm->result_status = htons (GNUNET_SET_STATUS_OK); - rm->request_id = htonl (op->spec->client_request_id); - rm->element_type = element->element_type; - memcpy (&rm[1], element->data, element->size); - if (NULL == ke->next_colliding) - { - GNUNET_MQ_notify_sent (ev, send_remaining_elements, op); - GNUNET_MQ_send (op->spec->set->client_mq, ev); - break; - } - GNUNET_MQ_send (op->spec->set->client_mq, ev); - ke = ke->next_colliding; - } -} + struct GNUNET_MQ_Envelope *ev; + op->state->phase = PHASE_DONE; + ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE); + GNUNET_MQ_send (op->mq, ev); -/** - * Send a result message to the client indicating - * that the operation is over. - * After the result done message has been sent to the client, - * destroy the evaluate operation. - * - * @param op union operation - */ -static void -finish_and_destroy (struct Operation *op) -{ - GNUNET_assert (GNUNET_NO == op->state->client_done_sent); - op->keep++; - if (GNUNET_SET_RESULT_FULL == op->spec->result_mode) + /* We now wait until the other peer closes the channel + * after it got all elements from us. */ + } + } + if (PHASE_FINISH_CLOSING == op->state->phase) { - /* prevent that the op is free'd by the tunnel end handler */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sending full result set\n"); - GNUNET_assert (NULL == op->state->full_result_iter); - op->state->full_result_iter = - GNUNET_CONTAINER_multihashmap32_iterator_create (op->state->key_to_element); - send_remaining_elements (op); - return; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "In PHASE_FINISH_CLOSING, pending %u demands\n", + num_demanded); + if (0 == num_demanded) + { + op->state->phase = PHASE_DONE; + send_done_and_destroy (op); + } } - send_done_and_destroy (op); } @@ -1141,65 +1058,106 @@ handle_p2p_elements (void *cls, { struct Operation *op = cls; struct ElementEntry *ee; + const struct GNUNET_SET_ElementMessage *emsg; uint16_t element_size; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got element from peer\n"); - if ( (op->state->phase != PHASE_EXPECT_ELEMENTS) && - (op->state->phase != PHASE_EXPECT_ELEMENTS_AND_REQUESTS) ) + if (0 == GNUNET_CONTAINER_multihashmap_size (op->state->demanded_hashes)) { + GNUNET_break_op (0); fail_union_operation (op); + return; + } + + if (ntohs (mh->size) < sizeof (struct GNUNET_SET_ElementMessage)) + { GNUNET_break_op (0); + fail_union_operation (op); return; } - element_size = ntohs (mh->size) - sizeof (struct GNUNET_MessageHeader); + + emsg = (struct GNUNET_SET_ElementMessage *) mh; + + element_size = ntohs (mh->size) - sizeof (struct GNUNET_SET_ElementMessage); ee = GNUNET_malloc (sizeof (struct ElementEntry) + element_size); - memcpy (&ee[1], &mh[1], element_size); + memcpy (&ee[1], &emsg[1], element_size); ee->element.size = element_size; ee->element.data = &ee[1]; + ee->element.element_type = ntohs (emsg->element_type); ee->remote = GNUNET_YES; GNUNET_CRYPTO_hash (ee->element.data, ee->element.size, &ee->element_hash); - if (GNUNET_YES == op_has_element (op, &ee->element_hash)) + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_remove (op->state->demanded_hashes, &ee->element_hash, NULL)) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "got existing element from peer\n"); + /* We got something we didn't demand, since it's not in our map. */ + GNUNET_break_op (0); GNUNET_free (ee); + fail_union_operation (op); return; } - op_register_element (op, ee); - /* only send results immediately if the client wants it */ - if (GNUNET_SET_RESULT_FULL != op->spec->result_mode) - send_client_element (op, &ee->element); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got element (size %u, hash %s) from peer\n", + (unsigned int) element_size, + GNUNET_h2s (&ee->element_hash)); + + if (GNUNET_YES == op_has_element (op, &ee->element_hash)) + { + /* Got repeated element. Should not happen since + * we track demands. */ + GNUNET_break (0); + GNUNET_free (ee); + } + else + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Registering new element from remote peer\n"); + op_register_element (op, ee); + /* only send results immediately if the client wants it */ + switch (op->spec->result_mode) + { + case GNUNET_SET_RESULT_ADDED: + send_client_element (op, &ee->element, GNUNET_SET_STATUS_OK); + break; + case GNUNET_SET_RESULT_SYMMETRIC: + send_client_element (op, &ee->element, GNUNET_SET_STATUS_ADD_LOCAL); + break; + default: + /* Result mode not supported, should have been caught earlier. */ + GNUNET_break (0); + break; + } + } + + maybe_finish (op); } /** - * Handle an element request from a remote peer. + * Send offers (for GNUNET_Hash-es) in response + * to inquiries (for IBF_Key-s). * * @param cls the union operation * @param mh the message */ static void -handle_p2p_element_requests (void *cls, - const struct GNUNET_MessageHeader *mh) +handle_p2p_inquiry (void *cls, + const struct GNUNET_MessageHeader *mh) { struct Operation *op = cls; const struct IBF_Key *ibf_key; unsigned int num_keys; /* look up elements and send them */ - if (op->state->phase != PHASE_EXPECT_ELEMENTS_AND_REQUESTS) + if (op->state->phase != PHASE_INVENTORY_PASSIVE) { GNUNET_break_op (0); fail_union_operation (op); return; } num_keys = (ntohs (mh->size) - sizeof (struct GNUNET_MessageHeader)) - / sizeof (struct IBF_Key); + / sizeof (struct IBF_Key); if ((ntohs (mh->size) - sizeof (struct GNUNET_MessageHeader)) != num_keys * sizeof (struct IBF_Key)) { @@ -1211,12 +1169,149 @@ handle_p2p_element_requests (void *cls, ibf_key = (const struct IBF_Key *) &mh[1]; while (0 != num_keys--) { - send_elements_for_key (op, *ibf_key); + send_offers_for_key (op, *ibf_key); ibf_key++; } } + +static void +handle_p2p_demand (void *cls, + const struct GNUNET_MessageHeader *mh) +{ + struct Operation *op = cls; + struct ElementEntry *ee; + struct GNUNET_SET_ElementMessage *emsg; + const struct GNUNET_HashCode *hash; + unsigned int num_hashes; + struct GNUNET_MQ_Envelope *ev; + + num_hashes = (ntohs (mh->size) - sizeof (struct GNUNET_MessageHeader)) + / sizeof (struct GNUNET_HashCode); + if ((ntohs (mh->size) - sizeof (struct GNUNET_MessageHeader)) + != num_hashes * sizeof (struct GNUNET_HashCode)) + { + GNUNET_break_op (0); + fail_union_operation (op); + return; + } + + for (hash = (const struct GNUNET_HashCode *) &mh[1]; + num_hashes > 0; + hash++, num_hashes--) + { + ee = GNUNET_CONTAINER_multihashmap_get (op->spec->set->content->elements, hash); + if (NULL == ee) + { + /* Demand for non-existing element. */ + GNUNET_break_op (0); + fail_union_operation (op); + return; + } + if (GNUNET_NO == _GSS_is_element_of_operation (ee, op)) + { + /* Probably confused lazily copied sets. */ + GNUNET_break_op (0); + fail_union_operation (op); + return; + } + ev = GNUNET_MQ_msg_extra (emsg, ee->element.size, GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS); + memcpy (&emsg[1], ee->element.data, ee->element.size); + emsg->reserved = htons (0); + emsg->element_type = htons (ee->element.element_type); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "[OP %x] Sending demanded element (size %u, hash %s) to peer\n", + (void *) op, + (unsigned int) ee->element.size, + GNUNET_h2s (&ee->element_hash)); + GNUNET_MQ_send (op->mq, ev); + + switch (op->spec->result_mode) + { + case GNUNET_SET_RESULT_ADDED: + /* Nothing to do. */ + break; + case GNUNET_SET_RESULT_SYMMETRIC: + send_client_element (op, &ee->element, GNUNET_SET_STATUS_ADD_REMOTE); + break; + default: + /* Result mode not supported, should have been caught earlier. */ + GNUNET_break (0); + break; + } + } +} + + +/** + * Handle offers (of GNUNET_HashCode-s) and + * respond with demands (of GNUNET_HashCode-s). + * + * @param cls the union operation + * @param mh the message + */ +static void +handle_p2p_offer (void *cls, + const struct GNUNET_MessageHeader *mh) +{ + struct Operation *op = cls; + const struct GNUNET_HashCode *hash; + unsigned int num_hashes; + + /* look up elements and send them */ + if ( (op->state->phase != PHASE_INVENTORY_PASSIVE) && + (op->state->phase != PHASE_INVENTORY_ACTIVE)) + { + GNUNET_break_op (0); + fail_union_operation (op); + return; + } + num_hashes = (ntohs (mh->size) - sizeof (struct GNUNET_MessageHeader)) + / sizeof (struct GNUNET_HashCode); + if ((ntohs (mh->size) - sizeof (struct GNUNET_MessageHeader)) + != num_hashes * sizeof (struct GNUNET_HashCode)) + { + GNUNET_break_op (0); + fail_union_operation (op); + return; + } + + for (hash = (const struct GNUNET_HashCode *) &mh[1]; + num_hashes > 0; + hash++, num_hashes--) + { + struct ElementEntry *ee; + struct GNUNET_MessageHeader *demands; + struct GNUNET_MQ_Envelope *ev; + ee = GNUNET_CONTAINER_multihashmap_get (op->spec->set->content->elements, hash); + if (NULL != ee) + if (GNUNET_YES == _GSS_is_element_of_operation (ee, op)) + continue; + + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (op->state->demanded_hashes, hash)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Skipped sending duplicate demand\n"); + continue; + } + + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (op->state->demanded_hashes, + hash, + NULL, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "[OP %x] Requesting element (hash %s)\n", + (void *) op, GNUNET_h2s (hash)); + ev = GNUNET_MQ_msg_header_extra (demands, sizeof (struct GNUNET_HashCode), GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND); + *(struct GNUNET_HashCode *) &demands[1] = *hash; + GNUNET_MQ_send (op->mq, ev); + } +} + + /** * Handle a done message from a remote peer * @@ -1228,25 +1323,40 @@ handle_p2p_done (void *cls, const struct GNUNET_MessageHeader *mh) { struct Operation *op = cls; - struct GNUNET_MQ_Envelope *ev; - if (op->state->phase == PHASE_EXPECT_ELEMENTS_AND_REQUESTS) + if (op->state->phase == PHASE_INVENTORY_PASSIVE) { - /* we got all requests, but still have to send our elements as response */ - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "got DONE, sending final DONE after elements\n"); - op->state->phase = PHASE_FINISHED; - ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE); - GNUNET_MQ_send (op->mq, ev); + /* We got all requests, but still have to send our elements in response. */ + + op->state->phase = PHASE_FINISH_WAITING; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "got DONE (as passive partner), waiting for our demands to be satisfied\n"); + /* The active peer is done sending offers + * and inquiries. This means that all + * our responses to that (demands and offers) + * must be in flight (queued or in mesh). + * + * We should notify the active peer once + * all our demands are satisfied, so that the active + * peer can quit if we gave him everything. + */ + maybe_finish (op); return; } - if (op->state->phase == PHASE_EXPECT_ELEMENTS) + if (op->state->phase == PHASE_INVENTORY_ACTIVE) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "got final DONE\n"); - op->state->phase = PHASE_FINISHED; - finish_and_destroy (op); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "got DONE (as active partner), waiting to finish\n"); + /* All demands of the other peer are satisfied, + * and we processed all offers, thus we know + * exactly what our demands must be. + * + * We'll close the channel + * to the other peer once our demands are met. + */ + op->state->phase = PHASE_FINISH_CLOSING; + maybe_finish (op); return; } GNUNET_break_op (0); @@ -1268,12 +1378,14 @@ union_evaluate (struct Operation *op, struct GNUNET_MQ_Envelope *ev; struct OperationRequestMessage *msg; + GNUNET_assert (NULL == op->state); op->state = GNUNET_new (struct OperationState); + op->state->demanded_hashes = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); /* copy the current generation's strata estimator for this operation */ op->state->se = strata_estimator_dup (op->spec->set->state->se); /* we started the operation, thus we have to send the operation request */ op->state->phase = PHASE_EXPECT_SE; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "Initiating union operation evaluation\n"); ev = GNUNET_MQ_msg_nested_mh (msg, GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST, @@ -1291,10 +1403,10 @@ union_evaluate (struct Operation *op, ev); if (NULL != opaque_context) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "sent op request with context message\n"); else - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "sent op request without context message\n"); } @@ -1308,10 +1420,12 @@ union_evaluate (struct Operation *op, static void union_accept (struct Operation *op) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "accepting set union operation\n"); + GNUNET_assert (NULL == op->state); op->state = GNUNET_new (struct OperationState); op->state->se = strata_estimator_dup (op->spec->set->state->se); + op->state->demanded_hashes = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); /* kick off the operation */ send_strata_estimator (op); } @@ -1330,7 +1444,7 @@ union_set_create (void) { struct SetState *set_state; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "union set created\n"); set_state = GNUNET_new (struct SetState); set_state->se = strata_estimator_create (SE_STRATA_COUNT, @@ -1397,10 +1511,10 @@ int union_handle_p2p_message (struct Operation *op, const struct GNUNET_MessageHeader *mh) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "received p2p message (t: %u, s: %u)\n", - ntohs (mh->type), - ntohs (mh->size)); + //LOG (GNUNET_ERROR_TYPE_DEBUG, + // "received p2p message (t: %u, s: %u)\n", + // ntohs (mh->type), + // ntohs (mh->size)); switch (ntohs (mh->type)) { case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF: @@ -1410,29 +1524,36 @@ union_handle_p2p_message (struct Operation *op, case GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS: handle_p2p_elements (op, mh); break; - case GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENT_REQUESTS: - handle_p2p_element_requests (op, mh); + case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY: + handle_p2p_inquiry (op, mh); break; case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE: handle_p2p_done (op, mh); break; + case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER: + handle_p2p_offer (op, mh); + break; + case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND: + handle_p2p_demand (op, mh); + break; default: - /* something wrong with cadet's message handlers? */ + /* Something wrong with cadet's message handlers? */ GNUNET_assert (0); } return GNUNET_OK; } + /** - * handler for peer-disconnects, notifies the client - * about the aborted operation in case the op was not concluded + * Handler for peer-disconnects, notifies the client + * about the aborted operation in case the op was not concluded. * * @param op the destroyed operation */ static void union_peer_disconnect (struct Operation *op) { - if (PHASE_FINISHED != op->state->phase) + if (PHASE_DONE != op->state->phase) { struct GNUNET_MQ_Envelope *ev; struct GNUNET_SET_ResultMessage *msg; @@ -1444,19 +1565,27 @@ union_peer_disconnect (struct Operation *op) msg->element_type = htons (0); GNUNET_MQ_send (op->spec->set->client_mq, ev); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "other peer disconnected prematurely\n"); + LOG (GNUNET_ERROR_TYPE_WARNING, + "other peer disconnected prematurely, phase %u\n", + op->state->phase); _GSS_operation_destroy (op, GNUNET_YES); return; } // else: the session has already been concluded - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_DEBUG, "other peer disconnected (finished)\n"); if (GNUNET_NO == op->state->client_done_sent) - finish_and_destroy (op); + send_done_and_destroy (op); } + +/** + * Copy union-specific set state. + * + * @param set source set for copying the union state + * @return a copy of the union-specific set state + */ static struct SetState * union_copy_state (struct Set *set) { @@ -1469,6 +1598,7 @@ union_copy_state (struct Set *set) return new_state; } + /** * Get the table with implementing functions for * set union. diff --git a/src/set/gnunet-set-profiler.c b/src/set/gnunet-set-profiler.c index 1569c29b7..2ee1b762d 100644 --- a/src/set/gnunet-set-profiler.c +++ b/src/set/gnunet-set-profiler.c @@ -98,6 +98,7 @@ set_result_cb (void *cls, enum GNUNET_SET_Status status) { struct SetInfo *info = cls; + struct GNUNET_HashCode hash; GNUNET_assert (GNUNET_NO == info->done); switch (status) @@ -114,15 +115,22 @@ set_result_cb (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "failure\n"); GNUNET_SCHEDULER_shutdown (); return; - case GNUNET_SET_STATUS_OK: + case GNUNET_SET_STATUS_ADD_LOCAL: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: local element\n", info->id); break; + case GNUNET_SET_STATUS_ADD_REMOTE: + GNUNET_CRYPTO_hash (element->data, element->size, &hash); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: remote element %s\n", info->id, + GNUNET_h2s (&hash)); + // XXX: record and check + return; default: GNUNET_assert (0); } if (element->size != sizeof (struct GNUNET_HashCode)) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "wrong element size: %u\n", element->size); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "wrong element size: %u, expected %u\n", element->size, sizeof (struct GNUNET_HashCode)); GNUNET_assert (0); } @@ -180,6 +188,8 @@ static void handle_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Shutting down set profiler\n"); if (NULL != set_listener) { GNUNET_SET_listen_cancel (set_listener); @@ -209,11 +219,13 @@ handle_shutdown (void *cls, static void -run (void *cls, char *const *args, const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) { unsigned int i; struct GNUNET_HashCode hash; + struct GNUNET_HashCode hashhash; config = cfg; @@ -239,6 +251,9 @@ run (void *cls, char *const *args, const char *cfgfile, for (i = 0; i < num_a; i++) { GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, &hash); + GNUNET_CRYPTO_hash (&hash, sizeof (struct GNUNET_HashCode), &hashhash); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Set a: Created element %s\n", + GNUNET_h2s (&hashhash)); GNUNET_CONTAINER_multihashmap_put (info1.sent, &hash, NULL, GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); } @@ -246,6 +261,9 @@ run (void *cls, char *const *args, const char *cfgfile, for (i = 0; i < num_b; i++) { GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, &hash); + GNUNET_CRYPTO_hash (&hash, sizeof (struct GNUNET_HashCode), &hashhash); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Set b: Created element %s\n", + GNUNET_h2s (&hashhash)); GNUNET_CONTAINER_multihashmap_put (info2.sent, &hash, NULL, GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); } @@ -253,12 +271,14 @@ run (void *cls, char *const *args, const char *cfgfile, for (i = 0; i < num_c; i++) { GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, &hash); + GNUNET_CRYPTO_hash (&hash, sizeof (struct GNUNET_HashCode), &hashhash); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Set c: Created element %s\n", + GNUNET_h2s (&hashhash)); GNUNET_CONTAINER_multihashmap_put (common_sent, &hash, NULL, GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); } - /* use last hash for app id */ - app_id = hash; + GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, &app_id); /* FIXME: also implement intersection etc. */ info1.set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION); @@ -281,6 +301,17 @@ run (void *cls, char *const *args, const char *cfgfile, } +static void +pre_run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + if (0 != GNUNET_TESTING_peer_run ("set-profiler", + cfgfile, + &run, NULL)) + ret = 2; +} + + int main (int argc, char **argv) { @@ -295,13 +326,13 @@ main (int argc, char **argv) gettext_noop ("number of values"), GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_c }, { 'x', "operation", NULL, - gettext_noop ("oeration to execute"), + gettext_noop ("operation to execute"), GNUNET_YES, &GNUNET_GETOPT_set_string, &op_str }, GNUNET_GETOPT_OPTION_END }; - GNUNET_PROGRAM_run (argc, argv, "gnunet-set-profiler", - "help", - options, &run, NULL); + GNUNET_PROGRAM_run2 (argc, argv, "gnunet-set-profiler", + "help", + options, &pre_run, NULL, GNUNET_YES); return ret; } diff --git a/src/set/set_api.c b/src/set/set_api.c index 51a494d7f..16aa87cd0 100644 --- a/src/set/set_api.c +++ b/src/set/set_api.c @@ -398,40 +398,49 @@ handle_result (void *cls, "Ignoring result from canceled operation\n"); return; } - if (GNUNET_SET_STATUS_OK != result_status) + + switch (result_status) { - /* status is not #GNUNET_SET_STATUS_OK => there's no attached element, - * and this is the last result message we get */ - GNUNET_MQ_assoc_remove (set->mq, - ntohl (msg->request_id)); - GNUNET_CONTAINER_DLL_remove (set->ops_head, - set->ops_tail, - oh); - if ( (GNUNET_YES == set->destroy_requested) && - (NULL == set->ops_head) ) - GNUNET_SET_destroy (set); - if (NULL != oh->result_cb) - oh->result_cb (oh->result_cls, - NULL, - result_status); - switch (result_status) - { case GNUNET_SET_STATUS_OK: case GNUNET_SET_STATUS_ADD_LOCAL: case GNUNET_SET_STATUS_ADD_REMOTE: - break; + goto do_element; case GNUNET_SET_STATUS_FAILURE: - oh->result_cb = NULL; - break; - case GNUNET_SET_STATUS_HALF_DONE: - break; case GNUNET_SET_STATUS_DONE: - oh->result_cb = NULL; - break; - } - GNUNET_free (oh); - return; + goto do_final; + case GNUNET_SET_STATUS_HALF_DONE: + /* not used anymore */ + GNUNET_assert (0); + } + +do_final: + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Treating result as final status\n"); + GNUNET_MQ_assoc_remove (set->mq, + ntohl (msg->request_id)); + GNUNET_CONTAINER_DLL_remove (set->ops_head, + set->ops_tail, + oh); + if (NULL != oh->result_cb) + { + oh->result_cb (oh->result_cls, + NULL, + result_status); } + else + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "No callback for final status\n"); + } + if ( (GNUNET_YES == set->destroy_requested) && + (NULL == set->ops_head) ) + GNUNET_SET_destroy (set); + GNUNET_free (oh); + return; + +do_element: + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Treating result as element\n"); e.data = &msg[1]; e.size = ntohs (mh->size) - sizeof (struct GNUNET_SET_ResultMessage); e.element_type = msg->element_type; diff --git a/src/set/test_set.conf b/src/set/test_set.conf index f3c0770fe..4f32b8854 100644 --- a/src/set/test_set.conf +++ b/src/set/test_set.conf @@ -1,11 +1,12 @@ +@INLINE@ ../../contrib/no_forcestart.conf + [PATHS] GNUNET_TEST_HOME = /tmp/test-gnunet-set/ [set] AUTOSTART = YES -@UNIXONLY@ PORT = 2106 #PREFIX = valgrind -#PREFIX = valgrind -v --leak-check=full +PREFIX = valgrind --leak-check=full #PREFIX = gdbserver :1234 OPTIONS = -L INFO @@ -21,38 +22,13 @@ USE_LOCALADDR = YES [peerinfo] NO_IO = YES -[nse] -WORKBITS = 0 - -[hostlist] -FORCESTART = NO -AUTOSTART = NO - -[fs] -FORCESTART = NO -AUTOSTART = NO - -[vpn] -FORCESTART = NO -AUTOSTART = NO - -[revocation] -FORCESTART = NO -AUTOSTART = NO - -[gns] -FORCESTART = NO -AUTOSTART = NO - -[namestore] -FORCESTART = NO -AUTOSTART = NO +[nat] +# Use addresses from the local network interfaces (inluding loopback, but also others) +USE_LOCALADDR = YES -[namecache] -FORCESTART = NO -AUTOSTART = NO +# Disable IPv6 support +DISABLEV6 = NO -[topology] -FORCESTART = NO -AUTOSTART = NO +# Do we use addresses from localhost address ranges? (::1, 127.0.0.0/8) +RETURN_LOCAL_ADDRESSES = YES diff --git a/src/set/test_set_union_result_full.c b/src/set/test_set_union_result_full.c deleted file mode 100644 index 7901c52be..000000000 --- a/src/set/test_set_union_result_full.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file set/test_set_union_result_full.c - * @brief testcase for full result mode of the union set operation - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_testing_lib.h" -#include "gnunet_set_service.h" - - -/** - * Value to return from #main(). - */ -static int ret; - -static struct GNUNET_PeerIdentity local_id; - -static struct GNUNET_HashCode app_id; -static struct GNUNET_SET_Handle *set1; - -static struct GNUNET_SET_Handle *set2; - -static struct GNUNET_SET_ListenHandle *listen_handle; - -static const struct GNUNET_CONFIGURATION_Handle *config; - -static int iter_count; - -/** - * Are we testing correctness for the empty set union? - */ -static int empty; - -/** - * Number of elements found in set 1 - */ -static unsigned int count_set1; - -/** - * Number of elements found in set 2 - */ -static unsigned int count_set2; - - -static void -result_cb_set1 (void *cls, - const struct GNUNET_SET_Element *element, - enum GNUNET_SET_Status status) -{ - switch (status) - { - case GNUNET_SET_STATUS_OK: - count_set1++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "set 1: got element\n"); - break; - case GNUNET_SET_STATUS_FAILURE: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "set 1: failure\n"); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - break; - case GNUNET_SET_STATUS_DONE: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "set 1: done\n"); - GNUNET_SET_destroy (set1); - set1 = NULL; - if (NULL == set2) - GNUNET_SCHEDULER_shutdown (); - break; - default: - GNUNET_assert (0); - } -} - - -static void -result_cb_set2 (void *cls, - const struct GNUNET_SET_Element *element, - enum GNUNET_SET_Status status) -{ - switch (status) - { - case GNUNET_SET_STATUS_OK: - count_set2++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "set 2: got element\n"); - break; - case GNUNET_SET_STATUS_FAILURE: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "set 2: failure\n"); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - break; - case GNUNET_SET_STATUS_DONE: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "set 2: done\n"); - GNUNET_SET_destroy (set2); - set2 = NULL; - if (NULL == set1) - GNUNET_SCHEDULER_shutdown (); - break; - default: - GNUNET_assert (0); - } -} - - -static void -listen_cb (void *cls, - const struct GNUNET_PeerIdentity *other_peer, - const struct GNUNET_MessageHeader *context_msg, - struct GNUNET_SET_Request *request) -{ - struct GNUNET_SET_OperationHandle *oh; - - GNUNET_assert (NULL != context_msg); - GNUNET_assert (ntohs (context_msg->type) == GNUNET_MESSAGE_TYPE_TEST); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "listen cb called\n"); - GNUNET_SET_listen_cancel (listen_handle); - oh = GNUNET_SET_accept (request, - GNUNET_SET_RESULT_FULL, - &result_cb_set2, - NULL); - GNUNET_SET_commit (oh, set2); -} - - -/** - * Start the set operation. - * - * @param cls closure, unused - */ -static void -start (void *cls) -{ - struct GNUNET_SET_OperationHandle *oh; - struct GNUNET_MessageHeader context_msg; - - context_msg.size = htons (sizeof context_msg); - context_msg.type = htons (GNUNET_MESSAGE_TYPE_TEST); - - listen_handle = GNUNET_SET_listen (config, - GNUNET_SET_OPERATION_UNION, - &app_id, - &listen_cb, NULL); - oh = GNUNET_SET_prepare (&local_id, - &app_id, - &context_msg, - GNUNET_SET_RESULT_FULL, - &result_cb_set1, NULL); - GNUNET_SET_commit (oh, set1); -} - - -/** - * Initialize the second set, continue - * - * @param cls closure, unused - */ -static void -init_set2 (void *cls) -{ - struct GNUNET_SET_Element element; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "initializing set 2\n"); - if (empty) - { - start (NULL); - return; - } - element.element_type = 0; - element.data = "hello"; - element.size = strlen(element.data); - GNUNET_SET_add_element (set2, - &element, - NULL, - NULL); - element.data = "quux"; - element.size = strlen(element.data); - GNUNET_SET_add_element (set2, - &element, - NULL, - NULL); - element.data = "baz"; - element.size = strlen(element.data); - GNUNET_SET_add_element (set2, - &element, - &start, NULL); -} - - -/** - * Initialize the first set, continue. - */ -static void -init_set1 (void) -{ - struct GNUNET_SET_Element element; - - if (empty) - { - init_set2 (NULL); - return; - } - element.element_type = 0; - element.data = "hello"; - element.size = strlen(element.data); - GNUNET_SET_add_element (set1, - &element, - NULL, - NULL); - element.data = "bar"; - element.size = strlen(element.data); - GNUNET_SET_add_element (set1, - &element, - &init_set2, - NULL); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "initialized set 1\n"); -} - - -static int -iter_cb (void *cls, - const struct GNUNET_SET_Element *element) -{ - if (NULL == element) - { - GNUNET_assert (iter_count == 3); - GNUNET_SET_destroy (cls); - return GNUNET_YES; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "iter: got element\n"); - iter_count++; - return GNUNET_YES; -} - - -static void -test_iter () -{ - struct GNUNET_SET_Element element; - struct GNUNET_SET_Handle *iter_set; - - iter_count = 0; - iter_set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION); - element.element_type = 0; - element.data = "hello"; - element.size = strlen(element.data); - GNUNET_SET_add_element (iter_set, &element, NULL, NULL); - element.data = "bar"; - element.size = strlen(element.data); - GNUNET_SET_add_element (iter_set, &element, NULL, NULL); - element.data = "quux"; - element.size = strlen(element.data); - GNUNET_SET_add_element (iter_set, &element, NULL, NULL); - - GNUNET_SET_iterate (iter_set, - &iter_cb, - iter_set); -} - - -/** - * Signature of the main function of a task. - * - * @param cls closure - * @param tc context information (why was this task triggered now) - */ -static void -timeout_fail (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - return; - GNUNET_SCHEDULER_shutdown (); - ret = 1; -} - - -/** - * Signature of the 'main' function for a (single-peer) testcase that - * is run using 'GNUNET_TESTING_peer_run'. - * - * @param cls closure - * @param cfg configuration of the peer that was started - * @param peer identity of the peer that was created - */ -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5), - &timeout_fail, - NULL); - - config = cfg; - GNUNET_TESTING_peer_get_identity (peer, - &local_id); - - test_iter (); - - set1 = GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_UNION); - set2 = GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_UNION); - GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &app_id); - - /* test the real set reconciliation */ - init_set1 (); -} - - -int -main (int argc, char **argv) -{ - empty = 1; - if (0 != GNUNET_TESTING_peer_run ("test_set_api", - "test_set.conf", - &run, NULL)) - { - return 1; - } - GNUNET_assert (0 == count_set1); - GNUNET_assert (0 == count_set2); - empty = 0; - if (0 != GNUNET_TESTING_peer_run ("test_set_api", - "test_set.conf", - &run, NULL)) - { - return 1; - } - GNUNET_assert (4 == count_set1); - GNUNET_assert (4 == count_set2); - return ret; -} diff --git a/src/set/test_set_union_result_symmetric.c b/src/set/test_set_union_result_symmetric.c new file mode 100644 index 000000000..7f119b528 --- /dev/null +++ b/src/set/test_set_union_result_symmetric.c @@ -0,0 +1,363 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @file set/test_set_union_result_smmetric + * @brief testcase for symmetric result mode of the union set operation + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" +#include "gnunet_set_service.h" + + +/** + * Value to return from #main(). + */ +static int ret; + +static struct GNUNET_PeerIdentity local_id; + +static struct GNUNET_HashCode app_id; +static struct GNUNET_SET_Handle *set1; + +static struct GNUNET_SET_Handle *set2; + +static struct GNUNET_SET_ListenHandle *listen_handle; + +static const struct GNUNET_CONFIGURATION_Handle *config; + +static int iter_count; + +/** + * Are we testing correctness for the empty set union? + */ +static int empty; + +/** + * Number of elements found in set 1 + */ +static unsigned int count_set1; + +/** + * Number of elements found in set 2 + */ +static unsigned int count_set2; + + +static void +result_cb_set1 (void *cls, + const struct GNUNET_SET_Element *element, + enum GNUNET_SET_Status status) +{ + switch (status) + { + case GNUNET_SET_STATUS_ADD_LOCAL: + count_set1++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "set 1: got element\n"); + break; + case GNUNET_SET_STATUS_FAILURE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "set 1: failure\n"); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + break; + case GNUNET_SET_STATUS_DONE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "set 1: done\n"); + GNUNET_SET_destroy (set1); + set1 = NULL; + if (NULL == set2) + GNUNET_SCHEDULER_shutdown (); + break; + case GNUNET_SET_STATUS_ADD_REMOTE: + break; + default: + GNUNET_assert (0); + } +} + + +static void +result_cb_set2 (void *cls, + const struct GNUNET_SET_Element *element, + enum GNUNET_SET_Status status) +{ + switch (status) + { + case GNUNET_SET_STATUS_ADD_LOCAL: + count_set2++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "set 2: got element\n"); + break; + case GNUNET_SET_STATUS_FAILURE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "set 2: failure\n"); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + break; + case GNUNET_SET_STATUS_DONE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "set 2: done\n"); + GNUNET_SET_destroy (set2); + set2 = NULL; + if (NULL == set1) + GNUNET_SCHEDULER_shutdown (); + break; + case GNUNET_SET_STATUS_ADD_REMOTE: + break; + default: + GNUNET_assert (0); + } +} + + +static void +listen_cb (void *cls, + const struct GNUNET_PeerIdentity *other_peer, + const struct GNUNET_MessageHeader *context_msg, + struct GNUNET_SET_Request *request) +{ + struct GNUNET_SET_OperationHandle *oh; + + GNUNET_assert (NULL != context_msg); + GNUNET_assert (ntohs (context_msg->type) == GNUNET_MESSAGE_TYPE_TEST); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "listen cb called\n"); + GNUNET_SET_listen_cancel (listen_handle); + oh = GNUNET_SET_accept (request, + GNUNET_SET_RESULT_SYMMETRIC, + &result_cb_set2, + NULL); + GNUNET_SET_commit (oh, set2); +} + + +/** + * Start the set operation. + * + * @param cls closure, unused + */ +static void +start (void *cls) +{ + struct GNUNET_SET_OperationHandle *oh; + struct GNUNET_MessageHeader context_msg; + + context_msg.size = htons (sizeof context_msg); + context_msg.type = htons (GNUNET_MESSAGE_TYPE_TEST); + + listen_handle = GNUNET_SET_listen (config, + GNUNET_SET_OPERATION_UNION, + &app_id, + &listen_cb, NULL); + oh = GNUNET_SET_prepare (&local_id, + &app_id, + &context_msg, + GNUNET_SET_RESULT_SYMMETRIC, + &result_cb_set1, NULL); + GNUNET_SET_commit (oh, set1); +} + + +/** + * Initialize the second set, continue + * + * @param cls closure, unused + */ +static void +init_set2 (void *cls) +{ + struct GNUNET_SET_Element element; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "initializing set 2\n"); + if (empty) + { + start (NULL); + return; + } + element.element_type = 0; + element.data = "hello"; + element.size = strlen(element.data); + GNUNET_SET_add_element (set2, + &element, + NULL, + NULL); + element.data = "quux"; + element.size = strlen(element.data); + GNUNET_SET_add_element (set2, + &element, + NULL, + NULL); + element.data = "baz"; + element.size = strlen(element.data); + GNUNET_SET_add_element (set2, + &element, + &start, NULL); +} + + +/** + * Initialize the first set, continue. + */ +static void +init_set1 (void) +{ + struct GNUNET_SET_Element element; + + if (empty) + { + init_set2 (NULL); + return; + } + element.element_type = 0; + element.data = "hello"; + element.size = strlen(element.data); + GNUNET_SET_add_element (set1, + &element, + NULL, + NULL); + element.data = "bar"; + element.size = strlen(element.data); + GNUNET_SET_add_element (set1, + &element, + &init_set2, + NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "initialized set 1\n"); +} + + +static int +iter_cb (void *cls, + const struct GNUNET_SET_Element *element) +{ + if (NULL == element) + { + GNUNET_assert (iter_count == 3); + GNUNET_SET_destroy (cls); + return GNUNET_YES; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "iter: got element\n"); + iter_count++; + return GNUNET_YES; +} + + +static void +test_iter () +{ + struct GNUNET_SET_Element element; + struct GNUNET_SET_Handle *iter_set; + + iter_count = 0; + iter_set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION); + element.element_type = 0; + element.data = "hello"; + element.size = strlen(element.data); + GNUNET_SET_add_element (iter_set, &element, NULL, NULL); + element.data = "bar"; + element.size = strlen(element.data); + GNUNET_SET_add_element (iter_set, &element, NULL, NULL); + element.data = "quux"; + element.size = strlen(element.data); + GNUNET_SET_add_element (iter_set, &element, NULL, NULL); + + GNUNET_SET_iterate (iter_set, + &iter_cb, + iter_set); +} + + +/** + * Signature of the main function of a task. + * + * @param cls closure + * @param tc context information (why was this task triggered now) + */ +static void +timeout_fail (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + GNUNET_SCHEDULER_shutdown (); + ret = 1; +} + + +/** + * Signature of the 'main' function for a (single-peer) testcase that + * is run using 'GNUNET_TESTING_peer_run'. + * + * @param cls closure + * @param cfg configuration of the peer that was started + * @param peer identity of the peer that was created + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5), + &timeout_fail, + NULL); + + config = cfg; + GNUNET_TESTING_peer_get_identity (peer, + &local_id); + + test_iter (); + + set1 = GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_UNION); + set2 = GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_UNION); + GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &app_id); + + /* test the real set reconciliation */ + init_set1 (); +} + + +int +main (int argc, char **argv) +{ + empty = 1; + if (0 != GNUNET_TESTING_peer_run ("test_set_api", + "test_set.conf", + &run, NULL)) + { + return 1; + } + GNUNET_assert (0 == count_set1); + GNUNET_assert (0 == count_set2); + empty = 0; + if (0 != GNUNET_TESTING_peer_run ("test_set_api", + "test_set.conf", + &run, NULL)) + { + return 1; + } + GNUNET_assert (2 == count_set1); + GNUNET_assert (1 == count_set2); + return ret; +} -- cgit v1.2.3