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 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, em->size_estimate),
418 * Handler for START message from client, triggers an
419 * immediate current network estimate notification.
420 * Also, we remember the client for updates upon future
421 * estimate measurements.
424 * @param client who sent the message
425 * @param message the message received
428 handle_start_message (void *cls,
429 struct GNUNET_SERVER_Client *client,
430 const struct GNUNET_MessageHeader *message)
432 struct GNUNET_NSE_ClientMessage em;
434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
435 "Received START message from client\n");
436 GNUNET_SERVER_notification_context_add (nc, client);
437 setup_estimate_message (&em);
438 GNUNET_SERVER_notification_context_unicast (nc, client, &em.header,
440 GNUNET_SERVER_receive_done (client, GNUNET_OK);
445 * How long should we delay a message to go the given number of
448 * @param matching_bits number of matching bits to consider
451 get_matching_bits_delay (uint32_t matching_bits)
453 /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */
454 // S is next_timestamp (ignored in return value)
455 // f is frequency (gnunet_nse_interval)
456 // x is matching_bits
457 // p' is current_size_estimate
458 return ((double) gnunet_nse_interval.rel_value_us / (double) 2.0) -
459 ((gnunet_nse_interval.rel_value_us / M_PI) *
460 atan (matching_bits - current_size_estimate));
465 * What delay randomization should we apply for a given number of matching bits?
467 * @param matching_bits number of matching bits
468 * @return random delay to apply
470 static struct GNUNET_TIME_Relative
471 get_delay_randomization (uint32_t matching_bits)
473 #if USE_RANDOM_DELAYS
474 struct GNUNET_TIME_Relative ret;
478 d = get_matching_bits_delay (matching_bits);
479 i = (uint32_t) (d / (double) (hop_count_max + 1));
480 ret.rel_value_us = i;
481 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482 "Randomizing flood using latencies up to %s\n",
483 GNUNET_STRINGS_relative_time_to_string (ret,
485 ret.rel_value_us = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1);
488 return GNUNET_TIME_UNIT_ZERO;
494 * Calculate the 'proof-of-work' hash (an expensive hash).
496 * @param buf data to hash
497 * @param buf_len number of bytes in @a buf
498 * @param result where to write the resulting hash
501 pow_hash (const void *buf,
503 struct GNUNET_HashCode *result)
506 gcry_kdf_derive (buf, buf_len,
509 "gnunet-proof-of-work", strlen ("gnunet-proof-of-work"),
510 2 /* iterations; keep cost of individual op small */,
511 sizeof (struct GNUNET_HashCode), result));
516 * Get the number of matching bits that the given timestamp has to the given peer ID.
518 * @param timestamp time to generate key
519 * @param id peer identity to compare with
520 * @return number of matching bits
523 get_matching_bits (struct GNUNET_TIME_Absolute timestamp,
524 const struct GNUNET_PeerIdentity *id)
526 struct GNUNET_HashCode timestamp_hash;
527 struct GNUNET_HashCode pid_hash;
529 GNUNET_CRYPTO_hash (×tamp.abs_value_us,
530 sizeof (timestamp.abs_value_us),
532 GNUNET_CRYPTO_hash (id,
533 sizeof (struct GNUNET_PeerIdentity),
535 return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash,
541 * Get the transmission delay that should be applied for a
544 * @param round_offset -1 for the previous round (random delay between 0 and 50ms)
545 * 0 for the current round (based on our proximity to time key)
546 * @return delay that should be applied
548 static struct GNUNET_TIME_Relative
549 get_transmit_delay (int round_offset)
551 struct GNUNET_TIME_Relative ret;
552 struct GNUNET_TIME_Absolute tgt;
554 uint32_t matching_bits;
556 switch (round_offset)
559 /* previous round is randomized between 0 and 50 ms */
560 #if USE_RANDOM_DELAYS
561 ret.rel_value_us = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50);
563 ret = GNUNET_TIME_UNIT_ZERO;
565 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
566 "Transmitting previous round behind schedule in %s\n",
567 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
570 /* current round is based on best-known matching_bits */
572 ntohl (size_estimate_messages[estimate_index].matching_bits);
573 dist_delay = get_matching_bits_delay (matching_bits);
574 dist_delay += get_delay_randomization (matching_bits).rel_value_us;
575 ret.rel_value_us = (uint64_t) dist_delay;
576 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
577 "For round %s, delay for %u matching bits is %s\n",
578 GNUNET_STRINGS_absolute_time_to_string (current_timestamp),
579 (unsigned int) matching_bits,
580 GNUNET_STRINGS_relative_time_to_string (ret,
582 /* now consider round start time and add delay to it */
583 tgt = GNUNET_TIME_absolute_add (current_timestamp, ret);
584 return GNUNET_TIME_absolute_get_remaining (tgt);
587 return GNUNET_TIME_UNIT_FOREVER_REL;
592 * Task that triggers a NSE P2P transmission.
594 * @param cls the `struct NSEPeerEntry *`
595 * @param tc scheduler context
598 transmit_task_cb (void *cls,
599 const struct GNUNET_SCHEDULER_TaskContext *tc);
603 * Called when core is ready to send a message we asked for
604 * out to the destination.
606 * @param cls closure with the `struct NSEPeerEntry *`
607 * @param size number of bytes available in @a buf
608 * @param buf where the callee should write the message
609 * @return number of bytes written to @a buf
612 transmit_ready (void *cls,
616 struct NSEPeerEntry *peer_entry = cls;
619 peer_entry->th = NULL;
622 /* client disconnected */
625 GNUNET_assert (size >= sizeof (struct GNUNET_NSE_FloodMessage));
626 idx = estimate_index;
627 if (GNUNET_NO == peer_entry->previous_round)
629 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
630 peer_entry->previous_round = GNUNET_YES;
631 peer_entry->transmit_task =
632 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0),
636 if ((0 == ntohl (size_estimate_messages[idx].hop_count)) &&
637 (GNUNET_SCHEDULER_NO_TASK != proof_task))
639 GNUNET_STATISTICS_update (stats,
640 "# flood messages not generated (no proof yet)",
644 if (0 == ntohs (size_estimate_messages[idx].header.size))
646 GNUNET_STATISTICS_update (stats,
647 "# flood messages not generated (lack of history)",
651 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
652 "In round s, sending to `%s' estimate with %u bits\n",
653 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (size_estimate_messages[idx].timestamp)),
654 GNUNET_i2s (&peer_entry->id),
655 (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
656 if (ntohl (size_estimate_messages[idx].hop_count) == 0)
657 GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO);
658 GNUNET_STATISTICS_update (stats, "# flood messages transmitted", 1,
660 #if ENABLE_NSE_HISTOGRAM
661 peer_entry->transmitted_messages++;
662 peer_entry->last_transmitted_size =
663 ntohl(size_estimate_messages[idx].matching_bits);
665 memcpy (buf, &size_estimate_messages[idx],
666 sizeof (struct GNUNET_NSE_FloodMessage));
667 return sizeof (struct GNUNET_NSE_FloodMessage);
672 * Task that triggers a NSE P2P transmission.
674 * @param cls the `struct NSEPeerEntry *`
675 * @param tc scheduler context
678 transmit_task_cb (void *cls,
679 const struct GNUNET_SCHEDULER_TaskContext *tc)
681 struct NSEPeerEntry *peer_entry = cls;
683 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
685 GNUNET_assert (NULL == peer_entry->th);
687 GNUNET_CORE_notify_transmit_ready (core_api, GNUNET_NO,
689 GNUNET_TIME_UNIT_FOREVER_REL,
692 GNUNET_NSE_FloodMessage),
693 &transmit_ready, peer_entry);
698 * We've sent on our flood message or one that we received which was
699 * validated and closer than ours. Update the global list of recent
700 * messages and the average. Also re-broadcast the message to any
704 update_network_size_estimate ()
706 struct GNUNET_NSE_ClientMessage em;
708 setup_estimate_message (&em);
709 GNUNET_SERVER_notification_context_broadcast (nc,
716 * Setup a flood message in our history array at the given
717 * slot offset for the given timestamp.
719 * @param slot index to use
720 * @param ts timestamp to use
723 setup_flood_message (unsigned int slot,
724 struct GNUNET_TIME_Absolute ts)
726 struct GNUNET_NSE_FloodMessage *fm;
727 uint32_t matching_bits;
729 matching_bits = get_matching_bits (ts, &my_identity);
730 fm = &size_estimate_messages[slot];
731 fm->header.size = htons (sizeof (struct GNUNET_NSE_FloodMessage));
732 fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
733 fm->hop_count = htonl (0);
734 fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
736 htonl (sizeof (struct GNUNET_NSE_FloodMessage) -
737 sizeof (struct GNUNET_MessageHeader) - sizeof (uint32_t) -
738 sizeof (struct GNUNET_CRYPTO_EddsaSignature));
739 fm->matching_bits = htonl (matching_bits);
740 fm->timestamp = GNUNET_TIME_absolute_hton (ts);
741 fm->origin = my_identity;
742 fm->proof_of_work = my_proof;
743 if (nse_work_required > 0)
744 GNUNET_assert (GNUNET_OK ==
745 GNUNET_CRYPTO_eddsa_sign (my_private_key, &fm->purpose,
748 memset (&fm->signature, 0, sizeof (fm->signature));
753 * Schedule transmission for the given peer for the current round based
754 * on what we know about the desired delay.
757 * @param key hash of peer identity
758 * @param value the `struct NSEPeerEntry`
759 * @return #GNUNET_OK (continue to iterate)
762 schedule_current_round (void *cls,
763 const struct GNUNET_PeerIdentity * key,
766 struct NSEPeerEntry *peer_entry = value;
767 struct GNUNET_TIME_Relative delay;
769 if (NULL != peer_entry->th)
771 peer_entry->previous_round = GNUNET_NO;
774 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
776 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
777 peer_entry->previous_round = GNUNET_NO;
779 #if ENABLE_NSE_HISTOGRAM
780 if (peer_entry->received_messages > 1)
781 GNUNET_STATISTICS_update(stats, "# extra messages",
782 peer_entry->received_messages - 1, GNUNET_NO);
783 peer_entry->transmitted_messages = 0;
784 peer_entry->last_transmitted_size = 0;
785 peer_entry->received_messages = 0;
788 get_transmit_delay ((peer_entry->previous_round == GNUNET_NO) ? -1 : 0);
789 peer_entry->transmit_task =
790 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
796 * Update our flood message to be sent (and our timestamps).
799 * @param tc context for this message
802 update_flood_message (void *cls,
803 const struct GNUNET_SCHEDULER_TaskContext *tc)
805 struct GNUNET_TIME_Relative offset;
808 flood_task = GNUNET_SCHEDULER_NO_TASK;
809 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
811 offset = GNUNET_TIME_absolute_get_remaining (next_timestamp);
812 if (0 != offset.rel_value_us)
814 /* somehow run early, delay more */
816 GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL);
819 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
820 if (estimate_count < HISTORY_SIZE)
822 current_timestamp = next_timestamp;
824 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
825 if ((current_timestamp.abs_value_us ==
826 GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value_us) &&
827 (get_matching_bits (current_timestamp, &my_identity) <
828 ntohl(next_message.matching_bits)))
830 /* we received a message for this round way early, use it! */
831 size_estimate_messages[estimate_index] = next_message;
832 size_estimate_messages[estimate_index].hop_count =
833 htonl (1 + ntohl (next_message.hop_count));
836 setup_flood_message (estimate_index, current_timestamp);
837 next_message.matching_bits = htonl (0); /* reset for 'next' round */
839 for (i = 0; i < HISTORY_SIZE; i++)
841 GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max);
842 GNUNET_CONTAINER_multipeermap_iterate (peers,
843 &schedule_current_round,
846 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
847 (next_timestamp), &update_flood_message,
853 * Count the leading zeroes in hash.
855 * @param hash to count leading zeros in
856 * @return the number of leading zero bits.
859 count_leading_zeroes (const struct GNUNET_HashCode *hash)
861 unsigned int hash_count;
864 while (0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count))
871 * Check whether the given public key and integer are a valid proof of
874 * @param pkey the public key
875 * @param val the integer
876 * @return #GNUNET_YES if valid, #GNUNET_NO if not
879 check_proof_of_work (const struct GNUNET_CRYPTO_EddsaPublicKey *pkey,
882 char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
883 sizeof (val)] GNUNET_ALIGN;
884 struct GNUNET_HashCode result;
886 memcpy (buf, &val, sizeof (val));
887 memcpy (&buf[sizeof (val)], pkey,
888 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
889 pow_hash (buf, sizeof (buf), &result);
890 return (count_leading_zeroes (&result) >=
891 nse_work_required) ? GNUNET_YES : GNUNET_NO;
896 * Write our current proof to disk.
904 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
906 if (sizeof (my_proof) !=
907 GNUNET_DISK_fn_write (proof, &my_proof, sizeof (my_proof),
908 GNUNET_DISK_PERM_USER_READ |
909 GNUNET_DISK_PERM_USER_WRITE))
910 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof);
917 * Find our proof of work.
919 * @param cls closure (unused)
920 * @param tc task context
923 find_proof (void *cls,
924 const struct GNUNET_SCHEDULER_TaskContext *tc)
926 #define ROUND_SIZE 10
928 char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
929 sizeof (uint64_t)] GNUNET_ALIGN;
930 struct GNUNET_HashCode result;
933 proof_task = GNUNET_SCHEDULER_NO_TASK;
934 memcpy (&buf[sizeof (uint64_t)], &my_identity,
935 sizeof (struct GNUNET_PeerIdentity));
938 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
940 memcpy (buf, &counter, sizeof (uint64_t));
941 pow_hash (buf, sizeof (buf), &result);
942 if (nse_work_required <= count_leading_zeroes (&result))
945 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work found: %llu!\n",
946 (unsigned long long) GNUNET_ntohll (counter));
948 setup_flood_message (estimate_index, current_timestamp);
954 if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
956 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing proofs currently at %llu\n",
957 (unsigned long long) counter);
958 /* remember progress every 100 rounds */
967 GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
968 GNUNET_SCHEDULER_PRIORITY_IDLE,
974 * An incoming flood message has been received which claims
975 * to have more bits matching than any we know in this time
976 * period. Verify the signature and/or proof of work.
978 * @param incoming_flood the message to verify
979 * @return #GNUNET_YES if the message is verified
980 * #GNUNET_NO if the key/signature don't verify
983 verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood)
986 check_proof_of_work (&incoming_flood->origin.public_key,
987 incoming_flood->proof_of_work))
989 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
990 "Proof of work invalid: %llu!\n",
992 GNUNET_ntohll (incoming_flood->proof_of_work));
996 if ((nse_work_required > 0) &&
998 GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
999 &incoming_flood->purpose,
1000 &incoming_flood->signature,
1001 &incoming_flood->origin.public_key)))
1003 GNUNET_break_op (0);
1011 * Update transmissions for the given peer for the current round based
1012 * on updated proximity information.
1014 * @param cls peer entry to exclude from updates
1015 * @param key hash of peer identity
1016 * @param value the `struct NSEPeerEntry *` of a peer to transmit to
1017 * @return #GNUNET_OK (continue to iterate)
1020 update_flood_times (void *cls,
1021 const struct GNUNET_PeerIdentity *key,
1024 struct NSEPeerEntry *exclude = cls;
1025 struct NSEPeerEntry *peer_entry = value;
1026 struct GNUNET_TIME_Relative delay;
1028 if (NULL != peer_entry->th)
1029 return GNUNET_OK; /* already active */
1030 if (peer_entry == exclude)
1031 return GNUNET_OK; /* trigger of the update */
1032 if (peer_entry->previous_round == GNUNET_NO)
1034 /* still stuck in previous round, no point to update, check that
1035 * we are active here though... */
1036 if ( (GNUNET_SCHEDULER_NO_TASK == peer_entry->transmit_task) &&
1037 (NULL == peer_entry->th) )
1043 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1045 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1046 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1048 delay = get_transmit_delay (0);
1049 peer_entry->transmit_task =
1050 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
1056 * Core handler for size estimate flooding messages.
1058 * @param cls closure unused
1059 * @param message message
1060 * @param peer peer identity this message is from (ignored)
1063 handle_p2p_size_estimate (void *cls,
1064 const struct GNUNET_PeerIdentity *peer,
1065 const struct GNUNET_MessageHeader *message)
1067 const struct GNUNET_NSE_FloodMessage *incoming_flood;
1068 struct GNUNET_TIME_Absolute ts;
1069 struct NSEPeerEntry *peer_entry;
1070 uint32_t matching_bits;
1073 #if ENABLE_NSE_HISTOGRAM
1077 t = GNUNET_TIME_absolute_get().abs_value_us;
1079 GNUNET_TESTBED_LOGGER_write (lh, &t, sizeof (uint64_t));
1080 if (NULL != histogram)
1081 GNUNET_BIO_write_int64 (histogram, t);
1084 incoming_flood = (const struct GNUNET_NSE_FloodMessage *) message;
1085 GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO);
1086 matching_bits = ntohl (incoming_flood->matching_bits);
1091 struct GNUNET_PeerIdentity os;
1093 GNUNET_snprintf (origin,
1096 GNUNET_i2s (&incoming_flood->origin));
1097 GNUNET_snprintf (pred,
1101 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1102 "Flood at %s from `%s' via `%s' at `%s' with bits %u\n",
1103 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp)),
1104 origin, pred, GNUNET_i2s (&my_identity),
1105 (unsigned int) matching_bits);
1109 peer_entry = GNUNET_CONTAINER_multipeermap_get (peers, peer);
1110 if (NULL == peer_entry)
1115 #if ENABLE_NSE_HISTOGRAM
1116 peer_entry->received_messages++;
1117 if (peer_entry->transmitted_messages > 0 &&
1118 peer_entry->last_transmitted_size >= matching_bits)
1119 GNUNET_STATISTICS_update(stats, "# cross messages", 1, GNUNET_NO);
1122 ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp);
1123 if (ts.abs_value_us == current_timestamp.abs_value_us)
1124 idx = estimate_index;
1125 else if (ts.abs_value_us ==
1126 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us)
1127 idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1128 else if (ts.abs_value_us == next_timestamp.abs_value_us)
1130 if (matching_bits <= ntohl (next_message.matching_bits))
1131 return GNUNET_OK; /* ignore, simply too early/late */
1132 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1134 GNUNET_break_op (0);
1137 next_message = *incoming_flood;
1142 GNUNET_STATISTICS_update (stats,
1143 "# flood messages discarded (clock skew too large)",
1147 if (0 == (memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))))
1149 /* send to self, update our own estimate IF this also comes from us! */
1151 memcmp (&incoming_flood->origin,
1152 &my_identity, sizeof (my_identity)))
1153 update_network_size_estimate ();
1156 if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits))
1158 /* Cancel transmission in the other direction, as this peer clearly has
1159 up-to-date information already. Even if we didn't talk to this peer in
1160 the previous round, we should no longer send it stale information as it
1161 told us about the current round! */
1162 peer_entry->previous_round = GNUNET_YES;
1163 if (idx != estimate_index)
1165 /* do not transmit information for the previous round to this peer
1166 anymore (but allow current round) */
1169 /* got up-to-date information for current round, cancel transmission to
1170 * this peer altogether */
1171 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1173 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1174 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1176 if (NULL != peer_entry->th)
1178 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1179 peer_entry->th = NULL;
1183 if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits))
1185 if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES)) {
1186 peer_entry->previous_round = GNUNET_NO;
1188 /* push back our result now, that peer is spreading bad information... */
1189 if (NULL == peer_entry->th)
1191 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1192 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1193 peer_entry->transmit_task =
1194 GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry);
1196 /* Not closer than our most recent message, no need to do work here */
1197 GNUNET_STATISTICS_update (stats,
1198 "# flood messages ignored (had closer already)",
1202 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1204 GNUNET_break_op (0);
1207 GNUNET_assert (matching_bits >
1208 ntohl (size_estimate_messages[idx].matching_bits));
1209 /* Cancel transmission in the other direction, as this peer clearly has
1210 * up-to-date information already.
1212 peer_entry->previous_round = GNUNET_YES;
1213 if (idx == estimate_index)
1215 /* cancel any activity for current round */
1216 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1218 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1219 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1221 if (peer_entry->th != NULL)
1223 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1224 peer_entry->th = NULL;
1227 size_estimate_messages[idx] = *incoming_flood;
1228 size_estimate_messages[idx].hop_count =
1229 htonl (ntohl (incoming_flood->hop_count) + 1);
1231 GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max);
1232 GNUNET_STATISTICS_set (stats,
1233 "# estimated network diameter",
1234 hop_count_max, GNUNET_NO);
1236 /* have a new, better size estimate, inform clients */
1237 update_network_size_estimate ();
1240 GNUNET_CONTAINER_multipeermap_iterate (peers, &update_flood_times,
1247 * Method called whenever a peer connects. Sets up the PeerEntry and
1248 * schedules the initial size info transmission to this peer.
1250 * @param cls closure
1251 * @param peer peer identity this notification is about
1254 handle_core_connect (void *cls,
1255 const struct GNUNET_PeerIdentity *peer)
1257 struct NSEPeerEntry *peer_entry;
1259 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1260 "Peer `%s' connected to us\n",
1262 peer_entry = GNUNET_new (struct NSEPeerEntry);
1263 peer_entry->id = *peer;
1264 GNUNET_assert (GNUNET_OK ==
1265 GNUNET_CONTAINER_multipeermap_put (peers,
1268 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1269 peer_entry->transmit_task =
1270 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1),
1273 GNUNET_STATISTICS_update (stats,
1274 "# peers connected",
1281 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
1282 * any pending transmission requests to that peer.
1284 * @param cls closure
1285 * @param peer peer identity this notification is about
1288 handle_core_disconnect (void *cls,
1289 const struct GNUNET_PeerIdentity *peer)
1291 struct NSEPeerEntry *pos;
1293 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1294 "Peer `%s' disconnected from us\n",
1296 pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
1302 GNUNET_assert (GNUNET_YES ==
1303 GNUNET_CONTAINER_multipeermap_remove (peers, peer,
1305 if (pos->transmit_task != GNUNET_SCHEDULER_NO_TASK) {
1306 GNUNET_SCHEDULER_cancel (pos->transmit_task);
1307 pos->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1309 if (NULL != pos->th)
1311 GNUNET_CORE_notify_transmit_ready_cancel (pos->th);
1315 GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO);
1319 #if ENABLE_NSE_HISTOGRAM
1321 * Functions of this type are called to notify a successful transmission of the
1322 * message to the logger service
1325 * @param size the amount of data sent (ignored)
1328 flush_comp_cb (void *cls,
1331 GNUNET_TESTBED_LOGGER_disconnect (lh);
1338 * Task run during shutdown.
1344 shutdown_task (void *cls,
1345 const struct GNUNET_SCHEDULER_TaskContext *tc)
1347 if (GNUNET_SCHEDULER_NO_TASK != flood_task)
1349 GNUNET_SCHEDULER_cancel (flood_task);
1350 flood_task = GNUNET_SCHEDULER_NO_TASK;
1352 if (GNUNET_SCHEDULER_NO_TASK != proof_task)
1354 GNUNET_SCHEDULER_cancel (proof_task);
1355 proof_task = GNUNET_SCHEDULER_NO_TASK;
1356 write_proof (); /* remember progress */
1360 GNUNET_SERVER_notification_context_destroy (nc);
1363 if (NULL != core_api)
1365 GNUNET_CORE_disconnect (core_api);
1370 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1375 GNUNET_CONTAINER_multipeermap_destroy (peers);
1378 if (NULL != my_private_key)
1380 GNUNET_free (my_private_key);
1381 my_private_key = NULL;
1383 #if ENABLE_NSE_HISTOGRAM
1384 if (NULL != logger_test)
1386 GNUNET_CLIENT_service_test_cancel (logger_test);
1391 struct GNUNET_TIME_Relative timeout;
1392 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30);
1393 GNUNET_TESTBED_LOGGER_flush (lh, timeout, &flush_comp_cb, NULL);
1395 if (NULL != histogram)
1397 GNUNET_BIO_write_close (histogram);
1405 * Called on core init/fail.
1407 * @param cls service closure
1408 * @param identity the public identity of this peer
1411 core_init (void *cls,
1412 const struct GNUNET_PeerIdentity *identity)
1414 struct GNUNET_TIME_Absolute now;
1415 struct GNUNET_TIME_Absolute prev_time;
1417 if (NULL == identity)
1419 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n");
1420 GNUNET_SCHEDULER_shutdown ();
1424 memcmp (&my_identity, identity,
1425 sizeof (struct GNUNET_PeerIdentity)));
1426 now = GNUNET_TIME_absolute_get ();
1427 current_timestamp.abs_value_us =
1428 (now.abs_value_us / gnunet_nse_interval.rel_value_us) *
1429 gnunet_nse_interval.rel_value_us;
1431 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
1432 estimate_index = HISTORY_SIZE - 1;
1434 if (GNUNET_YES == check_proof_of_work (&my_identity.public_key, my_proof))
1436 int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1437 prev_time.abs_value_us =
1438 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us;
1439 setup_flood_message (idx, prev_time);
1440 setup_flood_message (estimate_index, current_timestamp);
1444 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
1445 (next_timestamp), &update_flood_message,
1449 #if ENABLE_NSE_HISTOGRAM
1451 * Function called with the status of the testbed logger service
1454 * @param status GNUNET_YES if the service is running,
1455 * GNUNET_NO if the service is not running
1456 * GNUNET_SYSERR if the configuration is invalid
1459 status_cb (void *cls, int status)
1462 if (GNUNET_YES != status)
1464 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Testbed logger not running\n");
1467 if (NULL == (lh = GNUNET_TESTBED_LOGGER_connect (cfg)))
1469 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1470 "Cannot connect to the testbed logger. Exiting.\n");
1471 GNUNET_SCHEDULER_shutdown ();
1478 * Handle network size estimate clients.
1480 * @param cls closure
1481 * @param server the initialized server
1482 * @param c configuration to use
1486 struct GNUNET_SERVER_Handle *server,
1487 const struct GNUNET_CONFIGURATION_Handle *c)
1489 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1490 {&handle_start_message, NULL, GNUNET_MESSAGE_TYPE_NSE_START,
1491 sizeof (struct GNUNET_MessageHeader)},
1494 static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
1495 {&handle_p2p_size_estimate, GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1496 sizeof (struct GNUNET_NSE_FloodMessage)},
1500 struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
1505 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "INTERVAL",
1506 &gnunet_nse_interval))
1508 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1510 GNUNET_SCHEDULER_shutdown ();
1514 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "WORKDELAY",
1517 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1518 "NSE", "WORKDELAY");
1519 GNUNET_SCHEDULER_shutdown ();
1523 GNUNET_CONFIGURATION_get_value_number (cfg, "NSE", "WORKBITS",
1524 &nse_work_required))
1526 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1528 GNUNET_SCHEDULER_shutdown ();
1531 if (nse_work_required >= sizeof (struct GNUNET_HashCode) * 8)
1533 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1536 _("Value is too large.\n"));
1537 GNUNET_SCHEDULER_shutdown ();
1541 #if ENABLE_NSE_HISTOGRAM
1543 char *histogram_dir;
1547 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "HISTOGRAM_DIR",
1550 GNUNET_assert (0 < GNUNET_asprintf (&histogram_fn, "%s/timestamps",
1552 GNUNET_free (histogram_dir);
1553 histogram = GNUNET_BIO_write_open (histogram_fn);
1554 GNUNET_free (histogram_fn);
1555 if (NULL == histogram)
1556 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to open histogram file\n");
1559 GNUNET_CLIENT_service_test ("testbed-logger", cfg,
1560 GNUNET_TIME_UNIT_SECONDS,
1566 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1568 pk = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
1569 GNUNET_assert (NULL != pk);
1570 my_private_key = pk;
1571 GNUNET_CRYPTO_eddsa_key_get_public (my_private_key,
1572 &my_identity.public_key);
1574 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
1576 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "PROOFFILE");
1577 GNUNET_free (my_private_key);
1578 my_private_key = NULL;
1579 GNUNET_SCHEDULER_shutdown ();
1582 if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) ||
1583 (sizeof (my_proof) !=
1584 GNUNET_DISK_fn_read (proof, &my_proof, sizeof (my_proof))))
1586 GNUNET_free (proof);
1588 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1591 peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO);
1592 GNUNET_SERVER_add_handlers (srv, handlers);
1593 nc = GNUNET_SERVER_notification_context_create (srv, 1);
1594 /* Connect to core service and register core handlers */
1595 core_api = GNUNET_CORE_connect (cfg, /* Main configuration */
1596 NULL, /* Closure passed to functions */
1597 &core_init, /* Call core_init once connected */
1598 &handle_core_connect, /* Handle connects */
1599 &handle_core_disconnect, /* Handle disconnects */
1600 NULL, /* Don't want notified about all incoming messages */
1601 GNUNET_NO, /* For header only inbound notification */
1602 NULL, /* Don't want notified about all outbound messages */
1603 GNUNET_NO, /* For header only outbound notification */
1604 core_handlers); /* Register these handlers */
1605 if (NULL == core_api)
1607 GNUNET_SCHEDULER_shutdown ();
1610 stats = GNUNET_STATISTICS_create ("nse", cfg);
1615 * The main function for the network size estimation service.
1617 * @param argc number of arguments from the command line
1618 * @param argv command line arguments
1619 * @return 0 ok, 1 on error
1625 return (GNUNET_OK ==
1626 GNUNET_SERVICE_run (argc, argv, "nse", GNUNET_SERVICE_OPTION_NONE,
1627 &run, NULL)) ? 0 : 1;
1635 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1637 void __attribute__ ((constructor))
1638 GNUNET_ARM_memory_init ()
1640 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1641 mallopt (M_TOP_PAD, 1 * 1024);
1648 /* end of gnunet-service-nse.c */