2 This file is part of GNUnet.
3 (C) 2009, 2010, 2011, 2012 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file nse/gnunet-service-nse.c
23 * @brief network size estimation service
24 * @author Nathan Evans
25 * @author Christian Grothoff
27 * The purpose of this service is to estimate the size of the network.
28 * Given a specified interval, each peer hashes the most recent
29 * timestamp which is evenly divisible by that interval. This hash is
30 * compared in distance to the peer identity to choose an offset. The
31 * closer the peer identity to the hashed timestamp, the earlier the
32 * peer sends out a "nearest peer" message. The closest peer's
33 * message should thus be received before any others, which stops
34 * those peer from sending their messages at a later duration. So
35 * every peer should receive the same nearest peer message, and from
36 * this can calculate the expected number of peers in the network.
40 #include "gnunet_util_lib.h"
41 #include "gnunet_constants.h"
42 #include "gnunet_protocols.h"
43 #include "gnunet_signatures.h"
44 #include "gnunet_statistics_service.h"
45 #include "gnunet_core_service.h"
46 #include "gnunet_nse_service.h"
50 * Should messages be delayed randomly? This option should be set to
51 * GNUNET_NO only for experiments, not in production. It should also
52 * be removed once the initial experiments have been completed.
54 #define USE_RANDOM_DELAYS GNUNET_YES
57 * Should we generate a histogram with the time stamps of when we received
58 * NSE messages to disk? (for performance evaluation only, not useful in
59 * production). The associated code should also probably be removed
60 * once we're done with experiments.
62 #define ENABLE_HISTOGRAM GNUNET_NO
65 * Over how many values do we calculate the weighted average?
67 #define HISTORY_SIZE 64
70 * Message priority to use.
72 #define NSE_PRIORITY 5
75 #define log2(a) (log(a)/log(2))
79 * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits.
81 static unsigned long long nse_work_required;
84 * Interval for sending network size estimation flood requests.
86 static struct GNUNET_TIME_Relative gnunet_nse_interval;
89 * Interval between proof find runs.
91 static struct GNUNET_TIME_Relative proof_find_delay;
95 * Handle for writing when we received messages to disk.
97 static struct GNUNET_BIO_WriteHandle *wh;
102 * Per-peer information.
108 * Core handle for sending messages to this peer.
110 struct GNUNET_CORE_TransmitHandle *th;
113 * What is the identity of the peer?
115 struct GNUNET_PeerIdentity id;
118 * Task scheduled to send message to this peer.
120 GNUNET_SCHEDULER_TaskIdentifier transmit_task;
123 * Did we receive or send a message about the previous round
124 * to this peer yet? GNUNET_YES if the previous round has
125 * been taken care of.
132 * Amount of messages received from this peer on this round.
134 unsigned int received_messages;
137 * Amount of messages transmitted to this peer on this round.
139 unsigned int transmitted_messages;
142 * Which size did we tell the peer the network is?
144 unsigned int last_transmitted_size;
151 GNUNET_NETWORK_STRUCT_BEGIN
154 * Network size estimate reply; sent when "this"
155 * peer's timer has run out before receiving a
156 * valid reply from another peer.
158 struct GNUNET_NSE_FloodMessage
161 * Type: GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD
163 struct GNUNET_MessageHeader header;
166 * Number of hops this message has taken so far.
168 uint32_t hop_count GNUNET_PACKED;
173 struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
176 * The current timestamp value (which all
177 * peers should agree on).
179 struct GNUNET_TIME_AbsoluteNBO timestamp;
182 * Number of matching bits between the hash
183 * of timestamp and the initiator's public
186 uint32_t matching_bits GNUNET_PACKED;
189 * Public key of the originator.
191 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
194 * Proof of work, causing leading zeros when hashed with pkey.
196 uint64_t proof_of_work GNUNET_PACKED;
199 * Signature (over range specified in purpose).
201 struct GNUNET_CRYPTO_RsaSignature signature;
203 GNUNET_NETWORK_STRUCT_END
206 * Handle to our current configuration.
208 static const struct GNUNET_CONFIGURATION_Handle *cfg;
211 * Handle to the statistics service.
213 static struct GNUNET_STATISTICS_Handle *stats;
216 * Handle to the core service.
218 static struct GNUNET_CORE_Handle *coreAPI;
221 * Map of all connected peers.
223 static struct GNUNET_CONTAINER_MultiHashMap *peers;
226 * The current network size estimate. Number of bits matching on
229 static double current_size_estimate;
232 * The standard deviation of the last HISTORY_SIZE network
235 static double current_std_dev = NAN;
238 * Current hop counter estimate (estimate for network diameter).
240 static uint32_t hop_count_max;
243 * Message for the next round, if we got any.
245 static struct GNUNET_NSE_FloodMessage next_message;
248 * Array of recent size estimate messages.
250 static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE];
253 * Index of most recent estimate.
255 static unsigned int estimate_index;
258 * Number of valid entries in the history.
260 static unsigned int estimate_count;
263 * Task scheduled to update our flood message for the next round.
265 static GNUNET_SCHEDULER_TaskIdentifier flood_task;
268 * Task scheduled to compute our proof.
270 static GNUNET_SCHEDULER_TaskIdentifier proof_task;
273 * Notification context, simplifies client broadcasts.
275 static struct GNUNET_SERVER_NotificationContext *nc;
278 * The next major time.
280 static struct GNUNET_TIME_Absolute next_timestamp;
283 * The current major time.
285 static struct GNUNET_TIME_Absolute current_timestamp;
288 * The public key of this peer.
290 static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
293 * The private key of this peer.
295 static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
298 * The peer identity of this peer.
300 static struct GNUNET_PeerIdentity my_identity;
303 * Proof of work for this peer.
305 static uint64_t my_proof;
308 * Handle to this serivce's server.
310 static struct GNUNET_SERVER_Handle *srv;
313 * Hostkey generation context
315 static struct GNUNET_CRYPTO_RsaKeyGenerationContext *keygen;
319 * Initialize a message to clients with the current network
322 * @param em message to fill in
325 setup_estimate_message (struct GNUNET_NSE_ClientMessage *em)
337 /* Weighted incremental algorithm for stddev according to West (1979) */
349 for (i = 0; i < estimate_count; i++)
351 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
352 val = htonl (size_estimate_messages[j].matching_bits);
353 weight = estimate_count + 1 - i;
355 temp = weight + sumweight;
357 r = q * weight / temp;
359 sum += sumweight * q * r;
362 if (estimate_count > 0)
363 variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0);
365 /* trivial version for debugging */
368 /* non-weighted trivial version */
374 for (i = 0; i < estimate_count; i++)
376 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
377 val = htonl (size_estimate_messages[j].matching_bits);
381 if (0 != estimate_count)
383 mean = sum / estimate_count;
384 variance = (vsq - mean * sum) / (estimate_count - 1.0); // terrible for numerical stability...
388 std_dev = sqrt (variance);
390 std_dev = variance; /* must be infinity due to estimate_count == 0 */
391 current_std_dev = std_dev;
392 current_size_estimate = mean;
394 em->header.size = htons (sizeof (struct GNUNET_NSE_ClientMessage));
395 em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE);
396 em->reserved = htonl (0);
397 em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
398 double se = mean - 0.332747;
399 nsize = log2 (GNUNET_CONTAINER_multihashmap_size (peers) + 1);
400 em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se, nsize));
401 em->std_deviation = GNUNET_hton_double (std_dev);
402 GNUNET_STATISTICS_set (stats, "# nodes in the network (estimate)",
403 (uint64_t) pow (2, mean - 1.0 / 3.0), GNUNET_NO);
408 * Handler for START message from client, triggers an
409 * immediate current network estimate notification.
410 * Also, we remember the client for updates upon future
411 * estimate measurements.
414 * @param client who sent the message
415 * @param message the message received
418 handle_start_message (void *cls, struct GNUNET_SERVER_Client *client,
419 const struct GNUNET_MessageHeader *message)
421 struct GNUNET_NSE_ClientMessage em;
423 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received START message from client\n");
424 GNUNET_SERVER_notification_context_add (nc, client);
425 setup_estimate_message (&em);
426 GNUNET_SERVER_notification_context_unicast (nc, client, &em.header,
428 GNUNET_SERVER_receive_done (client, GNUNET_OK);
433 * How long should we delay a message to go the given number of
436 * @param matching_bits number of matching bits to consider
439 get_matching_bits_delay (uint32_t matching_bits)
441 /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */
442 // S is next_timestamp (ignored in return value)
443 // f is frequency (gnunet_nse_interval)
444 // x is matching_bits
445 // p' is current_size_estimate
446 return ((double) gnunet_nse_interval.rel_value / (double) 2.0) -
447 ((gnunet_nse_interval.rel_value / M_PI) *
448 atan (matching_bits - current_size_estimate));
453 * What delay randomization should we apply for a given number of matching bits?
455 * @param matching_bits number of matching bits
456 * @return random delay to apply
458 static struct GNUNET_TIME_Relative
459 get_delay_randomization (uint32_t matching_bits)
461 #if USE_RANDOM_DELAYS
462 struct GNUNET_TIME_Relative ret;
466 d = get_matching_bits_delay (matching_bits);
467 i = (uint32_t) (d / (double) (hop_count_max + 1));
468 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
469 "Randomizing flood using latencies up to %u ms\n",
471 ret.rel_value = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1);
474 return GNUNET_TIME_UNIT_ZERO;
480 * Get the number of matching bits that the given timestamp has to the given peer ID.
482 * @param timestamp time to generate key
483 * @param id peer identity to compare with
484 * @return number of matching bits
487 get_matching_bits (struct GNUNET_TIME_Absolute timestamp,
488 const struct GNUNET_PeerIdentity *id)
490 struct GNUNET_HashCode timestamp_hash;
492 GNUNET_CRYPTO_hash (×tamp.abs_value, sizeof (timestamp.abs_value),
494 return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash, &id->hashPubKey);
499 * Get the transmission delay that should be applied for a
502 * @param round_offset -1 for the previous round (random delay between 0 and 50ms)
503 * 0 for the current round (based on our proximity to time key)
504 * @return delay that should be applied
506 static struct GNUNET_TIME_Relative
507 get_transmit_delay (int round_offset)
509 struct GNUNET_TIME_Relative ret;
510 struct GNUNET_TIME_Absolute tgt;
512 uint32_t matching_bits;
514 switch (round_offset)
517 /* previous round is randomized between 0 and 50 ms */
518 #if USE_RANDOM_DELAYS
519 ret.rel_value = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50);
521 ret = GNUNET_TIME_UNIT_ZERO;
523 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
524 "Transmitting previous round behind schedule in %llu ms\n",
525 (unsigned long long) ret.rel_value);
528 /* current round is based on best-known matching_bits */
530 ntohl (size_estimate_messages[estimate_index].matching_bits);
531 dist_delay = get_matching_bits_delay (matching_bits);
532 dist_delay += get_delay_randomization (matching_bits).rel_value;
533 ret.rel_value = (uint64_t) dist_delay;
534 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
535 "For round %llu, delay for %u matching bits is %llu ms\n",
536 (unsigned long long) current_timestamp.abs_value,
537 (unsigned int) matching_bits,
538 (unsigned long long) ret.rel_value);
539 /* now consider round start time and add delay to it */
540 tgt = GNUNET_TIME_absolute_add (current_timestamp, ret);
541 return GNUNET_TIME_absolute_get_remaining (tgt);
544 return GNUNET_TIME_UNIT_FOREVER_REL;
549 * Task that triggers a NSE P2P transmission.
551 * @param cls the 'struct NSEPeerEntry'
552 * @param tc scheduler context
555 transmit_task_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
559 * Called when core is ready to send a message we asked for
560 * out to the destination.
562 * @param cls closure (NULL)
563 * @param size number of bytes available in buf
564 * @param buf where the callee should write the message
565 * @return number of bytes written to buf
568 transmit_ready (void *cls, size_t size, void *buf)
570 struct NSEPeerEntry *peer_entry = cls;
573 peer_entry->th = NULL;
576 /* client disconnected */
579 GNUNET_assert (size >= sizeof (struct GNUNET_NSE_FloodMessage));
580 idx = estimate_index;
581 if (GNUNET_NO == peer_entry->previous_round)
583 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
584 peer_entry->previous_round = GNUNET_YES;
585 peer_entry->transmit_task =
586 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0), &transmit_task_cb,
589 if ((0 == ntohl (size_estimate_messages[idx].hop_count)) &&
590 (GNUNET_SCHEDULER_NO_TASK != proof_task))
592 GNUNET_STATISTICS_update (stats,
593 "# flood messages not generated (no proof yet)",
597 if (0 == ntohs (size_estimate_messages[idx].header.size))
599 GNUNET_STATISTICS_update (stats,
600 "# flood messages not generated (lack of history)",
604 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
605 "In round %llu, sending to `%s' estimate with %u bits\n",
607 GNUNET_TIME_absolute_ntoh (size_estimate_messages[idx].
608 timestamp).abs_value,
609 GNUNET_i2s (&peer_entry->id),
610 (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
611 if (ntohl (size_estimate_messages[idx].hop_count) == 0)
612 GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO);
613 GNUNET_STATISTICS_update (stats, "# flood messages transmitted", 1,
616 peer_entry->transmitted_messages++;
617 peer_entry->last_transmitted_size =
618 ntohl(size_estimate_messages[idx].matching_bits);
620 memcpy (buf, &size_estimate_messages[idx],
621 sizeof (struct GNUNET_NSE_FloodMessage));
622 return sizeof (struct GNUNET_NSE_FloodMessage);
627 * Task that triggers a NSE P2P transmission.
629 * @param cls the 'struct NSEPeerEntry'
630 * @param tc scheduler context
633 transmit_task_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
635 struct NSEPeerEntry *peer_entry = cls;
637 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
639 GNUNET_assert (NULL == peer_entry->th);
641 GNUNET_CORE_notify_transmit_ready (coreAPI, GNUNET_NO, NSE_PRIORITY,
642 GNUNET_TIME_UNIT_FOREVER_REL,
645 GNUNET_NSE_FloodMessage),
646 &transmit_ready, peer_entry);
651 * We've sent on our flood message or one that we received which was
652 * validated and closer than ours. Update the global list of recent
653 * messages and the average. Also re-broadcast the message to any
657 update_network_size_estimate ()
659 struct GNUNET_NSE_ClientMessage em;
661 setup_estimate_message (&em);
662 GNUNET_SERVER_notification_context_broadcast (nc, &em.header, GNUNET_YES);
667 * Setup a flood message in our history array at the given
668 * slot offset for the given timestamp.
670 * @param slot index to use
671 * @param ts timestamp to use
674 setup_flood_message (unsigned int slot,
675 struct GNUNET_TIME_Absolute ts)
677 struct GNUNET_NSE_FloodMessage *fm;
678 uint32_t matching_bits;
680 matching_bits = get_matching_bits (ts, &my_identity);
681 fm = &size_estimate_messages[slot];
682 fm->header.size = htons (sizeof (struct GNUNET_NSE_FloodMessage));
683 fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
684 fm->hop_count = htonl (0);
685 fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
687 htonl (sizeof (struct GNUNET_NSE_FloodMessage) -
688 sizeof (struct GNUNET_MessageHeader) - sizeof (uint32_t) -
689 sizeof (struct GNUNET_CRYPTO_RsaSignature));
690 fm->matching_bits = htonl (matching_bits);
691 fm->timestamp = GNUNET_TIME_absolute_hton (ts);
692 fm->pkey = my_public_key;
693 fm->proof_of_work = my_proof;
694 if (nse_work_required > 0)
695 GNUNET_assert (GNUNET_OK ==
696 GNUNET_CRYPTO_rsa_sign (my_private_key, &fm->purpose,
699 memset (&fm->signature, 0, sizeof (fm->signature));
704 * Schedule transmission for the given peer for the current round based
705 * on what we know about the desired delay.
708 * @param key hash of peer identity
709 * @param value the 'struct NSEPeerEntry'
710 * @return GNUNET_OK (continue to iterate)
713 schedule_current_round (void *cls,
714 const struct GNUNET_HashCode * key,
717 struct NSEPeerEntry *peer_entry = value;
718 struct GNUNET_TIME_Relative delay;
720 if (NULL != peer_entry->th)
722 peer_entry->previous_round = GNUNET_NO;
725 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
727 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
728 peer_entry->previous_round = GNUNET_NO;
731 if (peer_entry->received_messages > 1)
732 GNUNET_STATISTICS_update(stats, "# extra messages",
733 peer_entry->received_messages - 1, GNUNET_NO);
734 peer_entry->transmitted_messages = 0;
735 peer_entry->last_transmitted_size = 0;
736 peer_entry->received_messages = 0;
739 get_transmit_delay ((peer_entry->previous_round == GNUNET_NO) ? -1 : 0);
740 peer_entry->transmit_task =
741 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
747 * Update our flood message to be sent (and our timestamps).
750 * @param tc context for this message
753 update_flood_message (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
755 struct GNUNET_TIME_Relative offset;
758 flood_task = GNUNET_SCHEDULER_NO_TASK;
759 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
761 offset = GNUNET_TIME_absolute_get_remaining (next_timestamp);
762 if (0 != offset.rel_value)
764 /* somehow run early, delay more */
766 GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL);
769 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
770 if (estimate_count < HISTORY_SIZE)
772 current_timestamp = next_timestamp;
774 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
775 if ((current_timestamp.abs_value ==
776 GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value) &&
777 (get_matching_bits (current_timestamp, &my_identity) <
778 ntohl(next_message.matching_bits)))
780 /* we received a message for this round way early, use it! */
781 size_estimate_messages[estimate_index] = next_message;
782 size_estimate_messages[estimate_index].hop_count =
783 htonl (1 + ntohl (next_message.hop_count));
786 setup_flood_message (estimate_index, current_timestamp);
787 next_message.matching_bits = htonl (0); /* reset for 'next' round */
789 for (i = 0; i < HISTORY_SIZE; i++)
791 GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max);
792 GNUNET_CONTAINER_multihashmap_iterate (peers, &schedule_current_round, NULL);
794 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
795 (next_timestamp), &update_flood_message,
801 * Count the leading zeroes in hash.
804 * @return the number of leading zero bits.
807 count_leading_zeroes (const struct GNUNET_HashCode * hash)
809 unsigned int hash_count;
812 while ((0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count)))
819 * Check whether the given public key
820 * and integer are a valid proof of work.
822 * @param pkey the public key
823 * @param val the integer
825 * @return GNUNET_YES if valid, GNUNET_NO if not
828 check_proof_of_work (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pkey,
831 char buf[sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
832 sizeof (val)] GNUNET_ALIGN;
833 struct GNUNET_HashCode result;
835 memcpy (buf, &val, sizeof (val));
836 memcpy (&buf[sizeof (val)], pkey,
837 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
838 GNUNET_CRYPTO_hash (buf, sizeof (buf), &result);
839 return (count_leading_zeroes (&result) >=
840 nse_work_required) ? GNUNET_YES : GNUNET_NO;
845 * Write our current proof to disk.
853 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
855 if (sizeof (my_proof) !=
856 GNUNET_DISK_fn_write (proof, &my_proof, sizeof (my_proof),
857 GNUNET_DISK_PERM_USER_READ |
858 GNUNET_DISK_PERM_USER_WRITE))
859 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof);
866 * Find our proof of work.
868 * @param cls closure (unused)
869 * @param tc task context
872 find_proof (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
874 #define ROUND_SIZE 10
876 char buf[sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
877 sizeof (uint64_t)] GNUNET_ALIGN;
878 struct GNUNET_HashCode result;
881 proof_task = GNUNET_SCHEDULER_NO_TASK;
882 memcpy (&buf[sizeof (uint64_t)], &my_public_key,
883 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
886 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
888 memcpy (buf, &counter, sizeof (uint64_t));
889 GNUNET_CRYPTO_hash (buf, sizeof (buf), &result);
890 if (nse_work_required <= count_leading_zeroes (&result))
893 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work found: %llu!\n",
894 (unsigned long long) GNUNET_ntohll (counter));
896 setup_flood_message (estimate_index, current_timestamp);
902 if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
904 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing proofs currently at %llu\n",
905 (unsigned long long) counter);
906 /* remember progress every 100 rounds */
915 GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
916 GNUNET_SCHEDULER_PRIORITY_IDLE,
922 * An incoming flood message has been received which claims
923 * to have more bits matching than any we know in this time
924 * period. Verify the signature and/or proof of work.
926 * @param incoming_flood the message to verify
928 * @return GNUNET_YES if the message is verified
929 * GNUNET_NO if the key/signature don't verify
932 verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood)
935 check_proof_of_work (&incoming_flood->pkey,
936 incoming_flood->proof_of_work))
938 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Proof of work invalid: %llu!\n"),
940 GNUNET_ntohll (incoming_flood->proof_of_work));
944 if ((nse_work_required > 0) &&
946 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
947 &incoming_flood->purpose,
948 &incoming_flood->signature,
949 &incoming_flood->pkey)))
959 * Update transmissions for the given peer for the current round based
960 * on updated proximity information.
962 * @param cls peer entry to exclude from updates
963 * @param key hash of peer identity
964 * @param value the 'struct NSEPeerEntry'
965 * @return GNUNET_OK (continue to iterate)
968 update_flood_times (void *cls, const struct GNUNET_HashCode * key, void *value)
970 struct NSEPeerEntry *exclude = cls;
971 struct NSEPeerEntry *peer_entry = value;
972 struct GNUNET_TIME_Relative delay;
974 if (NULL != peer_entry->th)
975 return GNUNET_OK; /* already active */
976 if (peer_entry == exclude)
977 return GNUNET_OK; /* trigger of the update */
978 if (peer_entry->previous_round == GNUNET_NO)
980 /* still stuck in previous round, no point to update, check that
981 * we are active here though... */
982 if ( (GNUNET_SCHEDULER_NO_TASK == peer_entry->transmit_task) &&
983 (NULL == peer_entry->th) )
989 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
991 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
992 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
994 delay = get_transmit_delay (0);
995 peer_entry->transmit_task =
996 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
1002 * Core handler for size estimate flooding messages.
1004 * @param cls closure unused
1005 * @param message message
1006 * @param peer peer identity this message is from (ignored)
1007 * @param atsi performance data (ignored)
1008 * @param atsi_count number of records in 'atsi'
1011 handle_p2p_size_estimate (void *cls, const struct GNUNET_PeerIdentity *peer,
1012 const struct GNUNET_MessageHeader *message,
1013 const struct GNUNET_ATS_Information *atsi,
1014 unsigned int atsi_count)
1016 const struct GNUNET_NSE_FloodMessage *incoming_flood;
1017 struct GNUNET_TIME_Absolute ts;
1018 struct NSEPeerEntry *peer_entry;
1019 uint32_t matching_bits;
1022 #if ENABLE_HISTOGRAM
1024 GNUNET_break (GNUNET_OK == GNUNET_BIO_write_int64 (wh, GNUNET_TIME_absolute_get ().abs_value));
1026 incoming_flood = (const struct GNUNET_NSE_FloodMessage *) message;
1027 GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO);
1028 matching_bits = ntohl (incoming_flood->matching_bits);
1033 struct GNUNET_PeerIdentity os;
1035 GNUNET_CRYPTO_hash (&incoming_flood->pkey,
1036 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1038 GNUNET_snprintf (origin, sizeof (origin), "%s", GNUNET_i2s (&os));
1039 GNUNET_snprintf (pred, sizeof (pred), "%s", GNUNET_i2s (peer));
1040 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1041 "Flood at %llu from `%s' via `%s' at `%s' with bits %u\n",
1042 (unsigned long long)
1043 GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp).abs_value,
1044 origin, pred, GNUNET_i2s (&my_identity),
1045 (unsigned int) matching_bits);
1049 peer_entry = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
1050 if (NULL == peer_entry)
1055 #if ENABLE_HISTOGRAM
1056 peer_entry->received_messages++;
1057 if (peer_entry->transmitted_messages > 0 &&
1058 peer_entry->last_transmitted_size >= matching_bits)
1059 GNUNET_STATISTICS_update(stats, "# cross messages", 1, GNUNET_NO);
1062 ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp);
1063 if (ts.abs_value == current_timestamp.abs_value)
1064 idx = estimate_index;
1065 else if (ts.abs_value ==
1066 current_timestamp.abs_value - gnunet_nse_interval.rel_value)
1067 idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1068 else if (ts.abs_value == next_timestamp.abs_value)
1070 if (matching_bits <= ntohl (next_message.matching_bits))
1071 return GNUNET_OK; /* ignore, simply too early/late */
1072 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1074 GNUNET_break_op (0);
1077 next_message = *incoming_flood;
1082 GNUNET_STATISTICS_update (stats,
1083 "# flood messages discarded (clock skew too large)",
1087 if (0 == (memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))))
1089 /* send to self, update our own estimate IF this also comes from us! */
1091 memcmp (&incoming_flood->pkey, &my_public_key, sizeof (my_public_key)))
1092 update_network_size_estimate ();
1095 if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits))
1097 /* Cancel transmission in the other direction, as this peer clearly has
1098 up-to-date information already. Even if we didn't talk to this peer in
1099 the previous round, we should no longer send it stale information as it
1100 told us about the current round! */
1101 peer_entry->previous_round = GNUNET_YES;
1102 if (idx != estimate_index)
1104 /* do not transmit information for the previous round to this peer
1105 anymore (but allow current round) */
1108 /* got up-to-date information for current round, cancel transmission to
1109 * this peer altogether */
1110 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1112 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1113 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1115 if (NULL != peer_entry->th)
1117 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1118 peer_entry->th = NULL;
1122 if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits))
1124 if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES)) {
1125 peer_entry->previous_round = GNUNET_NO;
1127 /* push back our result now, that peer is spreading bad information... */
1128 if (NULL == peer_entry->th)
1130 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1131 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1132 peer_entry->transmit_task =
1133 GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry);
1135 /* Not closer than our most recent message, no need to do work here */
1136 GNUNET_STATISTICS_update (stats,
1137 "# flood messages ignored (had closer already)",
1141 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1143 GNUNET_break_op (0);
1146 GNUNET_assert (matching_bits >
1147 ntohl (size_estimate_messages[idx].matching_bits));
1148 /* Cancel transmission in the other direction, as this peer clearly has
1149 * up-to-date information already.
1151 peer_entry->previous_round = GNUNET_YES;
1152 if (idx == estimate_index)
1154 /* cancel any activity for current round */
1155 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1157 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1158 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1160 if (peer_entry->th != NULL)
1162 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1163 peer_entry->th = NULL;
1166 size_estimate_messages[idx] = *incoming_flood;
1167 size_estimate_messages[idx].hop_count =
1168 htonl (ntohl (incoming_flood->hop_count) + 1);
1170 GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max);
1171 GNUNET_STATISTICS_set (stats,
1172 "# estimated network diameter",
1173 hop_count_max, GNUNET_NO);
1175 /* have a new, better size estimate, inform clients */
1176 update_network_size_estimate ();
1179 GNUNET_CONTAINER_multihashmap_iterate (peers, &update_flood_times,
1187 * Method called whenever a peer connects. Sets up the PeerEntry and
1188 * schedules the initial size info transmission to this peer.
1190 * @param cls closure
1191 * @param peer peer identity this notification is about
1192 * @param atsi performance data
1193 * @param atsi_count number of records in 'atsi'
1196 handle_core_connect (void *cls, const struct GNUNET_PeerIdentity *peer,
1197 const struct GNUNET_ATS_Information *atsi,
1198 unsigned int atsi_count)
1200 struct NSEPeerEntry *peer_entry;
1202 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' connected to us\n",
1204 peer_entry = GNUNET_malloc (sizeof (struct NSEPeerEntry));
1205 peer_entry->id = *peer;
1206 GNUNET_assert (GNUNET_OK ==
1207 GNUNET_CONTAINER_multihashmap_put (peers, &peer->hashPubKey,
1209 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1210 peer_entry->transmit_task =
1211 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1), &transmit_task_cb,
1213 GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO);
1218 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
1219 * any pending transmission requests to that peer.
1221 * @param cls closure
1222 * @param peer peer identity this notification is about
1225 handle_core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
1227 struct NSEPeerEntry *pos;
1229 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' disconnected from us\n",
1231 pos = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
1237 GNUNET_assert (GNUNET_YES ==
1238 GNUNET_CONTAINER_multihashmap_remove (peers, &peer->hashPubKey,
1240 if (pos->transmit_task != GNUNET_SCHEDULER_NO_TASK) {
1241 GNUNET_SCHEDULER_cancel (pos->transmit_task);
1242 pos->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1244 if (NULL != pos->th)
1246 GNUNET_CORE_notify_transmit_ready_cancel (pos->th);
1250 GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO);
1255 * Task run during shutdown.
1261 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1263 if (GNUNET_SCHEDULER_NO_TASK != flood_task)
1265 GNUNET_SCHEDULER_cancel (flood_task);
1266 flood_task = GNUNET_SCHEDULER_NO_TASK;
1268 if (GNUNET_SCHEDULER_NO_TASK != proof_task)
1270 GNUNET_SCHEDULER_cancel (proof_task);
1271 proof_task = GNUNET_SCHEDULER_NO_TASK;
1272 write_proof (); /* remember progress */
1276 GNUNET_CRYPTO_rsa_key_create_stop (keygen);
1281 GNUNET_SERVER_notification_context_destroy (nc);
1284 if (NULL != coreAPI)
1286 GNUNET_CORE_disconnect (coreAPI);
1291 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1296 GNUNET_CONTAINER_multihashmap_destroy (peers);
1299 if (NULL != my_private_key)
1301 GNUNET_CRYPTO_rsa_key_free (my_private_key);
1302 my_private_key = NULL;
1304 #if ENABLE_HISTOGRAM
1307 GNUNET_break (GNUNET_OK == GNUNET_BIO_write_close (wh));
1315 * Called on core init/fail.
1317 * @param cls service closure
1318 * @param server handle to the server for this service
1319 * @param identity the public identity of this peer
1322 core_init (void *cls, struct GNUNET_CORE_Handle *server,
1323 const struct GNUNET_PeerIdentity *identity)
1325 struct GNUNET_TIME_Absolute now;
1326 struct GNUNET_TIME_Absolute prev_time;
1330 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n");
1331 GNUNET_SCHEDULER_shutdown ();
1335 memcmp (&my_identity, identity,
1336 sizeof (struct GNUNET_PeerIdentity)));
1337 now = GNUNET_TIME_absolute_get ();
1338 current_timestamp.abs_value =
1339 (now.abs_value / gnunet_nse_interval.rel_value) *
1340 gnunet_nse_interval.rel_value;
1342 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
1343 estimate_index = HISTORY_SIZE - 1;
1345 if (GNUNET_YES == check_proof_of_work (&my_public_key, my_proof))
1347 int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1348 prev_time.abs_value =
1349 current_timestamp.abs_value - gnunet_nse_interval.rel_value;
1350 setup_flood_message (idx, prev_time);
1351 setup_flood_message (estimate_index, current_timestamp);
1355 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
1356 (next_timestamp), &update_flood_message,
1362 * Callback for hostkey read/generation
1365 * @param pk the private key
1366 * @param emsg error message
1369 key_generation_cb (void *cls,
1370 struct GNUNET_CRYPTO_RsaPrivateKey *pk,
1373 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1374 {&handle_start_message, NULL, GNUNET_MESSAGE_TYPE_NSE_START,
1375 sizeof (struct GNUNET_MessageHeader)},
1378 static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
1379 {&handle_p2p_size_estimate, GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1380 sizeof (struct GNUNET_NSE_FloodMessage)},
1388 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1389 _("NSE service could not access hostkey: %s\n"),
1391 GNUNET_SCHEDULER_shutdown ();
1394 my_private_key = pk;
1395 GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
1396 GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key),
1397 &my_identity.hashPubKey);
1399 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
1401 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1403 ("NSE service is lacking key configuration settings. Exiting.\n"));
1404 GNUNET_CRYPTO_rsa_key_free (my_private_key);
1405 my_private_key = NULL;
1406 GNUNET_SCHEDULER_shutdown ();
1409 if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) ||
1410 (sizeof (my_proof) !=
1411 GNUNET_DISK_fn_read (proof, &my_proof, sizeof (my_proof))))
1413 GNUNET_free (proof);
1415 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1418 peers = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_NO);
1419 GNUNET_SERVER_add_handlers (srv, handlers);
1420 nc = GNUNET_SERVER_notification_context_create (srv, 1);
1421 /* Connect to core service and register core handlers */
1422 coreAPI = GNUNET_CORE_connect (cfg, /* Main configuration */
1423 NULL, /* Closure passed to functions */
1424 &core_init, /* Call core_init once connected */
1425 &handle_core_connect, /* Handle connects */
1426 &handle_core_disconnect, /* Handle disconnects */
1427 NULL, /* Don't want notified about all incoming messages */
1428 GNUNET_NO, /* For header only inbound notification */
1429 NULL, /* Don't want notified about all outbound messages */
1430 GNUNET_NO, /* For header only outbound notification */
1431 core_handlers); /* Register these handlers */
1432 if (NULL == coreAPI)
1434 GNUNET_SCHEDULER_shutdown ();
1437 #if ENABLE_HISTOGRAM
1439 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "HISTOGRAM", &proof))
1441 wh = GNUNET_BIO_write_open (proof);
1442 GNUNET_free (proof);
1445 stats = GNUNET_STATISTICS_create ("nse", cfg);
1446 GNUNET_SERVER_resume (srv);
1451 * Handle network size estimate clients.
1453 * @param cls closure
1454 * @param server the initialized server
1455 * @param c configuration to use
1459 struct GNUNET_SERVER_Handle *server,
1460 const struct GNUNET_CONFIGURATION_Handle *c)
1467 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "INTERVAL",
1468 &gnunet_nse_interval)) ||
1470 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "WORKDELAY",
1471 &proof_find_delay)) ||
1473 GNUNET_CONFIGURATION_get_value_number (cfg, "NSE", "WORKBITS",
1474 &nse_work_required)))
1476 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1478 ("NSE service is lacking key configuration settings. Exiting.\n"));
1479 GNUNET_SCHEDULER_shutdown ();
1482 if (nse_work_required >= sizeof (struct GNUNET_HashCode) * 8)
1484 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1485 _("Invalid work requirement for NSE service. Exiting.\n"));
1486 GNUNET_SCHEDULER_shutdown ();
1490 GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY",
1493 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1495 ("NSE service is lacking key configuration settings. Exiting.\n"));
1496 GNUNET_SCHEDULER_shutdown ();
1499 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1501 GNUNET_SERVER_suspend (srv);
1502 keygen = GNUNET_CRYPTO_rsa_key_create_start (keyfile, &key_generation_cb, NULL);
1503 GNUNET_free (keyfile);
1508 * The main function for the network size estimation service.
1510 * @param argc number of arguments from the command line
1511 * @param argv command line arguments
1512 * @return 0 ok, 1 on error
1515 main (int argc, char *const *argv)
1517 return (GNUNET_OK ==
1518 GNUNET_SERVICE_run (argc, argv, "nse", GNUNET_SERVICE_OPTION_NONE,
1519 &run, NULL)) ? 0 : 1;
1527 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1529 void __attribute__ ((constructor)) GNUNET_ARM_memory_init ()
1531 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1532 mallopt (M_TOP_PAD, 1 * 1024);
1539 /* end of gnunet-service-nse.c */