2 This file is part of GNUnet.
3 (C) 2009, 2010, 2011 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;
309 * Initialize a message to clients with the current network
312 * @param em message to fill in
315 setup_estimate_message (struct GNUNET_NSE_ClientMessage *em)
327 /* Weighted incremental algorithm for stddev according to West (1979) */
339 for (i = 0; i < estimate_count; i++)
341 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
342 val = htonl (size_estimate_messages[j].matching_bits);
343 weight = estimate_count + 1 - i;
345 temp = weight + sumweight;
347 r = q * weight / temp;
349 sum += sumweight * q * r;
352 if (estimate_count > 0)
353 variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0);
355 /* trivial version for debugging */
358 /* non-weighted trivial version */
364 for (i = 0; i < estimate_count; i++)
366 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
367 val = htonl (size_estimate_messages[j].matching_bits);
371 if (0 != estimate_count)
373 mean = sum / estimate_count;
374 variance = (vsq - mean * sum) / (estimate_count - 1.0); // terrible for numerical stability...
378 std_dev = sqrt (variance);
380 std_dev = variance; /* must be infinity due to estimate_count == 0 */
381 current_std_dev = std_dev;
382 current_size_estimate = mean;
384 em->header.size = htons (sizeof (struct GNUNET_NSE_ClientMessage));
385 em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE);
386 em->reserved = htonl (0);
387 em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
388 double se = mean - 0.332747;
389 nsize = log2 (GNUNET_CONTAINER_multihashmap_size (peers) + 1);
390 em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se, nsize));
391 em->std_deviation = GNUNET_hton_double (std_dev);
392 GNUNET_STATISTICS_set (stats, "# nodes in the network (estimate)",
393 (uint64_t) pow (2, mean - 1.0 / 3.0), GNUNET_NO);
398 * Handler for START message from client, triggers an
399 * immediate current network estimate notification.
400 * Also, we remember the client for updates upon future
401 * estimate measurements.
404 * @param client who sent the message
405 * @param message the message received
408 handle_start_message (void *cls, struct GNUNET_SERVER_Client *client,
409 const struct GNUNET_MessageHeader *message)
411 struct GNUNET_NSE_ClientMessage em;
413 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received START message from client\n");
414 GNUNET_SERVER_notification_context_add (nc, client);
415 setup_estimate_message (&em);
416 GNUNET_SERVER_notification_context_unicast (nc, client, &em.header,
418 GNUNET_SERVER_receive_done (client, GNUNET_OK);
423 * How long should we delay a message to go the given number of
426 * @param matching_bits number of matching bits to consider
429 get_matching_bits_delay (uint32_t matching_bits)
431 /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */
432 // S is next_timestamp (ignored in return value)
433 // f is frequency (gnunet_nse_interval)
434 // x is matching_bits
435 // p' is current_size_estimate
436 return ((double) gnunet_nse_interval.rel_value / (double) 2.0) -
437 ((gnunet_nse_interval.rel_value / M_PI) *
438 atan (matching_bits - current_size_estimate));
443 * What delay randomization should we apply for a given number of matching bits?
445 * @param matching_bits number of matching bits
446 * @return random delay to apply
448 static struct GNUNET_TIME_Relative
449 get_delay_randomization (uint32_t matching_bits)
451 #if USE_RANDOM_DELAYS
452 struct GNUNET_TIME_Relative ret;
456 d = get_matching_bits_delay (matching_bits);
457 i = (uint32_t) (d / (double) (hop_count_max + 1));
458 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
459 "Randomizing flood using latencies up to %u ms\n",
461 ret.rel_value = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1);
464 return GNUNET_TIME_UNIT_ZERO;
470 * Get the number of matching bits that the given timestamp has to the given peer ID.
472 * @param timestamp time to generate key
473 * @param id peer identity to compare with
474 * @return number of matching bits
477 get_matching_bits (struct GNUNET_TIME_Absolute timestamp,
478 const struct GNUNET_PeerIdentity *id)
480 GNUNET_HashCode timestamp_hash;
482 GNUNET_CRYPTO_hash (×tamp.abs_value, sizeof (timestamp.abs_value),
484 return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash, &id->hashPubKey);
489 * Get the transmission delay that should be applied for a
492 * @param round_offset -1 for the previous round (random delay between 0 and 50ms)
493 * 0 for the current round (based on our proximity to time key)
494 * @return delay that should be applied
496 static struct GNUNET_TIME_Relative
497 get_transmit_delay (int round_offset)
499 struct GNUNET_TIME_Relative ret;
500 struct GNUNET_TIME_Absolute tgt;
502 uint32_t matching_bits;
504 switch (round_offset)
507 /* previous round is randomized between 0 and 50 ms */
508 #if USE_RANDOM_DELAYS
509 ret.rel_value = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50);
511 ret = GNUNET_TIME_UNIT_ZERO;
513 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
514 "Transmitting previous round behind schedule in %llu ms\n",
515 (unsigned long long) ret.rel_value);
518 /* current round is based on best-known matching_bits */
520 ntohl (size_estimate_messages[estimate_index].matching_bits);
521 dist_delay = get_matching_bits_delay (matching_bits);
522 dist_delay += get_delay_randomization (matching_bits).rel_value;
523 ret.rel_value = (uint64_t) dist_delay;
524 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
525 "For round %llu, delay for %u matching bits is %llu ms\n",
526 (unsigned long long) current_timestamp.abs_value,
527 (unsigned int) matching_bits,
528 (unsigned long long) ret.rel_value);
529 /* now consider round start time and add delay to it */
530 tgt = GNUNET_TIME_absolute_add (current_timestamp, ret);
531 return GNUNET_TIME_absolute_get_remaining (tgt);
534 return GNUNET_TIME_UNIT_FOREVER_REL;
539 * Task that triggers a NSE P2P transmission.
541 * @param cls the 'struct NSEPeerEntry'
542 * @param tc scheduler context
545 transmit_task_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
549 * Called when core is ready to send a message we asked for
550 * out to the destination.
552 * @param cls closure (NULL)
553 * @param size number of bytes available in buf
554 * @param buf where the callee should write the message
555 * @return number of bytes written to buf
558 transmit_ready (void *cls, size_t size, void *buf)
560 struct NSEPeerEntry *peer_entry = cls;
563 peer_entry->th = NULL;
566 /* client disconnected */
569 GNUNET_assert (size >= sizeof (struct GNUNET_NSE_FloodMessage));
570 idx = estimate_index;
571 if (GNUNET_NO == peer_entry->previous_round)
573 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
574 peer_entry->previous_round = GNUNET_YES;
575 peer_entry->transmit_task =
576 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0), &transmit_task_cb,
579 if ((ntohl (size_estimate_messages[idx].hop_count) == 0) &&
580 (GNUNET_SCHEDULER_NO_TASK != proof_task))
582 GNUNET_STATISTICS_update (stats,
583 "# flood messages not generated (no proof yet)",
587 if (ntohs (size_estimate_messages[idx].header.size) == 0)
589 GNUNET_STATISTICS_update (stats,
590 "# flood messages not generated (lack of history)",
594 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
595 "In round %llu, sending to `%s' estimate with %u bits\n",
597 GNUNET_TIME_absolute_ntoh (size_estimate_messages[idx].
598 timestamp).abs_value,
599 GNUNET_i2s (&peer_entry->id),
600 (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
601 if (ntohl (size_estimate_messages[idx].hop_count) == 0)
602 GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO);
603 GNUNET_STATISTICS_update (stats, "# flood messages transmitted", 1,
606 peer_entry->transmitted_messages++;
607 peer_entry->last_transmitted_size =
608 ntohl(size_estimate_messages[idx].matching_bits);
610 memcpy (buf, &size_estimate_messages[idx],
611 sizeof (struct GNUNET_NSE_FloodMessage));
612 return sizeof (struct GNUNET_NSE_FloodMessage);
617 * Task that triggers a NSE P2P transmission.
619 * @param cls the 'struct NSEPeerEntry'
620 * @param tc scheduler context
623 transmit_task_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
625 struct NSEPeerEntry *peer_entry = cls;
627 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
629 GNUNET_assert (NULL == peer_entry->th);
631 GNUNET_CORE_notify_transmit_ready (coreAPI, GNUNET_NO, NSE_PRIORITY,
632 GNUNET_TIME_UNIT_FOREVER_REL,
635 GNUNET_NSE_FloodMessage),
636 &transmit_ready, peer_entry);
641 * We've sent on our flood message or one that we received which was
642 * validated and closer than ours. Update the global list of recent
643 * messages and the average. Also re-broadcast the message to any
647 update_network_size_estimate ()
649 struct GNUNET_NSE_ClientMessage em;
651 setup_estimate_message (&em);
652 GNUNET_SERVER_notification_context_broadcast (nc, &em.header, GNUNET_YES);
657 * Setup a flood message in our history array at the given
658 * slot offset for the given timestamp.
660 * @param slot index to use
661 * @param ts timestamp to use
664 setup_flood_message (unsigned int slot, struct GNUNET_TIME_Absolute ts)
666 struct GNUNET_NSE_FloodMessage *fm;
667 uint32_t matching_bits;
669 matching_bits = get_matching_bits (ts, &my_identity);
670 fm = &size_estimate_messages[slot];
671 fm->header.size = htons (sizeof (struct GNUNET_NSE_FloodMessage));
672 fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
673 fm->hop_count = htonl (0);
674 fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
676 htonl (sizeof (struct GNUNET_NSE_FloodMessage) -
677 sizeof (struct GNUNET_MessageHeader) - sizeof (uint32_t) -
678 sizeof (struct GNUNET_CRYPTO_RsaSignature));
679 fm->matching_bits = htonl (matching_bits);
680 fm->timestamp = GNUNET_TIME_absolute_hton (ts);
681 fm->pkey = my_public_key;
682 fm->proof_of_work = my_proof;
683 if (nse_work_required > 0)
684 GNUNET_assert (GNUNET_OK ==
685 GNUNET_CRYPTO_rsa_sign (my_private_key, &fm->purpose,
688 memset (&fm->signature, 0, sizeof (fm->signature));
693 * Schedule transmission for the given peer for the current round based
694 * on what we know about the desired delay.
697 * @param key hash of peer identity
698 * @param value the 'struct NSEPeerEntry'
699 * @return GNUNET_OK (continue to iterate)
702 schedule_current_round (void *cls, const GNUNET_HashCode * key, void *value)
704 struct NSEPeerEntry *peer_entry = value;
705 struct GNUNET_TIME_Relative delay;
707 if (NULL != peer_entry->th)
709 peer_entry->previous_round = GNUNET_NO;
712 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
714 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
715 peer_entry->previous_round = GNUNET_NO;
718 if (peer_entry->received_messages > 1)
719 GNUNET_STATISTICS_update(stats, "# extra messages",
720 peer_entry->received_messages - 1, GNUNET_NO);
721 peer_entry->transmitted_messages = 0;
722 peer_entry->last_transmitted_size = 0;
723 peer_entry->received_messages = 0;
726 get_transmit_delay ((peer_entry->previous_round == GNUNET_NO) ? -1 : 0);
727 peer_entry->transmit_task =
728 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
734 * Update our flood message to be sent (and our timestamps).
737 * @param tc context for this message
740 update_flood_message (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
742 struct GNUNET_TIME_Relative offset;
745 flood_task = GNUNET_SCHEDULER_NO_TASK;
746 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
748 offset = GNUNET_TIME_absolute_get_remaining (next_timestamp);
749 if (0 != offset.rel_value)
751 /* somehow run early, delay more */
753 GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL);
756 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
757 if (estimate_count < HISTORY_SIZE)
759 current_timestamp = next_timestamp;
761 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
762 if ((current_timestamp.abs_value ==
763 GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value) &&
764 (get_matching_bits (current_timestamp, &my_identity) <
765 ntohl(next_message.matching_bits)))
767 /* we received a message for this round way early, use it! */
768 size_estimate_messages[estimate_index] = next_message;
769 size_estimate_messages[estimate_index].hop_count =
770 htonl (1 + ntohl (next_message.hop_count));
773 setup_flood_message (estimate_index, current_timestamp);
774 next_message.matching_bits = htonl (0); /* reset for 'next' round */
776 for (i = 0; i < HISTORY_SIZE; i++)
778 GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max);
779 GNUNET_CONTAINER_multihashmap_iterate (peers, &schedule_current_round, NULL);
781 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
782 (next_timestamp), &update_flood_message,
788 * Count the leading zeroes in hash.
791 * @return the number of leading zero bits.
794 count_leading_zeroes (const GNUNET_HashCode * hash)
796 unsigned int hash_count;
799 while ((0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count)))
806 * Check whether the given public key
807 * and integer are a valid proof of work.
809 * @param pkey the public key
810 * @param val the integer
812 * @return GNUNET_YES if valid, GNUNET_NO if not
815 check_proof_of_work (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pkey,
818 char buf[sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
819 sizeof (val)] GNUNET_ALIGN;
820 GNUNET_HashCode result;
822 memcpy (buf, &val, sizeof (val));
823 memcpy (&buf[sizeof (val)], pkey,
824 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
825 GNUNET_CRYPTO_hash (buf, sizeof (buf), &result);
826 return (count_leading_zeroes (&result) >=
827 nse_work_required) ? GNUNET_YES : GNUNET_NO;
832 * Write our current proof to disk.
840 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
842 if (sizeof (my_proof) !=
843 GNUNET_DISK_fn_write (proof, &my_proof, sizeof (my_proof),
844 GNUNET_DISK_PERM_USER_READ |
845 GNUNET_DISK_PERM_USER_WRITE))
846 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof);
853 * Find our proof of work.
855 * @param cls closure (unused)
856 * @param tc task context
859 find_proof (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
861 #define ROUND_SIZE 10
863 char buf[sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
864 sizeof (uint64_t)] GNUNET_ALIGN;
865 GNUNET_HashCode result;
868 proof_task = GNUNET_SCHEDULER_NO_TASK;
869 memcpy (&buf[sizeof (uint64_t)], &my_public_key,
870 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
873 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
875 memcpy (buf, &counter, sizeof (uint64_t));
876 GNUNET_CRYPTO_hash (buf, sizeof (buf), &result);
877 if (nse_work_required <= count_leading_zeroes (&result))
880 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work found: %llu!\n",
881 (unsigned long long) GNUNET_ntohll (counter));
883 setup_flood_message (estimate_index, current_timestamp);
889 if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
891 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing proofs currently at %llu\n",
892 (unsigned long long) counter);
893 /* remember progress every 100 rounds */
902 GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
903 GNUNET_SCHEDULER_PRIORITY_IDLE,
909 * An incoming flood message has been received which claims
910 * to have more bits matching than any we know in this time
911 * period. Verify the signature and/or proof of work.
913 * @param incoming_flood the message to verify
915 * @return GNUNET_YES if the message is verified
916 * GNUNET_NO if the key/signature don't verify
919 verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood)
922 check_proof_of_work (&incoming_flood->pkey,
923 incoming_flood->proof_of_work))
925 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Proof of work invalid: %llu!\n"),
927 GNUNET_ntohll (incoming_flood->proof_of_work));
931 if ((nse_work_required > 0) &&
933 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
934 &incoming_flood->purpose,
935 &incoming_flood->signature,
936 &incoming_flood->pkey)))
946 * Update transmissions for the given peer for the current round based
947 * on updated proximity information.
949 * @param cls peer entry to exclude from updates
950 * @param key hash of peer identity
951 * @param value the 'struct NSEPeerEntry'
952 * @return GNUNET_OK (continue to iterate)
955 update_flood_times (void *cls, const GNUNET_HashCode * key, void *value)
957 struct NSEPeerEntry *exclude = cls;
958 struct NSEPeerEntry *peer_entry = value;
959 struct GNUNET_TIME_Relative delay;
961 if (peer_entry->th != NULL)
962 return GNUNET_OK; /* already active */
963 if (peer_entry == exclude)
964 return GNUNET_OK; /* trigger of the update */
965 if (peer_entry->previous_round == GNUNET_NO)
967 /* still stuck in previous round, no point to update, check that
968 * we are active here though... */
969 if (GNUNET_SCHEDULER_NO_TASK == peer_entry->transmit_task &&
970 NULL == peer_entry->th)
976 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
978 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
979 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
981 delay = get_transmit_delay (0);
982 peer_entry->transmit_task =
983 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
989 * Core handler for size estimate flooding messages.
991 * @param cls closure unused
992 * @param message message
993 * @param peer peer identity this message is from (ignored)
994 * @param atsi performance data (ignored)
995 * @param atsi_count number of records in 'atsi'
998 handle_p2p_size_estimate (void *cls, const struct GNUNET_PeerIdentity *peer,
999 const struct GNUNET_MessageHeader *message,
1000 const struct GNUNET_ATS_Information *atsi,
1001 unsigned int atsi_count)
1003 const struct GNUNET_NSE_FloodMessage *incoming_flood;
1004 struct GNUNET_TIME_Absolute ts;
1005 struct NSEPeerEntry *peer_entry;
1006 uint32_t matching_bits;
1009 #if ENABLE_HISTOGRAM
1011 GNUNET_break (GNUNET_OK == GNUNET_BIO_write_int64 (wh, GNUNET_TIME_absolute_get ().abs_value));
1013 incoming_flood = (const struct GNUNET_NSE_FloodMessage *) message;
1014 GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO);
1015 matching_bits = ntohl (incoming_flood->matching_bits);
1020 struct GNUNET_PeerIdentity os;
1022 GNUNET_CRYPTO_hash (&incoming_flood->pkey,
1023 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1025 GNUNET_snprintf (origin, sizeof (origin), "%s", GNUNET_i2s (&os));
1026 GNUNET_snprintf (pred, sizeof (pred), "%s", GNUNET_i2s (peer));
1027 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1028 "Flood at %llu from `%s' via `%s' at `%s' with bits %u\n",
1029 (unsigned long long)
1030 GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp).abs_value,
1031 origin, pred, GNUNET_i2s (&my_identity),
1032 (unsigned int) matching_bits);
1036 peer_entry = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
1037 if (NULL == peer_entry)
1042 #if ENABLE_HISTOGRAM
1043 peer_entry->received_messages++;
1044 if (peer_entry->transmitted_messages > 0 &&
1045 peer_entry->last_transmitted_size >= matching_bits)
1046 GNUNET_STATISTICS_update(stats, "# cross messages", 1, GNUNET_NO);
1049 ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp);
1050 if (ts.abs_value == current_timestamp.abs_value)
1051 idx = estimate_index;
1052 else if (ts.abs_value ==
1053 current_timestamp.abs_value - gnunet_nse_interval.rel_value)
1054 idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1055 else if (ts.abs_value == next_timestamp.abs_value)
1057 if (matching_bits <= ntohl (next_message.matching_bits))
1058 return GNUNET_OK; /* ignore, simply too early/late */
1059 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1061 GNUNET_break_op (0);
1064 next_message = *incoming_flood;
1069 GNUNET_STATISTICS_update (stats,
1070 "# flood messages discarded (clock skew too large)",
1074 if (0 == (memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))))
1076 /* send to self, update our own estimate IF this also comes from us! */
1078 memcmp (&incoming_flood->pkey, &my_public_key, sizeof (my_public_key)))
1079 update_network_size_estimate ();
1082 if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits))
1084 /* Cancel transmission in the other direction, as this peer clearly has
1085 up-to-date information already. Even if we didn't talk to this peer in
1086 the previous round, we should no longer send it stale information as it
1087 told us about the current round! */
1088 peer_entry->previous_round = GNUNET_YES;
1089 if (idx != estimate_index)
1091 /* do not transmit information for the previous round to this peer
1092 anymore (but allow current round) */
1095 /* got up-to-date information for current round, cancel transmission to
1096 * this peer altogether */
1097 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1099 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1100 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1102 if (peer_entry->th != NULL)
1104 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1105 peer_entry->th = NULL;
1109 if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits))
1111 if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES)) {
1112 peer_entry->previous_round = GNUNET_NO;
1114 /* push back our result now, that peer is spreading bad information... */
1115 if (NULL == peer_entry->th)
1117 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1118 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1119 peer_entry->transmit_task =
1120 GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry);
1122 /* Not closer than our most recent message, no need to do work here */
1123 GNUNET_STATISTICS_update (stats,
1124 "# flood messages ignored (had closer already)",
1128 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1130 GNUNET_break_op (0);
1133 GNUNET_assert (matching_bits >
1134 ntohl (size_estimate_messages[idx].matching_bits));
1135 /* Cancel transmission in the other direction, as this peer clearly has
1136 * up-to-date information already.
1138 peer_entry->previous_round = GNUNET_YES;
1139 if (idx == estimate_index)
1141 /* cancel any activity for current round */
1142 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1144 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1145 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1147 if (peer_entry->th != NULL)
1149 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1150 peer_entry->th = NULL;
1153 size_estimate_messages[idx] = *incoming_flood;
1154 size_estimate_messages[idx].hop_count =
1155 htonl (ntohl (incoming_flood->hop_count) + 1);
1157 GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max);
1158 GNUNET_STATISTICS_set (stats,
1159 "# estimated network diameter",
1160 hop_count_max, GNUNET_NO);
1162 /* have a new, better size estimate, inform clients */
1163 update_network_size_estimate ();
1166 GNUNET_CONTAINER_multihashmap_iterate (peers, &update_flood_times,
1174 * Method called whenever a peer connects. Sets up the PeerEntry and
1175 * schedules the initial size info transmission to this peer.
1177 * @param cls closure
1178 * @param peer peer identity this notification is about
1179 * @param atsi performance data
1180 * @param atsi_count number of records in 'atsi'
1183 handle_core_connect (void *cls, const struct GNUNET_PeerIdentity *peer,
1184 const struct GNUNET_ATS_Information *atsi,
1185 unsigned int atsi_count)
1187 struct NSEPeerEntry *peer_entry;
1189 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' connected to us\n",
1191 peer_entry = GNUNET_malloc (sizeof (struct NSEPeerEntry));
1192 peer_entry->id = *peer;
1193 GNUNET_assert (GNUNET_OK ==
1194 GNUNET_CONTAINER_multihashmap_put (peers, &peer->hashPubKey,
1196 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1197 peer_entry->transmit_task =
1198 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1), &transmit_task_cb,
1200 GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO);
1205 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
1206 * any pending transmission requests to that peer.
1208 * @param cls closure
1209 * @param peer peer identity this notification is about
1212 handle_core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
1214 struct NSEPeerEntry *pos;
1216 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' disconnected from us\n",
1218 pos = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
1224 GNUNET_assert (GNUNET_YES ==
1225 GNUNET_CONTAINER_multihashmap_remove (peers, &peer->hashPubKey,
1227 if (pos->transmit_task != GNUNET_SCHEDULER_NO_TASK) {
1228 GNUNET_SCHEDULER_cancel (pos->transmit_task);
1229 pos->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1231 if (pos->th != NULL)
1233 GNUNET_CORE_notify_transmit_ready_cancel (pos->th);
1237 GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO);
1242 * Task run during shutdown.
1248 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1250 if (flood_task != GNUNET_SCHEDULER_NO_TASK)
1252 GNUNET_SCHEDULER_cancel (flood_task);
1253 flood_task = GNUNET_SCHEDULER_NO_TASK;
1255 if (proof_task != GNUNET_SCHEDULER_NO_TASK)
1257 GNUNET_SCHEDULER_cancel (proof_task);
1258 proof_task = GNUNET_SCHEDULER_NO_TASK;
1259 write_proof (); /* remember progress */
1263 GNUNET_SERVER_notification_context_destroy (nc);
1266 if (coreAPI != NULL)
1268 GNUNET_CORE_disconnect (coreAPI);
1273 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1278 GNUNET_CONTAINER_multihashmap_destroy (peers);
1281 if (my_private_key != NULL)
1283 GNUNET_CRYPTO_rsa_key_free (my_private_key);
1284 my_private_key = NULL;
1286 #if ENABLE_HISTOGRAM
1289 GNUNET_break (GNUNET_OK == GNUNET_BIO_write_close (wh));
1297 * Called on core init/fail.
1299 * @param cls service closure
1300 * @param server handle to the server for this service
1301 * @param identity the public identity of this peer
1304 core_init (void *cls, struct GNUNET_CORE_Handle *server,
1305 const struct GNUNET_PeerIdentity *identity)
1307 struct GNUNET_TIME_Absolute now;
1308 struct GNUNET_TIME_Absolute prev_time;
1312 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n");
1313 GNUNET_SCHEDULER_shutdown ();
1317 memcmp (&my_identity, identity,
1318 sizeof (struct GNUNET_PeerIdentity)));
1319 now = GNUNET_TIME_absolute_get ();
1320 current_timestamp.abs_value =
1321 (now.abs_value / gnunet_nse_interval.rel_value) *
1322 gnunet_nse_interval.rel_value;
1324 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
1325 estimate_index = HISTORY_SIZE - 1;
1327 if (GNUNET_YES == check_proof_of_work (&my_public_key, my_proof))
1329 int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1330 prev_time.abs_value =
1331 current_timestamp.abs_value - gnunet_nse_interval.rel_value;
1332 setup_flood_message (idx, prev_time);
1333 setup_flood_message (estimate_index, current_timestamp);
1337 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
1338 (next_timestamp), &update_flood_message,
1344 * Handle network size estimate clients.
1346 * @param cls closure
1347 * @param server the initialized server
1348 * @param c configuration to use
1351 run (void *cls, struct GNUNET_SERVER_Handle *server,
1352 const struct GNUNET_CONFIGURATION_Handle *c)
1357 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1358 {&handle_start_message, NULL, GNUNET_MESSAGE_TYPE_NSE_START,
1359 sizeof (struct GNUNET_MessageHeader)},
1362 static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
1363 {&handle_p2p_size_estimate, GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1364 sizeof (struct GNUNET_NSE_FloodMessage)},
1370 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "INTERVAL",
1371 &gnunet_nse_interval)) ||
1373 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "WORKDELAY",
1374 &proof_find_delay)) ||
1376 GNUNET_CONFIGURATION_get_value_number (cfg, "NSE", "WORKBITS",
1377 &nse_work_required)))
1379 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1381 ("NSE service is lacking key configuration settings. Exiting.\n"));
1382 GNUNET_SCHEDULER_shutdown ();
1385 if (nse_work_required >= sizeof (GNUNET_HashCode) * 8)
1387 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1388 _("Invalid work requirement for NSE service. Exiting.\n"));
1389 GNUNET_SCHEDULER_shutdown ();
1395 GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY",
1398 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1400 ("NSE service is lacking key configuration settings. Exiting.\n"));
1401 GNUNET_SCHEDULER_shutdown ();
1404 my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
1405 GNUNET_free (keyfile);
1406 if (my_private_key == NULL)
1408 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1409 _("NSE service could not access hostkey. Exiting.\n"));
1410 GNUNET_SCHEDULER_shutdown ();
1413 GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
1414 GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key),
1415 &my_identity.hashPubKey);
1417 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
1419 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1421 ("NSE service is lacking key configuration settings. Exiting.\n"));
1422 if (my_private_key != NULL)
1424 GNUNET_CRYPTO_rsa_key_free (my_private_key);
1425 my_private_key = NULL;
1427 GNUNET_SCHEDULER_shutdown ();
1430 if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) ||
1431 (sizeof (my_proof) !=
1432 GNUNET_DISK_fn_read (proof, &my_proof, sizeof (my_proof))))
1434 GNUNET_free (proof);
1436 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1439 peers = GNUNET_CONTAINER_multihashmap_create (128);
1440 GNUNET_SERVER_add_handlers (server, handlers);
1441 nc = GNUNET_SERVER_notification_context_create (server, 1);
1442 /* Connect to core service and register core handlers */
1443 coreAPI = GNUNET_CORE_connect (cfg, /* Main configuration */
1444 NULL, /* Closure passed to functions */
1445 &core_init, /* Call core_init once connected */
1446 &handle_core_connect, /* Handle connects */
1447 &handle_core_disconnect, /* Handle disconnects */
1448 NULL, /* Don't want notified about all incoming messages */
1449 GNUNET_NO, /* For header only inbound notification */
1450 NULL, /* Don't want notified about all outbound messages */
1451 GNUNET_NO, /* For header only outbound notification */
1452 core_handlers); /* Register these handlers */
1453 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1455 #if ENABLE_HISTOGRAM
1457 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "HISTOGRAM", &proof))
1459 wh = GNUNET_BIO_write_open (proof);
1460 GNUNET_free (proof);
1463 if (coreAPI == NULL)
1465 GNUNET_SCHEDULER_shutdown ();
1468 stats = GNUNET_STATISTICS_create ("nse", cfg);
1473 * The main function for the statistics service.
1475 * @param argc number of arguments from the command line
1476 * @param argv command line arguments
1477 * @return 0 ok, 1 on error
1480 main (int argc, char *const *argv)
1482 return (GNUNET_OK ==
1483 GNUNET_SERVICE_run (argc, argv, "nse", GNUNET_SERVICE_OPTION_NONE,
1484 &run, NULL)) ? 0 : 1;
1487 /* end of gnunet-service-nse.c */