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
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_PeerIdentity origin;
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_EddsaSignature 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 *core_api;
222 * Map of all connected peers.
224 static struct GNUNET_CONTAINER_MultiPeerMap *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 private key of this peer.
291 static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
294 * The peer identity of this peer.
296 static struct GNUNET_PeerIdentity my_identity;
299 * Proof of work for this peer.
301 static uint64_t my_proof;
304 * Handle to this serivce's server.
306 static struct GNUNET_SERVER_Handle *srv;
310 * Initialize a message to clients with the current network
313 * @param em message to fill in
316 setup_estimate_message (struct GNUNET_NSE_ClientMessage *em)
328 /* Weighted incremental algorithm for stddev according to West (1979) */
340 for (i = 0; i < estimate_count; i++)
342 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
343 val = htonl (size_estimate_messages[j].matching_bits);
344 weight = estimate_count + 1 - i;
346 temp = weight + sumweight;
348 r = q * weight / temp;
350 sum += sumweight * q * r;
353 if (estimate_count > 0)
354 variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0);
356 /* trivial version for debugging */
359 /* non-weighted trivial version */
365 for (i = 0; i < estimate_count; i++)
367 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
368 val = htonl (size_estimate_messages[j].matching_bits);
372 if (0 != estimate_count)
374 mean = sum / estimate_count;
375 variance = (vsq - mean * sum) / (estimate_count - 1.0); // terrible for numerical stability...
379 std_dev = sqrt (variance);
381 std_dev = variance; /* must be infinity due to estimate_count == 0 */
382 current_std_dev = std_dev;
383 current_size_estimate = mean;
385 em->header.size = htons (sizeof (struct GNUNET_NSE_ClientMessage));
386 em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE);
387 em->reserved = htonl (0);
388 em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
389 double se = mean - 0.332747;
390 nsize = log2 (GNUNET_CONTAINER_multipeermap_size (peers) + 1);
391 em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se, nsize));
392 em->std_deviation = GNUNET_hton_double (std_dev);
393 GNUNET_STATISTICS_set (stats, "# nodes in the network (estimate)",
394 (uint64_t) pow (2, mean - 1.0 / 3.0), GNUNET_NO);
399 * Handler for START message from client, triggers an
400 * immediate current network estimate notification.
401 * Also, we remember the client for updates upon future
402 * estimate measurements.
405 * @param client who sent the message
406 * @param message the message received
409 handle_start_message (void *cls,
410 struct GNUNET_SERVER_Client *client,
411 const struct GNUNET_MessageHeader *message)
413 struct GNUNET_NSE_ClientMessage em;
415 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
416 "Received START message from client\n");
417 GNUNET_SERVER_notification_context_add (nc, client);
418 setup_estimate_message (&em);
419 GNUNET_SERVER_notification_context_unicast (nc, client, &em.header,
421 GNUNET_SERVER_receive_done (client, GNUNET_OK);
426 * How long should we delay a message to go the given number of
429 * @param matching_bits number of matching bits to consider
432 get_matching_bits_delay (uint32_t matching_bits)
434 /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */
435 // S is next_timestamp (ignored in return value)
436 // f is frequency (gnunet_nse_interval)
437 // x is matching_bits
438 // p' is current_size_estimate
439 return ((double) gnunet_nse_interval.rel_value_us / (double) 2.0) -
440 ((gnunet_nse_interval.rel_value_us / M_PI) *
441 atan (matching_bits - current_size_estimate));
446 * What delay randomization should we apply for a given number of matching bits?
448 * @param matching_bits number of matching bits
449 * @return random delay to apply
451 static struct GNUNET_TIME_Relative
452 get_delay_randomization (uint32_t matching_bits)
454 #if USE_RANDOM_DELAYS
455 struct GNUNET_TIME_Relative ret;
459 d = get_matching_bits_delay (matching_bits);
460 i = (uint32_t) (d / (double) (hop_count_max + 1));
461 ret.rel_value_us = i;
462 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
463 "Randomizing flood using latencies up to %s\n",
464 GNUNET_STRINGS_relative_time_to_string (ret,
466 ret.rel_value_us = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1);
469 return GNUNET_TIME_UNIT_ZERO;
475 * Calculate the 'proof-of-work' hash (an expensive hash).
477 * @param buf data to hash
478 * @param buf_len number of bytes in @a buf
479 * @param result where to write the resulting hash
482 pow_hash (const void *buf,
484 struct GNUNET_HashCode *result)
487 gcry_kdf_derive (buf, buf_len,
490 "gnunet-proof-of-work", strlen ("gnunet-proof-of-work"),
491 2 /* iterations; keep cost of individual op small */,
492 sizeof (struct GNUNET_HashCode), result));
497 * Get the number of matching bits that the given timestamp has to the given peer ID.
499 * @param timestamp time to generate key
500 * @param id peer identity to compare with
501 * @return number of matching bits
504 get_matching_bits (struct GNUNET_TIME_Absolute timestamp,
505 const struct GNUNET_PeerIdentity *id)
507 struct GNUNET_HashCode timestamp_hash;
508 struct GNUNET_HashCode pid_hash;
510 GNUNET_CRYPTO_hash (×tamp.abs_value_us, sizeof (timestamp.abs_value_us),
512 GNUNET_CRYPTO_hash (id, sizeof (struct GNUNET_PeerIdentity), &pid_hash);
513 return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash, &pid_hash);
518 * Get the transmission delay that should be applied for a
521 * @param round_offset -1 for the previous round (random delay between 0 and 50ms)
522 * 0 for the current round (based on our proximity to time key)
523 * @return delay that should be applied
525 static struct GNUNET_TIME_Relative
526 get_transmit_delay (int round_offset)
528 struct GNUNET_TIME_Relative ret;
529 struct GNUNET_TIME_Absolute tgt;
531 uint32_t matching_bits;
533 switch (round_offset)
536 /* previous round is randomized between 0 and 50 ms */
537 #if USE_RANDOM_DELAYS
538 ret.rel_value_us = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50);
540 ret = GNUNET_TIME_UNIT_ZERO;
542 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
543 "Transmitting previous round behind schedule in %s\n",
544 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
547 /* current round is based on best-known matching_bits */
549 ntohl (size_estimate_messages[estimate_index].matching_bits);
550 dist_delay = get_matching_bits_delay (matching_bits);
551 dist_delay += get_delay_randomization (matching_bits).rel_value_us;
552 ret.rel_value_us = (uint64_t) dist_delay;
553 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
554 "For round %s, delay for %u matching bits is %s\n",
555 GNUNET_STRINGS_absolute_time_to_string (current_timestamp),
556 (unsigned int) matching_bits,
557 GNUNET_STRINGS_relative_time_to_string (ret,
559 /* now consider round start time and add delay to it */
560 tgt = GNUNET_TIME_absolute_add (current_timestamp, ret);
561 return GNUNET_TIME_absolute_get_remaining (tgt);
564 return GNUNET_TIME_UNIT_FOREVER_REL;
569 * Task that triggers a NSE P2P transmission.
571 * @param cls the `struct NSEPeerEntry *`
572 * @param tc scheduler context
575 transmit_task_cb (void *cls,
576 const struct GNUNET_SCHEDULER_TaskContext *tc);
580 * Called when core is ready to send a message we asked for
581 * out to the destination.
583 * @param cls closure with the `struct NSEPeerEntry *`
584 * @param size number of bytes available in @a buf
585 * @param buf where the callee should write the message
586 * @return number of bytes written to @a buf
589 transmit_ready (void *cls,
593 struct NSEPeerEntry *peer_entry = cls;
596 peer_entry->th = NULL;
599 /* client disconnected */
602 GNUNET_assert (size >= sizeof (struct GNUNET_NSE_FloodMessage));
603 idx = estimate_index;
604 if (GNUNET_NO == peer_entry->previous_round)
606 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
607 peer_entry->previous_round = GNUNET_YES;
608 peer_entry->transmit_task =
609 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0), &transmit_task_cb,
612 if ((0 == ntohl (size_estimate_messages[idx].hop_count)) &&
613 (GNUNET_SCHEDULER_NO_TASK != proof_task))
615 GNUNET_STATISTICS_update (stats,
616 "# flood messages not generated (no proof yet)",
620 if (0 == ntohs (size_estimate_messages[idx].header.size))
622 GNUNET_STATISTICS_update (stats,
623 "# flood messages not generated (lack of history)",
627 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
628 "In round s, sending to `%s' estimate with %u bits\n",
629 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (size_estimate_messages[idx].timestamp)),
630 GNUNET_i2s (&peer_entry->id),
631 (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
632 if (ntohl (size_estimate_messages[idx].hop_count) == 0)
633 GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO);
634 GNUNET_STATISTICS_update (stats, "# flood messages transmitted", 1,
636 #if ENABLE_NSE_HISTOGRAM
637 peer_entry->transmitted_messages++;
638 peer_entry->last_transmitted_size =
639 ntohl(size_estimate_messages[idx].matching_bits);
641 memcpy (buf, &size_estimate_messages[idx],
642 sizeof (struct GNUNET_NSE_FloodMessage));
643 return sizeof (struct GNUNET_NSE_FloodMessage);
648 * Task that triggers a NSE P2P transmission.
650 * @param cls the `struct NSEPeerEntry *`
651 * @param tc scheduler context
654 transmit_task_cb (void *cls,
655 const struct GNUNET_SCHEDULER_TaskContext *tc)
657 struct NSEPeerEntry *peer_entry = cls;
659 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
661 GNUNET_assert (NULL == peer_entry->th);
663 GNUNET_CORE_notify_transmit_ready (core_api, GNUNET_NO,
665 GNUNET_TIME_UNIT_FOREVER_REL,
668 GNUNET_NSE_FloodMessage),
669 &transmit_ready, peer_entry);
674 * We've sent on our flood message or one that we received which was
675 * validated and closer than ours. Update the global list of recent
676 * messages and the average. Also re-broadcast the message to any
680 update_network_size_estimate ()
682 struct GNUNET_NSE_ClientMessage em;
684 setup_estimate_message (&em);
685 GNUNET_SERVER_notification_context_broadcast (nc,
692 * Setup a flood message in our history array at the given
693 * slot offset for the given timestamp.
695 * @param slot index to use
696 * @param ts timestamp to use
699 setup_flood_message (unsigned int slot,
700 struct GNUNET_TIME_Absolute ts)
702 struct GNUNET_NSE_FloodMessage *fm;
703 uint32_t matching_bits;
705 matching_bits = get_matching_bits (ts, &my_identity);
706 fm = &size_estimate_messages[slot];
707 fm->header.size = htons (sizeof (struct GNUNET_NSE_FloodMessage));
708 fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
709 fm->hop_count = htonl (0);
710 fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
712 htonl (sizeof (struct GNUNET_NSE_FloodMessage) -
713 sizeof (struct GNUNET_MessageHeader) - sizeof (uint32_t) -
714 sizeof (struct GNUNET_CRYPTO_EddsaSignature));
715 fm->matching_bits = htonl (matching_bits);
716 fm->timestamp = GNUNET_TIME_absolute_hton (ts);
717 fm->origin = my_identity;
718 fm->proof_of_work = my_proof;
719 if (nse_work_required > 0)
720 GNUNET_assert (GNUNET_OK ==
721 GNUNET_CRYPTO_eddsa_sign (my_private_key, &fm->purpose,
724 memset (&fm->signature, 0, sizeof (fm->signature));
729 * Schedule transmission for the given peer for the current round based
730 * on what we know about the desired delay.
733 * @param key hash of peer identity
734 * @param value the `struct NSEPeerEntry`
735 * @return #GNUNET_OK (continue to iterate)
738 schedule_current_round (void *cls,
739 const struct GNUNET_PeerIdentity * key,
742 struct NSEPeerEntry *peer_entry = value;
743 struct GNUNET_TIME_Relative delay;
745 if (NULL != peer_entry->th)
747 peer_entry->previous_round = GNUNET_NO;
750 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
752 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
753 peer_entry->previous_round = GNUNET_NO;
755 #if ENABLE_NSE_HISTOGRAM
756 if (peer_entry->received_messages > 1)
757 GNUNET_STATISTICS_update(stats, "# extra messages",
758 peer_entry->received_messages - 1, GNUNET_NO);
759 peer_entry->transmitted_messages = 0;
760 peer_entry->last_transmitted_size = 0;
761 peer_entry->received_messages = 0;
764 get_transmit_delay ((peer_entry->previous_round == GNUNET_NO) ? -1 : 0);
765 peer_entry->transmit_task =
766 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
772 * Update our flood message to be sent (and our timestamps).
775 * @param tc context for this message
778 update_flood_message (void *cls,
779 const struct GNUNET_SCHEDULER_TaskContext *tc)
781 struct GNUNET_TIME_Relative offset;
784 flood_task = GNUNET_SCHEDULER_NO_TASK;
785 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
787 offset = GNUNET_TIME_absolute_get_remaining (next_timestamp);
788 if (0 != offset.rel_value_us)
790 /* somehow run early, delay more */
792 GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL);
795 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
796 if (estimate_count < HISTORY_SIZE)
798 current_timestamp = next_timestamp;
800 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
801 if ((current_timestamp.abs_value_us ==
802 GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value_us) &&
803 (get_matching_bits (current_timestamp, &my_identity) <
804 ntohl(next_message.matching_bits)))
806 /* we received a message for this round way early, use it! */
807 size_estimate_messages[estimate_index] = next_message;
808 size_estimate_messages[estimate_index].hop_count =
809 htonl (1 + ntohl (next_message.hop_count));
812 setup_flood_message (estimate_index, current_timestamp);
813 next_message.matching_bits = htonl (0); /* reset for 'next' round */
815 for (i = 0; i < HISTORY_SIZE; i++)
817 GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max);
818 GNUNET_CONTAINER_multipeermap_iterate (peers, &schedule_current_round, NULL);
820 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
821 (next_timestamp), &update_flood_message,
827 * Count the leading zeroes in hash.
829 * @param hash to count leading zeros in
830 * @return the number of leading zero bits.
833 count_leading_zeroes (const struct GNUNET_HashCode *hash)
835 unsigned int hash_count;
838 while (0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count))
845 * Check whether the given public key and integer are a valid proof of
848 * @param pkey the public key
849 * @param val the integer
850 * @return #GNUNET_YES if valid, #GNUNET_NO if not
853 check_proof_of_work (const struct GNUNET_CRYPTO_EddsaPublicKey *pkey,
856 char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
857 sizeof (val)] GNUNET_ALIGN;
858 struct GNUNET_HashCode result;
860 memcpy (buf, &val, sizeof (val));
861 memcpy (&buf[sizeof (val)], pkey,
862 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
863 pow_hash (buf, sizeof (buf), &result);
864 return (count_leading_zeroes (&result) >=
865 nse_work_required) ? GNUNET_YES : GNUNET_NO;
870 * Write our current proof to disk.
878 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
880 if (sizeof (my_proof) !=
881 GNUNET_DISK_fn_write (proof, &my_proof, sizeof (my_proof),
882 GNUNET_DISK_PERM_USER_READ |
883 GNUNET_DISK_PERM_USER_WRITE))
884 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof);
891 * Find our proof of work.
893 * @param cls closure (unused)
894 * @param tc task context
897 find_proof (void *cls,
898 const struct GNUNET_SCHEDULER_TaskContext *tc)
900 #define ROUND_SIZE 10
902 char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
903 sizeof (uint64_t)] GNUNET_ALIGN;
904 struct GNUNET_HashCode result;
907 proof_task = GNUNET_SCHEDULER_NO_TASK;
908 memcpy (&buf[sizeof (uint64_t)], &my_identity,
909 sizeof (struct GNUNET_PeerIdentity));
912 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
914 memcpy (buf, &counter, sizeof (uint64_t));
915 pow_hash (buf, sizeof (buf), &result);
916 if (nse_work_required <= count_leading_zeroes (&result))
919 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work found: %llu!\n",
920 (unsigned long long) GNUNET_ntohll (counter));
922 setup_flood_message (estimate_index, current_timestamp);
928 if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
930 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing proofs currently at %llu\n",
931 (unsigned long long) counter);
932 /* remember progress every 100 rounds */
941 GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
942 GNUNET_SCHEDULER_PRIORITY_IDLE,
948 * An incoming flood message has been received which claims
949 * to have more bits matching than any we know in this time
950 * period. Verify the signature and/or proof of work.
952 * @param incoming_flood the message to verify
953 * @return #GNUNET_YES if the message is verified
954 * #GNUNET_NO if the key/signature don't verify
957 verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood)
960 check_proof_of_work (&incoming_flood->origin.public_key,
961 incoming_flood->proof_of_work))
963 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
964 "Proof of work invalid: %llu!\n",
966 GNUNET_ntohll (incoming_flood->proof_of_work));
970 if ((nse_work_required > 0) &&
972 GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
973 &incoming_flood->purpose,
974 &incoming_flood->signature,
975 &incoming_flood->origin.public_key)))
985 * Update transmissions for the given peer for the current round based
986 * on updated proximity information.
988 * @param cls peer entry to exclude from updates
989 * @param key hash of peer identity
990 * @param value the `struct NSEPeerEntry *` of a peer to transmit to
991 * @return #GNUNET_OK (continue to iterate)
994 update_flood_times (void *cls,
995 const struct GNUNET_PeerIdentity *key,
998 struct NSEPeerEntry *exclude = cls;
999 struct NSEPeerEntry *peer_entry = value;
1000 struct GNUNET_TIME_Relative delay;
1002 if (NULL != peer_entry->th)
1003 return GNUNET_OK; /* already active */
1004 if (peer_entry == exclude)
1005 return GNUNET_OK; /* trigger of the update */
1006 if (peer_entry->previous_round == GNUNET_NO)
1008 /* still stuck in previous round, no point to update, check that
1009 * we are active here though... */
1010 if ( (GNUNET_SCHEDULER_NO_TASK == peer_entry->transmit_task) &&
1011 (NULL == peer_entry->th) )
1017 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1019 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1020 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1022 delay = get_transmit_delay (0);
1023 peer_entry->transmit_task =
1024 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
1030 * Core handler for size estimate flooding messages.
1032 * @param cls closure unused
1033 * @param message message
1034 * @param peer peer identity this message is from (ignored)
1037 handle_p2p_size_estimate (void *cls,
1038 const struct GNUNET_PeerIdentity *peer,
1039 const struct GNUNET_MessageHeader *message)
1041 const struct GNUNET_NSE_FloodMessage *incoming_flood;
1042 struct GNUNET_TIME_Absolute ts;
1043 struct NSEPeerEntry *peer_entry;
1044 uint32_t matching_bits;
1047 #if ENABLE_NSE_HISTOGRAM
1051 t = GNUNET_TIME_absolute_get().abs_value_us;
1052 GNUNET_TESTBED_LOGGER_write (lh, &t, sizeof (uint64_t));
1055 incoming_flood = (const struct GNUNET_NSE_FloodMessage *) message;
1056 GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO);
1057 matching_bits = ntohl (incoming_flood->matching_bits);
1062 struct GNUNET_PeerIdentity os;
1064 GNUNET_snprintf (origin,
1067 GNUNET_i2s (&incoming_flood->origin));
1068 GNUNET_snprintf (pred,
1072 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1073 "Flood at %s from `%s' via `%s' at `%s' with bits %u\n",
1074 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp)),
1075 origin, pred, GNUNET_i2s (&my_identity),
1076 (unsigned int) matching_bits);
1080 peer_entry = GNUNET_CONTAINER_multipeermap_get (peers, peer);
1081 if (NULL == peer_entry)
1086 #if ENABLE_NSE_HISTOGRAM
1087 peer_entry->received_messages++;
1088 if (peer_entry->transmitted_messages > 0 &&
1089 peer_entry->last_transmitted_size >= matching_bits)
1090 GNUNET_STATISTICS_update(stats, "# cross messages", 1, GNUNET_NO);
1093 ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp);
1094 if (ts.abs_value_us == current_timestamp.abs_value_us)
1095 idx = estimate_index;
1096 else if (ts.abs_value_us ==
1097 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us)
1098 idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1099 else if (ts.abs_value_us == next_timestamp.abs_value_us)
1101 if (matching_bits <= ntohl (next_message.matching_bits))
1102 return GNUNET_OK; /* ignore, simply too early/late */
1103 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1105 GNUNET_break_op (0);
1108 next_message = *incoming_flood;
1113 GNUNET_STATISTICS_update (stats,
1114 "# flood messages discarded (clock skew too large)",
1118 if (0 == (memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))))
1120 /* send to self, update our own estimate IF this also comes from us! */
1122 memcmp (&incoming_flood->origin,
1123 &my_identity, sizeof (my_identity)))
1124 update_network_size_estimate ();
1127 if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits))
1129 /* Cancel transmission in the other direction, as this peer clearly has
1130 up-to-date information already. Even if we didn't talk to this peer in
1131 the previous round, we should no longer send it stale information as it
1132 told us about the current round! */
1133 peer_entry->previous_round = GNUNET_YES;
1134 if (idx != estimate_index)
1136 /* do not transmit information for the previous round to this peer
1137 anymore (but allow current round) */
1140 /* got up-to-date information for current round, cancel transmission to
1141 * this peer altogether */
1142 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1144 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1145 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1147 if (NULL != peer_entry->th)
1149 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1150 peer_entry->th = NULL;
1154 if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits))
1156 if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES)) {
1157 peer_entry->previous_round = GNUNET_NO;
1159 /* push back our result now, that peer is spreading bad information... */
1160 if (NULL == peer_entry->th)
1162 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1163 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1164 peer_entry->transmit_task =
1165 GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry);
1167 /* Not closer than our most recent message, no need to do work here */
1168 GNUNET_STATISTICS_update (stats,
1169 "# flood messages ignored (had closer already)",
1173 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1175 GNUNET_break_op (0);
1178 GNUNET_assert (matching_bits >
1179 ntohl (size_estimate_messages[idx].matching_bits));
1180 /* Cancel transmission in the other direction, as this peer clearly has
1181 * up-to-date information already.
1183 peer_entry->previous_round = GNUNET_YES;
1184 if (idx == estimate_index)
1186 /* cancel any activity for current round */
1187 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1189 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1190 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1192 if (peer_entry->th != NULL)
1194 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1195 peer_entry->th = NULL;
1198 size_estimate_messages[idx] = *incoming_flood;
1199 size_estimate_messages[idx].hop_count =
1200 htonl (ntohl (incoming_flood->hop_count) + 1);
1202 GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max);
1203 GNUNET_STATISTICS_set (stats,
1204 "# estimated network diameter",
1205 hop_count_max, GNUNET_NO);
1207 /* have a new, better size estimate, inform clients */
1208 update_network_size_estimate ();
1211 GNUNET_CONTAINER_multipeermap_iterate (peers, &update_flood_times,
1219 * Method called whenever a peer connects. Sets up the PeerEntry and
1220 * schedules the initial size info transmission to this peer.
1222 * @param cls closure
1223 * @param peer peer identity this notification is about
1226 handle_core_connect (void *cls,
1227 const struct GNUNET_PeerIdentity *peer)
1229 struct NSEPeerEntry *peer_entry;
1231 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' connected to us\n",
1233 peer_entry = GNUNET_new (struct NSEPeerEntry);
1234 peer_entry->id = *peer;
1235 GNUNET_assert (GNUNET_OK ==
1236 GNUNET_CONTAINER_multipeermap_put (peers, peer,
1238 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1239 peer_entry->transmit_task =
1240 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1), &transmit_task_cb,
1242 GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO);
1247 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
1248 * any pending transmission requests to that peer.
1250 * @param cls closure
1251 * @param peer peer identity this notification is about
1254 handle_core_disconnect (void *cls,
1255 const struct GNUNET_PeerIdentity *peer)
1257 struct NSEPeerEntry *pos;
1259 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1260 "Peer `%s' disconnected from us\n",
1262 pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
1268 GNUNET_assert (GNUNET_YES ==
1269 GNUNET_CONTAINER_multipeermap_remove (peers, peer,
1271 if (pos->transmit_task != GNUNET_SCHEDULER_NO_TASK) {
1272 GNUNET_SCHEDULER_cancel (pos->transmit_task);
1273 pos->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1275 if (NULL != pos->th)
1277 GNUNET_CORE_notify_transmit_ready_cancel (pos->th);
1281 GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO);
1285 #if ENABLE_NSE_HISTOGRAM
1287 * Functions of this type are called to notify a successful transmission of the
1288 * message to the logger service
1291 * @param size the amount of data sent (ignored)
1294 flush_comp_cb (void *cls,
1297 GNUNET_TESTBED_LOGGER_disconnect (lh);
1304 * Task run during shutdown.
1310 shutdown_task (void *cls,
1311 const struct GNUNET_SCHEDULER_TaskContext *tc)
1313 if (GNUNET_SCHEDULER_NO_TASK != flood_task)
1315 GNUNET_SCHEDULER_cancel (flood_task);
1316 flood_task = GNUNET_SCHEDULER_NO_TASK;
1318 if (GNUNET_SCHEDULER_NO_TASK != proof_task)
1320 GNUNET_SCHEDULER_cancel (proof_task);
1321 proof_task = GNUNET_SCHEDULER_NO_TASK;
1322 write_proof (); /* remember progress */
1326 GNUNET_SERVER_notification_context_destroy (nc);
1329 if (NULL != core_api)
1331 GNUNET_CORE_disconnect (core_api);
1336 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1341 GNUNET_CONTAINER_multipeermap_destroy (peers);
1344 if (NULL != my_private_key)
1346 GNUNET_free (my_private_key);
1347 my_private_key = NULL;
1349 #if ENABLE_NSE_HISTOGRAM
1350 struct GNUNET_TIME_Relative timeout;
1351 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30);
1352 GNUNET_TESTBED_LOGGER_flush (lh, timeout, &flush_comp_cb, NULL);
1358 * Called on core init/fail.
1360 * @param cls service closure
1361 * @param identity the public identity of this peer
1364 core_init (void *cls,
1365 const struct GNUNET_PeerIdentity *identity)
1367 struct GNUNET_TIME_Absolute now;
1368 struct GNUNET_TIME_Absolute prev_time;
1370 if (NULL == identity)
1372 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n");
1373 GNUNET_SCHEDULER_shutdown ();
1377 memcmp (&my_identity, identity,
1378 sizeof (struct GNUNET_PeerIdentity)));
1379 now = GNUNET_TIME_absolute_get ();
1380 current_timestamp.abs_value_us =
1381 (now.abs_value_us / gnunet_nse_interval.rel_value_us) *
1382 gnunet_nse_interval.rel_value_us;
1384 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
1385 estimate_index = HISTORY_SIZE - 1;
1387 if (GNUNET_YES == check_proof_of_work (&my_identity.public_key, my_proof))
1389 int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1390 prev_time.abs_value_us =
1391 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us;
1392 setup_flood_message (idx, prev_time);
1393 setup_flood_message (estimate_index, current_timestamp);
1397 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
1398 (next_timestamp), &update_flood_message,
1404 * Handle network size estimate clients.
1406 * @param cls closure
1407 * @param server the initialized server
1408 * @param c configuration to use
1412 struct GNUNET_SERVER_Handle *server,
1413 const struct GNUNET_CONFIGURATION_Handle *c)
1415 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1416 {&handle_start_message, NULL, GNUNET_MESSAGE_TYPE_NSE_START,
1417 sizeof (struct GNUNET_MessageHeader)},
1420 static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
1421 {&handle_p2p_size_estimate, GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1422 sizeof (struct GNUNET_NSE_FloodMessage)},
1426 struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
1431 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "INTERVAL",
1432 &gnunet_nse_interval))
1434 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1436 GNUNET_SCHEDULER_shutdown ();
1440 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "WORKDELAY",
1443 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1444 "NSE", "WORKDELAY");
1445 GNUNET_SCHEDULER_shutdown ();
1449 GNUNET_CONFIGURATION_get_value_number (cfg, "NSE", "WORKBITS",
1450 &nse_work_required))
1452 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1454 GNUNET_SCHEDULER_shutdown ();
1457 if (nse_work_required >= sizeof (struct GNUNET_HashCode) * 8)
1459 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1462 _("Value is too large.\n"));
1463 GNUNET_SCHEDULER_shutdown ();
1467 #if ENABLE_NSE_HISTOGRAM
1468 if (NULL == (lh = GNUNET_TESTBED_LOGGER_connect (cfg)))
1470 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1471 "Cannot connect to the testbed logger. Exiting.\n");
1472 GNUNET_SCHEDULER_shutdown ();
1477 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1479 pk = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
1480 GNUNET_assert (NULL != pk);
1481 my_private_key = pk;
1482 GNUNET_CRYPTO_eddsa_key_get_public (my_private_key,
1483 &my_identity.public_key);
1485 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
1487 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "PROOFFILE");
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_multipeermap_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 core_api = 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 == core_api)
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 */