2 This file is part of GNUnet.
3 (C) 2009, 2010, 2011, 2012, 2013 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"
47 #if ENABLE_NSE_HISTOGRAM
48 #include "gnunet_testbed_logger_service.h"
55 * Should messages be delayed randomly? This option should be set to
56 * #GNUNET_NO only for experiments, not in production.
58 #define USE_RANDOM_DELAYS GNUNET_YES
61 * Generate extensive debug-level log messages?
63 #define DEBUG_NSE GNUNET_NO
66 * Over how many values do we calculate the weighted average?
68 #define HISTORY_SIZE 64
71 * Message priority to use.
73 #define NSE_PRIORITY GNUNET_CORE_PRIO_CRITICAL_CONTROL
76 #define log2(a) (log(a)/log(2))
80 * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits.
82 static unsigned long long nse_work_required;
85 * Interval for sending network size estimation flood requests.
87 static struct GNUNET_TIME_Relative gnunet_nse_interval;
90 * Interval between proof find runs.
92 static struct GNUNET_TIME_Relative proof_find_delay;
94 #if ENABLE_NSE_HISTOGRAM
97 * Handle to test if testbed logger service is running or not
99 struct GNUNET_CLIENT_TestHandle *logger_test;
102 * Handle for writing when we received messages to disk.
104 static struct GNUNET_TESTBED_LOGGER_Handle *lh;
107 * Handle for writing message received timestamp information to disk.
109 static struct GNUNET_BIO_WriteHandle *histogram;
115 * Per-peer information.
121 * Core handle for sending messages to this peer.
123 struct GNUNET_CORE_TransmitHandle *th;
126 * What is the identity of the peer?
128 struct GNUNET_PeerIdentity id;
131 * Task scheduled to send message to this peer.
133 struct GNUNET_SCHEDULER_Task * transmit_task;
136 * Did we receive or send a message about the previous round
137 * to this peer yet? #GNUNET_YES if the previous round has
138 * been taken care of.
142 #if ENABLE_NSE_HISTOGRAM
145 * Amount of messages received from this peer on this round.
147 unsigned int received_messages;
150 * Amount of messages transmitted to this peer on this round.
152 unsigned int transmitted_messages;
155 * Which size did we tell the peer the network is?
157 unsigned int last_transmitted_size;
164 GNUNET_NETWORK_STRUCT_BEGIN
167 * Network size estimate reply; sent when "this"
168 * peer's timer has run out before receiving a
169 * valid reply from another peer.
171 struct GNUNET_NSE_FloodMessage
174 * Type: #GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD
176 struct GNUNET_MessageHeader header;
179 * Number of hops this message has taken so far.
181 uint32_t hop_count GNUNET_PACKED;
186 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
189 * The current timestamp value (which all
190 * peers should agree on).
192 struct GNUNET_TIME_AbsoluteNBO timestamp;
195 * Number of matching bits between the hash
196 * of timestamp and the initiator's public
199 uint32_t matching_bits GNUNET_PACKED;
202 * Public key of the originator.
204 struct GNUNET_PeerIdentity origin;
207 * Proof of work, causing leading zeros when hashed with pkey.
209 uint64_t proof_of_work GNUNET_PACKED;
212 * Signature (over range specified in purpose).
214 struct GNUNET_CRYPTO_EddsaSignature signature;
216 GNUNET_NETWORK_STRUCT_END
219 * Handle to our current configuration.
221 static const struct GNUNET_CONFIGURATION_Handle *cfg;
224 * Handle to the statistics service.
226 static struct GNUNET_STATISTICS_Handle *stats;
229 * Handle to the core service.
231 static struct GNUNET_CORE_Handle *core_api;
234 * Map of all connected peers.
236 static struct GNUNET_CONTAINER_MultiPeerMap *peers;
239 * The current network size estimate. Number of bits matching on
242 static double current_size_estimate;
245 * The standard deviation of the last #HISTORY_SIZE network
248 static double current_std_dev = NAN;
251 * Current hop counter estimate (estimate for network diameter).
253 static uint32_t hop_count_max;
256 * Message for the next round, if we got any.
258 static struct GNUNET_NSE_FloodMessage next_message;
261 * Array of recent size estimate messages.
263 static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE];
266 * Index of most recent estimate.
268 static unsigned int estimate_index;
271 * Number of valid entries in the history.
273 static unsigned int estimate_count;
276 * Task scheduled to update our flood message for the next round.
278 static struct GNUNET_SCHEDULER_Task * flood_task;
281 * Task scheduled to compute our proof.
283 static struct GNUNET_SCHEDULER_Task * proof_task;
286 * Notification context, simplifies client broadcasts.
288 static struct GNUNET_SERVER_NotificationContext *nc;
291 * The next major time.
293 static struct GNUNET_TIME_Absolute next_timestamp;
296 * The current major time.
298 static struct GNUNET_TIME_Absolute current_timestamp;
301 * The private key of this peer.
303 static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
306 * The peer identity of this peer.
308 static struct GNUNET_PeerIdentity my_identity;
311 * Proof of work for this peer.
313 static uint64_t my_proof;
316 * Handle to this serivce's server.
318 static struct GNUNET_SERVER_Handle *srv;
322 * Initialize a message to clients with the current network
325 * @param em message to fill in
328 setup_estimate_message (struct GNUNET_NSE_ClientMessage *em)
340 /* Weighted incremental algorithm for stddev according to West (1979) */
352 for (i = 0; i < estimate_count; i++)
354 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
355 val = htonl (size_estimate_messages[j].matching_bits);
356 weight = estimate_count + 1 - i;
358 temp = weight + sumweight;
360 r = q * weight / temp;
362 sum += sumweight * q * r;
365 if (estimate_count > 0)
366 variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0);
368 /* trivial version for debugging */
371 /* non-weighted trivial version */
377 for (i = 0; i < estimate_count; i++)
379 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
380 val = htonl (size_estimate_messages[j].matching_bits);
384 if (0 != estimate_count)
386 mean = sum / estimate_count;
387 variance = (vsq - mean * sum) / (estimate_count - 1.0); // terrible for numerical stability...
391 std_dev = sqrt (variance);
393 std_dev = variance; /* must be infinity due to estimate_count == 0 */
394 current_std_dev = std_dev;
395 current_size_estimate = mean;
397 em->header.size = htons (sizeof (struct GNUNET_NSE_ClientMessage));
398 em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE);
399 em->reserved = htonl (0);
400 em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
401 double se = mean - 0.332747;
402 j = GNUNET_CONTAINER_multipeermap_size (peers);
404 j = 1; /* Avoid log2(0); can only happen if CORE didn't report
405 connection to self yet */
407 em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se,
409 em->std_deviation = GNUNET_hton_double (std_dev);
410 GNUNET_STATISTICS_set (stats,
411 "# nodes in the network (estimate)",
412 (uint64_t) pow (2, GNUNET_MAX (se,
419 * Handler for START message from client, triggers an
420 * immediate current network estimate notification.
421 * Also, we remember the client for updates upon future
422 * estimate measurements.
425 * @param client who sent the message
426 * @param message the message received
429 handle_start_message (void *cls,
430 struct GNUNET_SERVER_Client *client,
431 const struct GNUNET_MessageHeader *message)
433 struct GNUNET_NSE_ClientMessage em;
435 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
436 "Received START message from client\n");
437 GNUNET_SERVER_notification_context_add (nc, client);
438 setup_estimate_message (&em);
439 GNUNET_SERVER_notification_context_unicast (nc, client, &em.header,
441 GNUNET_SERVER_receive_done (client, GNUNET_OK);
446 * How long should we delay a message to go the given number of
449 * @param matching_bits number of matching bits to consider
452 get_matching_bits_delay (uint32_t matching_bits)
454 /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */
455 // S is next_timestamp (ignored in return value)
456 // f is frequency (gnunet_nse_interval)
457 // x is matching_bits
458 // p' is current_size_estimate
459 return ((double) gnunet_nse_interval.rel_value_us / (double) 2.0) -
460 ((gnunet_nse_interval.rel_value_us / M_PI) *
461 atan (matching_bits - current_size_estimate));
466 * What delay randomization should we apply for a given number of matching bits?
468 * @param matching_bits number of matching bits
469 * @return random delay to apply
471 static struct GNUNET_TIME_Relative
472 get_delay_randomization (uint32_t matching_bits)
474 #if USE_RANDOM_DELAYS
475 struct GNUNET_TIME_Relative ret;
479 d = get_matching_bits_delay (matching_bits);
480 i = (uint32_t) (d / (double) (hop_count_max + 1));
481 ret.rel_value_us = i;
482 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
483 "Randomizing flood using latencies up to %s\n",
484 GNUNET_STRINGS_relative_time_to_string (ret,
486 ret.rel_value_us = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1);
489 return GNUNET_TIME_UNIT_ZERO;
495 * Calculate the 'proof-of-work' hash (an expensive hash).
497 * @param buf data to hash
498 * @param buf_len number of bytes in @a buf
499 * @param result where to write the resulting hash
502 pow_hash (const void *buf,
504 struct GNUNET_HashCode *result)
507 gcry_kdf_derive (buf, buf_len,
510 "gnunet-proof-of-work", strlen ("gnunet-proof-of-work"),
511 2 /* iterations; keep cost of individual op small */,
512 sizeof (struct GNUNET_HashCode), result));
517 * Get the number of matching bits that the given timestamp has to the given peer ID.
519 * @param timestamp time to generate key
520 * @param id peer identity to compare with
521 * @return number of matching bits
524 get_matching_bits (struct GNUNET_TIME_Absolute timestamp,
525 const struct GNUNET_PeerIdentity *id)
527 struct GNUNET_HashCode timestamp_hash;
528 struct GNUNET_HashCode pid_hash;
530 GNUNET_CRYPTO_hash (×tamp.abs_value_us,
531 sizeof (timestamp.abs_value_us),
533 GNUNET_CRYPTO_hash (id,
534 sizeof (struct GNUNET_PeerIdentity),
536 return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash,
542 * Get the transmission delay that should be applied for a
545 * @param round_offset -1 for the previous round (random delay between 0 and 50ms)
546 * 0 for the current round (based on our proximity to time key)
547 * @return delay that should be applied
549 static struct GNUNET_TIME_Relative
550 get_transmit_delay (int round_offset)
552 struct GNUNET_TIME_Relative ret;
553 struct GNUNET_TIME_Absolute tgt;
555 uint32_t matching_bits;
557 switch (round_offset)
560 /* previous round is randomized between 0 and 50 ms */
561 #if USE_RANDOM_DELAYS
562 ret.rel_value_us = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50);
564 ret = GNUNET_TIME_UNIT_ZERO;
566 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
567 "Transmitting previous round behind schedule in %s\n",
568 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
571 /* current round is based on best-known matching_bits */
573 ntohl (size_estimate_messages[estimate_index].matching_bits);
574 dist_delay = get_matching_bits_delay (matching_bits);
575 dist_delay += get_delay_randomization (matching_bits).rel_value_us;
576 ret.rel_value_us = (uint64_t) dist_delay;
577 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
578 "For round %s, delay for %u matching bits is %s\n",
579 GNUNET_STRINGS_absolute_time_to_string (current_timestamp),
580 (unsigned int) matching_bits,
581 GNUNET_STRINGS_relative_time_to_string (ret,
583 /* now consider round start time and add delay to it */
584 tgt = GNUNET_TIME_absolute_add (current_timestamp, ret);
585 return GNUNET_TIME_absolute_get_remaining (tgt);
588 return GNUNET_TIME_UNIT_FOREVER_REL;
593 * Task that triggers a NSE P2P transmission.
595 * @param cls the `struct NSEPeerEntry *`
596 * @param tc scheduler context
599 transmit_task_cb (void *cls,
600 const struct GNUNET_SCHEDULER_TaskContext *tc);
604 * Called when core is ready to send a message we asked for
605 * out to the destination.
607 * @param cls closure with the `struct NSEPeerEntry *`
608 * @param size number of bytes available in @a buf
609 * @param buf where the callee should write the message
610 * @return number of bytes written to @a buf
613 transmit_ready (void *cls,
617 struct NSEPeerEntry *peer_entry = cls;
620 peer_entry->th = NULL;
623 /* client disconnected */
626 GNUNET_assert (size >= sizeof (struct GNUNET_NSE_FloodMessage));
627 idx = estimate_index;
628 if (GNUNET_NO == peer_entry->previous_round)
630 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
631 peer_entry->previous_round = GNUNET_YES;
632 peer_entry->transmit_task =
633 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0),
637 if ((0 == ntohl (size_estimate_messages[idx].hop_count)) &&
638 (NULL != proof_task))
640 GNUNET_STATISTICS_update (stats,
641 "# flood messages not generated (no proof yet)",
645 if (0 == ntohs (size_estimate_messages[idx].header.size))
647 GNUNET_STATISTICS_update (stats,
648 "# flood messages not generated (lack of history)",
652 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
653 "In round s, sending to `%s' estimate with %u bits\n",
654 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (size_estimate_messages[idx].timestamp)),
655 GNUNET_i2s (&peer_entry->id),
656 (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
657 if (ntohl (size_estimate_messages[idx].hop_count) == 0)
658 GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO);
659 GNUNET_STATISTICS_update (stats, "# flood messages transmitted", 1,
661 #if ENABLE_NSE_HISTOGRAM
662 peer_entry->transmitted_messages++;
663 peer_entry->last_transmitted_size =
664 ntohl(size_estimate_messages[idx].matching_bits);
666 memcpy (buf, &size_estimate_messages[idx],
667 sizeof (struct GNUNET_NSE_FloodMessage));
668 return sizeof (struct GNUNET_NSE_FloodMessage);
673 * Task that triggers a NSE P2P transmission.
675 * @param cls the `struct NSEPeerEntry *`
676 * @param tc scheduler context
679 transmit_task_cb (void *cls,
680 const struct GNUNET_SCHEDULER_TaskContext *tc)
682 struct NSEPeerEntry *peer_entry = cls;
684 peer_entry->transmit_task = NULL;
686 GNUNET_assert (NULL == peer_entry->th);
688 GNUNET_CORE_notify_transmit_ready (core_api, GNUNET_NO,
690 GNUNET_TIME_UNIT_FOREVER_REL,
693 GNUNET_NSE_FloodMessage),
694 &transmit_ready, peer_entry);
699 * We've sent on our flood message or one that we received which was
700 * validated and closer than ours. Update the global list of recent
701 * messages and the average. Also re-broadcast the message to any
705 update_network_size_estimate ()
707 struct GNUNET_NSE_ClientMessage em;
709 setup_estimate_message (&em);
710 GNUNET_SERVER_notification_context_broadcast (nc,
717 * Setup a flood message in our history array at the given
718 * slot offset for the given timestamp.
720 * @param slot index to use
721 * @param ts timestamp to use
724 setup_flood_message (unsigned int slot,
725 struct GNUNET_TIME_Absolute ts)
727 struct GNUNET_NSE_FloodMessage *fm;
728 uint32_t matching_bits;
730 matching_bits = get_matching_bits (ts, &my_identity);
731 fm = &size_estimate_messages[slot];
732 fm->header.size = htons (sizeof (struct GNUNET_NSE_FloodMessage));
733 fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
734 fm->hop_count = htonl (0);
735 fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
737 htonl (sizeof (struct GNUNET_NSE_FloodMessage) -
738 sizeof (struct GNUNET_MessageHeader) - sizeof (uint32_t) -
739 sizeof (struct GNUNET_CRYPTO_EddsaSignature));
740 fm->matching_bits = htonl (matching_bits);
741 fm->timestamp = GNUNET_TIME_absolute_hton (ts);
742 fm->origin = my_identity;
743 fm->proof_of_work = my_proof;
744 if (nse_work_required > 0)
745 GNUNET_assert (GNUNET_OK ==
746 GNUNET_CRYPTO_eddsa_sign (my_private_key, &fm->purpose,
749 memset (&fm->signature, 0, sizeof (fm->signature));
754 * Schedule transmission for the given peer for the current round based
755 * on what we know about the desired delay.
758 * @param key hash of peer identity
759 * @param value the `struct NSEPeerEntry`
760 * @return #GNUNET_OK (continue to iterate)
763 schedule_current_round (void *cls,
764 const struct GNUNET_PeerIdentity * key,
767 struct NSEPeerEntry *peer_entry = value;
768 struct GNUNET_TIME_Relative delay;
770 if (NULL != peer_entry->th)
772 peer_entry->previous_round = GNUNET_NO;
775 if (NULL != peer_entry->transmit_task)
777 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
778 peer_entry->previous_round = GNUNET_NO;
780 #if ENABLE_NSE_HISTOGRAM
781 if (peer_entry->received_messages > 1)
782 GNUNET_STATISTICS_update(stats, "# extra messages",
783 peer_entry->received_messages - 1, GNUNET_NO);
784 peer_entry->transmitted_messages = 0;
785 peer_entry->last_transmitted_size = 0;
786 peer_entry->received_messages = 0;
789 get_transmit_delay ((peer_entry->previous_round == GNUNET_NO) ? -1 : 0);
790 peer_entry->transmit_task =
791 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
797 * Update our flood message to be sent (and our timestamps).
800 * @param tc context for this message
803 update_flood_message (void *cls,
804 const struct GNUNET_SCHEDULER_TaskContext *tc)
806 struct GNUNET_TIME_Relative offset;
810 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
812 offset = GNUNET_TIME_absolute_get_remaining (next_timestamp);
813 if (0 != offset.rel_value_us)
815 /* somehow run early, delay more */
817 GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL);
820 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
821 if (estimate_count < HISTORY_SIZE)
823 current_timestamp = next_timestamp;
825 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
826 if ((current_timestamp.abs_value_us ==
827 GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value_us) &&
828 (get_matching_bits (current_timestamp, &my_identity) <
829 ntohl(next_message.matching_bits)))
831 /* we received a message for this round way early, use it! */
832 size_estimate_messages[estimate_index] = next_message;
833 size_estimate_messages[estimate_index].hop_count =
834 htonl (1 + ntohl (next_message.hop_count));
837 setup_flood_message (estimate_index, current_timestamp);
838 next_message.matching_bits = htonl (0); /* reset for 'next' round */
840 for (i = 0; i < HISTORY_SIZE; i++)
842 GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max);
843 GNUNET_CONTAINER_multipeermap_iterate (peers,
844 &schedule_current_round,
847 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
848 (next_timestamp), &update_flood_message,
854 * Count the leading zeroes in hash.
856 * @param hash to count leading zeros in
857 * @return the number of leading zero bits.
860 count_leading_zeroes (const struct GNUNET_HashCode *hash)
862 unsigned int hash_count;
865 while (0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count))
872 * Check whether the given public key and integer are a valid proof of
875 * @param pkey the public key
876 * @param val the integer
877 * @return #GNUNET_YES if valid, #GNUNET_NO if not
880 check_proof_of_work (const struct GNUNET_CRYPTO_EddsaPublicKey *pkey,
883 char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
884 sizeof (val)] GNUNET_ALIGN;
885 struct GNUNET_HashCode result;
887 memcpy (buf, &val, sizeof (val));
888 memcpy (&buf[sizeof (val)], pkey,
889 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
890 pow_hash (buf, sizeof (buf), &result);
891 return (count_leading_zeroes (&result) >=
892 nse_work_required) ? GNUNET_YES : GNUNET_NO;
897 * Write our current proof to disk.
905 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
907 if (sizeof (my_proof) !=
908 GNUNET_DISK_fn_write (proof, &my_proof, sizeof (my_proof),
909 GNUNET_DISK_PERM_USER_READ |
910 GNUNET_DISK_PERM_USER_WRITE))
911 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof);
918 * Find our proof of work.
920 * @param cls closure (unused)
921 * @param tc task context
924 find_proof (void *cls,
925 const struct GNUNET_SCHEDULER_TaskContext *tc)
927 #define ROUND_SIZE 10
929 char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
930 sizeof (uint64_t)] GNUNET_ALIGN;
931 struct GNUNET_HashCode result;
935 memcpy (&buf[sizeof (uint64_t)], &my_identity,
936 sizeof (struct GNUNET_PeerIdentity));
939 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
941 memcpy (buf, &counter, sizeof (uint64_t));
942 pow_hash (buf, sizeof (buf), &result);
943 if (nse_work_required <= count_leading_zeroes (&result))
946 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work found: %llu!\n",
947 (unsigned long long) GNUNET_ntohll (counter));
949 setup_flood_message (estimate_index, current_timestamp);
955 if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
957 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing proofs currently at %llu\n",
958 (unsigned long long) counter);
959 /* remember progress every 100 rounds */
968 GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
969 GNUNET_SCHEDULER_PRIORITY_IDLE,
975 * An incoming flood message has been received which claims
976 * to have more bits matching than any we know in this time
977 * period. Verify the signature and/or proof of work.
979 * @param incoming_flood the message to verify
980 * @return #GNUNET_YES if the message is verified
981 * #GNUNET_NO if the key/signature don't verify
984 verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood)
987 check_proof_of_work (&incoming_flood->origin.public_key,
988 incoming_flood->proof_of_work))
990 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
991 "Proof of work invalid: %llu!\n",
993 GNUNET_ntohll (incoming_flood->proof_of_work));
997 if ((nse_work_required > 0) &&
999 GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
1000 &incoming_flood->purpose,
1001 &incoming_flood->signature,
1002 &incoming_flood->origin.public_key)))
1004 GNUNET_break_op (0);
1012 * Update transmissions for the given peer for the current round based
1013 * on updated proximity information.
1015 * @param cls peer entry to exclude from updates
1016 * @param key hash of peer identity
1017 * @param value the `struct NSEPeerEntry *` of a peer to transmit to
1018 * @return #GNUNET_OK (continue to iterate)
1021 update_flood_times (void *cls,
1022 const struct GNUNET_PeerIdentity *key,
1025 struct NSEPeerEntry *exclude = cls;
1026 struct NSEPeerEntry *peer_entry = value;
1027 struct GNUNET_TIME_Relative delay;
1029 if (NULL != peer_entry->th)
1030 return GNUNET_OK; /* already active */
1031 if (peer_entry == exclude)
1032 return GNUNET_OK; /* trigger of the update */
1033 if (peer_entry->previous_round == GNUNET_NO)
1035 /* still stuck in previous round, no point to update, check that
1036 * we are active here though... */
1037 if ( (NULL == peer_entry->transmit_task) &&
1038 (NULL == peer_entry->th) )
1044 if (NULL != peer_entry->transmit_task)
1046 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1047 peer_entry->transmit_task = NULL;
1049 delay = get_transmit_delay (0);
1050 peer_entry->transmit_task =
1051 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
1057 * Core handler for size estimate flooding messages.
1059 * @param cls closure unused
1060 * @param message message
1061 * @param peer peer identity this message is from (ignored)
1064 handle_p2p_size_estimate (void *cls,
1065 const struct GNUNET_PeerIdentity *peer,
1066 const struct GNUNET_MessageHeader *message)
1068 const struct GNUNET_NSE_FloodMessage *incoming_flood;
1069 struct GNUNET_TIME_Absolute ts;
1070 struct NSEPeerEntry *peer_entry;
1071 uint32_t matching_bits;
1074 #if ENABLE_NSE_HISTOGRAM
1078 t = GNUNET_TIME_absolute_get().abs_value_us;
1080 GNUNET_TESTBED_LOGGER_write (lh, &t, sizeof (uint64_t));
1081 if (NULL != histogram)
1082 GNUNET_BIO_write_int64 (histogram, t);
1085 incoming_flood = (const struct GNUNET_NSE_FloodMessage *) message;
1086 GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO);
1087 matching_bits = ntohl (incoming_flood->matching_bits);
1092 struct GNUNET_PeerIdentity os;
1094 GNUNET_snprintf (origin,
1097 GNUNET_i2s (&incoming_flood->origin));
1098 GNUNET_snprintf (pred,
1102 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1103 "Flood at %s from `%s' via `%s' at `%s' with bits %u\n",
1104 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp)),
1105 origin, pred, GNUNET_i2s (&my_identity),
1106 (unsigned int) matching_bits);
1110 peer_entry = GNUNET_CONTAINER_multipeermap_get (peers, peer);
1111 if (NULL == peer_entry)
1116 #if ENABLE_NSE_HISTOGRAM
1117 peer_entry->received_messages++;
1118 if (peer_entry->transmitted_messages > 0 &&
1119 peer_entry->last_transmitted_size >= matching_bits)
1120 GNUNET_STATISTICS_update(stats, "# cross messages", 1, GNUNET_NO);
1123 ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp);
1124 if (ts.abs_value_us == current_timestamp.abs_value_us)
1125 idx = estimate_index;
1126 else if (ts.abs_value_us ==
1127 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us)
1128 idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1129 else if (ts.abs_value_us == next_timestamp.abs_value_us)
1131 if (matching_bits <= ntohl (next_message.matching_bits))
1132 return GNUNET_OK; /* ignore, simply too early/late */
1133 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1135 GNUNET_break_op (0);
1138 next_message = *incoming_flood;
1143 GNUNET_STATISTICS_update (stats,
1144 "# flood messages discarded (clock skew too large)",
1148 if (0 == (memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))))
1150 /* send to self, update our own estimate IF this also comes from us! */
1152 memcmp (&incoming_flood->origin,
1153 &my_identity, sizeof (my_identity)))
1154 update_network_size_estimate ();
1157 if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits))
1159 /* Cancel transmission in the other direction, as this peer clearly has
1160 up-to-date information already. Even if we didn't talk to this peer in
1161 the previous round, we should no longer send it stale information as it
1162 told us about the current round! */
1163 peer_entry->previous_round = GNUNET_YES;
1164 if (idx != estimate_index)
1166 /* do not transmit information for the previous round to this peer
1167 anymore (but allow current round) */
1170 /* got up-to-date information for current round, cancel transmission to
1171 * this peer altogether */
1172 if (NULL != peer_entry->transmit_task)
1174 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1175 peer_entry->transmit_task = NULL;
1177 if (NULL != peer_entry->th)
1179 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1180 peer_entry->th = NULL;
1184 if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits))
1186 if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES)) {
1187 peer_entry->previous_round = GNUNET_NO;
1189 /* push back our result now, that peer is spreading bad information... */
1190 if (NULL == peer_entry->th)
1192 if (peer_entry->transmit_task != NULL)
1193 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1194 peer_entry->transmit_task =
1195 GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry);
1197 /* Not closer than our most recent message, no need to do work here */
1198 GNUNET_STATISTICS_update (stats,
1199 "# flood messages ignored (had closer already)",
1203 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1205 GNUNET_break_op (0);
1208 GNUNET_assert (matching_bits >
1209 ntohl (size_estimate_messages[idx].matching_bits));
1210 /* Cancel transmission in the other direction, as this peer clearly has
1211 * up-to-date information already.
1213 peer_entry->previous_round = GNUNET_YES;
1214 if (idx == estimate_index)
1216 /* cancel any activity for current round */
1217 if (peer_entry->transmit_task != NULL)
1219 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1220 peer_entry->transmit_task = NULL;
1222 if (peer_entry->th != NULL)
1224 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1225 peer_entry->th = NULL;
1228 size_estimate_messages[idx] = *incoming_flood;
1229 size_estimate_messages[idx].hop_count =
1230 htonl (ntohl (incoming_flood->hop_count) + 1);
1232 GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max);
1233 GNUNET_STATISTICS_set (stats,
1234 "# estimated network diameter",
1235 hop_count_max, GNUNET_NO);
1237 /* have a new, better size estimate, inform clients */
1238 update_network_size_estimate ();
1241 GNUNET_CONTAINER_multipeermap_iterate (peers, &update_flood_times,
1248 * Method called whenever a peer connects. Sets up the PeerEntry and
1249 * schedules the initial size info transmission to this peer.
1251 * @param cls closure
1252 * @param peer peer identity this notification is about
1255 handle_core_connect (void *cls,
1256 const struct GNUNET_PeerIdentity *peer)
1258 struct NSEPeerEntry *peer_entry;
1260 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1261 "Peer `%s' connected to us\n",
1263 peer_entry = GNUNET_new (struct NSEPeerEntry);
1264 peer_entry->id = *peer;
1265 GNUNET_assert (GNUNET_OK ==
1266 GNUNET_CONTAINER_multipeermap_put (peers,
1269 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1270 peer_entry->transmit_task =
1271 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1),
1274 GNUNET_STATISTICS_update (stats,
1275 "# peers connected",
1282 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
1283 * any pending transmission requests to that peer.
1285 * @param cls closure
1286 * @param peer peer identity this notification is about
1289 handle_core_disconnect (void *cls,
1290 const struct GNUNET_PeerIdentity *peer)
1292 struct NSEPeerEntry *pos;
1294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1295 "Peer `%s' disconnected from us\n",
1297 pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
1303 GNUNET_assert (GNUNET_YES ==
1304 GNUNET_CONTAINER_multipeermap_remove (peers, peer,
1306 if (pos->transmit_task != NULL) {
1307 GNUNET_SCHEDULER_cancel (pos->transmit_task);
1308 pos->transmit_task = NULL;
1310 if (NULL != pos->th)
1312 GNUNET_CORE_notify_transmit_ready_cancel (pos->th);
1316 GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO);
1320 #if ENABLE_NSE_HISTOGRAM
1322 * Functions of this type are called to notify a successful transmission of the
1323 * message to the logger service
1326 * @param size the amount of data sent (ignored)
1329 flush_comp_cb (void *cls,
1332 GNUNET_TESTBED_LOGGER_disconnect (lh);
1339 * Task run during shutdown.
1345 shutdown_task (void *cls,
1346 const struct GNUNET_SCHEDULER_TaskContext *tc)
1348 if (NULL != flood_task)
1350 GNUNET_SCHEDULER_cancel (flood_task);
1353 if (NULL != proof_task)
1355 GNUNET_SCHEDULER_cancel (proof_task);
1357 write_proof (); /* remember progress */
1361 GNUNET_SERVER_notification_context_destroy (nc);
1364 if (NULL != core_api)
1366 GNUNET_CORE_disconnect (core_api);
1371 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1376 GNUNET_CONTAINER_multipeermap_destroy (peers);
1379 if (NULL != my_private_key)
1381 GNUNET_free (my_private_key);
1382 my_private_key = NULL;
1384 #if ENABLE_NSE_HISTOGRAM
1385 if (NULL != logger_test)
1387 GNUNET_CLIENT_service_test_cancel (logger_test);
1392 struct GNUNET_TIME_Relative timeout;
1393 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30);
1394 GNUNET_TESTBED_LOGGER_flush (lh, timeout, &flush_comp_cb, NULL);
1396 if (NULL != histogram)
1398 GNUNET_BIO_write_close (histogram);
1406 * Called on core init/fail.
1408 * @param cls service closure
1409 * @param identity the public identity of this peer
1412 core_init (void *cls,
1413 const struct GNUNET_PeerIdentity *identity)
1415 struct GNUNET_TIME_Absolute now;
1416 struct GNUNET_TIME_Absolute prev_time;
1418 if (NULL == identity)
1420 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n");
1421 GNUNET_SCHEDULER_shutdown ();
1425 memcmp (&my_identity, identity,
1426 sizeof (struct GNUNET_PeerIdentity)));
1427 now = GNUNET_TIME_absolute_get ();
1428 current_timestamp.abs_value_us =
1429 (now.abs_value_us / gnunet_nse_interval.rel_value_us) *
1430 gnunet_nse_interval.rel_value_us;
1432 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
1433 estimate_index = HISTORY_SIZE - 1;
1435 if (GNUNET_YES == check_proof_of_work (&my_identity.public_key, my_proof))
1437 int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1438 prev_time.abs_value_us =
1439 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us;
1440 setup_flood_message (idx, prev_time);
1441 setup_flood_message (estimate_index, current_timestamp);
1445 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
1446 (next_timestamp), &update_flood_message,
1450 #if ENABLE_NSE_HISTOGRAM
1452 * Function called with the status of the testbed logger service
1455 * @param status GNUNET_YES if the service is running,
1456 * GNUNET_NO if the service is not running
1457 * GNUNET_SYSERR if the configuration is invalid
1460 status_cb (void *cls, int status)
1463 if (GNUNET_YES != status)
1465 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Testbed logger not running\n");
1468 if (NULL == (lh = GNUNET_TESTBED_LOGGER_connect (cfg)))
1470 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1471 "Cannot connect to the testbed logger. Exiting.\n");
1472 GNUNET_SCHEDULER_shutdown ();
1479 * Handle network size estimate clients.
1481 * @param cls closure
1482 * @param server the initialized server
1483 * @param c configuration to use
1487 struct GNUNET_SERVER_Handle *server,
1488 const struct GNUNET_CONFIGURATION_Handle *c)
1490 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1491 {&handle_start_message, NULL, GNUNET_MESSAGE_TYPE_NSE_START,
1492 sizeof (struct GNUNET_MessageHeader)},
1495 static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
1496 {&handle_p2p_size_estimate, GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1497 sizeof (struct GNUNET_NSE_FloodMessage)},
1501 struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
1506 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "INTERVAL",
1507 &gnunet_nse_interval))
1509 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1511 GNUNET_SCHEDULER_shutdown ();
1515 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "WORKDELAY",
1518 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1519 "NSE", "WORKDELAY");
1520 GNUNET_SCHEDULER_shutdown ();
1524 GNUNET_CONFIGURATION_get_value_number (cfg, "NSE", "WORKBITS",
1525 &nse_work_required))
1527 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1529 GNUNET_SCHEDULER_shutdown ();
1532 if (nse_work_required >= sizeof (struct GNUNET_HashCode) * 8)
1534 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1537 _("Value is too large.\n"));
1538 GNUNET_SCHEDULER_shutdown ();
1542 #if ENABLE_NSE_HISTOGRAM
1544 char *histogram_dir;
1548 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "HISTOGRAM_DIR",
1551 GNUNET_assert (0 < GNUNET_asprintf (&histogram_fn, "%s/timestamps",
1553 GNUNET_free (histogram_dir);
1554 histogram = GNUNET_BIO_write_open (histogram_fn);
1555 GNUNET_free (histogram_fn);
1556 if (NULL == histogram)
1557 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to open histogram file\n");
1560 GNUNET_CLIENT_service_test ("testbed-logger", cfg,
1561 GNUNET_TIME_UNIT_SECONDS,
1567 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1569 pk = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
1570 GNUNET_assert (NULL != pk);
1571 my_private_key = pk;
1572 GNUNET_CRYPTO_eddsa_key_get_public (my_private_key,
1573 &my_identity.public_key);
1575 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
1577 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "PROOFFILE");
1578 GNUNET_free (my_private_key);
1579 my_private_key = NULL;
1580 GNUNET_SCHEDULER_shutdown ();
1583 if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) ||
1584 (sizeof (my_proof) !=
1585 GNUNET_DISK_fn_read (proof, &my_proof, sizeof (my_proof))))
1587 GNUNET_free (proof);
1589 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1592 peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO);
1593 GNUNET_SERVER_add_handlers (srv, handlers);
1594 nc = GNUNET_SERVER_notification_context_create (srv, 1);
1595 /* Connect to core service and register core handlers */
1596 core_api = GNUNET_CORE_connect (cfg, /* Main configuration */
1597 NULL, /* Closure passed to functions */
1598 &core_init, /* Call core_init once connected */
1599 &handle_core_connect, /* Handle connects */
1600 &handle_core_disconnect, /* Handle disconnects */
1601 NULL, /* Don't want notified about all incoming messages */
1602 GNUNET_NO, /* For header only inbound notification */
1603 NULL, /* Don't want notified about all outbound messages */
1604 GNUNET_NO, /* For header only outbound notification */
1605 core_handlers); /* Register these handlers */
1606 if (NULL == core_api)
1608 GNUNET_SCHEDULER_shutdown ();
1611 stats = GNUNET_STATISTICS_create ("nse", cfg);
1616 * The main function for the network size estimation service.
1618 * @param argc number of arguments from the command line
1619 * @param argv command line arguments
1620 * @return 0 ok, 1 on error
1626 return (GNUNET_OK ==
1627 GNUNET_SERVICE_run (argc, argv, "nse", GNUNET_SERVICE_OPTION_NONE,
1628 &run, NULL)) ? 0 : 1;
1636 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1638 void __attribute__ ((constructor))
1639 GNUNET_ARM_memory_init ()
1641 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1642 mallopt (M_TOP_PAD, 1 * 1024);
1649 /* end of gnunet-service-nse.c */