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 5
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
96 * Handle for writing when we received messages to disk.
98 static struct GNUNET_TESTBED_LOGGER_Handle *lh;
103 * Per-peer information.
109 * Core handle for sending messages to this peer.
111 struct GNUNET_CORE_TransmitHandle *th;
114 * What is the identity of the peer?
116 struct GNUNET_PeerIdentity id;
119 * Task scheduled to send message to this peer.
121 GNUNET_SCHEDULER_TaskIdentifier transmit_task;
124 * Did we receive or send a message about the previous round
125 * to this peer yet? GNUNET_YES if the previous round has
126 * been taken care of.
130 #if ENABLE_NSE_HISTOGRAM
133 * Amount of messages received from this peer on this round.
135 unsigned int received_messages;
138 * Amount of messages transmitted to this peer on this round.
140 unsigned int transmitted_messages;
143 * Which size did we tell the peer the network is?
145 unsigned int last_transmitted_size;
152 GNUNET_NETWORK_STRUCT_BEGIN
155 * Network size estimate reply; sent when "this"
156 * peer's timer has run out before receiving a
157 * valid reply from another peer.
159 struct GNUNET_NSE_FloodMessage
162 * Type: GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD
164 struct GNUNET_MessageHeader header;
167 * Number of hops this message has taken so far.
169 uint32_t hop_count GNUNET_PACKED;
174 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
177 * The current timestamp value (which all
178 * peers should agree on).
180 struct GNUNET_TIME_AbsoluteNBO timestamp;
183 * Number of matching bits between the hash
184 * of timestamp and the initiator's public
187 uint32_t matching_bits GNUNET_PACKED;
190 * Public key of the originator.
192 struct GNUNET_CRYPTO_EccPublicSignKey pkey;
195 * Proof of work, causing leading zeros when hashed with pkey.
197 uint64_t proof_of_work GNUNET_PACKED;
200 * Signature (over range specified in purpose).
202 struct GNUNET_CRYPTO_EccSignature signature;
204 GNUNET_NETWORK_STRUCT_END
207 * Handle to our current configuration.
209 static const struct GNUNET_CONFIGURATION_Handle *cfg;
212 * Handle to the statistics service.
214 static struct GNUNET_STATISTICS_Handle *stats;
217 * Handle to the core service.
219 static struct GNUNET_CORE_Handle *coreAPI;
222 * Map of all connected peers.
224 static struct GNUNET_CONTAINER_MultiHashMap *peers;
227 * The current network size estimate. Number of bits matching on
230 static double current_size_estimate;
233 * The standard deviation of the last #HISTORY_SIZE network
236 static double current_std_dev = NAN;
239 * Current hop counter estimate (estimate for network diameter).
241 static uint32_t hop_count_max;
244 * Message for the next round, if we got any.
246 static struct GNUNET_NSE_FloodMessage next_message;
249 * Array of recent size estimate messages.
251 static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE];
254 * Index of most recent estimate.
256 static unsigned int estimate_index;
259 * Number of valid entries in the history.
261 static unsigned int estimate_count;
264 * Task scheduled to update our flood message for the next round.
266 static GNUNET_SCHEDULER_TaskIdentifier flood_task;
269 * Task scheduled to compute our proof.
271 static GNUNET_SCHEDULER_TaskIdentifier proof_task;
274 * Notification context, simplifies client broadcasts.
276 static struct GNUNET_SERVER_NotificationContext *nc;
279 * The next major time.
281 static struct GNUNET_TIME_Absolute next_timestamp;
284 * The current major time.
286 static struct GNUNET_TIME_Absolute current_timestamp;
289 * The public key of this peer.
291 static struct GNUNET_CRYPTO_EccPublicSignKey my_public_key;
294 * The private key of this peer.
296 static struct GNUNET_CRYPTO_EccPrivateKey *my_private_key;
299 * The peer identity of this peer.
301 static struct GNUNET_PeerIdentity my_identity;
304 * Proof of work for this peer.
306 static uint64_t my_proof;
309 * Handle to this serivce's server.
311 static struct GNUNET_SERVER_Handle *srv;
315 * Initialize a message to clients with the current network
318 * @param em message to fill in
321 setup_estimate_message (struct GNUNET_NSE_ClientMessage *em)
333 /* Weighted incremental algorithm for stddev according to West (1979) */
345 for (i = 0; i < estimate_count; i++)
347 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
348 val = htonl (size_estimate_messages[j].matching_bits);
349 weight = estimate_count + 1 - i;
351 temp = weight + sumweight;
353 r = q * weight / temp;
355 sum += sumweight * q * r;
358 if (estimate_count > 0)
359 variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0);
361 /* trivial version for debugging */
364 /* non-weighted trivial version */
370 for (i = 0; i < estimate_count; i++)
372 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
373 val = htonl (size_estimate_messages[j].matching_bits);
377 if (0 != estimate_count)
379 mean = sum / estimate_count;
380 variance = (vsq - mean * sum) / (estimate_count - 1.0); // terrible for numerical stability...
384 std_dev = sqrt (variance);
386 std_dev = variance; /* must be infinity due to estimate_count == 0 */
387 current_std_dev = std_dev;
388 current_size_estimate = mean;
390 em->header.size = htons (sizeof (struct GNUNET_NSE_ClientMessage));
391 em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE);
392 em->reserved = htonl (0);
393 em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
394 double se = mean - 0.332747;
395 nsize = log2 (GNUNET_CONTAINER_multihashmap_size (peers) + 1);
396 em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se, nsize));
397 em->std_deviation = GNUNET_hton_double (std_dev);
398 GNUNET_STATISTICS_set (stats, "# nodes in the network (estimate)",
399 (uint64_t) pow (2, mean - 1.0 / 3.0), GNUNET_NO);
404 * Handler for START message from client, triggers an
405 * immediate current network estimate notification.
406 * Also, we remember the client for updates upon future
407 * estimate measurements.
410 * @param client who sent the message
411 * @param message the message received
414 handle_start_message (void *cls,
415 struct GNUNET_SERVER_Client *client,
416 const struct GNUNET_MessageHeader *message)
418 struct GNUNET_NSE_ClientMessage em;
420 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
421 "Received START message from client\n");
422 GNUNET_SERVER_notification_context_add (nc, client);
423 setup_estimate_message (&em);
424 GNUNET_SERVER_notification_context_unicast (nc, client, &em.header,
426 GNUNET_SERVER_receive_done (client, GNUNET_OK);
431 * How long should we delay a message to go the given number of
434 * @param matching_bits number of matching bits to consider
437 get_matching_bits_delay (uint32_t matching_bits)
439 /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */
440 // S is next_timestamp (ignored in return value)
441 // f is frequency (gnunet_nse_interval)
442 // x is matching_bits
443 // p' is current_size_estimate
444 return ((double) gnunet_nse_interval.rel_value_us / (double) 2.0) -
445 ((gnunet_nse_interval.rel_value_us / M_PI) *
446 atan (matching_bits - current_size_estimate));
451 * What delay randomization should we apply for a given number of matching bits?
453 * @param matching_bits number of matching bits
454 * @return random delay to apply
456 static struct GNUNET_TIME_Relative
457 get_delay_randomization (uint32_t matching_bits)
459 #if USE_RANDOM_DELAYS
460 struct GNUNET_TIME_Relative ret;
464 d = get_matching_bits_delay (matching_bits);
465 i = (uint32_t) (d / (double) (hop_count_max + 1));
466 ret.rel_value_us = i;
467 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
468 "Randomizing flood using latencies up to %s\n",
469 GNUNET_STRINGS_relative_time_to_string (ret,
471 ret.rel_value_us = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1);
474 return GNUNET_TIME_UNIT_ZERO;
480 * Calculate the 'proof-of-work' hash (an expensive hash).
482 * @param buf data to hash
483 * @param buf_len number of bytes in @a buf
484 * @param result where to write the resulting hash
487 pow_hash (const void *buf,
489 struct GNUNET_HashCode *result)
492 gcry_kdf_derive (buf, buf_len,
495 "gnunet-proof-of-work", strlen ("gnunet-proof-of-work"),
496 2 /* iterations; keep cost of individual op small */,
497 sizeof (struct GNUNET_HashCode), result));
502 * Get the number of matching bits that the given timestamp has to the given peer ID.
504 * @param timestamp time to generate key
505 * @param id peer identity to compare with
506 * @return number of matching bits
509 get_matching_bits (struct GNUNET_TIME_Absolute timestamp,
510 const struct GNUNET_PeerIdentity *id)
512 struct GNUNET_HashCode timestamp_hash;
514 GNUNET_CRYPTO_hash (×tamp.abs_value_us, sizeof (timestamp.abs_value_us),
516 return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash, &id->hashPubKey);
521 * Get the transmission delay that should be applied for a
524 * @param round_offset -1 for the previous round (random delay between 0 and 50ms)
525 * 0 for the current round (based on our proximity to time key)
526 * @return delay that should be applied
528 static struct GNUNET_TIME_Relative
529 get_transmit_delay (int round_offset)
531 struct GNUNET_TIME_Relative ret;
532 struct GNUNET_TIME_Absolute tgt;
534 uint32_t matching_bits;
536 switch (round_offset)
539 /* previous round is randomized between 0 and 50 ms */
540 #if USE_RANDOM_DELAYS
541 ret.rel_value_us = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50);
543 ret = GNUNET_TIME_UNIT_ZERO;
545 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
546 "Transmitting previous round behind schedule in %s\n",
547 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
550 /* current round is based on best-known matching_bits */
552 ntohl (size_estimate_messages[estimate_index].matching_bits);
553 dist_delay = get_matching_bits_delay (matching_bits);
554 dist_delay += get_delay_randomization (matching_bits).rel_value_us;
555 ret.rel_value_us = (uint64_t) dist_delay;
556 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
557 "For round %s, delay for %u matching bits is %s\n",
558 GNUNET_STRINGS_absolute_time_to_string (current_timestamp),
559 (unsigned int) matching_bits,
560 GNUNET_STRINGS_relative_time_to_string (ret,
562 /* now consider round start time and add delay to it */
563 tgt = GNUNET_TIME_absolute_add (current_timestamp, ret);
564 return GNUNET_TIME_absolute_get_remaining (tgt);
567 return GNUNET_TIME_UNIT_FOREVER_REL;
572 * Task that triggers a NSE P2P transmission.
574 * @param cls the `struct NSEPeerEntry *`
575 * @param tc scheduler context
578 transmit_task_cb (void *cls,
579 const struct GNUNET_SCHEDULER_TaskContext *tc);
583 * Called when core is ready to send a message we asked for
584 * out to the destination.
586 * @param cls closure with the `struct NSEPeerEntry *`
587 * @param size number of bytes available in @a buf
588 * @param buf where the callee should write the message
589 * @return number of bytes written to @a buf
592 transmit_ready (void *cls,
596 struct NSEPeerEntry *peer_entry = cls;
599 peer_entry->th = NULL;
602 /* client disconnected */
605 GNUNET_assert (size >= sizeof (struct GNUNET_NSE_FloodMessage));
606 idx = estimate_index;
607 if (GNUNET_NO == peer_entry->previous_round)
609 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
610 peer_entry->previous_round = GNUNET_YES;
611 peer_entry->transmit_task =
612 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0), &transmit_task_cb,
615 if ((0 == ntohl (size_estimate_messages[idx].hop_count)) &&
616 (GNUNET_SCHEDULER_NO_TASK != proof_task))
618 GNUNET_STATISTICS_update (stats,
619 "# flood messages not generated (no proof yet)",
623 if (0 == ntohs (size_estimate_messages[idx].header.size))
625 GNUNET_STATISTICS_update (stats,
626 "# flood messages not generated (lack of history)",
630 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
631 "In round s, sending to `%s' estimate with %u bits\n",
632 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (size_estimate_messages[idx].timestamp)),
633 GNUNET_i2s (&peer_entry->id),
634 (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
635 if (ntohl (size_estimate_messages[idx].hop_count) == 0)
636 GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO);
637 GNUNET_STATISTICS_update (stats, "# flood messages transmitted", 1,
639 #if ENABLE_NSE_HISTOGRAM
640 peer_entry->transmitted_messages++;
641 peer_entry->last_transmitted_size =
642 ntohl(size_estimate_messages[idx].matching_bits);
644 memcpy (buf, &size_estimate_messages[idx],
645 sizeof (struct GNUNET_NSE_FloodMessage));
646 return sizeof (struct GNUNET_NSE_FloodMessage);
651 * Task that triggers a NSE P2P transmission.
653 * @param cls the `struct NSEPeerEntry *`
654 * @param tc scheduler context
657 transmit_task_cb (void *cls,
658 const struct GNUNET_SCHEDULER_TaskContext *tc)
660 struct NSEPeerEntry *peer_entry = cls;
662 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
664 GNUNET_assert (NULL == peer_entry->th);
666 GNUNET_CORE_notify_transmit_ready (coreAPI, GNUNET_NO, NSE_PRIORITY,
667 GNUNET_TIME_UNIT_FOREVER_REL,
670 GNUNET_NSE_FloodMessage),
671 &transmit_ready, peer_entry);
676 * We've sent on our flood message or one that we received which was
677 * validated and closer than ours. Update the global list of recent
678 * messages and the average. Also re-broadcast the message to any
682 update_network_size_estimate ()
684 struct GNUNET_NSE_ClientMessage em;
686 setup_estimate_message (&em);
687 GNUNET_SERVER_notification_context_broadcast (nc,
694 * Setup a flood message in our history array at the given
695 * slot offset for the given timestamp.
697 * @param slot index to use
698 * @param ts timestamp to use
701 setup_flood_message (unsigned int slot,
702 struct GNUNET_TIME_Absolute ts)
704 struct GNUNET_NSE_FloodMessage *fm;
705 uint32_t matching_bits;
707 matching_bits = get_matching_bits (ts, &my_identity);
708 fm = &size_estimate_messages[slot];
709 fm->header.size = htons (sizeof (struct GNUNET_NSE_FloodMessage));
710 fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
711 fm->hop_count = htonl (0);
712 fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
714 htonl (sizeof (struct GNUNET_NSE_FloodMessage) -
715 sizeof (struct GNUNET_MessageHeader) - sizeof (uint32_t) -
716 sizeof (struct GNUNET_CRYPTO_EccSignature));
717 fm->matching_bits = htonl (matching_bits);
718 fm->timestamp = GNUNET_TIME_absolute_hton (ts);
719 fm->pkey = my_public_key;
720 fm->proof_of_work = my_proof;
721 if (nse_work_required > 0)
722 GNUNET_assert (GNUNET_OK ==
723 GNUNET_CRYPTO_ecc_sign (my_private_key, &fm->purpose,
726 memset (&fm->signature, 0, sizeof (fm->signature));
731 * Schedule transmission for the given peer for the current round based
732 * on what we know about the desired delay.
735 * @param key hash of peer identity
736 * @param value the `struct NSEPeerEntry`
737 * @return #GNUNET_OK (continue to iterate)
740 schedule_current_round (void *cls,
741 const struct GNUNET_HashCode * key,
744 struct NSEPeerEntry *peer_entry = value;
745 struct GNUNET_TIME_Relative delay;
747 if (NULL != peer_entry->th)
749 peer_entry->previous_round = GNUNET_NO;
752 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
754 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
755 peer_entry->previous_round = GNUNET_NO;
757 #if ENABLE_NSE_HISTOGRAM
758 if (peer_entry->received_messages > 1)
759 GNUNET_STATISTICS_update(stats, "# extra messages",
760 peer_entry->received_messages - 1, GNUNET_NO);
761 peer_entry->transmitted_messages = 0;
762 peer_entry->last_transmitted_size = 0;
763 peer_entry->received_messages = 0;
766 get_transmit_delay ((peer_entry->previous_round == GNUNET_NO) ? -1 : 0);
767 peer_entry->transmit_task =
768 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
774 * Update our flood message to be sent (and our timestamps).
777 * @param tc context for this message
780 update_flood_message (void *cls,
781 const struct GNUNET_SCHEDULER_TaskContext *tc)
783 struct GNUNET_TIME_Relative offset;
786 flood_task = GNUNET_SCHEDULER_NO_TASK;
787 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
789 offset = GNUNET_TIME_absolute_get_remaining (next_timestamp);
790 if (0 != offset.rel_value_us)
792 /* somehow run early, delay more */
794 GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL);
797 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
798 if (estimate_count < HISTORY_SIZE)
800 current_timestamp = next_timestamp;
802 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
803 if ((current_timestamp.abs_value_us ==
804 GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value_us) &&
805 (get_matching_bits (current_timestamp, &my_identity) <
806 ntohl(next_message.matching_bits)))
808 /* we received a message for this round way early, use it! */
809 size_estimate_messages[estimate_index] = next_message;
810 size_estimate_messages[estimate_index].hop_count =
811 htonl (1 + ntohl (next_message.hop_count));
814 setup_flood_message (estimate_index, current_timestamp);
815 next_message.matching_bits = htonl (0); /* reset for 'next' round */
817 for (i = 0; i < HISTORY_SIZE; i++)
819 GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max);
820 GNUNET_CONTAINER_multihashmap_iterate (peers, &schedule_current_round, NULL);
822 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
823 (next_timestamp), &update_flood_message,
829 * Count the leading zeroes in hash.
831 * @param hash to count leading zeros in
832 * @return the number of leading zero bits.
835 count_leading_zeroes (const struct GNUNET_HashCode *hash)
837 unsigned int hash_count;
840 while ((0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count)))
847 * Check whether the given public key and integer are a valid proof of
850 * @param pkey the public key
851 * @param val the integer
852 * @return #GNUNET_YES if valid, #GNUNET_NO if not
855 check_proof_of_work (const struct GNUNET_CRYPTO_EccPublicSignKey *pkey,
858 char buf[sizeof (struct GNUNET_CRYPTO_EccPublicSignKey) +
859 sizeof (val)] GNUNET_ALIGN;
860 struct GNUNET_HashCode result;
862 memcpy (buf, &val, sizeof (val));
863 memcpy (&buf[sizeof (val)], pkey,
864 sizeof (struct GNUNET_CRYPTO_EccPublicSignKey));
865 pow_hash (buf, sizeof (buf), &result);
866 return (count_leading_zeroes (&result) >=
867 nse_work_required) ? GNUNET_YES : GNUNET_NO;
872 * Write our current proof to disk.
880 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
882 if (sizeof (my_proof) !=
883 GNUNET_DISK_fn_write (proof, &my_proof, sizeof (my_proof),
884 GNUNET_DISK_PERM_USER_READ |
885 GNUNET_DISK_PERM_USER_WRITE))
886 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof);
893 * Find our proof of work.
895 * @param cls closure (unused)
896 * @param tc task context
899 find_proof (void *cls,
900 const struct GNUNET_SCHEDULER_TaskContext *tc)
902 #define ROUND_SIZE 10
904 char buf[sizeof (struct GNUNET_CRYPTO_EccPublicSignKey) +
905 sizeof (uint64_t)] GNUNET_ALIGN;
906 struct GNUNET_HashCode result;
909 proof_task = GNUNET_SCHEDULER_NO_TASK;
910 memcpy (&buf[sizeof (uint64_t)], &my_public_key,
911 sizeof (struct GNUNET_CRYPTO_EccPublicSignKey));
914 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
916 memcpy (buf, &counter, sizeof (uint64_t));
917 pow_hash (buf, sizeof (buf), &result);
918 if (nse_work_required <= count_leading_zeroes (&result))
921 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work found: %llu!\n",
922 (unsigned long long) GNUNET_ntohll (counter));
924 setup_flood_message (estimate_index, current_timestamp);
930 if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
932 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing proofs currently at %llu\n",
933 (unsigned long long) counter);
934 /* remember progress every 100 rounds */
943 GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
944 GNUNET_SCHEDULER_PRIORITY_IDLE,
950 * An incoming flood message has been received which claims
951 * to have more bits matching than any we know in this time
952 * period. Verify the signature and/or proof of work.
954 * @param incoming_flood the message to verify
955 * @return #GNUNET_YES if the message is verified
956 * #GNUNET_NO if the key/signature don't verify
959 verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood)
962 check_proof_of_work (&incoming_flood->pkey,
963 incoming_flood->proof_of_work))
965 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work invalid: %llu!\n",
967 GNUNET_ntohll (incoming_flood->proof_of_work));
971 if ((nse_work_required > 0) &&
973 GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
974 &incoming_flood->purpose,
975 &incoming_flood->signature,
976 &incoming_flood->pkey)))
986 * Update transmissions for the given peer for the current round based
987 * on updated proximity information.
989 * @param cls peer entry to exclude from updates
990 * @param key hash of peer identity
991 * @param value the `struct NSEPeerEntry *` of a peer to transmit to
992 * @return #GNUNET_OK (continue to iterate)
995 update_flood_times (void *cls,
996 const struct GNUNET_HashCode *key,
999 struct NSEPeerEntry *exclude = cls;
1000 struct NSEPeerEntry *peer_entry = value;
1001 struct GNUNET_TIME_Relative delay;
1003 if (NULL != peer_entry->th)
1004 return GNUNET_OK; /* already active */
1005 if (peer_entry == exclude)
1006 return GNUNET_OK; /* trigger of the update */
1007 if (peer_entry->previous_round == GNUNET_NO)
1009 /* still stuck in previous round, no point to update, check that
1010 * we are active here though... */
1011 if ( (GNUNET_SCHEDULER_NO_TASK == peer_entry->transmit_task) &&
1012 (NULL == peer_entry->th) )
1018 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1020 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1021 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1023 delay = get_transmit_delay (0);
1024 peer_entry->transmit_task =
1025 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
1031 * Core handler for size estimate flooding messages.
1033 * @param cls closure unused
1034 * @param message message
1035 * @param peer peer identity this message is from (ignored)
1038 handle_p2p_size_estimate (void *cls,
1039 const struct GNUNET_PeerIdentity *peer,
1040 const struct GNUNET_MessageHeader *message)
1042 const struct GNUNET_NSE_FloodMessage *incoming_flood;
1043 struct GNUNET_TIME_Absolute ts;
1044 struct NSEPeerEntry *peer_entry;
1045 uint32_t matching_bits;
1048 #if ENABLE_NSE_HISTOGRAM
1052 t = GNUNET_TIME_absolute_get().abs_value_us;
1053 GNUNET_TESTBED_LOGGER_write (lh, &t, sizeof (uint64_t));
1056 incoming_flood = (const struct GNUNET_NSE_FloodMessage *) message;
1057 GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO);
1058 matching_bits = ntohl (incoming_flood->matching_bits);
1063 struct GNUNET_PeerIdentity os;
1065 GNUNET_CRYPTO_hash (&incoming_flood->pkey,
1066 sizeof (struct GNUNET_CRYPTO_EccPublicSignKey),
1068 GNUNET_snprintf (origin, sizeof (origin), "%s", GNUNET_i2s (&os));
1069 GNUNET_snprintf (pred, sizeof (pred), "%s", GNUNET_i2s (peer));
1070 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1071 "Flood at %s from `%s' via `%s' at `%s' with bits %u\n",
1072 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp)),
1073 origin, pred, GNUNET_i2s (&my_identity),
1074 (unsigned int) matching_bits);
1078 peer_entry = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
1079 if (NULL == peer_entry)
1084 #if ENABLE_NSE_HISTOGRAM
1085 peer_entry->received_messages++;
1086 if (peer_entry->transmitted_messages > 0 &&
1087 peer_entry->last_transmitted_size >= matching_bits)
1088 GNUNET_STATISTICS_update(stats, "# cross messages", 1, GNUNET_NO);
1091 ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp);
1092 if (ts.abs_value_us == current_timestamp.abs_value_us)
1093 idx = estimate_index;
1094 else if (ts.abs_value_us ==
1095 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us)
1096 idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1097 else if (ts.abs_value_us == next_timestamp.abs_value_us)
1099 if (matching_bits <= ntohl (next_message.matching_bits))
1100 return GNUNET_OK; /* ignore, simply too early/late */
1101 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1103 GNUNET_break_op (0);
1106 next_message = *incoming_flood;
1111 GNUNET_STATISTICS_update (stats,
1112 "# flood messages discarded (clock skew too large)",
1116 if (0 == (memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))))
1118 /* send to self, update our own estimate IF this also comes from us! */
1120 memcmp (&incoming_flood->pkey, &my_public_key, sizeof (my_public_key)))
1121 update_network_size_estimate ();
1124 if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits))
1126 /* Cancel transmission in the other direction, as this peer clearly has
1127 up-to-date information already. Even if we didn't talk to this peer in
1128 the previous round, we should no longer send it stale information as it
1129 told us about the current round! */
1130 peer_entry->previous_round = GNUNET_YES;
1131 if (idx != estimate_index)
1133 /* do not transmit information for the previous round to this peer
1134 anymore (but allow current round) */
1137 /* got up-to-date information for current round, cancel transmission to
1138 * this peer altogether */
1139 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1141 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1142 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1144 if (NULL != peer_entry->th)
1146 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1147 peer_entry->th = NULL;
1151 if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits))
1153 if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES)) {
1154 peer_entry->previous_round = GNUNET_NO;
1156 /* push back our result now, that peer is spreading bad information... */
1157 if (NULL == peer_entry->th)
1159 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1160 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1161 peer_entry->transmit_task =
1162 GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry);
1164 /* Not closer than our most recent message, no need to do work here */
1165 GNUNET_STATISTICS_update (stats,
1166 "# flood messages ignored (had closer already)",
1170 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1172 GNUNET_break_op (0);
1175 GNUNET_assert (matching_bits >
1176 ntohl (size_estimate_messages[idx].matching_bits));
1177 /* Cancel transmission in the other direction, as this peer clearly has
1178 * up-to-date information already.
1180 peer_entry->previous_round = GNUNET_YES;
1181 if (idx == estimate_index)
1183 /* cancel any activity for current round */
1184 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1186 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1187 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1189 if (peer_entry->th != NULL)
1191 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1192 peer_entry->th = NULL;
1195 size_estimate_messages[idx] = *incoming_flood;
1196 size_estimate_messages[idx].hop_count =
1197 htonl (ntohl (incoming_flood->hop_count) + 1);
1199 GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max);
1200 GNUNET_STATISTICS_set (stats,
1201 "# estimated network diameter",
1202 hop_count_max, GNUNET_NO);
1204 /* have a new, better size estimate, inform clients */
1205 update_network_size_estimate ();
1208 GNUNET_CONTAINER_multihashmap_iterate (peers, &update_flood_times,
1216 * Method called whenever a peer connects. Sets up the PeerEntry and
1217 * schedules the initial size info transmission to this peer.
1219 * @param cls closure
1220 * @param peer peer identity this notification is about
1223 handle_core_connect (void *cls,
1224 const struct GNUNET_PeerIdentity *peer)
1226 struct NSEPeerEntry *peer_entry;
1228 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' connected to us\n",
1230 peer_entry = GNUNET_new (struct NSEPeerEntry);
1231 peer_entry->id = *peer;
1232 GNUNET_assert (GNUNET_OK ==
1233 GNUNET_CONTAINER_multihashmap_put (peers, &peer->hashPubKey,
1235 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1236 peer_entry->transmit_task =
1237 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1), &transmit_task_cb,
1239 GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO);
1244 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
1245 * any pending transmission requests to that peer.
1247 * @param cls closure
1248 * @param peer peer identity this notification is about
1251 handle_core_disconnect (void *cls,
1252 const struct GNUNET_PeerIdentity *peer)
1254 struct NSEPeerEntry *pos;
1256 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1257 "Peer `%s' disconnected from us\n",
1259 pos = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
1265 GNUNET_assert (GNUNET_YES ==
1266 GNUNET_CONTAINER_multihashmap_remove (peers, &peer->hashPubKey,
1268 if (pos->transmit_task != GNUNET_SCHEDULER_NO_TASK) {
1269 GNUNET_SCHEDULER_cancel (pos->transmit_task);
1270 pos->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1272 if (NULL != pos->th)
1274 GNUNET_CORE_notify_transmit_ready_cancel (pos->th);
1278 GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO);
1282 #if ENABLE_NSE_HISTOGRAM
1284 * Functions of this type are called to notify a successful transmission of the
1285 * message to the logger service
1288 * @param size the amount of data sent (ignored)
1291 flush_comp_cb (void *cls,
1294 GNUNET_TESTBED_LOGGER_disconnect (lh);
1301 * Task run during shutdown.
1307 shutdown_task (void *cls,
1308 const struct GNUNET_SCHEDULER_TaskContext *tc)
1310 if (GNUNET_SCHEDULER_NO_TASK != flood_task)
1312 GNUNET_SCHEDULER_cancel (flood_task);
1313 flood_task = GNUNET_SCHEDULER_NO_TASK;
1315 if (GNUNET_SCHEDULER_NO_TASK != proof_task)
1317 GNUNET_SCHEDULER_cancel (proof_task);
1318 proof_task = GNUNET_SCHEDULER_NO_TASK;
1319 write_proof (); /* remember progress */
1323 GNUNET_SERVER_notification_context_destroy (nc);
1326 if (NULL != coreAPI)
1328 GNUNET_CORE_disconnect (coreAPI);
1333 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1338 GNUNET_CONTAINER_multihashmap_destroy (peers);
1341 if (NULL != my_private_key)
1343 GNUNET_free (my_private_key);
1344 my_private_key = NULL;
1346 #if ENABLE_NSE_HISTOGRAM
1347 struct GNUNET_TIME_Relative timeout;
1348 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30);
1349 GNUNET_TESTBED_LOGGER_flush (lh, timeout, &flush_comp_cb, NULL);
1355 * Called on core init/fail.
1357 * @param cls service closure
1358 * @param identity the public identity of this peer
1361 core_init (void *cls,
1362 const struct GNUNET_PeerIdentity *identity)
1364 struct GNUNET_TIME_Absolute now;
1365 struct GNUNET_TIME_Absolute prev_time;
1367 if (NULL == identity)
1369 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n");
1370 GNUNET_SCHEDULER_shutdown ();
1374 memcmp (&my_identity, identity,
1375 sizeof (struct GNUNET_PeerIdentity)));
1376 now = GNUNET_TIME_absolute_get ();
1377 current_timestamp.abs_value_us =
1378 (now.abs_value_us / gnunet_nse_interval.rel_value_us) *
1379 gnunet_nse_interval.rel_value_us;
1381 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
1382 estimate_index = HISTORY_SIZE - 1;
1384 if (GNUNET_YES == check_proof_of_work (&my_public_key, my_proof))
1386 int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1387 prev_time.abs_value_us =
1388 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us;
1389 setup_flood_message (idx, prev_time);
1390 setup_flood_message (estimate_index, current_timestamp);
1394 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
1395 (next_timestamp), &update_flood_message,
1401 * Handle network size estimate clients.
1403 * @param cls closure
1404 * @param server the initialized server
1405 * @param c configuration to use
1409 struct GNUNET_SERVER_Handle *server,
1410 const struct GNUNET_CONFIGURATION_Handle *c)
1412 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1413 {&handle_start_message, NULL, GNUNET_MESSAGE_TYPE_NSE_START,
1414 sizeof (struct GNUNET_MessageHeader)},
1417 static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
1418 {&handle_p2p_size_estimate, GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1419 sizeof (struct GNUNET_NSE_FloodMessage)},
1424 struct GNUNET_CRYPTO_EccPrivateKey *pk;
1429 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "INTERVAL",
1430 &gnunet_nse_interval)) ||
1432 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "WORKDELAY",
1433 &proof_find_delay)) ||
1435 GNUNET_CONFIGURATION_get_value_number (cfg, "NSE", "WORKBITS",
1436 &nse_work_required)))
1438 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1440 ("%s service is lacking key configuration settings (%s). Exiting.\n"),
1441 "NSE", "interval/workdelay/workbits");
1442 GNUNET_SCHEDULER_shutdown ();
1445 if (nse_work_required >= sizeof (struct GNUNET_HashCode) * 8)
1447 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1448 _("Invalid work requirement for NSE service. Exiting.\n"));
1449 GNUNET_SCHEDULER_shutdown ();
1453 GNUNET_CONFIGURATION_get_value_filename (c, "PEER", "PRIVATE_KEY",
1456 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1458 ("%s service is lacking key configuration settings (%s). Exiting.\n"),
1459 "NSE", "peer/privatekey");
1460 GNUNET_SCHEDULER_shutdown ();
1463 #if ENABLE_NSE_HISTOGRAM
1464 if (NULL == (lh = GNUNET_TESTBED_LOGGER_connect (cfg)))
1466 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1467 "Cannot connect to the testbed logger. Exiting.\n");
1468 GNUNET_SCHEDULER_shutdown ();
1473 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1475 pk = GNUNET_CRYPTO_ecc_key_create_from_file (keyfile);
1476 GNUNET_free (keyfile);
1477 GNUNET_assert (NULL != pk);
1478 my_private_key = pk;
1479 GNUNET_CRYPTO_ecc_key_get_public_for_signature (my_private_key, &my_public_key);
1480 GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key),
1481 &my_identity.hashPubKey);
1483 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
1485 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1487 ("NSE service is lacking key configuration settings. Exiting.\n"));
1488 GNUNET_free (my_private_key);
1489 my_private_key = NULL;
1490 GNUNET_SCHEDULER_shutdown ();
1493 if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) ||
1494 (sizeof (my_proof) !=
1495 GNUNET_DISK_fn_read (proof, &my_proof, sizeof (my_proof))))
1497 GNUNET_free (proof);
1499 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1502 peers = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_NO);
1503 GNUNET_SERVER_add_handlers (srv, handlers);
1504 nc = GNUNET_SERVER_notification_context_create (srv, 1);
1505 /* Connect to core service and register core handlers */
1506 coreAPI = GNUNET_CORE_connect (cfg, /* Main configuration */
1507 NULL, /* Closure passed to functions */
1508 &core_init, /* Call core_init once connected */
1509 &handle_core_connect, /* Handle connects */
1510 &handle_core_disconnect, /* Handle disconnects */
1511 NULL, /* Don't want notified about all incoming messages */
1512 GNUNET_NO, /* For header only inbound notification */
1513 NULL, /* Don't want notified about all outbound messages */
1514 GNUNET_NO, /* For header only outbound notification */
1515 core_handlers); /* Register these handlers */
1516 if (NULL == coreAPI)
1518 GNUNET_SCHEDULER_shutdown ();
1521 stats = GNUNET_STATISTICS_create ("nse", cfg);
1526 * The main function for the network size estimation service.
1528 * @param argc number of arguments from the command line
1529 * @param argv command line arguments
1530 * @return 0 ok, 1 on error
1536 return (GNUNET_OK ==
1537 GNUNET_SERVICE_run (argc, argv, "nse", GNUNET_SERVICE_OPTION_NONE,
1538 &run, NULL)) ? 0 : 1;
1546 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1548 void __attribute__ ((constructor))
1549 GNUNET_ARM_memory_init ()
1551 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1552 mallopt (M_TOP_PAD, 1 * 1024);
1559 /* end of gnunet-service-nse.c */