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 GNUNET_SCHEDULER_TaskIdentifier 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 GNUNET_SCHEDULER_TaskIdentifier flood_task;
281 * Task scheduled to compute our proof.
283 static GNUNET_SCHEDULER_TaskIdentifier 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 nsize = log2 (GNUNET_CONTAINER_multipeermap_size (peers) + 1);
403 em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se, nsize));
404 em->std_deviation = GNUNET_hton_double (std_dev);
405 GNUNET_STATISTICS_set (stats, "# nodes in the network (estimate)",
406 (uint64_t) pow (2, mean - 1.0 / 3.0), GNUNET_NO);
411 * Handler for START message from client, triggers an
412 * immediate current network estimate notification.
413 * Also, we remember the client for updates upon future
414 * estimate measurements.
417 * @param client who sent the message
418 * @param message the message received
421 handle_start_message (void *cls,
422 struct GNUNET_SERVER_Client *client,
423 const struct GNUNET_MessageHeader *message)
425 struct GNUNET_NSE_ClientMessage em;
427 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
428 "Received START message from client\n");
429 GNUNET_SERVER_notification_context_add (nc, client);
430 setup_estimate_message (&em);
431 GNUNET_SERVER_notification_context_unicast (nc, client, &em.header,
433 GNUNET_SERVER_receive_done (client, GNUNET_OK);
438 * How long should we delay a message to go the given number of
441 * @param matching_bits number of matching bits to consider
444 get_matching_bits_delay (uint32_t matching_bits)
446 /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */
447 // S is next_timestamp (ignored in return value)
448 // f is frequency (gnunet_nse_interval)
449 // x is matching_bits
450 // p' is current_size_estimate
451 return ((double) gnunet_nse_interval.rel_value_us / (double) 2.0) -
452 ((gnunet_nse_interval.rel_value_us / M_PI) *
453 atan (matching_bits - current_size_estimate));
458 * What delay randomization should we apply for a given number of matching bits?
460 * @param matching_bits number of matching bits
461 * @return random delay to apply
463 static struct GNUNET_TIME_Relative
464 get_delay_randomization (uint32_t matching_bits)
466 #if USE_RANDOM_DELAYS
467 struct GNUNET_TIME_Relative ret;
471 d = get_matching_bits_delay (matching_bits);
472 i = (uint32_t) (d / (double) (hop_count_max + 1));
473 ret.rel_value_us = i;
474 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
475 "Randomizing flood using latencies up to %s\n",
476 GNUNET_STRINGS_relative_time_to_string (ret,
478 ret.rel_value_us = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1);
481 return GNUNET_TIME_UNIT_ZERO;
487 * Calculate the 'proof-of-work' hash (an expensive hash).
489 * @param buf data to hash
490 * @param buf_len number of bytes in @a buf
491 * @param result where to write the resulting hash
494 pow_hash (const void *buf,
496 struct GNUNET_HashCode *result)
499 gcry_kdf_derive (buf, buf_len,
502 "gnunet-proof-of-work", strlen ("gnunet-proof-of-work"),
503 2 /* iterations; keep cost of individual op small */,
504 sizeof (struct GNUNET_HashCode), result));
509 * Get the number of matching bits that the given timestamp has to the given peer ID.
511 * @param timestamp time to generate key
512 * @param id peer identity to compare with
513 * @return number of matching bits
516 get_matching_bits (struct GNUNET_TIME_Absolute timestamp,
517 const struct GNUNET_PeerIdentity *id)
519 struct GNUNET_HashCode timestamp_hash;
520 struct GNUNET_HashCode pid_hash;
522 GNUNET_CRYPTO_hash (×tamp.abs_value_us, sizeof (timestamp.abs_value_us),
524 GNUNET_CRYPTO_hash (id, sizeof (struct GNUNET_PeerIdentity), &pid_hash);
525 return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash, &pid_hash);
530 * Get the transmission delay that should be applied for a
533 * @param round_offset -1 for the previous round (random delay between 0 and 50ms)
534 * 0 for the current round (based on our proximity to time key)
535 * @return delay that should be applied
537 static struct GNUNET_TIME_Relative
538 get_transmit_delay (int round_offset)
540 struct GNUNET_TIME_Relative ret;
541 struct GNUNET_TIME_Absolute tgt;
543 uint32_t matching_bits;
545 switch (round_offset)
548 /* previous round is randomized between 0 and 50 ms */
549 #if USE_RANDOM_DELAYS
550 ret.rel_value_us = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50);
552 ret = GNUNET_TIME_UNIT_ZERO;
554 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
555 "Transmitting previous round behind schedule in %s\n",
556 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
559 /* current round is based on best-known matching_bits */
561 ntohl (size_estimate_messages[estimate_index].matching_bits);
562 dist_delay = get_matching_bits_delay (matching_bits);
563 dist_delay += get_delay_randomization (matching_bits).rel_value_us;
564 ret.rel_value_us = (uint64_t) dist_delay;
565 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
566 "For round %s, delay for %u matching bits is %s\n",
567 GNUNET_STRINGS_absolute_time_to_string (current_timestamp),
568 (unsigned int) matching_bits,
569 GNUNET_STRINGS_relative_time_to_string (ret,
571 /* now consider round start time and add delay to it */
572 tgt = GNUNET_TIME_absolute_add (current_timestamp, ret);
573 return GNUNET_TIME_absolute_get_remaining (tgt);
576 return GNUNET_TIME_UNIT_FOREVER_REL;
581 * Task that triggers a NSE P2P transmission.
583 * @param cls the `struct NSEPeerEntry *`
584 * @param tc scheduler context
587 transmit_task_cb (void *cls,
588 const struct GNUNET_SCHEDULER_TaskContext *tc);
592 * Called when core is ready to send a message we asked for
593 * out to the destination.
595 * @param cls closure with the `struct NSEPeerEntry *`
596 * @param size number of bytes available in @a buf
597 * @param buf where the callee should write the message
598 * @return number of bytes written to @a buf
601 transmit_ready (void *cls,
605 struct NSEPeerEntry *peer_entry = cls;
608 peer_entry->th = NULL;
611 /* client disconnected */
614 GNUNET_assert (size >= sizeof (struct GNUNET_NSE_FloodMessage));
615 idx = estimate_index;
616 if (GNUNET_NO == peer_entry->previous_round)
618 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
619 peer_entry->previous_round = GNUNET_YES;
620 peer_entry->transmit_task =
621 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0), &transmit_task_cb,
624 if ((0 == ntohl (size_estimate_messages[idx].hop_count)) &&
625 (GNUNET_SCHEDULER_NO_TASK != proof_task))
627 GNUNET_STATISTICS_update (stats,
628 "# flood messages not generated (no proof yet)",
632 if (0 == ntohs (size_estimate_messages[idx].header.size))
634 GNUNET_STATISTICS_update (stats,
635 "# flood messages not generated (lack of history)",
639 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
640 "In round s, sending to `%s' estimate with %u bits\n",
641 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (size_estimate_messages[idx].timestamp)),
642 GNUNET_i2s (&peer_entry->id),
643 (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
644 if (ntohl (size_estimate_messages[idx].hop_count) == 0)
645 GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO);
646 GNUNET_STATISTICS_update (stats, "# flood messages transmitted", 1,
648 #if ENABLE_NSE_HISTOGRAM
649 peer_entry->transmitted_messages++;
650 peer_entry->last_transmitted_size =
651 ntohl(size_estimate_messages[idx].matching_bits);
653 memcpy (buf, &size_estimate_messages[idx],
654 sizeof (struct GNUNET_NSE_FloodMessage));
655 return sizeof (struct GNUNET_NSE_FloodMessage);
660 * Task that triggers a NSE P2P transmission.
662 * @param cls the `struct NSEPeerEntry *`
663 * @param tc scheduler context
666 transmit_task_cb (void *cls,
667 const struct GNUNET_SCHEDULER_TaskContext *tc)
669 struct NSEPeerEntry *peer_entry = cls;
671 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
673 GNUNET_assert (NULL == peer_entry->th);
675 GNUNET_CORE_notify_transmit_ready (core_api, GNUNET_NO,
677 GNUNET_TIME_UNIT_FOREVER_REL,
680 GNUNET_NSE_FloodMessage),
681 &transmit_ready, peer_entry);
686 * We've sent on our flood message or one that we received which was
687 * validated and closer than ours. Update the global list of recent
688 * messages and the average. Also re-broadcast the message to any
692 update_network_size_estimate ()
694 struct GNUNET_NSE_ClientMessage em;
696 setup_estimate_message (&em);
697 GNUNET_SERVER_notification_context_broadcast (nc,
704 * Setup a flood message in our history array at the given
705 * slot offset for the given timestamp.
707 * @param slot index to use
708 * @param ts timestamp to use
711 setup_flood_message (unsigned int slot,
712 struct GNUNET_TIME_Absolute ts)
714 struct GNUNET_NSE_FloodMessage *fm;
715 uint32_t matching_bits;
717 matching_bits = get_matching_bits (ts, &my_identity);
718 fm = &size_estimate_messages[slot];
719 fm->header.size = htons (sizeof (struct GNUNET_NSE_FloodMessage));
720 fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
721 fm->hop_count = htonl (0);
722 fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
724 htonl (sizeof (struct GNUNET_NSE_FloodMessage) -
725 sizeof (struct GNUNET_MessageHeader) - sizeof (uint32_t) -
726 sizeof (struct GNUNET_CRYPTO_EddsaSignature));
727 fm->matching_bits = htonl (matching_bits);
728 fm->timestamp = GNUNET_TIME_absolute_hton (ts);
729 fm->origin = my_identity;
730 fm->proof_of_work = my_proof;
731 if (nse_work_required > 0)
732 GNUNET_assert (GNUNET_OK ==
733 GNUNET_CRYPTO_eddsa_sign (my_private_key, &fm->purpose,
736 memset (&fm->signature, 0, sizeof (fm->signature));
741 * Schedule transmission for the given peer for the current round based
742 * on what we know about the desired delay.
745 * @param key hash of peer identity
746 * @param value the `struct NSEPeerEntry`
747 * @return #GNUNET_OK (continue to iterate)
750 schedule_current_round (void *cls,
751 const struct GNUNET_PeerIdentity * key,
754 struct NSEPeerEntry *peer_entry = value;
755 struct GNUNET_TIME_Relative delay;
757 if (NULL != peer_entry->th)
759 peer_entry->previous_round = GNUNET_NO;
762 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
764 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
765 peer_entry->previous_round = GNUNET_NO;
767 #if ENABLE_NSE_HISTOGRAM
768 if (peer_entry->received_messages > 1)
769 GNUNET_STATISTICS_update(stats, "# extra messages",
770 peer_entry->received_messages - 1, GNUNET_NO);
771 peer_entry->transmitted_messages = 0;
772 peer_entry->last_transmitted_size = 0;
773 peer_entry->received_messages = 0;
776 get_transmit_delay ((peer_entry->previous_round == GNUNET_NO) ? -1 : 0);
777 peer_entry->transmit_task =
778 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
784 * Update our flood message to be sent (and our timestamps).
787 * @param tc context for this message
790 update_flood_message (void *cls,
791 const struct GNUNET_SCHEDULER_TaskContext *tc)
793 struct GNUNET_TIME_Relative offset;
796 flood_task = GNUNET_SCHEDULER_NO_TASK;
797 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
799 offset = GNUNET_TIME_absolute_get_remaining (next_timestamp);
800 if (0 != offset.rel_value_us)
802 /* somehow run early, delay more */
804 GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL);
807 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
808 if (estimate_count < HISTORY_SIZE)
810 current_timestamp = next_timestamp;
812 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
813 if ((current_timestamp.abs_value_us ==
814 GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value_us) &&
815 (get_matching_bits (current_timestamp, &my_identity) <
816 ntohl(next_message.matching_bits)))
818 /* we received a message for this round way early, use it! */
819 size_estimate_messages[estimate_index] = next_message;
820 size_estimate_messages[estimate_index].hop_count =
821 htonl (1 + ntohl (next_message.hop_count));
824 setup_flood_message (estimate_index, current_timestamp);
825 next_message.matching_bits = htonl (0); /* reset for 'next' round */
827 for (i = 0; i < HISTORY_SIZE; i++)
829 GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max);
830 GNUNET_CONTAINER_multipeermap_iterate (peers, &schedule_current_round, NULL);
832 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
833 (next_timestamp), &update_flood_message,
839 * Count the leading zeroes in hash.
841 * @param hash to count leading zeros in
842 * @return the number of leading zero bits.
845 count_leading_zeroes (const struct GNUNET_HashCode *hash)
847 unsigned int hash_count;
850 while (0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count))
857 * Check whether the given public key and integer are a valid proof of
860 * @param pkey the public key
861 * @param val the integer
862 * @return #GNUNET_YES if valid, #GNUNET_NO if not
865 check_proof_of_work (const struct GNUNET_CRYPTO_EddsaPublicKey *pkey,
868 char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
869 sizeof (val)] GNUNET_ALIGN;
870 struct GNUNET_HashCode result;
872 memcpy (buf, &val, sizeof (val));
873 memcpy (&buf[sizeof (val)], pkey,
874 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
875 pow_hash (buf, sizeof (buf), &result);
876 return (count_leading_zeroes (&result) >=
877 nse_work_required) ? GNUNET_YES : GNUNET_NO;
882 * Write our current proof to disk.
890 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
892 if (sizeof (my_proof) !=
893 GNUNET_DISK_fn_write (proof, &my_proof, sizeof (my_proof),
894 GNUNET_DISK_PERM_USER_READ |
895 GNUNET_DISK_PERM_USER_WRITE))
896 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof);
903 * Find our proof of work.
905 * @param cls closure (unused)
906 * @param tc task context
909 find_proof (void *cls,
910 const struct GNUNET_SCHEDULER_TaskContext *tc)
912 #define ROUND_SIZE 10
914 char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
915 sizeof (uint64_t)] GNUNET_ALIGN;
916 struct GNUNET_HashCode result;
919 proof_task = GNUNET_SCHEDULER_NO_TASK;
920 memcpy (&buf[sizeof (uint64_t)], &my_identity,
921 sizeof (struct GNUNET_PeerIdentity));
924 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
926 memcpy (buf, &counter, sizeof (uint64_t));
927 pow_hash (buf, sizeof (buf), &result);
928 if (nse_work_required <= count_leading_zeroes (&result))
931 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work found: %llu!\n",
932 (unsigned long long) GNUNET_ntohll (counter));
934 setup_flood_message (estimate_index, current_timestamp);
940 if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
942 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing proofs currently at %llu\n",
943 (unsigned long long) counter);
944 /* remember progress every 100 rounds */
953 GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
954 GNUNET_SCHEDULER_PRIORITY_IDLE,
960 * An incoming flood message has been received which claims
961 * to have more bits matching than any we know in this time
962 * period. Verify the signature and/or proof of work.
964 * @param incoming_flood the message to verify
965 * @return #GNUNET_YES if the message is verified
966 * #GNUNET_NO if the key/signature don't verify
969 verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood)
972 check_proof_of_work (&incoming_flood->origin.public_key,
973 incoming_flood->proof_of_work))
975 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
976 "Proof of work invalid: %llu!\n",
978 GNUNET_ntohll (incoming_flood->proof_of_work));
982 if ((nse_work_required > 0) &&
984 GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
985 &incoming_flood->purpose,
986 &incoming_flood->signature,
987 &incoming_flood->origin.public_key)))
997 * Update transmissions for the given peer for the current round based
998 * on updated proximity information.
1000 * @param cls peer entry to exclude from updates
1001 * @param key hash of peer identity
1002 * @param value the `struct NSEPeerEntry *` of a peer to transmit to
1003 * @return #GNUNET_OK (continue to iterate)
1006 update_flood_times (void *cls,
1007 const struct GNUNET_PeerIdentity *key,
1010 struct NSEPeerEntry *exclude = cls;
1011 struct NSEPeerEntry *peer_entry = value;
1012 struct GNUNET_TIME_Relative delay;
1014 if (NULL != peer_entry->th)
1015 return GNUNET_OK; /* already active */
1016 if (peer_entry == exclude)
1017 return GNUNET_OK; /* trigger of the update */
1018 if (peer_entry->previous_round == GNUNET_NO)
1020 /* still stuck in previous round, no point to update, check that
1021 * we are active here though... */
1022 if ( (GNUNET_SCHEDULER_NO_TASK == peer_entry->transmit_task) &&
1023 (NULL == peer_entry->th) )
1029 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1031 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1032 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1034 delay = get_transmit_delay (0);
1035 peer_entry->transmit_task =
1036 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
1042 * Core handler for size estimate flooding messages.
1044 * @param cls closure unused
1045 * @param message message
1046 * @param peer peer identity this message is from (ignored)
1049 handle_p2p_size_estimate (void *cls,
1050 const struct GNUNET_PeerIdentity *peer,
1051 const struct GNUNET_MessageHeader *message)
1053 const struct GNUNET_NSE_FloodMessage *incoming_flood;
1054 struct GNUNET_TIME_Absolute ts;
1055 struct NSEPeerEntry *peer_entry;
1056 uint32_t matching_bits;
1059 #if ENABLE_NSE_HISTOGRAM
1063 t = GNUNET_TIME_absolute_get().abs_value_us;
1065 GNUNET_TESTBED_LOGGER_write (lh, &t, sizeof (uint64_t));
1066 if (NULL != histogram)
1067 GNUNET_BIO_write_int64 (histogram, t);
1070 incoming_flood = (const struct GNUNET_NSE_FloodMessage *) message;
1071 GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO);
1072 matching_bits = ntohl (incoming_flood->matching_bits);
1077 struct GNUNET_PeerIdentity os;
1079 GNUNET_snprintf (origin,
1082 GNUNET_i2s (&incoming_flood->origin));
1083 GNUNET_snprintf (pred,
1087 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1088 "Flood at %s from `%s' via `%s' at `%s' with bits %u\n",
1089 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp)),
1090 origin, pred, GNUNET_i2s (&my_identity),
1091 (unsigned int) matching_bits);
1095 peer_entry = GNUNET_CONTAINER_multipeermap_get (peers, peer);
1096 if (NULL == peer_entry)
1101 #if ENABLE_NSE_HISTOGRAM
1102 peer_entry->received_messages++;
1103 if (peer_entry->transmitted_messages > 0 &&
1104 peer_entry->last_transmitted_size >= matching_bits)
1105 GNUNET_STATISTICS_update(stats, "# cross messages", 1, GNUNET_NO);
1108 ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp);
1109 if (ts.abs_value_us == current_timestamp.abs_value_us)
1110 idx = estimate_index;
1111 else if (ts.abs_value_us ==
1112 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us)
1113 idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1114 else if (ts.abs_value_us == next_timestamp.abs_value_us)
1116 if (matching_bits <= ntohl (next_message.matching_bits))
1117 return GNUNET_OK; /* ignore, simply too early/late */
1118 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1120 GNUNET_break_op (0);
1123 next_message = *incoming_flood;
1128 GNUNET_STATISTICS_update (stats,
1129 "# flood messages discarded (clock skew too large)",
1133 if (0 == (memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))))
1135 /* send to self, update our own estimate IF this also comes from us! */
1137 memcmp (&incoming_flood->origin,
1138 &my_identity, sizeof (my_identity)))
1139 update_network_size_estimate ();
1142 if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits))
1144 /* Cancel transmission in the other direction, as this peer clearly has
1145 up-to-date information already. Even if we didn't talk to this peer in
1146 the previous round, we should no longer send it stale information as it
1147 told us about the current round! */
1148 peer_entry->previous_round = GNUNET_YES;
1149 if (idx != estimate_index)
1151 /* do not transmit information for the previous round to this peer
1152 anymore (but allow current round) */
1155 /* got up-to-date information for current round, cancel transmission to
1156 * this peer altogether */
1157 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1159 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1160 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1162 if (NULL != peer_entry->th)
1164 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1165 peer_entry->th = NULL;
1169 if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits))
1171 if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES)) {
1172 peer_entry->previous_round = GNUNET_NO;
1174 /* push back our result now, that peer is spreading bad information... */
1175 if (NULL == peer_entry->th)
1177 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1178 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1179 peer_entry->transmit_task =
1180 GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry);
1182 /* Not closer than our most recent message, no need to do work here */
1183 GNUNET_STATISTICS_update (stats,
1184 "# flood messages ignored (had closer already)",
1188 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1190 GNUNET_break_op (0);
1193 GNUNET_assert (matching_bits >
1194 ntohl (size_estimate_messages[idx].matching_bits));
1195 /* Cancel transmission in the other direction, as this peer clearly has
1196 * up-to-date information already.
1198 peer_entry->previous_round = GNUNET_YES;
1199 if (idx == estimate_index)
1201 /* cancel any activity for current round */
1202 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1204 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1205 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1207 if (peer_entry->th != NULL)
1209 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1210 peer_entry->th = NULL;
1213 size_estimate_messages[idx] = *incoming_flood;
1214 size_estimate_messages[idx].hop_count =
1215 htonl (ntohl (incoming_flood->hop_count) + 1);
1217 GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max);
1218 GNUNET_STATISTICS_set (stats,
1219 "# estimated network diameter",
1220 hop_count_max, GNUNET_NO);
1222 /* have a new, better size estimate, inform clients */
1223 update_network_size_estimate ();
1226 GNUNET_CONTAINER_multipeermap_iterate (peers, &update_flood_times,
1234 * Method called whenever a peer connects. Sets up the PeerEntry and
1235 * schedules the initial size info transmission to this peer.
1237 * @param cls closure
1238 * @param peer peer identity this notification is about
1241 handle_core_connect (void *cls,
1242 const struct GNUNET_PeerIdentity *peer)
1244 struct NSEPeerEntry *peer_entry;
1246 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' connected to us\n",
1248 peer_entry = GNUNET_new (struct NSEPeerEntry);
1249 peer_entry->id = *peer;
1250 GNUNET_assert (GNUNET_OK ==
1251 GNUNET_CONTAINER_multipeermap_put (peers, peer,
1253 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1254 peer_entry->transmit_task =
1255 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1), &transmit_task_cb,
1257 GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO);
1262 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
1263 * any pending transmission requests to that peer.
1265 * @param cls closure
1266 * @param peer peer identity this notification is about
1269 handle_core_disconnect (void *cls,
1270 const struct GNUNET_PeerIdentity *peer)
1272 struct NSEPeerEntry *pos;
1274 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1275 "Peer `%s' disconnected from us\n",
1277 pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
1283 GNUNET_assert (GNUNET_YES ==
1284 GNUNET_CONTAINER_multipeermap_remove (peers, peer,
1286 if (pos->transmit_task != GNUNET_SCHEDULER_NO_TASK) {
1287 GNUNET_SCHEDULER_cancel (pos->transmit_task);
1288 pos->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1290 if (NULL != pos->th)
1292 GNUNET_CORE_notify_transmit_ready_cancel (pos->th);
1296 GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO);
1300 #if ENABLE_NSE_HISTOGRAM
1302 * Functions of this type are called to notify a successful transmission of the
1303 * message to the logger service
1306 * @param size the amount of data sent (ignored)
1309 flush_comp_cb (void *cls,
1312 GNUNET_TESTBED_LOGGER_disconnect (lh);
1319 * Task run during shutdown.
1325 shutdown_task (void *cls,
1326 const struct GNUNET_SCHEDULER_TaskContext *tc)
1328 if (GNUNET_SCHEDULER_NO_TASK != flood_task)
1330 GNUNET_SCHEDULER_cancel (flood_task);
1331 flood_task = GNUNET_SCHEDULER_NO_TASK;
1333 if (GNUNET_SCHEDULER_NO_TASK != proof_task)
1335 GNUNET_SCHEDULER_cancel (proof_task);
1336 proof_task = GNUNET_SCHEDULER_NO_TASK;
1337 write_proof (); /* remember progress */
1341 GNUNET_SERVER_notification_context_destroy (nc);
1344 if (NULL != core_api)
1346 GNUNET_CORE_disconnect (core_api);
1351 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1356 GNUNET_CONTAINER_multipeermap_destroy (peers);
1359 if (NULL != my_private_key)
1361 GNUNET_free (my_private_key);
1362 my_private_key = NULL;
1364 #if ENABLE_NSE_HISTOGRAM
1365 if (NULL != logger_test)
1367 GNUNET_CLIENT_service_test_cancel (logger_test);
1372 struct GNUNET_TIME_Relative timeout;
1373 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30);
1374 GNUNET_TESTBED_LOGGER_flush (lh, timeout, &flush_comp_cb, NULL);
1376 if (NULL != histogram)
1378 GNUNET_BIO_write_close (histogram);
1386 * Called on core init/fail.
1388 * @param cls service closure
1389 * @param identity the public identity of this peer
1392 core_init (void *cls,
1393 const struct GNUNET_PeerIdentity *identity)
1395 struct GNUNET_TIME_Absolute now;
1396 struct GNUNET_TIME_Absolute prev_time;
1398 if (NULL == identity)
1400 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n");
1401 GNUNET_SCHEDULER_shutdown ();
1405 memcmp (&my_identity, identity,
1406 sizeof (struct GNUNET_PeerIdentity)));
1407 now = GNUNET_TIME_absolute_get ();
1408 current_timestamp.abs_value_us =
1409 (now.abs_value_us / gnunet_nse_interval.rel_value_us) *
1410 gnunet_nse_interval.rel_value_us;
1412 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
1413 estimate_index = HISTORY_SIZE - 1;
1415 if (GNUNET_YES == check_proof_of_work (&my_identity.public_key, my_proof))
1417 int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1418 prev_time.abs_value_us =
1419 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us;
1420 setup_flood_message (idx, prev_time);
1421 setup_flood_message (estimate_index, current_timestamp);
1425 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
1426 (next_timestamp), &update_flood_message,
1430 #if ENABLE_NSE_HISTOGRAM
1432 * Function called with the status of the testbed logger service
1435 * @param status GNUNET_YES if the service is running,
1436 * GNUNET_NO if the service is not running
1437 * GNUNET_SYSERR if the configuration is invalid
1440 status_cb (void *cls, int status)
1443 if (GNUNET_YES != status)
1445 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Testbed logger not running\n");
1448 if (NULL == (lh = GNUNET_TESTBED_LOGGER_connect (cfg)))
1450 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1451 "Cannot connect to the testbed logger. Exiting.\n");
1452 GNUNET_SCHEDULER_shutdown ();
1459 * Handle network size estimate clients.
1461 * @param cls closure
1462 * @param server the initialized server
1463 * @param c configuration to use
1467 struct GNUNET_SERVER_Handle *server,
1468 const struct GNUNET_CONFIGURATION_Handle *c)
1470 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1471 {&handle_start_message, NULL, GNUNET_MESSAGE_TYPE_NSE_START,
1472 sizeof (struct GNUNET_MessageHeader)},
1475 static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
1476 {&handle_p2p_size_estimate, GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1477 sizeof (struct GNUNET_NSE_FloodMessage)},
1481 struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
1486 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "INTERVAL",
1487 &gnunet_nse_interval))
1489 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1491 GNUNET_SCHEDULER_shutdown ();
1495 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "WORKDELAY",
1498 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1499 "NSE", "WORKDELAY");
1500 GNUNET_SCHEDULER_shutdown ();
1504 GNUNET_CONFIGURATION_get_value_number (cfg, "NSE", "WORKBITS",
1505 &nse_work_required))
1507 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1509 GNUNET_SCHEDULER_shutdown ();
1512 if (nse_work_required >= sizeof (struct GNUNET_HashCode) * 8)
1514 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1517 _("Value is too large.\n"));
1518 GNUNET_SCHEDULER_shutdown ();
1522 #if ENABLE_NSE_HISTOGRAM
1524 char *histogram_dir;
1528 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "HISTOGRAM_DIR",
1531 GNUNET_assert (0 < GNUNET_asprintf (&histogram_fn, "%s/timestamps",
1533 GNUNET_free (histogram_dir);
1534 histogram = GNUNET_BIO_write_open (histogram_fn);
1535 GNUNET_free (histogram_fn);
1536 if (NULL == histogram)
1537 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to open histogram file\n");
1540 GNUNET_CLIENT_service_test ("testbed-logger", cfg,
1541 GNUNET_TIME_UNIT_SECONDS,
1547 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1549 pk = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
1550 GNUNET_assert (NULL != pk);
1551 my_private_key = pk;
1552 GNUNET_CRYPTO_eddsa_key_get_public (my_private_key,
1553 &my_identity.public_key);
1555 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
1557 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "PROOFFILE");
1558 GNUNET_free (my_private_key);
1559 my_private_key = NULL;
1560 GNUNET_SCHEDULER_shutdown ();
1563 if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) ||
1564 (sizeof (my_proof) !=
1565 GNUNET_DISK_fn_read (proof, &my_proof, sizeof (my_proof))))
1567 GNUNET_free (proof);
1569 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1572 peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO);
1573 GNUNET_SERVER_add_handlers (srv, handlers);
1574 nc = GNUNET_SERVER_notification_context_create (srv, 1);
1575 /* Connect to core service and register core handlers */
1576 core_api = GNUNET_CORE_connect (cfg, /* Main configuration */
1577 NULL, /* Closure passed to functions */
1578 &core_init, /* Call core_init once connected */
1579 &handle_core_connect, /* Handle connects */
1580 &handle_core_disconnect, /* Handle disconnects */
1581 NULL, /* Don't want notified about all incoming messages */
1582 GNUNET_NO, /* For header only inbound notification */
1583 NULL, /* Don't want notified about all outbound messages */
1584 GNUNET_NO, /* For header only outbound notification */
1585 core_handlers); /* Register these handlers */
1586 if (NULL == core_api)
1588 GNUNET_SCHEDULER_shutdown ();
1591 stats = GNUNET_STATISTICS_create ("nse", cfg);
1596 * The main function for the network size estimation service.
1598 * @param argc number of arguments from the command line
1599 * @param argv command line arguments
1600 * @return 0 ok, 1 on error
1606 return (GNUNET_OK ==
1607 GNUNET_SERVICE_run (argc, argv, "nse", GNUNET_SERVICE_OPTION_NONE,
1608 &run, NULL)) ? 0 : 1;
1616 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1618 void __attribute__ ((constructor))
1619 GNUNET_ARM_memory_init ()
1621 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1622 mallopt (M_TOP_PAD, 1 * 1024);
1629 /* end of gnunet-service-nse.c */