2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011, 2012, 2013, 2016 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
17 * @file nse/gnunet-service-nse.c
18 * @brief network size estimation service
19 * @author Nathan Evans
20 * @author Christian Grothoff
22 * The purpose of this service is to estimate the size of the network.
23 * Given a specified interval, each peer hashes the most recent
24 * timestamp which is evenly divisible by that interval. This hash is
25 * compared in distance to the peer identity to choose an offset. The
26 * closer the peer identity to the hashed timestamp, the earlier the
27 * peer sends out a "nearest peer" message. The closest peer's
28 * message should thus be received before any others, which stops
29 * those peer from sending their messages at a later duration. So
30 * every peer should receive the same nearest peer message, and from
31 * this can calculate the expected number of peers in the network.
35 #include "gnunet_util_lib.h"
36 #include "gnunet_constants.h"
37 #include "gnunet_protocols.h"
38 #include "gnunet_signatures.h"
39 #include "gnunet_statistics_service.h"
40 #include "gnunet_core_service.h"
41 #include "gnunet_nse_service.h"
42 #if ENABLE_NSE_HISTOGRAM
43 #include "gnunet_testbed_logger_service.h"
51 * Should messages be delayed randomly? This option should be set to
52 * #GNUNET_NO only for experiments, not in production.
54 #define USE_RANDOM_DELAYS GNUNET_YES
57 * Generate extensive debug-level log messages?
59 #define DEBUG_NSE GNUNET_NO
62 * Over how many values do we calculate the weighted average?
64 #define HISTORY_SIZE 64
67 * Message priority to use.
69 #define NSE_PRIORITY GNUNET_CORE_PRIO_CRITICAL_CONTROL
72 #define log2(a) (log(a)/log(2))
76 * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits.
78 static unsigned long long nse_work_required;
81 * Interval for sending network size estimation flood requests.
83 static struct GNUNET_TIME_Relative gnunet_nse_interval;
86 * Interval between proof find runs.
88 static struct GNUNET_TIME_Relative proof_find_delay;
90 #if ENABLE_NSE_HISTOGRAM
93 * Handle to test if testbed logger service is running or not
95 struct GNUNET_CLIENT_TestHandle *logger_test;
98 * Handle for writing when we received messages to disk.
100 static struct GNUNET_TESTBED_LOGGER_Handle *lh;
103 * Handle for writing message received timestamp information to disk.
105 static struct GNUNET_BIO_WriteHandle *histogram;
111 * Per-peer information.
117 * Core handle for sending messages to this peer.
119 struct GNUNET_MQ_Handle *mq;
122 * What is the identity of the peer?
124 const struct GNUNET_PeerIdentity *id;
127 * Task scheduled to send message to this peer.
129 struct GNUNET_SCHEDULER_Task *transmit_task;
132 * Did we receive or send a message about the previous round
133 * to this peer yet? #GNUNET_YES if the previous round has
134 * been taken care of.
138 #if ENABLE_NSE_HISTOGRAM
141 * Amount of messages received from this peer on this round.
143 unsigned int received_messages;
146 * Amount of messages transmitted to this peer on this round.
148 unsigned int transmitted_messages;
151 * Which size did we tell the peer the network is?
153 unsigned int last_transmitted_size;
160 GNUNET_NETWORK_STRUCT_BEGIN
163 * Network size estimate reply; sent when "this"
164 * peer's timer has run out before receiving a
165 * valid reply from another peer.
167 struct GNUNET_NSE_FloodMessage
170 * Type: #GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD
172 struct GNUNET_MessageHeader header;
175 * Number of hops this message has taken so far.
177 uint32_t hop_count GNUNET_PACKED;
182 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
185 * The current timestamp value (which all
186 * peers should agree on).
188 struct GNUNET_TIME_AbsoluteNBO timestamp;
191 * Number of matching bits between the hash
192 * of timestamp and the initiator's public
195 uint32_t matching_bits GNUNET_PACKED;
198 * Public key of the originator.
200 struct GNUNET_PeerIdentity origin;
203 * Proof of work, causing leading zeros when hashed with pkey.
205 uint64_t proof_of_work GNUNET_PACKED;
208 * Signature (over range specified in purpose).
210 struct GNUNET_CRYPTO_EddsaSignature signature;
212 GNUNET_NETWORK_STRUCT_END
215 * Handle to our current configuration.
217 static const struct GNUNET_CONFIGURATION_Handle *cfg;
220 * Handle to the statistics service.
222 static struct GNUNET_STATISTICS_Handle *stats;
225 * Handle to the core service.
227 static struct GNUNET_CORE_Handle *core_api;
230 * Map of all connected peers.
232 static struct GNUNET_CONTAINER_MultiPeerMap *peers;
235 * The current network size estimate. Number of bits matching on
238 static double current_size_estimate;
241 * The standard deviation of the last #HISTORY_SIZE network
244 static double current_std_dev = NAN;
247 * Current hop counter estimate (estimate for network diameter).
249 static uint32_t hop_count_max;
252 * Message for the next round, if we got any.
254 static struct GNUNET_NSE_FloodMessage next_message;
257 * Array of recent size estimate messages.
259 static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE];
262 * Index of most recent estimate.
264 static unsigned int estimate_index;
267 * Number of valid entries in the history.
269 static unsigned int estimate_count;
272 * Task scheduled to update our flood message for the next round.
274 static struct GNUNET_SCHEDULER_Task *flood_task;
277 * Task scheduled to compute our proof.
279 static struct GNUNET_SCHEDULER_Task *proof_task;
282 * Notification context, simplifies client broadcasts.
284 static struct GNUNET_NotificationContext *nc;
287 * The next major time.
289 static struct GNUNET_TIME_Absolute next_timestamp;
292 * The current major time.
294 static struct GNUNET_TIME_Absolute current_timestamp;
297 * The private key of this peer.
299 static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
302 * The peer identity of this peer.
304 static struct GNUNET_PeerIdentity my_identity;
307 * Proof of work for this peer.
309 static uint64_t my_proof;
313 * Initialize a message to clients with the current network
316 * @param em message to fill in
319 setup_estimate_message (struct GNUNET_NSE_ClientMessage *em)
329 /* Weighted incremental algorithm for stddev according to West (1979) */
341 for (unsigned int i = 0; i < estimate_count; i++)
343 unsigned int j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
345 val = htonl (size_estimate_messages[j].matching_bits);
346 weight = estimate_count + 1 - i;
348 temp = weight + sumweight;
350 r = q * weight / temp;
352 sum += sumweight * q * r;
355 if (estimate_count > 0)
356 variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0);
358 /* trivial version for debugging */
361 /* non-weighted trivial version */
367 for (unsigned int i = 0; i < estimate_count; i++)
369 unsigned int j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
371 val = htonl (size_estimate_messages[j].matching_bits);
375 if (0 != estimate_count)
377 mean = sum / estimate_count;
378 variance = (vsq - mean * sum) / (estimate_count - 1.0); // terrible for numerical stability...
382 std_dev = sqrt (variance);
384 std_dev = variance; /* must be infinity due to estimate_count == 0 */
385 current_std_dev = std_dev;
386 current_size_estimate = mean;
388 em->header.size = htons (sizeof (struct GNUNET_NSE_ClientMessage));
389 em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE);
390 em->reserved = htonl (0);
391 em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
393 double se = mean - 0.332747;
394 unsigned int j = GNUNET_CONTAINER_multipeermap_size (peers);
396 j = 1; /* Avoid log2(0); can only happen if CORE didn't report
397 connection to self yet */
399 em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se,
401 em->std_deviation = GNUNET_hton_double (std_dev);
402 GNUNET_STATISTICS_set (stats,
403 "# nodes in the network (estimate)",
404 (uint64_t) pow (2, GNUNET_MAX (se,
412 * Handler for START message from client, triggers an
413 * immediate current network estimate notification.
414 * Also, we remember the client for updates upon future
415 * estimate measurements.
417 * @param cls client who sent the message
418 * @param message the message received
421 handle_start (void *cls,
422 const struct GNUNET_MessageHeader *message)
424 struct GNUNET_SERVICE_Client *client = cls;
425 struct GNUNET_MQ_Handle *mq;
426 struct GNUNET_NSE_ClientMessage em;
427 struct GNUNET_MQ_Envelope *env;
429 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
430 "Received START message from client\n");
431 mq = GNUNET_SERVICE_client_get_mq (client);
432 GNUNET_notification_context_add (nc,
434 setup_estimate_message (&em);
435 env = GNUNET_MQ_msg_copy (&em.header);
438 GNUNET_SERVICE_client_continue (client);
443 * How long should we delay a message to go the given number of
446 * @param matching_bits number of matching bits to consider
449 get_matching_bits_delay (uint32_t matching_bits)
451 /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */
452 // S is next_timestamp (ignored in return value)
453 // f is frequency (gnunet_nse_interval)
454 // x is matching_bits
455 // p' is current_size_estimate
456 return ((double) gnunet_nse_interval.rel_value_us / (double) 2.0) -
457 ((gnunet_nse_interval.rel_value_us / M_PI) *
458 atan (matching_bits - current_size_estimate));
463 * What delay randomization should we apply for a given number of matching bits?
465 * @param matching_bits number of matching bits
466 * @return random delay to apply
468 static struct GNUNET_TIME_Relative
469 get_delay_randomization (uint32_t matching_bits)
471 #if USE_RANDOM_DELAYS
472 struct GNUNET_TIME_Relative ret;
476 d = get_matching_bits_delay (matching_bits);
477 i = (uint32_t) (d / (double) (hop_count_max + 1));
478 ret.rel_value_us = i;
479 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
480 "Randomizing flood using latencies up to %s\n",
481 GNUNET_STRINGS_relative_time_to_string (ret,
483 ret.rel_value_us = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1);
486 return GNUNET_TIME_UNIT_ZERO;
492 * Calculate the 'proof-of-work' hash (an expensive hash).
494 * @param buf data to hash
495 * @param buf_len number of bytes in @a buf
496 * @param result where to write the resulting hash
499 pow_hash (const void *buf,
501 struct GNUNET_HashCode *result)
504 gcry_kdf_derive (buf, buf_len,
507 "gnunet-proof-of-work",
508 strlen ("gnunet-proof-of-work"),
509 2 /* iterations; keep cost of individual op small */,
510 sizeof (struct GNUNET_HashCode),
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,
564 ret = GNUNET_TIME_UNIT_ZERO;
566 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
567 "Transmitting previous round behind schedule in %s\n",
568 GNUNET_STRINGS_relative_time_to_string (ret,
572 /* current round is based on best-known matching_bits */
574 ntohl (size_estimate_messages[estimate_index].matching_bits);
575 dist_delay = get_matching_bits_delay (matching_bits);
576 dist_delay += get_delay_randomization (matching_bits).rel_value_us;
577 ret.rel_value_us = (uint64_t) dist_delay;
578 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
579 "For round %s, delay for %u matching bits is %s\n",
580 GNUNET_STRINGS_absolute_time_to_string (current_timestamp),
581 (unsigned int) matching_bits,
582 GNUNET_STRINGS_relative_time_to_string (ret,
584 /* now consider round start time and add delay to it */
585 tgt = GNUNET_TIME_absolute_add (current_timestamp,
587 return GNUNET_TIME_absolute_get_remaining (tgt);
590 return GNUNET_TIME_UNIT_FOREVER_REL;
595 * Task that triggers a NSE P2P transmission.
597 * @param cls the `struct NSEPeerEntry *`
600 transmit_task_cb (void *cls)
602 struct NSEPeerEntry *peer_entry = cls;
604 struct GNUNET_MQ_Envelope *env;
606 peer_entry->transmit_task = NULL;
607 idx = estimate_index;
608 if (GNUNET_NO == peer_entry->previous_round)
610 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
611 peer_entry->previous_round = GNUNET_YES;
612 peer_entry->transmit_task
613 = GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0),
617 if ((0 == ntohl (size_estimate_messages[idx].hop_count)) &&
618 (NULL != proof_task))
620 GNUNET_STATISTICS_update (stats,
621 "# flood messages not generated (no proof yet)",
626 if (0 == ntohs (size_estimate_messages[idx].header.size))
628 GNUNET_STATISTICS_update (stats,
629 "# flood messages not generated (lack of history)",
634 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
635 "In round %s, sending to `%s' estimate with %u bits\n",
636 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (size_estimate_messages[idx].timestamp)),
637 GNUNET_i2s (peer_entry->id),
638 (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
639 if (0 == ntohl (size_estimate_messages[idx].hop_count))
640 GNUNET_STATISTICS_update (stats,
641 "# flood messages started",
644 GNUNET_STATISTICS_update (stats,
645 "# flood messages transmitted",
648 #if ENABLE_NSE_HISTOGRAM
649 peer_entry->transmitted_messages++;
650 peer_entry->last_transmitted_size
651 = ntohl(size_estimate_messages[idx].matching_bits);
653 env = GNUNET_MQ_msg_copy (&size_estimate_messages[idx].header);
654 GNUNET_MQ_send (peer_entry->mq,
660 * We've sent on our flood message or one that we received which was
661 * validated and closer than ours. Update the global list of recent
662 * messages and the average. Also re-broadcast the message to any
666 update_network_size_estimate ()
668 struct GNUNET_NSE_ClientMessage em;
670 setup_estimate_message (&em);
671 GNUNET_notification_context_broadcast (nc,
678 * Setup a flood message in our history array at the given
679 * slot offset for the given timestamp.
681 * @param slot index to use
682 * @param ts timestamp to use
685 setup_flood_message (unsigned int slot,
686 struct GNUNET_TIME_Absolute ts)
688 struct GNUNET_NSE_FloodMessage *fm;
689 uint32_t matching_bits;
691 matching_bits = get_matching_bits (ts,
693 fm = &size_estimate_messages[slot];
694 fm->header.size = htons (sizeof (struct GNUNET_NSE_FloodMessage));
695 fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
696 fm->hop_count = htonl (0);
697 fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
699 htonl (sizeof (struct GNUNET_NSE_FloodMessage) -
700 sizeof (struct GNUNET_MessageHeader) - sizeof (uint32_t) -
701 sizeof (struct GNUNET_CRYPTO_EddsaSignature));
702 fm->matching_bits = htonl (matching_bits);
703 fm->timestamp = GNUNET_TIME_absolute_hton (ts);
704 fm->origin = my_identity;
705 fm->proof_of_work = my_proof;
706 if (nse_work_required > 0)
707 GNUNET_assert (GNUNET_OK ==
708 GNUNET_CRYPTO_eddsa_sign (my_private_key,
712 memset (&fm->signature,
714 sizeof (fm->signature));
719 * Schedule transmission for the given peer for the current round based
720 * on what we know about the desired delay.
723 * @param key hash of peer identity
724 * @param value the `struct NSEPeerEntry`
725 * @return #GNUNET_OK (continue to iterate)
728 schedule_current_round (void *cls,
729 const struct GNUNET_PeerIdentity * key,
732 struct NSEPeerEntry *peer_entry = value;
733 struct GNUNET_TIME_Relative delay;
735 if (NULL != peer_entry->transmit_task)
737 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
738 peer_entry->previous_round = GNUNET_NO;
740 #if ENABLE_NSE_HISTOGRAM
741 if (peer_entry->received_messages > 1)
742 GNUNET_STATISTICS_update(stats,
744 peer_entry->received_messages - 1,
746 peer_entry->transmitted_messages = 0;
747 peer_entry->last_transmitted_size = 0;
748 peer_entry->received_messages = 0;
751 get_transmit_delay ((GNUNET_NO == peer_entry->previous_round) ? -1 : 0);
752 peer_entry->transmit_task =
753 GNUNET_SCHEDULER_add_delayed (delay,
761 * Update our flood message to be sent (and our timestamps).
766 update_flood_message (void *cls)
768 struct GNUNET_TIME_Relative offset;
772 offset = GNUNET_TIME_absolute_get_remaining (next_timestamp);
773 if (0 != offset.rel_value_us)
775 /* somehow run early, delay more */
777 GNUNET_SCHEDULER_add_delayed (offset,
778 &update_flood_message,
782 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
783 if (estimate_count < HISTORY_SIZE)
785 current_timestamp = next_timestamp;
787 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
788 if ( (current_timestamp.abs_value_us ==
789 GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value_us) &&
790 (get_matching_bits (current_timestamp, &my_identity) <
791 ntohl(next_message.matching_bits)) )
793 /* we received a message for this round way early, use it! */
794 size_estimate_messages[estimate_index] = next_message;
795 size_estimate_messages[estimate_index].hop_count =
796 htonl (1 + ntohl (next_message.hop_count));
799 setup_flood_message (estimate_index,
801 next_message.matching_bits = htonl (0); /* reset for 'next' round */
803 for (i = 0; i < HISTORY_SIZE; i++)
804 hop_count_max = GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count),
806 GNUNET_CONTAINER_multipeermap_iterate (peers,
807 &schedule_current_round,
810 = GNUNET_SCHEDULER_add_at (next_timestamp,
811 &update_flood_message,
817 * Count the leading zeroes in hash.
819 * @param hash to count leading zeros in
820 * @return the number of leading zero bits.
823 count_leading_zeroes (const struct GNUNET_HashCode *hash)
825 unsigned int hash_count;
828 while (0 == GNUNET_CRYPTO_hash_get_bit (hash,
836 * Check whether the given public key and integer are a valid proof of
839 * @param pkey the public key
840 * @param val the integer
841 * @return #GNUNET_YES if valid, #GNUNET_NO if not
844 check_proof_of_work (const struct GNUNET_CRYPTO_EddsaPublicKey *pkey,
847 char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
848 sizeof (val)] GNUNET_ALIGN;
849 struct GNUNET_HashCode result;
854 GNUNET_memcpy (&buf[sizeof (val)],
856 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
860 return (count_leading_zeroes (&result) >=
861 nse_work_required) ? GNUNET_YES : GNUNET_NO;
866 * Write our current proof to disk.
874 GNUNET_CONFIGURATION_get_value_filename (cfg,
879 if (sizeof (my_proof) !=
880 GNUNET_DISK_fn_write (proof,
883 GNUNET_DISK_PERM_USER_READ |
884 GNUNET_DISK_PERM_USER_WRITE))
885 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
893 * Find our proof of work.
895 * @param cls closure (unused)
898 find_proof (void *cls)
900 #define ROUND_SIZE 10
902 char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
903 sizeof (uint64_t)] GNUNET_ALIGN;
904 struct GNUNET_HashCode result;
908 GNUNET_memcpy (&buf[sizeof (uint64_t)], &my_identity,
909 sizeof (struct GNUNET_PeerIdentity));
912 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
914 GNUNET_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,
931 "Testing proofs currently at %llu\n",
932 (unsigned long long) counter);
933 /* remember progress every 100 rounds */
942 GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
943 GNUNET_SCHEDULER_PRIORITY_IDLE,
949 * An incoming flood message has been received which claims
950 * to have more bits matching than any we know in this time
951 * period. Verify the signature and/or proof of work.
953 * @param incoming_flood the message to verify
954 * @return #GNUNET_YES if the message is verified
955 * #GNUNET_NO if the key/signature don't verify
958 verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood)
961 check_proof_of_work (&incoming_flood->origin.public_key,
962 incoming_flood->proof_of_work))
964 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
965 "Proof of work invalid: %llu!\n",
967 GNUNET_ntohll (incoming_flood->proof_of_work));
971 if ((nse_work_required > 0) &&
973 GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
974 &incoming_flood->purpose,
975 &incoming_flood->signature,
976 &incoming_flood->origin.public_key)))
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_PeerIdentity *key,
999 struct NSEPeerEntry *exclude = cls;
1000 struct NSEPeerEntry *peer_entry = value;
1001 struct GNUNET_TIME_Relative delay;
1003 if (peer_entry == exclude)
1004 return GNUNET_OK; /* trigger of the update */
1005 if (GNUNET_NO == peer_entry->previous_round)
1007 /* still stuck in previous round, no point to update, check that
1008 * we are active here though... */
1009 if (NULL == peer_entry->transmit_task)
1015 if (NULL != peer_entry->transmit_task)
1017 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1018 peer_entry->transmit_task = NULL;
1020 delay = get_transmit_delay (0);
1021 peer_entry->transmit_task =
1022 GNUNET_SCHEDULER_add_delayed (delay,
1023 &transmit_task_cb, peer_entry);
1029 * Core handler for size estimate flooding messages.
1031 * @param cls peer this message is from
1032 * @param incoming_flood received message
1035 handle_p2p_estimate (void *cls,
1036 const struct GNUNET_NSE_FloodMessage *incoming_flood)
1038 struct NSEPeerEntry *peer_entry = cls;
1039 struct GNUNET_TIME_Absolute ts;
1040 uint32_t matching_bits;
1043 #if ENABLE_NSE_HISTOGRAM
1047 t = GNUNET_TIME_absolute_get().abs_value_us;
1049 GNUNET_TESTBED_LOGGER_write (lh, &t, sizeof (uint64_t));
1050 if (NULL != histogram)
1051 GNUNET_BIO_write_int64 (histogram, t);
1054 GNUNET_STATISTICS_update (stats,
1055 "# flood messages received",
1058 matching_bits = ntohl (incoming_flood->matching_bits);
1063 struct GNUNET_PeerIdentity os;
1065 GNUNET_snprintf (origin,
1068 GNUNET_i2s (&incoming_flood->origin));
1069 GNUNET_snprintf (pred,
1072 GNUNET_i2s (peer_entry->id));
1073 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1074 "Flood at %s from `%s' via `%s' at `%s' with bits %u\n",
1075 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp)),
1078 GNUNET_i2s (&my_identity),
1079 (unsigned int) matching_bits);
1083 #if ENABLE_NSE_HISTOGRAM
1084 peer_entry->received_messages++;
1085 if (peer_entry->transmitted_messages > 0 &&
1086 peer_entry->last_transmitted_size >= matching_bits)
1087 GNUNET_STATISTICS_update(stats,
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; /* ignore, simply too early/late */
1104 verify_message_crypto (incoming_flood))
1106 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1107 "Peer %s is likely ill-configured!\n",
1108 GNUNET_i2s (peer_entry->id));
1109 GNUNET_break_op (0);
1112 next_message = *incoming_flood;
1117 GNUNET_STATISTICS_update (stats,
1118 "# flood messages discarded (clock skew too large)",
1122 if (0 == (memcmp (peer_entry->id,
1124 sizeof (struct GNUNET_PeerIdentity))))
1126 /* send to self, update our own estimate IF this also comes from us! */
1128 memcmp (&incoming_flood->origin,
1129 &my_identity, sizeof (my_identity)))
1130 update_network_size_estimate ();
1133 if (matching_bits ==
1134 ntohl (size_estimate_messages[idx].matching_bits))
1136 /* Cancel transmission in the other direction, as this peer clearly has
1137 up-to-date information already. Even if we didn't talk to this peer in
1138 the previous round, we should no longer send it stale information as it
1139 told us about the current round! */
1140 peer_entry->previous_round = GNUNET_YES;
1141 if (idx != estimate_index)
1143 /* do not transmit information for the previous round to this peer
1144 anymore (but allow current round) */
1147 /* got up-to-date information for current round, cancel transmission to
1148 * this peer altogether */
1149 if (NULL != peer_entry->transmit_task)
1151 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1152 peer_entry->transmit_task = NULL;
1156 if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits))
1158 if ( (idx < estimate_index) &&
1159 (peer_entry->previous_round == GNUNET_YES))
1161 peer_entry->previous_round = GNUNET_NO;
1163 /* push back our result now, that peer is spreading bad information... */
1164 if (NULL != peer_entry->transmit_task)
1165 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1166 peer_entry->transmit_task
1167 = GNUNET_SCHEDULER_add_now (&transmit_task_cb,
1169 /* Not closer than our most recent message, no need to do work here */
1170 GNUNET_STATISTICS_update (stats,
1171 "# flood messages ignored (had closer already)",
1177 verify_message_crypto (incoming_flood))
1179 GNUNET_break_op (0);
1182 GNUNET_assert (matching_bits >
1183 ntohl (size_estimate_messages[idx].matching_bits));
1184 /* Cancel transmission in the other direction, as this peer clearly has
1185 * up-to-date information already.
1187 peer_entry->previous_round = GNUNET_YES;
1188 if (idx == estimate_index)
1190 /* cancel any activity for current round */
1191 if (NULL != peer_entry->transmit_task)
1193 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1194 peer_entry->transmit_task = NULL;
1197 size_estimate_messages[idx] = *incoming_flood;
1198 size_estimate_messages[idx].hop_count =
1199 htonl (ntohl (incoming_flood->hop_count) + 1);
1201 GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1,
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,
1212 &update_flood_times,
1218 * Method called whenever a peer connects. Sets up the PeerEntry and
1219 * schedules the initial size info transmission to this peer.
1221 * @param cls closure
1222 * @param peer peer identity this notification is about
1225 handle_core_connect (void *cls,
1226 const struct GNUNET_PeerIdentity *peer,
1227 struct GNUNET_MQ_Handle *mq)
1229 struct NSEPeerEntry *peer_entry;
1233 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1234 "Peer `%s' connected to us\n",
1236 /* set our default transmission options */
1237 extra = GNUNET_CORE_get_mq_options (GNUNET_NO,
1240 GNUNET_MQ_set_options (mq,
1243 /* create our peer entry for this peer */
1244 peer_entry = GNUNET_new (struct NSEPeerEntry);
1245 peer_entry->id = peer;
1246 peer_entry->mq = mq;
1247 GNUNET_assert (GNUNET_OK ==
1248 GNUNET_CONTAINER_multipeermap_put (peers,
1251 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1252 peer_entry->transmit_task =
1253 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1),
1256 GNUNET_STATISTICS_update (stats,
1257 "# peers connected",
1265 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
1266 * any pending transmission requests to that peer.
1268 * @param cls closure
1269 * @param peer peer identity this notification is about
1270 * @parma internal_cls the `struct NSEPeerEntry` for the @a peer
1273 handle_core_disconnect (void *cls,
1274 const struct GNUNET_PeerIdentity *peer,
1277 struct NSEPeerEntry *pos = internal_cls;
1279 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1280 "Peer `%s' disconnected from us\n",
1282 GNUNET_assert (GNUNET_YES ==
1283 GNUNET_CONTAINER_multipeermap_remove (peers,
1286 if (NULL != pos->transmit_task)
1288 GNUNET_SCHEDULER_cancel (pos->transmit_task);
1289 pos->transmit_task = NULL;
1292 GNUNET_STATISTICS_update (stats,
1293 "# peers connected",
1299 #if ENABLE_NSE_HISTOGRAM
1301 * Functions of this type are called to notify a successful transmission of the
1302 * message to the logger service
1305 * @param size the amount of data sent (ignored)
1308 flush_comp_cb (void *cls,
1311 GNUNET_TESTBED_LOGGER_disconnect (lh);
1318 * Task run during shutdown.
1323 shutdown_task (void *cls)
1325 if (NULL != flood_task)
1327 GNUNET_SCHEDULER_cancel (flood_task);
1330 if (NULL != proof_task)
1332 GNUNET_SCHEDULER_cancel (proof_task);
1334 write_proof (); /* remember progress */
1338 GNUNET_notification_context_destroy (nc);
1341 if (NULL != core_api)
1343 GNUNET_CORE_disconnect (core_api);
1348 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1353 GNUNET_CONTAINER_multipeermap_destroy (peers);
1356 if (NULL != my_private_key)
1358 GNUNET_free (my_private_key);
1359 my_private_key = NULL;
1361 #if ENABLE_NSE_HISTOGRAM
1362 if (NULL != logger_test)
1364 GNUNET_CLIENT_service_test_cancel (logger_test);
1369 GNUNET_TESTBED_LOGGER_flush (lh,
1373 if (NULL != histogram)
1375 GNUNET_BIO_write_close (histogram);
1383 * Called on core init/fail.
1385 * @param cls service closure
1386 * @param identity the public identity of this peer
1389 core_init (void *cls,
1390 const struct GNUNET_PeerIdentity *identity)
1392 struct GNUNET_TIME_Absolute now;
1393 struct GNUNET_TIME_Absolute prev_time;
1395 if (NULL == identity)
1397 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1398 "Connection to core FAILED!\n");
1399 GNUNET_SCHEDULER_shutdown ();
1403 memcmp (&my_identity,
1405 sizeof (struct GNUNET_PeerIdentity)));
1406 now = GNUNET_TIME_absolute_get ();
1407 current_timestamp.abs_value_us =
1408 (now.abs_value_us / gnunet_nse_interval.rel_value_us) *
1409 gnunet_nse_interval.rel_value_us;
1411 GNUNET_TIME_absolute_add (current_timestamp,
1412 gnunet_nse_interval);
1413 estimate_index = HISTORY_SIZE - 1;
1416 check_proof_of_work (&my_identity.public_key,
1419 int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1420 prev_time.abs_value_us =
1421 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us;
1422 setup_flood_message (idx,
1424 setup_flood_message (estimate_index,
1429 = GNUNET_SCHEDULER_add_at (next_timestamp,
1430 &update_flood_message,
1435 #if ENABLE_NSE_HISTOGRAM
1437 * Function called with the status of the testbed logger service
1440 * @param status #GNUNET_YES if the service is running,
1441 * #GNUNET_NO if the service is not running
1442 * #GNUNET_SYSERR if the configuration is invalid
1445 status_cb (void *cls,
1449 if (GNUNET_YES != status)
1451 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1452 "Testbed logger not running\n");
1455 if (NULL == (lh = GNUNET_TESTBED_LOGGER_connect (cfg)))
1457 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1458 "Cannot connect to the testbed logger. Exiting.\n");
1459 GNUNET_SCHEDULER_shutdown ();
1466 * Handle network size estimate clients.
1468 * @param cls closure
1469 * @param c configuration to use
1470 * @param service the initialized service
1474 const struct GNUNET_CONFIGURATION_Handle *c,
1475 struct GNUNET_SERVICE_Handle *service)
1477 struct GNUNET_MQ_MessageHandler core_handlers[] = {
1478 GNUNET_MQ_hd_fixed_size (p2p_estimate,
1479 GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1480 struct GNUNET_NSE_FloodMessage,
1482 GNUNET_MQ_handler_end ()
1485 struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
1489 GNUNET_CONFIGURATION_get_value_time (cfg,
1492 &gnunet_nse_interval))
1494 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1497 GNUNET_SCHEDULER_shutdown ();
1501 GNUNET_CONFIGURATION_get_value_time (cfg,
1506 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1509 GNUNET_SCHEDULER_shutdown ();
1513 GNUNET_CONFIGURATION_get_value_number (cfg,
1516 &nse_work_required))
1518 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1521 GNUNET_SCHEDULER_shutdown ();
1524 if (nse_work_required >= sizeof (struct GNUNET_HashCode) * 8)
1526 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1529 _("Value is too large.\n"));
1530 GNUNET_SCHEDULER_shutdown ();
1534 #if ENABLE_NSE_HISTOGRAM
1536 char *histogram_dir;
1540 GNUNET_CONFIGURATION_get_value_filename (cfg,
1545 GNUNET_assert (0 < GNUNET_asprintf (&histogram_fn,
1548 GNUNET_free (histogram_dir);
1549 histogram = GNUNET_BIO_write_open (histogram_fn);
1550 if (NULL == histogram)
1551 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1552 "Unable to open histogram file `%s'\n",
1554 GNUNET_free (histogram_fn);
1557 GNUNET_CLIENT_service_test ("testbed-logger",
1559 GNUNET_TIME_UNIT_SECONDS,
1566 GNUNET_SCHEDULER_add_shutdown (&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,
1579 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1582 GNUNET_free (my_private_key);
1583 my_private_key = NULL;
1584 GNUNET_SCHEDULER_shutdown ();
1587 if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) ||
1588 (sizeof (my_proof) !=
1589 GNUNET_DISK_fn_read (proof,
1591 sizeof (my_proof))))
1593 GNUNET_free (proof);
1595 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1599 peers = GNUNET_CONTAINER_multipeermap_create (128,
1601 nc = GNUNET_notification_context_create (1);
1602 /* Connect to core service and register core handlers */
1603 core_api = GNUNET_CORE_connect (cfg, /* Main configuration */
1604 NULL, /* Closure passed to functions */
1605 &core_init, /* Call core_init once connected */
1606 &handle_core_connect, /* Handle connects */
1607 &handle_core_disconnect, /* Handle disconnects */
1608 core_handlers); /* Register these handlers */
1609 if (NULL == core_api)
1611 GNUNET_SCHEDULER_shutdown ();
1614 stats = GNUNET_STATISTICS_create ("nse",
1620 * Callback called when a client connects to the service.
1622 * @param cls closure for the service
1623 * @param c the new client that connected to the service
1624 * @param mq the message queue used to send messages to the client
1628 client_connect_cb (void *cls,
1629 struct GNUNET_SERVICE_Client *c,
1630 struct GNUNET_MQ_Handle *mq)
1637 * Callback called when a client disconnected from the service
1639 * @param cls closure for the service
1640 * @param c the client that disconnected
1641 * @param internal_cls should be equal to @a c
1644 client_disconnect_cb (void *cls,
1645 struct GNUNET_SERVICE_Client *c,
1648 GNUNET_assert (c == internal_cls);
1653 * Define "main" method using service macro.
1657 GNUNET_SERVICE_OPTION_NONE,
1660 &client_disconnect_cb,
1662 GNUNET_MQ_hd_fixed_size (start,
1663 GNUNET_MESSAGE_TYPE_NSE_START,
1664 struct GNUNET_MessageHeader,
1666 GNUNET_MQ_handler_end ());
1669 #if defined(LINUX) && defined(__GLIBC__)
1673 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1675 void __attribute__ ((constructor))
1676 GNUNET_ARM_memory_init ()
1678 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1679 mallopt (M_TOP_PAD, 1 * 1024);
1686 /* end of gnunet-service-nse.c */