X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fnse%2Fgnunet-service-nse.c;h=d7fb439e2269abccbf2678925ea196a2f6aca488;hb=a60b958f984d08525b636a2c7eae564ebec54ae6;hp=874b8b6f6738e1f9603163b2811df49e73afa73c;hpb=0265e48ab879a6593bc531de3bc3a82d2fbfef30;p=oweals%2Fgnunet.git diff --git a/src/nse/gnunet-service-nse.c b/src/nse/gnunet-service-nse.c index 874b8b6f6..d7fb439e2 100644 --- a/src/nse/gnunet-service-nse.c +++ b/src/nse/gnunet-service-nse.c @@ -1,116 +1,206 @@ /* - This file is part of GNUnet. - (C) 2009, 2010, 2011 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. + This file is part of GNUnet. + (C) 2009, 2010, 2011, 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ /** * @file nse/gnunet-service-nse.c * @brief network size estimation service * @author Nathan Evans + * @author Christian Grothoff * * The purpose of this service is to estimate the size of the network. * Given a specified interval, each peer hashes the most recent - * timestamp which is evenly divisible by that interval. This hash - * is compared in distance to the peer identity to choose an offset. - * The closer the peer identity to the hashed timestamp, the earlier - * the peer sends out a "nearest peer" message. The closest peer's + * timestamp which is evenly divisible by that interval. This hash is + * compared in distance to the peer identity to choose an offset. The + * closer the peer identity to the hashed timestamp, the earlier the + * peer sends out a "nearest peer" message. The closest peer's * message should thus be received before any others, which stops * those peer from sending their messages at a later duration. So - * every peer should receive the same nearest peer message, and - * from this can calculate the expected number of peers in the - * network. - * + * every peer should receive the same nearest peer message, and from + * this can calculate the expected number of peers in the network. */ #include "platform.h" -#include "gnunet_client_lib.h" +#include +#include "gnunet_util_lib.h" #include "gnunet_constants.h" -#include "gnunet_container_lib.h" #include "gnunet_protocols.h" #include "gnunet_signatures.h" -#include "gnunet_service_lib.h" -#include "gnunet_server_lib.h" #include "gnunet_statistics_service.h" #include "gnunet_core_service.h" -#include "gnunet_time_lib.h" #include "gnunet_nse_service.h" #include "nse.h" -#define DEFAULT_HISTORY_SIZE 50 +/** + * Should messages be delayed randomly? This option should be set to + * GNUNET_NO only for experiments, not in production. It should also + * be removed once the initial experiments have been completed. + */ +#define USE_RANDOM_DELAYS GNUNET_YES + +/** + * Should we generate a histogram with the time stamps of when we received + * NSE messages to disk? (for performance evaluation only, not useful in + * production). The associated code should also probably be removed + * once we're done with experiments. + */ +#define ENABLE_HISTOGRAM GNUNET_NO + +/** + * Over how many values do we calculate the weighted average? + */ +#define HISTORY_SIZE 64 + +/** + * Message priority to use. + */ +#define NSE_PRIORITY 5 + +#if FREEBSD +#define log2(a) (log(a)/log(2)) +#endif + +/** + * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits. + */ +static unsigned long long nse_work_required; + +/** + * Interval for sending network size estimation flood requests. + */ +static struct GNUNET_TIME_Relative gnunet_nse_interval; -#define DEFAULT_CORE_QUEUE_SIZE 32 +/** + * Interval between proof find runs. + */ +static struct GNUNET_TIME_Relative proof_find_delay; -#define DEFAULT_NSE_PRIORITY 5 +#if ENABLE_HISTOGRAM +/** + * Handle for writing when we received messages to disk. + */ +static struct GNUNET_BIO_WriteHandle *wh; +#endif -#define DO_FORWARD GNUNET_YES /** - * Entry in the list of clients which - * should be notified upon a new network - * size estimate calculation. + * Per-peer information. */ -struct ClientListEntry +struct NSEPeerEntry { + /** - * Pointer to previous entry + * Core handle for sending messages to this peer. */ - struct ClientListEntry *prev; + struct GNUNET_CORE_TransmitHandle *th; /** - * Pointer to next entry + * What is the identity of the peer? */ - struct ClientListEntry *next; + struct GNUNET_PeerIdentity id; /** - * Client to notify. + * Task scheduled to send message to this peer. */ - struct GNUNET_SERVER_Client *client; + GNUNET_SCHEDULER_TaskIdentifier transmit_task; + + /** + * Did we receive or send a message about the previous round + * to this peer yet? GNUNET_YES if the previous round has + * been taken care of. + */ + int previous_round; + +#if ENABLE_HISTOGRAM + + /** + * Amount of messages received from this peer on this round. + */ + unsigned int received_messages; + + /** + * Amount of messages transmitted to this peer on this round. + */ + unsigned int transmitted_messages; + + /** + * Which size did we tell the peer the network is? + */ + unsigned int last_transmitted_size; + +#endif + }; + +GNUNET_NETWORK_STRUCT_BEGIN + /** - * Per-peer information. + * Network size estimate reply; sent when "this" + * peer's timer has run out before receiving a + * valid reply from another peer. */ -struct NSEPeerEntry +struct GNUNET_NSE_FloodMessage { /** - * Next peer entry (DLL) + * Type: GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD */ - struct NSEPeerEntry *next; + struct GNUNET_MessageHeader header; /** - * Prev peer entry (DLL) + * Number of hops this message has taken so far. */ - struct NSEPeerEntry *prev; + uint32_t hop_count GNUNET_PACKED; /** - * Pending message for this peer. + * Purpose. */ - struct GNUNET_MessageHeader *pending_message; + struct GNUNET_CRYPTO_RsaSignaturePurpose purpose; /** - * Core handle for sending messages to this peer. + * The current timestamp value (which all + * peers should agree on). */ - struct GNUNET_CORE_TransmitHandle *th; + struct GNUNET_TIME_AbsoluteNBO timestamp; /** - * What is the identity of the peer? + * Number of matching bits between the hash + * of timestamp and the initiator's public + * key. */ - struct GNUNET_PeerIdentity id; + uint32_t matching_bits GNUNET_PACKED; + + /** + * Public key of the originator. + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; + + /** + * Proof of work, causing leading zeros when hashed with pkey. + */ + uint64_t proof_of_work GNUNET_PACKED; + + /** + * Signature (over range specified in purpose). + */ + struct GNUNET_CRYPTO_RsaSignature signature; }; +GNUNET_NETWORK_STRUCT_END /** * Handle to our current configuration. @@ -128,89 +218,71 @@ static struct GNUNET_STATISTICS_Handle *stats; static struct GNUNET_CORE_Handle *coreAPI; /** - * Head of global list of peers. - */ -static struct NSEPeerEntry *peers_head; - -/** - * Head of global list of clients. + * Map of all connected peers. */ -static struct NSEPeerEntry *peers_tail; +static struct GNUNET_CONTAINER_MultiHashMap *peers; /** - * Head of global list of clients. + * The current network size estimate. Number of bits matching on + * average thus far. */ -static struct ClientListEntry *cle_head; +static double current_size_estimate; /** - * Tail of global list of clients. + * The standard deviation of the last HISTORY_SIZE network + * size estimates. */ -static struct ClientListEntry *cle_tail; +static double current_std_dev = NAN; /** - * The current network size estimate. - * Number of bits matching on average - * thus far. + * Current hop counter estimate (estimate for network diameter). */ -static double current_size_estimate; +static uint32_t hop_count_max; /** - * The standard deviation of the last - * DEFAULT_HISTORY_SIZE network size estimates. + * Message for the next round, if we got any. */ -static double current_std_dev; +static struct GNUNET_NSE_FloodMessage next_message; /** - * Array of the last DEFAULT_HISTORY_SIZE - * network size estimates (matching bits, actually). + * Array of recent size estimate messages. */ -static unsigned int size_estimates[DEFAULT_HISTORY_SIZE]; +static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE]; /** - * Array of size estimate messages. + * Index of most recent estimate. */ -static struct GNUNET_NSE_FloodMessage - size_estimate_messages[DEFAULT_HISTORY_SIZE]; +static unsigned int estimate_index; /** - * Index of most recent estimate. + * Number of valid entries in the history. */ -static unsigned int estimate_index; +static unsigned int estimate_count; /** - * Task scheduled to send flood message. + * Task scheduled to update our flood message for the next round. */ static GNUNET_SCHEDULER_TaskIdentifier flood_task; /** - * Task to schedule flood message and update state. + * Task scheduled to compute our proof. */ -static GNUNET_SCHEDULER_TaskIdentifier schedule_flood_task; +static GNUNET_SCHEDULER_TaskIdentifier proof_task; /** * Notification context, simplifies client broadcasts. */ static struct GNUNET_SERVER_NotificationContext *nc; -/** - * The previous major time. - */ -static struct GNUNET_TIME_Absolute previous_timestamp; - /** * The next major time. */ static struct GNUNET_TIME_Absolute next_timestamp; /** - * Base increment of time to add to send time. - */ -static struct GNUNET_TIME_Relative increment; - -/** - * The current network size estimate message. + * The current major time. */ -static struct GNUNET_NSE_ClientMessage current_estimate_message; +static struct GNUNET_TIME_Absolute current_timestamp; /** * The public key of this peer. @@ -228,9 +300,109 @@ static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; static struct GNUNET_PeerIdentity my_identity; /** - * Our flood message, updated whenever a flood is sent. + * Proof of work for this peer. + */ +static uint64_t my_proof; + +/** + * Handle to this serivce's server. */ -static struct GNUNET_NSE_FloodMessage flood_message; +static struct GNUNET_SERVER_Handle *srv; + +/** + * Hostkey generation context + */ +static struct GNUNET_CRYPTO_RsaKeyGenerationContext *keygen; + + +/** + * Initialize a message to clients with the current network + * size estimate. + * + * @param em message to fill in + */ +static void +setup_estimate_message (struct GNUNET_NSE_ClientMessage *em) +{ + unsigned int i; + unsigned int j; + double mean; + double sum; + double std_dev; + double variance; + double val; + double nsize; + +#define WEST 1 + /* Weighted incremental algorithm for stddev according to West (1979) */ +#if WEST + double sumweight; + double weight; + double q; + double r; + double temp; + + mean = 0.0; + sum = 0.0; + sumweight = 0.0; + variance = 0.0; + for (i = 0; i < estimate_count; i++) + { + j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE; + val = htonl (size_estimate_messages[j].matching_bits); + weight = estimate_count + 1 - i; + + temp = weight + sumweight; + q = val - mean; + r = q * weight / temp; + mean += r; + sum += sumweight * q * r; + sumweight = temp; + } + if (estimate_count > 0) + variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0); +#else + /* trivial version for debugging */ + double vsq; + + /* non-weighted trivial version */ + sum = 0.0; + vsq = 0.0; + variance = 0.0; + mean = 0.0; + + for (i = 0; i < estimate_count; i++) + { + j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE; + val = htonl (size_estimate_messages[j].matching_bits); + sum += val; + vsq += val * val; + } + if (0 != estimate_count) + { + mean = sum / estimate_count; + variance = (vsq - mean * sum) / (estimate_count - 1.0); // terrible for numerical stability... + } +#endif + if (variance >= 0) + std_dev = sqrt (variance); + else + std_dev = variance; /* must be infinity due to estimate_count == 0 */ + current_std_dev = std_dev; + current_size_estimate = mean; + + em->header.size = htons (sizeof (struct GNUNET_NSE_ClientMessage)); + em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE); + em->reserved = htonl (0); + em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); + double se = mean - 0.332747; + nsize = log2 (GNUNET_CONTAINER_multihashmap_size (peers) + 1); + em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se, nsize)); + em->std_deviation = GNUNET_hton_double (std_dev); + GNUNET_STATISTICS_set (stats, "# nodes in the network (estimate)", + (uint64_t) pow (2, mean - 1.0 / 3.0), GNUNET_NO); +} + /** * Handler for START message from client, triggers an @@ -243,21 +415,146 @@ static struct GNUNET_NSE_FloodMessage flood_message; * @param message the message received */ static void -handle_start_message(void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) +handle_start_message (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) { - if ((ntohs (message->size) != sizeof(struct GNUNET_MessageHeader)) - || (ntohs (message->type) != GNUNET_MESSAGE_TYPE_NSE_START)) - return; + struct GNUNET_NSE_ClientMessage em; -#if DEBUG_NSE - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "NSE", - "Received START message from client\n"); -#endif + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received START message from client\n"); GNUNET_SERVER_notification_context_add (nc, client); + setup_estimate_message (&em); + GNUNET_SERVER_notification_context_unicast (nc, client, &em.header, + GNUNET_YES); GNUNET_SERVER_receive_done (client, GNUNET_OK); } + +/** + * How long should we delay a message to go the given number of + * matching bits? + * + * @param matching_bits number of matching bits to consider + */ +static double +get_matching_bits_delay (uint32_t matching_bits) +{ + /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */ + // S is next_timestamp (ignored in return value) + // f is frequency (gnunet_nse_interval) + // x is matching_bits + // p' is current_size_estimate + return ((double) gnunet_nse_interval.rel_value / (double) 2.0) - + ((gnunet_nse_interval.rel_value / M_PI) * + atan (matching_bits - current_size_estimate)); +} + + +/** + * What delay randomization should we apply for a given number of matching bits? + * + * @param matching_bits number of matching bits + * @return random delay to apply + */ +static struct GNUNET_TIME_Relative +get_delay_randomization (uint32_t matching_bits) +{ +#if USE_RANDOM_DELAYS + struct GNUNET_TIME_Relative ret; + uint32_t i; + double d; + + d = get_matching_bits_delay (matching_bits); + i = (uint32_t) (d / (double) (hop_count_max + 1)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Randomizing flood using latencies up to %u ms\n", + (unsigned int) i); + ret.rel_value = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1); + return ret; +#else + return GNUNET_TIME_UNIT_ZERO; +#endif +} + + +/** + * Get the number of matching bits that the given timestamp has to the given peer ID. + * + * @param timestamp time to generate key + * @param id peer identity to compare with + * @return number of matching bits + */ +static uint32_t +get_matching_bits (struct GNUNET_TIME_Absolute timestamp, + const struct GNUNET_PeerIdentity *id) +{ + struct GNUNET_HashCode timestamp_hash; + + GNUNET_CRYPTO_hash (×tamp.abs_value, sizeof (timestamp.abs_value), + ×tamp_hash); + return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash, &id->hashPubKey); +} + + +/** + * Get the transmission delay that should be applied for a + * particular round. + * + * @param round_offset -1 for the previous round (random delay between 0 and 50ms) + * 0 for the current round (based on our proximity to time key) + * @return delay that should be applied + */ +static struct GNUNET_TIME_Relative +get_transmit_delay (int round_offset) +{ + struct GNUNET_TIME_Relative ret; + struct GNUNET_TIME_Absolute tgt; + double dist_delay; + uint32_t matching_bits; + + switch (round_offset) + { + case -1: + /* previous round is randomized between 0 and 50 ms */ +#if USE_RANDOM_DELAYS + ret.rel_value = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50); +#else + ret = GNUNET_TIME_UNIT_ZERO; +#endif + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting previous round behind schedule in %llu ms\n", + (unsigned long long) ret.rel_value); + return ret; + case 0: + /* current round is based on best-known matching_bits */ + matching_bits = + ntohl (size_estimate_messages[estimate_index].matching_bits); + dist_delay = get_matching_bits_delay (matching_bits); + dist_delay += get_delay_randomization (matching_bits).rel_value; + ret.rel_value = (uint64_t) dist_delay; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "For round %llu, delay for %u matching bits is %llu ms\n", + (unsigned long long) current_timestamp.abs_value, + (unsigned int) matching_bits, + (unsigned long long) ret.rel_value); + /* now consider round start time and add delay to it */ + tgt = GNUNET_TIME_absolute_add (current_timestamp, ret); + return GNUNET_TIME_absolute_get_remaining (tgt); + } + GNUNET_break (0); + return GNUNET_TIME_UNIT_FOREVER_REL; +} + + +/** + * Task that triggers a NSE P2P transmission. + * + * @param cls the 'struct NSEPeerEntry' + * @param tc scheduler context + */ +static void +transmit_task_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + /** * Called when core is ready to send a message we asked for * out to the destination. @@ -268,313 +565,358 @@ handle_start_message(void *cls, struct GNUNET_SERVER_Client *client, * @return number of bytes written to buf */ static size_t -transmit_ready(void *cls, size_t size, void *buf) +transmit_ready (void *cls, size_t size, void *buf) { struct NSEPeerEntry *peer_entry = cls; - char *cbuf = buf; + unsigned int idx; - size_t msize; peer_entry->th = NULL; -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s: transmit_ready called\n", - GNUNET_i2s (&my_identity)); + if (NULL == buf) + { + /* client disconnected */ + return 0; + } + GNUNET_assert (size >= sizeof (struct GNUNET_NSE_FloodMessage)); + idx = estimate_index; + if (GNUNET_NO == peer_entry->previous_round) + { + idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE; + peer_entry->previous_round = GNUNET_YES; + peer_entry->transmit_task = + GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0), &transmit_task_cb, + peer_entry); + } + if ((0 == ntohl (size_estimate_messages[idx].hop_count)) && + (GNUNET_SCHEDULER_NO_TASK != proof_task)) + { + GNUNET_STATISTICS_update (stats, + "# flood messages not generated (no proof yet)", + 1, GNUNET_NO); + return 0; + } + if (0 == ntohs (size_estimate_messages[idx].header.size)) + { + GNUNET_STATISTICS_update (stats, + "# flood messages not generated (lack of history)", + 1, GNUNET_NO); + return 0; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "In round %llu, sending to `%s' estimate with %u bits\n", + (unsigned long long) + GNUNET_TIME_absolute_ntoh (size_estimate_messages[idx]. + timestamp).abs_value, + GNUNET_i2s (&peer_entry->id), + (unsigned int) ntohl (size_estimate_messages[idx].matching_bits)); + if (ntohl (size_estimate_messages[idx].hop_count) == 0) + GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO); + GNUNET_STATISTICS_update (stats, "# flood messages transmitted", 1, + GNUNET_NO); +#if ENABLE_HISTOGRAM + peer_entry->transmitted_messages++; + peer_entry->last_transmitted_size = + ntohl(size_estimate_messages[idx].matching_bits); #endif - if (buf == NULL) /* client disconnected */ - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: transmit_ready called (disconnect)\n", - GNUNET_i2s (&my_identity)); - return 0; - } + memcpy (buf, &size_estimate_messages[idx], + sizeof (struct GNUNET_NSE_FloodMessage)); + return sizeof (struct GNUNET_NSE_FloodMessage); +} - if (peer_entry->pending_message == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: transmit_ready called (no message)\n", - GNUNET_i2s (&my_identity)); - return 0; - } - msize = ntohs (peer_entry->pending_message->size); - if (msize <= size) - memcpy (cbuf, peer_entry->pending_message, msize); -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: transmit_ready called (transmit %d bytes)\n", - GNUNET_i2s (&my_identity), msize); -#endif - return msize; +/** + * Task that triggers a NSE P2P transmission. + * + * @param cls the 'struct NSEPeerEntry' + * @param tc scheduler context + */ +static void +transmit_task_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct NSEPeerEntry *peer_entry = cls; + + peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK; + + GNUNET_assert (NULL == peer_entry->th); + peer_entry->th = + GNUNET_CORE_notify_transmit_ready (coreAPI, GNUNET_NO, NSE_PRIORITY, + GNUNET_TIME_UNIT_FOREVER_REL, + &peer_entry->id, + sizeof (struct + GNUNET_NSE_FloodMessage), + &transmit_ready, peer_entry); +} + + +/** + * We've sent on our flood message or one that we received which was + * validated and closer than ours. Update the global list of recent + * messages and the average. Also re-broadcast the message to any + * clients. + */ +static void +update_network_size_estimate () +{ + struct GNUNET_NSE_ClientMessage em; + + setup_estimate_message (&em); + GNUNET_SERVER_notification_context_broadcast (nc, &em.header, GNUNET_YES); } + /** - * We sent on our flood message or one that we received - * which was validated and closer than ours. Update the - * global list of recent messages and the average. Also - * re-broadcast the message to any clients. + * Setup a flood message in our history array at the given + * slot offset for the given timestamp. * - * @param message the network flood message + * @param slot index to use + * @param ts timestamp to use */ static void -update_network_size_estimate(struct GNUNET_NSE_FloodMessage *message) +setup_flood_message (unsigned int slot, + struct GNUNET_TIME_Absolute ts) { - unsigned int i; - unsigned int count; - double average; - double std_dev; - double diff; + struct GNUNET_NSE_FloodMessage *fm; + uint32_t matching_bits; + + matching_bits = get_matching_bits (ts, &my_identity); + fm = &size_estimate_messages[slot]; + fm->header.size = htons (sizeof (struct GNUNET_NSE_FloodMessage)); + fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD); + fm->hop_count = htonl (0); + fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND); + fm->purpose.size = + htonl (sizeof (struct GNUNET_NSE_FloodMessage) - + sizeof (struct GNUNET_MessageHeader) - sizeof (uint32_t) - + sizeof (struct GNUNET_CRYPTO_RsaSignature)); + fm->matching_bits = htonl (matching_bits); + fm->timestamp = GNUNET_TIME_absolute_hton (ts); + fm->pkey = my_public_key; + fm->proof_of_work = my_proof; + if (nse_work_required > 0) + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_rsa_sign (my_private_key, &fm->purpose, + &fm->signature)); + else + memset (&fm->signature, 0, sizeof (fm->signature)); +} - size_estimates[estimate_index] = htonl (message->distance); - memcpy (&size_estimate_messages[estimate_index], message, - sizeof(struct GNUNET_NSE_FloodMessage)); - count = 0; - std_dev = 0.0; - average = 0.0; - for (i = 0; i < DEFAULT_HISTORY_SIZE; i++) - { - if (size_estimate_messages[i].distance != 0) - { -#if AVERAGE_SQUARE - average += (1 << htonl (size_estimate_messages[i].distance)); -#else - average += htonl (size_estimate_messages[i].distance); -#endif - count++; - } - } +/** + * Schedule transmission for the given peer for the current round based + * on what we know about the desired delay. + * + * @param cls unused + * @param key hash of peer identity + * @param value the 'struct NSEPeerEntry' + * @return GNUNET_OK (continue to iterate) + */ +static int +schedule_current_round (void *cls, + const struct GNUNET_HashCode * key, + void *value) +{ + struct NSEPeerEntry *peer_entry = value; + struct GNUNET_TIME_Relative delay; - if (count > 0) - { - average /= (double) count; - for (i = 0; i < DEFAULT_HISTORY_SIZE; i++) - { - if (size_estimate_messages[i].distance != 0) - { -#if DEBUG_NSE - GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s: estimate %d %d\n", GNUNET_i2s(&my_identity), i, (1 << htonl(size_estimate_messages[i].distance))); -#endif -#if AVERAGE_SQUARE - diff = average - - (1 << htonl (size_estimate_messages[i].distance)); -#else - diff = average - htonl (size_estimate_messages[i].distance); -#endif - std_dev += diff * diff; - } - } - std_dev /= count; - std_dev = sqrt (std_dev); - current_estimate_message.header.size - = htons (sizeof(struct GNUNET_NSE_ClientMessage)); - current_estimate_message.header.type - = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE); -#if AVERAGE_SQUARE - current_estimate_message.size_estimate = average; - current_estimate_message.std_deviation = std_dev; -#else - current_estimate_message.size_estimate = pow(2, average); - current_estimate_message.std_deviation = pow(2, std_dev); -#endif - /* Finally, broadcast the current estimate to all clients */ -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: sending estimate %f -- %f to client\n", - GNUNET_i2s (&my_identity), - average, - std_dev); + if (NULL != peer_entry->th) + { + peer_entry->previous_round = GNUNET_NO; + return GNUNET_OK; + } + if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task) + { + GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); + peer_entry->previous_round = GNUNET_NO; + } +#if ENABLE_HISTOGRAM + if (peer_entry->received_messages > 1) + GNUNET_STATISTICS_update(stats, "# extra messages", + peer_entry->received_messages - 1, GNUNET_NO); + peer_entry->transmitted_messages = 0; + peer_entry->last_transmitted_size = 0; + peer_entry->received_messages = 0; #endif - GNUNET_SERVER_notification_context_broadcast ( - nc, - ¤t_estimate_message.header, - GNUNET_NO); - - GNUNET_STATISTICS_set (stats, "Current network size estimate", - (uint64_t) average, GNUNET_NO); - } + delay = + get_transmit_delay ((peer_entry->previous_round == GNUNET_NO) ? -1 : 0); + peer_entry->transmit_task = + GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry); + return GNUNET_OK; } -static void -send_flood_message(void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc); /** - * Schedule a flood message to be sent. + * Update our flood message to be sent (and our timestamps). * * @param cls unused * @param tc context for this message - * - * This should be called on startup, - * when a valid flood message is received (and - * the next send flood message hasn't been - * scheduled yet) and when this peer sends - * a valid flood message. As such, there should - * always be a message scheduled to be sent. */ static void -schedule_flood_message(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +update_flood_message (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { - GNUNET_HashCode timestamp_hash; - struct GNUNET_TIME_Absolute curr_time; struct GNUNET_TIME_Relative offset; - unsigned int matching_bits; - double millisecond_offset; + unsigned int i; - schedule_flood_task = GNUNET_SCHEDULER_NO_TASK; - if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) + flood_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) return; + offset = GNUNET_TIME_absolute_get_remaining (next_timestamp); + if (0 != offset.rel_value) + { + /* somehow run early, delay more */ + flood_task = + GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL); + return; + } + estimate_index = (estimate_index + 1) % HISTORY_SIZE; + if (estimate_count < HISTORY_SIZE) + estimate_count++; + current_timestamp = next_timestamp; + next_timestamp = + GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval); + if ((current_timestamp.abs_value == + GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value) && + (get_matching_bits (current_timestamp, &my_identity) < + ntohl(next_message.matching_bits))) + { + /* we received a message for this round way early, use it! */ + size_estimate_messages[estimate_index] = next_message; + size_estimate_messages[estimate_index].hop_count = + htonl (1 + ntohl (next_message.hop_count)); + } + else + setup_flood_message (estimate_index, current_timestamp); + next_message.matching_bits = htonl (0); /* reset for 'next' round */ + hop_count_max = 0; + for (i = 0; i < HISTORY_SIZE; i++) + hop_count_max = + GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max); + GNUNET_CONTAINER_multihashmap_iterate (peers, &schedule_current_round, NULL); + flood_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining + (next_timestamp), &update_flood_message, + NULL); +} - GNUNET_assert(flood_task == GNUNET_SCHEDULER_NO_TASK); - - if (0 != GNUNET_TIME_absolute_get_remaining (next_timestamp).rel_value) - { - GNUNET_break(0); /* Shouldn't ever happen! */ - schedule_flood_task - = GNUNET_SCHEDULER_add_delayed ( - GNUNET_TIME_absolute_get_remaining ( - next_timestamp), - &schedule_flood_message, NULL); - } - - /* Get the current UTC time */ - curr_time = GNUNET_TIME_absolute_get (); - /* Find the previous interval start time */ - previous_timestamp.abs_value = (curr_time.abs_value / GNUNET_NSE_INTERVAL) - * GNUNET_NSE_INTERVAL; - /* Find the next interval start time */ - next_timestamp.abs_value = previous_timestamp.abs_value + GNUNET_NSE_INTERVAL; -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: curr_time %lu, prev timestamp %lu, next timestamp %lu\n", - GNUNET_i2s (&my_identity), curr_time.abs_value, - previous_timestamp.abs_value, next_timestamp.abs_value); -#endif - GNUNET_CRYPTO_hash (&next_timestamp.abs_value, - sizeof(next_timestamp.abs_value), ×tamp_hash); - matching_bits = GNUNET_CRYPTO_hash_matching_bits (×tamp_hash, - &my_identity.hashPubKey); - - flood_message.header.size = htons (sizeof(struct GNUNET_NSE_FloodMessage)); - flood_message.header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD); - flood_message.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND); - flood_message.purpose.size = htonl (sizeof(struct GNUNET_NSE_FloodMessage) - - sizeof(struct GNUNET_MessageHeader) - sizeof(flood_message.signature)); - flood_message.distance = htonl (matching_bits); - flood_message.timestamp = GNUNET_TIME_absolute_hton (next_timestamp); - memcpy (&flood_message.pkey, &my_public_key, - sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); - flood_message.proof_of_work = htonl (0); - GNUNET_CRYPTO_rsa_sign (my_private_key, &flood_message.purpose, - &flood_message.signature); - - /*S + f/2 - (f / pi) * (atan(x - p'))*/ - - // S is next_timestamp - // f is frequency (GNUNET_NSE_INTERVAL) - // x is matching_bits - // p' is current_size_estimate - millisecond_offset = ((double) GNUNET_NSE_INTERVAL / (double) 2) - - ((GNUNET_NSE_INTERVAL / M_PI) * atan (matching_bits - - current_size_estimate)); -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: id matches %d bits, offset is %lu\n\n", - GNUNET_i2s (&my_identity), matching_bits, - (uint64_t) millisecond_offset); -#endif - /* Stop initial call from incrementing */ - if (size_estimate_messages[estimate_index].distance != 0) - estimate_index += 1; - - if (estimate_index >= DEFAULT_HISTORY_SIZE) - estimate_index = 0; - if (millisecond_offset < curr_time.abs_value - previous_timestamp.abs_value) - offset.rel_value = 0; - else - offset.rel_value = (uint64_t) millisecond_offset + curr_time.abs_value - - previous_timestamp.abs_value; -#if DEBUG_NSE - GNUNET_log ( - GNUNET_ERROR_TYPE_WARNING, - "%s: milliseconds until next timestamp %lu, sending flood in %lu\n", - GNUNET_i2s (&my_identity), - GNUNET_TIME_absolute_get_remaining (next_timestamp).rel_value, - offset.rel_value); -#endif - flood_task = GNUNET_SCHEDULER_add_delayed (offset, &send_flood_message, NULL); +/** + * Count the leading zeroes in hash. + * + * @param hash + * @return the number of leading zero bits. + */ +static unsigned int +count_leading_zeroes (const struct GNUNET_HashCode * hash) +{ + unsigned int hash_count; + hash_count = 0; + while ((0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count))) + hash_count++; + return hash_count; } -#if VERIFY_CRYPTO + /** * Check whether the given public key * and integer are a valid proof of work. * * @param pkey the public key * @param val the integer - * @param want the number of trailing zeroes * * @return GNUNET_YES if valid, GNUNET_NO if not */ -static int check_proof_of_work(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey, uint64_t val, unsigned int want) - { +static int +check_proof_of_work (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pkey, + uint64_t val) +{ + char buf[sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) + + sizeof (val)] GNUNET_ALIGN; + struct GNUNET_HashCode result; + + memcpy (buf, &val, sizeof (val)); + memcpy (&buf[sizeof (val)], pkey, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); + GNUNET_CRYPTO_hash (buf, sizeof (buf), &result); + return (count_leading_zeroes (&result) >= + nse_work_required) ? GNUNET_YES : GNUNET_NO; +} - return GNUNET_YES; - } /** - * Count the trailing zeroes in hash. - * - * @param hash - * - * @return the number of trailing zero bits. + * Write our current proof to disk. */ -static unsigned int count_trailing_zeroes(GNUNET_HashCode *hash) - { - unsigned int hash_count; +static void +write_proof () +{ + char *proof; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof)) + return; + if (sizeof (my_proof) != + GNUNET_DISK_fn_write (proof, &my_proof, sizeof (my_proof), + GNUNET_DISK_PERM_USER_READ | + GNUNET_DISK_PERM_USER_WRITE)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof); + GNUNET_free (proof); + +} - hash_count = sizeof(GNUNET_HashCode) * 8; - while ((0 == GNUNET_CRYPTO_hash_get_bit(hash, hash_count))) - hash_count--; - return (sizeof(GNUNET_HashCode) * 8) - hash_count; - } /** - * Given a public key, find an integer such that - * the hash of the key concatenated with the integer - * has want trailing 0 bits. + * Find our proof of work. * - * @param pkey the public key - * @param want the number of trailing 0 bits - * - * @return 64 bit number that satisfies the - * requirements - * - * FIXME: use pointer and return GNUNET_YES or - * GNUNET_NO in case no such number works? + * @param cls closure (unused) + * @param tc task context */ -static uint64_t find_proof_of_work(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pkey, unsigned int want) +static void +find_proof (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#define ROUND_SIZE 10 + uint64_t counter; + char buf[sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) + + sizeof (uint64_t)] GNUNET_ALIGN; + struct GNUNET_HashCode result; + unsigned int i; + + proof_task = GNUNET_SCHEDULER_NO_TASK; + memcpy (&buf[sizeof (uint64_t)], &my_public_key, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); + i = 0; + counter = my_proof; + while ((counter != UINT64_MAX) && (i < ROUND_SIZE)) { - uint64_t counter; - static char buf[sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) + sizeof(uint64_t)]; - unsigned int data_size; - static GNUNET_HashCode result; - - data_size = sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) + sizeof(uint64_t); - memcpy(buf, pkey, sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); - counter = 0; - while (counter != (uint64_t)-1) - { - memcpy(&buf[sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)], &counter, sizeof(uint64_t)); - GNUNET_CRYPTO_hash(buf, data_size, &result); - if (want == count_trailing_zeroes(&result)) /* Found good proof of work! */ - break; - counter++; - } - if (counter < (uint64_t)-1) - return counter; /* Found valid proof of work */ - else - return 0; /* Did not find valid proof of work */ + memcpy (buf, &counter, sizeof (uint64_t)); + GNUNET_CRYPTO_hash (buf, sizeof (buf), &result); + if (nse_work_required <= count_leading_zeroes (&result)) + { + my_proof = counter; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work found: %llu!\n", + (unsigned long long) GNUNET_ntohll (counter)); + write_proof (); + setup_flood_message (estimate_index, current_timestamp); + return; + } + counter++; + i++; } + if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing proofs currently at %llu\n", + (unsigned long long) counter); + /* remember progress every 100 rounds */ + my_proof = counter; + write_proof (); + } + else + { + my_proof = counter; + } + proof_task = + GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay, + GNUNET_SCHEDULER_PRIORITY_IDLE, + &find_proof, NULL); +} + /** * An incoming flood message has been received which claims @@ -586,19 +928,75 @@ static uint64_t find_proof_of_work(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncode * @return GNUNET_YES if the message is verified * GNUNET_NO if the key/signature don't verify */ -static int verify_message_crypto(struct GNUNET_NSE_FloodMessage *incoming_flood) +static int +verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood) +{ + if (GNUNET_YES != + check_proof_of_work (&incoming_flood->pkey, + incoming_flood->proof_of_work)) { - int ret; - if (GNUNET_OK == (ret - = GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND, - &incoming_flood->purpose, - &incoming_flood->signature, - &incoming_flood->pkey))) - return GNUNET_YES; - + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Proof of work invalid: %llu!\n"), + (unsigned long long) + GNUNET_ntohll (incoming_flood->proof_of_work)); + GNUNET_break_op (0); return GNUNET_NO; } -#endif + if ((nse_work_required > 0) && + (GNUNET_OK != + GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND, + &incoming_flood->purpose, + &incoming_flood->signature, + &incoming_flood->pkey))) + { + GNUNET_break_op (0); + return GNUNET_NO; + } + return GNUNET_YES; +} + + +/** + * Update transmissions for the given peer for the current round based + * on updated proximity information. + * + * @param cls peer entry to exclude from updates + * @param key hash of peer identity + * @param value the 'struct NSEPeerEntry' + * @return GNUNET_OK (continue to iterate) + */ +static int +update_flood_times (void *cls, const struct GNUNET_HashCode * key, void *value) +{ + struct NSEPeerEntry *exclude = cls; + struct NSEPeerEntry *peer_entry = value; + struct GNUNET_TIME_Relative delay; + + if (NULL != peer_entry->th) + return GNUNET_OK; /* already active */ + if (peer_entry == exclude) + return GNUNET_OK; /* trigger of the update */ + if (peer_entry->previous_round == GNUNET_NO) + { + /* still stuck in previous round, no point to update, check that + * we are active here though... */ + if ( (GNUNET_SCHEDULER_NO_TASK == peer_entry->transmit_task) && + (NULL == peer_entry->th) ) + { + GNUNET_break (0); + } + return GNUNET_OK; + } + if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task) + { + GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); + peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK; + } + delay = get_transmit_delay (0); + peer_entry->transmit_task = + GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry); + return GNUNET_OK; +} + /** * Core handler for size estimate flooding messages. @@ -607,268 +1005,251 @@ static int verify_message_crypto(struct GNUNET_NSE_FloodMessage *incoming_flood) * @param message message * @param peer peer identity this message is from (ignored) * @param atsi performance data (ignored) - * + * @param atsi_count number of records in 'atsi' */ static int -handle_p2p_size_estimate(void *cls, const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_MessageHeader *message, - const struct GNUNET_TRANSPORT_ATS_Information *atsi) +handle_p2p_size_estimate (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) { - struct GNUNET_NSE_FloodMessage *incoming_flood; - struct GNUNET_TIME_Absolute curr_time; - uint64_t drift; + const struct GNUNET_NSE_FloodMessage *incoming_flood; + struct GNUNET_TIME_Absolute ts; + struct NSEPeerEntry *peer_entry; + uint32_t matching_bits; + unsigned int idx; -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s: received flood message!\n", - GNUNET_i2s (&my_identity)); +#if ENABLE_HISTOGRAM + if (NULL != wh) + GNUNET_break (GNUNET_OK == GNUNET_BIO_write_int64 (wh, GNUNET_TIME_absolute_get ().abs_value)); #endif - if (ntohs (message->size) != sizeof(struct GNUNET_NSE_FloodMessage)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s: bad message size!\n", - GNUNET_i2s (&my_identity)); - return GNUNET_NO; - } - + incoming_flood = (const struct GNUNET_NSE_FloodMessage *) message; GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO); - incoming_flood = (struct GNUNET_NSE_FloodMessage *) message; - if (ntohl (incoming_flood->distance) - <= ntohl (size_estimate_messages[estimate_index].distance)) /* Not closer than our most recent message */ - { + matching_bits = ntohl (incoming_flood->matching_bits); #if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: distance %d not greater than %d, discarding\n", - GNUNET_i2s (&my_identity), ntohl (incoming_flood->distance), - ntohl (size_estimate_messages[estimate_index].distance)); + { + char origin[5]; + char pred[5]; + struct GNUNET_PeerIdentity os; + + GNUNET_CRYPTO_hash (&incoming_flood->pkey, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &os.hashPubKey); + GNUNET_snprintf (origin, sizeof (origin), "%s", GNUNET_i2s (&os)); + GNUNET_snprintf (pred, sizeof (pred), "%s", GNUNET_i2s (peer)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Flood at %llu from `%s' via `%s' at `%s' with bits %u\n", + (unsigned long long) + GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp).abs_value, + origin, pred, GNUNET_i2s (&my_identity), + (unsigned int) matching_bits); + } #endif - GNUNET_STATISTICS_update (stats, - "# flood messages discarded (had closer)", 1, - GNUNET_NO); - return GNUNET_OK; - } - curr_time = GNUNET_TIME_absolute_get (); - if (curr_time.abs_value - > GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp).abs_value) - drift = curr_time.abs_value - - GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp).abs_value; - else - drift = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp).abs_value - - curr_time.abs_value; + peer_entry = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey); + if (NULL == peer_entry) + { + GNUNET_break (0); + return GNUNET_OK; + } +#if ENABLE_HISTOGRAM + peer_entry->received_messages++; + if (peer_entry->transmitted_messages > 0 && + peer_entry->last_transmitted_size >= matching_bits) + GNUNET_STATISTICS_update(stats, "# cross messages", 1, GNUNET_NO); +#endif - if (drift > GNUNET_NSE_DRIFT_TOLERANCE) + ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp); + if (ts.abs_value == current_timestamp.abs_value) + idx = estimate_index; + else if (ts.abs_value == + current_timestamp.abs_value - gnunet_nse_interval.rel_value) + idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE; + else if (ts.abs_value == next_timestamp.abs_value) + { + if (matching_bits <= ntohl (next_message.matching_bits)) + return GNUNET_OK; /* ignore, simply too early/late */ + if (GNUNET_YES != verify_message_crypto (incoming_flood)) { - GNUNET_STATISTICS_update ( - stats, - "# flood messages discarded (clock skew too high)", - 1, GNUNET_NO); + GNUNET_break_op (0); return GNUNET_OK; } - -#if VERIFY_CRYPTO - if (GNUNET_YES != verify_message_crypto(incoming_flood)) + next_message = *incoming_flood; + return GNUNET_OK; + } + else + { + GNUNET_STATISTICS_update (stats, + "# flood messages discarded (clock skew too large)", + 1, GNUNET_NO); + return GNUNET_OK; + } + if (0 == (memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity)))) + { + /* send to self, update our own estimate IF this also comes from us! */ + if (0 == + memcmp (&incoming_flood->pkey, &my_public_key, sizeof (my_public_key))) + update_network_size_estimate (); + return GNUNET_OK; + } + if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits)) + { + /* Cancel transmission in the other direction, as this peer clearly has + up-to-date information already. Even if we didn't talk to this peer in + the previous round, we should no longer send it stale information as it + told us about the current round! */ + peer_entry->previous_round = GNUNET_YES; + if (idx != estimate_index) { - GNUNET_STATISTICS_update (stats, - "# flood messages discarded (bad crypto)", - 1, GNUNET_NO); + /* do not transmit information for the previous round to this peer + anymore (but allow current round) */ return GNUNET_OK; } -#endif - - /* Have a new, better size estimate! */ - update_network_size_estimate (incoming_flood); - - if (flood_task != GNUNET_SCHEDULER_NO_TASK) + /* got up-to-date information for current round, cancel transmission to + * this peer altogether */ + if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task) { -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s: received closer message, canceling my flood task!\n", GNUNET_i2s(&my_identity)); -#endif - GNUNET_SCHEDULER_cancel (flood_task); - flood_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); + peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK; } - - /** Commenting out prevents forwarding of messages */ -#if DO_FORWARD - GNUNET_SCHEDULER_add_now(&send_flood_message, &size_estimate_messages[estimate_index]); -#endif - if (schedule_flood_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (schedule_flood_task); - - schedule_flood_task - = GNUNET_SCHEDULER_add_delayed ( - GNUNET_TIME_absolute_get_remaining ( - next_timestamp), - &schedule_flood_message, NULL); - - return GNUNET_OK; -} - -/** - * Send a flood message. - * - * If we've gotten here, it means either we haven't received - * a network size estimate message closer than ours, or - * we need to forward a message we received which was closer - * than ours. - */ -static void -send_flood_message(void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc) -{ - struct NSEPeerEntry *peer_entry; - struct GNUNET_NSE_FloodMessage *to_send; - - if (cls == NULL) /* Means we are sending our OWN flood message */ - to_send = &flood_message; - else - /* Received a message from another peer that should be forwarded */ - to_send = (struct GNUNET_NSE_FloodMessage *) cls; - - flood_task = GNUNET_SCHEDULER_NO_TASK; - if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) - return; -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: my time has come, sending flood message of size %d!\n", - GNUNET_i2s (&my_identity), ntohs (to_send->header.size)); -#endif - peer_entry = peers_head; - - while (peer_entry != NULL) + if (NULL != peer_entry->th) { - peer_entry->pending_message = &to_send->header; - peer_entry->th - = GNUNET_CORE_notify_transmit_ready ( - coreAPI, - GNUNET_NO, - DEFAULT_NSE_PRIORITY, - GNUNET_TIME_absolute_get_remaining ( - next_timestamp), - &peer_entry->id, - ntohs (to_send->header.size), - &transmit_ready, peer_entry); - if (peer_entry->th == NULL) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: transmit handle is null!\n", GNUNET_i2s (&my_identity)); -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: Sending flood message (distance %d) to %s!\n", - GNUNET_i2s (&my_identity), ntohl (to_send->distance), - GNUNET_h2s (&peer_entry->id.hashPubKey)); -#endif - peer_entry = peer_entry->next; + GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th); + peer_entry->th = NULL; } - - if (cls == NULL) /* Need to update our size estimate */ + return GNUNET_OK; + } + if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits)) + { + if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES)) { + peer_entry->previous_round = GNUNET_NO; + } + /* push back our result now, that peer is spreading bad information... */ + if (NULL == peer_entry->th) { - update_network_size_estimate (to_send); - GNUNET_STATISTICS_update (stats, "# flood messages sent", 1, GNUNET_NO); + if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); + peer_entry->transmit_task = + GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry); } - else - GNUNET_STATISTICS_update (stats, "# flood messages forwarded", 1, GNUNET_NO); - -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: scheduling schedule_flood_message in %lu\n", - GNUNET_i2s (&my_identity), - GNUNET_TIME_absolute_get_remaining (next_timestamp).rel_value); -#endif - if (schedule_flood_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (schedule_flood_task); - - schedule_flood_task - = GNUNET_SCHEDULER_add_delayed ( - GNUNET_TIME_absolute_get_remaining ( - next_timestamp), - &schedule_flood_message, NULL); + /* Not closer than our most recent message, no need to do work here */ + GNUNET_STATISTICS_update (stats, + "# flood messages ignored (had closer already)", + 1, GNUNET_NO); + return GNUNET_OK; + } + if (GNUNET_YES != verify_message_crypto (incoming_flood)) + { + GNUNET_break_op (0); + return GNUNET_OK; + } + GNUNET_assert (matching_bits > + ntohl (size_estimate_messages[idx].matching_bits)); + /* Cancel transmission in the other direction, as this peer clearly has + * up-to-date information already. + */ + peer_entry->previous_round = GNUNET_YES; + if (idx == estimate_index) + { + /* cancel any activity for current round */ + if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); + peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK; + } + if (peer_entry->th != NULL) + { + GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th); + peer_entry->th = NULL; + } + } + size_estimate_messages[idx] = *incoming_flood; + size_estimate_messages[idx].hop_count = + htonl (ntohl (incoming_flood->hop_count) + 1); + hop_count_max = + GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max); + GNUNET_STATISTICS_set (stats, + "# estimated network diameter", + hop_count_max, GNUNET_NO); + + /* have a new, better size estimate, inform clients */ + update_network_size_estimate (); + + /* flood to rest */ + GNUNET_CONTAINER_multihashmap_iterate (peers, &update_flood_times, + peer_entry); + return GNUNET_OK; } + + /** - * Method called whenever a peer connects. + * Method called whenever a peer connects. Sets up the PeerEntry and + * schedules the initial size info transmission to this peer. * * @param cls closure * @param peer peer identity this notification is about * @param atsi performance data + * @param atsi_count number of records in 'atsi' */ static void -handle_core_connect(void *cls, const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_TRANSPORT_ATS_Information *atsi) +handle_core_connect (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) { struct NSEPeerEntry *peer_entry; - if (0 == (memcmp (peer, &my_identity, sizeof(struct GNUNET_PeerIdentity)))) - return; /* Do not connect to self... */ - - peer_entry = GNUNET_malloc(sizeof(struct NSEPeerEntry)); - memcpy (&peer_entry->id, peer, sizeof(struct GNUNET_PeerIdentity)); - GNUNET_CONTAINER_DLL_insert(peers_head, peers_tail, peer_entry); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' connected to us\n", + GNUNET_i2s (peer)); + peer_entry = GNUNET_malloc (sizeof (struct NSEPeerEntry)); + peer_entry->id = *peer; + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (peers, &peer->hashPubKey, + peer_entry, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + peer_entry->transmit_task = + GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1), &transmit_task_cb, + peer_entry); + GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO); } + /** - * Method called whenever a peer disconnects. + * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels + * any pending transmission requests to that peer. * * @param cls closure * @param peer peer identity this notification is about */ static void -handle_core_disconnect(void *cls, const struct GNUNET_PeerIdentity *peer) +handle_core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer) { struct NSEPeerEntry *pos; - if (0 == (memcmp (peer, &my_identity, sizeof(struct GNUNET_PeerIdentity)))) - return; /* Ignore disconnect from self... */ - - pos = peers_head; - while ((NULL != pos) && (0 != memcmp (&pos->id, peer, - sizeof(struct GNUNET_PeerIdentity)))) - pos = pos->next; - if (pos == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Received disconnect before connect!\n"); - GNUNET_break(0); /* Should never receive a disconnect message for a peer we don't know about... */ - return; - } - - /* TODO: decide whether to copy the message, or always use the static pointer */ -#if TODO - if (pos->pending_message != NULL) - GNUNET_free(pos->pending_message); -#endif - - if (pos->th != NULL) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' disconnected from us\n", + GNUNET_i2s (peer)); + pos = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey); + if (NULL == pos) + { + GNUNET_break (0); + return; + } + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (peers, &peer->hashPubKey, + pos)); + if (pos->transmit_task != GNUNET_SCHEDULER_NO_TASK) { + GNUNET_SCHEDULER_cancel (pos->transmit_task); + pos->transmit_task = GNUNET_SCHEDULER_NO_TASK; + } + if (NULL != pos->th) + { GNUNET_CORE_notify_transmit_ready_cancel (pos->th); - GNUNET_CONTAINER_DLL_remove(peers_head, peers_tail, pos); - GNUNET_free(pos); + pos->th = NULL; + } + GNUNET_free (pos); + GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO); } -/** - * A client disconnected. Remove it from the - * global DLL of clients. - * - * @param cls closure, NULL - * @param client identification of the client - */ -static void -handle_client_disconnect(void *cls, struct GNUNET_SERVER_Client* client) -{ - struct ClientListEntry *cle; - - while (NULL != (cle = cle_head)) - cle = cle->next; - - if (cle != NULL) - { - GNUNET_SERVER_client_drop (cle->client); - GNUNET_CONTAINER_DLL_remove(cle_head, - cle_tail, - cle); - GNUNET_free(cle); - } - if (coreAPI != NULL) - { - GNUNET_CORE_disconnect (coreAPI); - coreAPI = NULL; - } -} /** * Task run during shutdown. @@ -877,95 +1258,195 @@ handle_client_disconnect(void *cls, struct GNUNET_SERVER_Client* client) * @param tc unused */ static void -shutdown_task(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { - struct ClientListEntry *cle; - - if (flood_task != GNUNET_SCHEDULER_NO_TASK) + if (GNUNET_SCHEDULER_NO_TASK != flood_task) + { GNUNET_SCHEDULER_cancel (flood_task); - GNUNET_SERVER_notification_context_destroy (nc); - nc = NULL; - while (NULL != (cle = cle_head)) - { - GNUNET_SERVER_client_drop (cle->client); - GNUNET_CONTAINER_DLL_remove (cle_head, - cle_tail, - cle); - GNUNET_free (cle); - } - - if (coreAPI != NULL) - { - GNUNET_CORE_disconnect (coreAPI); - coreAPI = NULL; - } - - if (stats != NULL) + flood_task = GNUNET_SCHEDULER_NO_TASK; + } + if (GNUNET_SCHEDULER_NO_TASK != proof_task) + { + GNUNET_SCHEDULER_cancel (proof_task); + proof_task = GNUNET_SCHEDULER_NO_TASK; + write_proof (); /* remember progress */ + } + if (NULL != keygen) + { + GNUNET_CRYPTO_rsa_key_create_stop (keygen); + keygen = NULL; + } + if (NULL != nc) + { + GNUNET_SERVER_notification_context_destroy (nc); + nc = NULL; + } + if (NULL != coreAPI) + { + GNUNET_CORE_disconnect (coreAPI); + coreAPI = NULL; + } + if (NULL != stats) + { GNUNET_STATISTICS_destroy (stats, GNUNET_NO); - + stats = NULL; + } + if (NULL != peers) + { + GNUNET_CONTAINER_multihashmap_destroy (peers); + peers = NULL; + } + if (NULL != my_private_key) + { + GNUNET_CRYPTO_rsa_key_free (my_private_key); + my_private_key = NULL; + } +#if ENABLE_HISTOGRAM + if (NULL != wh) + { + GNUNET_break (GNUNET_OK == GNUNET_BIO_write_close (wh)); + wh = NULL; + } +#endif } + /** * Called on core init/fail. * * @param cls service closure * @param server handle to the server for this service * @param identity the public identity of this peer - * @param publicKey the public key of this peer */ -void -core_init(void *cls, struct GNUNET_CORE_Handle *server, - const struct GNUNET_PeerIdentity *identity, - const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey) +static void +core_init (void *cls, struct GNUNET_CORE_Handle *server, + const struct GNUNET_PeerIdentity *identity) { - struct GNUNET_TIME_Absolute curr_time; - if (server == NULL) - { -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s: Connection to core FAILED!\n", - "nse", GNUNET_i2s (identity)); -#endif - GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); - return; - } + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Absolute prev_time; - /* Copy our identity so we can use it */ - memcpy (&my_identity, identity, sizeof(struct GNUNET_PeerIdentity)); - /* Copy our public key for inclusion in flood messages */ - memcpy (&my_public_key, publicKey, - sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); - - if (flood_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (flood_task); + if (NULL == server) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n"); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_assert (0 == + memcmp (&my_identity, identity, + sizeof (struct GNUNET_PeerIdentity))); + now = GNUNET_TIME_absolute_get (); + current_timestamp.abs_value = + (now.abs_value / gnunet_nse_interval.rel_value) * + gnunet_nse_interval.rel_value; + next_timestamp = + GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval); + estimate_index = HISTORY_SIZE - 1; + estimate_count = 0; + if (GNUNET_YES == check_proof_of_work (&my_public_key, my_proof)) + { + int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE; + prev_time.abs_value = + current_timestamp.abs_value - gnunet_nse_interval.rel_value; + setup_flood_message (idx, prev_time); + setup_flood_message (estimate_index, current_timestamp); + estimate_count++; + } + flood_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining + (next_timestamp), &update_flood_message, + NULL); +} - /* Get the current UTC time */ - curr_time = GNUNET_TIME_absolute_get (); - /* Find the previous interval start time */ - previous_timestamp.abs_value = (curr_time.abs_value / GNUNET_NSE_INTERVAL) - * GNUNET_NSE_INTERVAL; - /* Find the next interval start time */ - next_timestamp.abs_value = previous_timestamp.abs_value + GNUNET_NSE_INTERVAL; -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "%s: Core connection initialized, I am peer: %s, scheduling flood task in %lu\n", "nse", - GNUNET_i2s (identity), GNUNET_TIME_absolute_get_remaining(next_timestamp)); +/** + * Callback for hostkey read/generation + * + * @param cls NULL + * @param pk the private key + * @param emsg error message + */ +static void +key_generation_cb (void *cls, + struct GNUNET_CRYPTO_RsaPrivateKey *pk, + const char *emsg) +{ + static const struct GNUNET_SERVER_MessageHandler handlers[] = { + {&handle_start_message, NULL, GNUNET_MESSAGE_TYPE_NSE_START, + sizeof (struct GNUNET_MessageHeader)}, + {NULL, NULL, 0, 0} + }; + static const struct GNUNET_CORE_MessageHandler core_handlers[] = { + {&handle_p2p_size_estimate, GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD, + sizeof (struct GNUNET_NSE_FloodMessage)}, + {NULL, 0, 0} + }; + char *proof; + + keygen = NULL; + if (NULL == pk) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("NSE service could not access hostkey: %s\n"), + emsg); + GNUNET_SCHEDULER_shutdown (); + return; + } + my_private_key = pk; + GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key); + GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), + &my_identity.hashPubKey); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("NSE service is lacking key configuration settings. Exiting.\n")); + GNUNET_CRYPTO_rsa_key_free (my_private_key); + my_private_key = NULL; + GNUNET_SCHEDULER_shutdown (); + return; + } + if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) || + (sizeof (my_proof) != + GNUNET_DISK_fn_read (proof, &my_proof, sizeof (my_proof)))) + my_proof = 0; + GNUNET_free (proof); + proof_task = + GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, + &find_proof, NULL); + + peers = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_NO); + GNUNET_SERVER_add_handlers (srv, handlers); + nc = GNUNET_SERVER_notification_context_create (srv, 1); + /* Connect to core service and register core handlers */ + coreAPI = GNUNET_CORE_connect (cfg, /* Main configuration */ + NULL, /* Closure passed to functions */ + &core_init, /* Call core_init once connected */ + &handle_core_connect, /* Handle connects */ + &handle_core_disconnect, /* Handle disconnects */ + NULL, /* Don't want notified about all incoming messages */ + GNUNET_NO, /* For header only inbound notification */ + NULL, /* Don't want notified about all outbound messages */ + GNUNET_NO, /* For header only outbound notification */ + core_handlers); /* Register these handlers */ + if (NULL == coreAPI) + { + GNUNET_SCHEDULER_shutdown (); + return; + } +#if ENABLE_HISTOGRAM + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "HISTOGRAM", &proof)) + { + wh = GNUNET_BIO_write_open (proof); + GNUNET_free (proof); + } #endif - /* FIXME: In production, we'd likely want to do this immediately, but in test-beds it causes stupid behavior */ - if (schedule_flood_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (schedule_flood_task); - schedule_flood_task - = GNUNET_SCHEDULER_add_delayed ( - GNUNET_TIME_absolute_get_remaining ( - next_timestamp), - &schedule_flood_message, NULL); - - GNUNET_SERVER_notification_context_broadcast ( - nc, - ¤t_estimate_message.header, - GNUNET_NO); + stats = GNUNET_STATISTICS_create ("nse", cfg); + GNUNET_SERVER_resume (srv); } + /** * Handle network size estimate clients. * @@ -974,101 +1455,68 @@ core_init(void *cls, struct GNUNET_CORE_Handle *server, * @param c configuration to use */ static void -run(void *cls, struct GNUNET_SERVER_Handle *server, - const struct GNUNET_CONFIGURATION_Handle *c) +run (void *cls, + struct GNUNET_SERVER_Handle *server, + const struct GNUNET_CONFIGURATION_Handle *c) { char *keyfile; - static const struct GNUNET_SERVER_MessageHandler handlers[] = - { - { &handle_start_message, NULL, GNUNET_MESSAGE_TYPE_NSE_START, 0 }, - { NULL, NULL, 0, 0 } }; - - static const struct GNUNET_CORE_MessageHandler core_handlers[] = - { - { &handle_p2p_size_estimate, GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD, 0 }, - { NULL, 0, 0 } }; cfg = c; - - if (GNUNET_OK - != GNUNET_CONFIGURATION_get_value_filename (c, "GNUNETD", "HOSTKEY", - &keyfile)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ - ("NSE service is lacking key configuration settings. Exiting.\n")); - GNUNET_SCHEDULER_shutdown (); - return; - } - - my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); - GNUNET_free (keyfile); - if (my_private_key == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("NSE Service could not access hostkey. Exiting.\n")); - GNUNET_SCHEDULER_shutdown (); - return; - } - - GNUNET_SERVER_add_handlers (server, handlers); - nc = GNUNET_SERVER_notification_context_create (server, 16); - GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL); - - flood_task = GNUNET_SCHEDULER_NO_TASK; - /** Connect to core service and register core handlers */ - coreAPI = GNUNET_CORE_connect (cfg, /* Main configuration */ - DEFAULT_CORE_QUEUE_SIZE, /* queue size */ - NULL, /* Closure passed to functions */ - &core_init, /* Call core_init once connected */ - &handle_core_connect, /* Handle connects */ - &handle_core_disconnect, /* Handle disconnects */ - NULL, /* Do we care about "status" updates? */ - NULL, /* Don't want notified about all incoming messages */ - GNUNET_NO, /* For header only inbound notification */ - NULL, /* Don't want notified about all outbound messages */ - GNUNET_NO, /* For header only outbound notification */ - core_handlers); /* Register these handlers */ - - if (coreAPI == NULL) - { - GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); - return; - } - - stats = GNUNET_STATISTICS_create ("NSE", cfg); - - increment - = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, - GNUNET_NSE_INTERVAL - / (sizeof(GNUNET_HashCode) * 8)); - /* Set we have no idea defaults for network size estimate */ - current_size_estimate = 0.0; - current_std_dev = NAN; - size_estimates[estimate_index] = 0; - current_estimate_message.header.size - = htons (sizeof(struct GNUNET_NSE_ClientMessage)); - current_estimate_message.header.type - = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE); - current_estimate_message.size_estimate = current_size_estimate; - current_estimate_message.std_deviation = current_std_dev; + srv = server; + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "INTERVAL", + &gnunet_nse_interval)) || + (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "WORKDELAY", + &proof_find_delay)) || + (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, "NSE", "WORKBITS", + &nse_work_required))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("NSE service is lacking key configuration settings. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + if (nse_work_required >= sizeof (struct GNUNET_HashCode) * 8) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Invalid work requirement for NSE service. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY", + &keyfile)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("NSE service is lacking key configuration settings. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, NULL); + GNUNET_SERVER_suspend (srv); + keygen = GNUNET_CRYPTO_rsa_key_create_start (keyfile, &key_generation_cb, NULL); + GNUNET_free (keyfile); } + /** - * The main function for the statistics service. + * The main function for the network size estimation 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) +main (int argc, char *const *argv) { - return (GNUNET_OK == GNUNET_SERVICE_run (argc, argv, "nse", - GNUNET_SERVICE_OPTION_NONE, &run, - NULL)) ? 0 : 1; + return (GNUNET_OK == + GNUNET_SERVICE_run (argc, argv, "nse", GNUNET_SERVICE_OPTION_NONE, + &run, NULL)) ? 0 : 1; } -/* End of gnunet-service-nse.c */ - +/* end of gnunet-service-nse.c */