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 Affero 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.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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. No real rush, reliability not
72 * required. Corking OK.
74 #define NSE_PRIORITY \
75 (GNUNET_MQ_PRIO_BACKGROUND | GNUNET_MQ_PREF_UNRELIABLE | \
76 GNUNET_MQ_PREF_CORK_ALLOWED)
79 #define log2(a) (log (a) / log (2))
83 * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits.
85 static unsigned long long nse_work_required;
88 * Interval for sending network size estimation flood requests.
90 static struct GNUNET_TIME_Relative gnunet_nse_interval;
93 * Interval between proof find runs.
95 static struct GNUNET_TIME_Relative proof_find_delay;
97 #if ENABLE_NSE_HISTOGRAM
100 * Handle to test if testbed logger service is running or not
102 struct GNUNET_CLIENT_TestHandle *logger_test;
105 * Handle for writing when we received messages to disk.
107 static struct GNUNET_TESTBED_LOGGER_Handle *lh;
110 * Handle for writing message received timestamp information to disk.
112 static struct GNUNET_BIO_WriteHandle *histogram;
118 * Per-peer information.
124 * Core handle for sending messages to this peer.
126 struct GNUNET_MQ_Handle *mq;
129 * What is the identity of the peer?
131 const struct GNUNET_PeerIdentity *id;
134 * Task scheduled to send message to this peer.
136 struct GNUNET_SCHEDULER_Task *transmit_task;
139 * Did we receive or send a message about the previous round
140 * to this peer yet? #GNUNET_YES if the previous round has
141 * been taken care of.
145 #if ENABLE_NSE_HISTOGRAM
148 * Amount of messages received from this peer on this round.
150 unsigned int received_messages;
153 * Amount of messages transmitted to this peer on this round.
155 unsigned int transmitted_messages;
158 * Which size did we tell the peer the network is?
160 unsigned int last_transmitted_size;
166 GNUNET_NETWORK_STRUCT_BEGIN
169 * Network size estimate reply; sent when "this"
170 * peer's timer has run out before receiving a
171 * valid reply from another peer.
173 struct GNUNET_NSE_FloodMessage
176 * Type: #GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD
178 struct GNUNET_MessageHeader header;
181 * Number of hops this message has taken so far.
183 uint32_t hop_count GNUNET_PACKED;
188 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
191 * The current timestamp value (which all
192 * peers should agree on).
194 struct GNUNET_TIME_AbsoluteNBO timestamp;
197 * Number of matching bits between the hash
198 * of timestamp and the initiator's public
201 uint32_t matching_bits GNUNET_PACKED;
204 * Public key of the originator.
206 struct GNUNET_PeerIdentity origin;
209 * Proof of work, causing leading zeros when hashed with pkey.
211 uint64_t proof_of_work GNUNET_PACKED;
214 * Signature (over range specified in purpose).
216 struct GNUNET_CRYPTO_EddsaSignature signature;
218 GNUNET_NETWORK_STRUCT_END
221 * Handle to our current configuration.
223 static const struct GNUNET_CONFIGURATION_Handle *cfg;
226 * Handle to the statistics service.
228 static struct GNUNET_STATISTICS_Handle *stats;
231 * Handle to the core service.
233 static struct GNUNET_CORE_Handle *core_api;
236 * Map of all connected peers.
238 static struct GNUNET_CONTAINER_MultiPeerMap *peers;
241 * The current network size estimate. Number of bits matching on
244 static double current_size_estimate;
247 * The standard deviation of the last #HISTORY_SIZE network
250 static double current_std_dev = NAN;
253 * Current hop counter estimate (estimate for network diameter).
255 static uint32_t hop_count_max;
258 * Message for the next round, if we got any.
260 static struct GNUNET_NSE_FloodMessage next_message;
263 * Array of recent size estimate messages.
265 static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE];
268 * Index of most recent estimate.
270 static unsigned int estimate_index;
273 * Number of valid entries in the history.
275 static unsigned int estimate_count;
278 * Task scheduled to update our flood message for the next round.
280 static struct GNUNET_SCHEDULER_Task *flood_task;
283 * Task scheduled to compute our proof.
285 static struct GNUNET_SCHEDULER_Task *proof_task;
288 * Notification context, simplifies client broadcasts.
290 static struct GNUNET_NotificationContext *nc;
293 * The next major time.
295 static struct GNUNET_TIME_Absolute next_timestamp;
298 * The current major time.
300 static struct GNUNET_TIME_Absolute current_timestamp;
303 * The private key of this peer.
305 static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
308 * The peer identity of this peer.
310 static struct GNUNET_PeerIdentity my_identity;
313 * Proof of work for this peer.
315 static uint64_t my_proof;
319 * Initialize a message to clients with the current network
322 * @param em message to fill in
325 setup_estimate_message (struct GNUNET_NSE_ClientMessage *em)
335 /* Weighted incremental algorithm for stddev according to West (1979) */
347 for (unsigned int i = 0; i < estimate_count; i++)
349 unsigned int j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
351 val = htonl (size_estimate_messages[j].matching_bits);
352 weight = estimate_count + 1 - i;
354 temp = weight + sumweight;
356 r = q * weight / temp;
358 sum += sumweight * q * r;
361 if (estimate_count > 0)
362 variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0);
364 /* trivial version for debugging */
367 /* non-weighted trivial version */
373 for (unsigned int i = 0; i < estimate_count; i++)
375 unsigned int j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
377 val = htonl (size_estimate_messages[j].matching_bits);
381 if (0 != estimate_count)
383 mean = sum / estimate_count;
384 variance = (vsq - mean * sum) /
385 (estimate_count - 1.0); // terrible for numerical stability...
389 std_dev = sqrt (variance);
391 std_dev = variance; /* must be infinity due to estimate_count == 0 */
392 current_std_dev = std_dev;
393 current_size_estimate = mean;
395 em->header.size = htons (sizeof (struct GNUNET_NSE_ClientMessage));
396 em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE);
397 em->reserved = htonl (0);
398 em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
400 double se = mean - 0.332747;
401 unsigned int j = GNUNET_CONTAINER_multipeermap_size (peers);
403 j = 1; /* Avoid log2(0); can only happen if CORE didn't report
404 connection to self yet */
406 em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se, nsize));
407 em->std_deviation = GNUNET_hton_double (std_dev);
408 GNUNET_STATISTICS_set (stats,
409 "# nodes in the network (estimate)",
410 (uint64_t) pow (2, GNUNET_MAX (se, nsize)),
417 * Handler for START message from client, triggers an
418 * immediate current network estimate notification.
419 * Also, we remember the client for updates upon future
420 * estimate measurements.
422 * @param cls client who sent the message
423 * @param message the message received
426 handle_start (void *cls, const struct GNUNET_MessageHeader *message)
428 struct GNUNET_SERVICE_Client *client = cls;
429 struct GNUNET_MQ_Handle *mq;
430 struct GNUNET_NSE_ClientMessage em;
431 struct GNUNET_MQ_Envelope *env;
434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received START message from client\n");
435 mq = GNUNET_SERVICE_client_get_mq (client);
436 GNUNET_notification_context_add (nc, mq);
437 setup_estimate_message (&em);
438 env = GNUNET_MQ_msg_copy (&em.header);
439 GNUNET_MQ_send (mq, env);
440 GNUNET_SERVICE_client_continue (client);
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, GNUNET_YES));
485 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, size_t buf_len, struct GNUNET_HashCode *result)
504 0 == gcry_kdf_derive (buf,
508 "gnunet-proof-of-work",
509 strlen ("gnunet-proof-of-work"),
510 2 /* iterations; keep cost of individual op small */,
511 sizeof (struct GNUNET_HashCode),
517 * Get the number of matching bits that the given timestamp has to the given peer ID.
519 * @param timestamp time to generate key
520 * @param id peer identity to compare with
521 * @return number of matching bits
524 get_matching_bits (struct GNUNET_TIME_Absolute timestamp,
525 const struct GNUNET_PeerIdentity *id)
527 struct GNUNET_HashCode timestamp_hash;
528 struct GNUNET_HashCode pid_hash;
530 GNUNET_CRYPTO_hash (×tamp.abs_value_us,
531 sizeof (timestamp.abs_value_us),
533 GNUNET_CRYPTO_hash (id, sizeof (struct GNUNET_PeerIdentity), &pid_hash);
534 return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash, &pid_hash);
539 * Get the transmission delay that should be applied for a
542 * @param round_offset -1 for the previous round (random delay between 0 and 50ms)
543 * 0 for the current round (based on our proximity to time key)
544 * @return delay that should be applied
546 static struct GNUNET_TIME_Relative
547 get_transmit_delay (int round_offset)
549 struct GNUNET_TIME_Relative ret;
550 struct GNUNET_TIME_Absolute tgt;
552 uint32_t matching_bits;
554 switch (round_offset)
557 /* previous round is randomized between 0 and 50 ms */
558 #if USE_RANDOM_DELAYS
560 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50);
562 ret = GNUNET_TIME_UNIT_ZERO;
564 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
565 "Transmitting previous round behind schedule in %s\n",
566 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
569 /* current round is based on best-known matching_bits */
571 ntohl (size_estimate_messages[estimate_index].matching_bits);
572 dist_delay = get_matching_bits_delay (matching_bits);
573 dist_delay += get_delay_randomization (matching_bits).rel_value_us;
574 ret.rel_value_us = (uint64_t) dist_delay;
575 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
576 "For round %s, delay for %u matching bits is %s\n",
577 GNUNET_STRINGS_absolute_time_to_string (current_timestamp),
578 (unsigned int) matching_bits,
579 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
580 /* now consider round start time and add delay to it */
581 tgt = GNUNET_TIME_absolute_add (current_timestamp, ret);
582 return GNUNET_TIME_absolute_get_remaining (tgt);
585 return GNUNET_TIME_UNIT_FOREVER_REL;
590 * Task that triggers a NSE P2P transmission.
592 * @param cls the `struct NSEPeerEntry *`
595 transmit_task_cb (void *cls)
597 struct NSEPeerEntry *peer_entry = cls;
599 struct GNUNET_MQ_Envelope *env;
601 peer_entry->transmit_task = NULL;
602 idx = estimate_index;
603 if (GNUNET_NO == peer_entry->previous_round)
605 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
606 peer_entry->previous_round = GNUNET_YES;
607 peer_entry->transmit_task =
608 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0),
612 if ((0 == ntohl (size_estimate_messages[idx].hop_count)) &&
613 (NULL != proof_task))
615 GNUNET_STATISTICS_update (stats,
616 "# flood messages not generated (no proof yet)",
621 if (0 == ntohs (size_estimate_messages[idx].header.size))
623 GNUNET_STATISTICS_update (stats,
624 "# flood messages not generated (lack of history)",
629 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
630 "In round %s, sending to `%s' estimate with %u bits\n",
631 GNUNET_STRINGS_absolute_time_to_string (
632 GNUNET_TIME_absolute_ntoh (
633 size_estimate_messages[idx].timestamp)),
634 GNUNET_i2s (peer_entry->id),
635 (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
636 if (0 == ntohl (size_estimate_messages[idx].hop_count))
637 GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO);
638 GNUNET_STATISTICS_update (stats,
639 "# flood messages transmitted",
642 #if ENABLE_NSE_HISTOGRAM
643 peer_entry->transmitted_messages++;
644 peer_entry->last_transmitted_size =
645 ntohl (size_estimate_messages[idx].matching_bits);
647 env = GNUNET_MQ_msg_copy (&size_estimate_messages[idx].header);
648 GNUNET_MQ_send (peer_entry->mq, env);
653 * We've sent on our flood message or one that we received which was
654 * validated and closer than ours. Update the global list of recent
655 * messages and the average. Also re-broadcast the message to any
659 update_network_size_estimate ()
661 struct GNUNET_NSE_ClientMessage em;
663 setup_estimate_message (&em);
664 GNUNET_notification_context_broadcast (nc, &em.header, GNUNET_YES);
669 * Setup a flood message in our history array at the given
670 * slot offset for the given timestamp.
672 * @param slot index to use
673 * @param ts timestamp to use
676 setup_flood_message (unsigned int slot, struct GNUNET_TIME_Absolute ts)
678 struct GNUNET_NSE_FloodMessage *fm;
679 uint32_t matching_bits;
681 matching_bits = get_matching_bits (ts, &my_identity);
682 fm = &size_estimate_messages[slot];
683 fm->header.size = htons (sizeof (struct GNUNET_NSE_FloodMessage));
684 fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
685 fm->hop_count = htonl (0);
686 fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
688 htonl (sizeof (struct GNUNET_NSE_FloodMessage) -
689 sizeof (struct GNUNET_MessageHeader) - sizeof (uint32_t) -
690 sizeof (struct GNUNET_CRYPTO_EddsaSignature));
691 fm->matching_bits = htonl (matching_bits);
692 fm->timestamp = GNUNET_TIME_absolute_hton (ts);
693 fm->origin = my_identity;
694 fm->proof_of_work = my_proof;
695 if (nse_work_required > 0)
696 GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign (my_private_key,
700 memset (&fm->signature, 0, sizeof (fm->signature));
705 * Schedule transmission for the given peer for the current round based
706 * on what we know about the desired delay.
709 * @param key hash of peer identity
710 * @param value the `struct NSEPeerEntry`
711 * @return #GNUNET_OK (continue to iterate)
714 schedule_current_round (void *cls,
715 const struct GNUNET_PeerIdentity *key,
718 struct NSEPeerEntry *peer_entry = value;
719 struct GNUNET_TIME_Relative delay;
723 if (NULL != peer_entry->transmit_task)
725 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
726 peer_entry->previous_round = GNUNET_NO;
728 #if ENABLE_NSE_HISTOGRAM
729 if (peer_entry->received_messages > 1)
730 GNUNET_STATISTICS_update (stats,
732 peer_entry->received_messages - 1,
734 peer_entry->transmitted_messages = 0;
735 peer_entry->last_transmitted_size = 0;
736 peer_entry->received_messages = 0;
739 get_transmit_delay ((GNUNET_NO == peer_entry->previous_round) ? -1 : 0);
740 peer_entry->transmit_task =
741 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
747 * Update our flood message to be sent (and our timestamps).
752 update_flood_message (void *cls)
754 struct GNUNET_TIME_Relative offset;
758 offset = GNUNET_TIME_absolute_get_remaining (next_timestamp);
759 if (0 != offset.rel_value_us)
761 /* somehow run early, delay more */
763 GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL);
766 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
767 if (estimate_count < HISTORY_SIZE)
769 current_timestamp = next_timestamp;
771 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
772 if ((current_timestamp.abs_value_us ==
773 GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value_us) &&
774 (get_matching_bits (current_timestamp, &my_identity) <
775 ntohl (next_message.matching_bits)))
777 /* we received a message for this round way early, use it! */
778 size_estimate_messages[estimate_index] = next_message;
779 size_estimate_messages[estimate_index].hop_count =
780 htonl (1 + ntohl (next_message.hop_count));
783 setup_flood_message (estimate_index, current_timestamp);
784 next_message.matching_bits = htonl (0); /* reset for 'next' round */
786 for (unsigned int i = 0; i < HISTORY_SIZE; i++)
788 GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max);
789 GNUNET_CONTAINER_multipeermap_iterate (peers, &schedule_current_round, NULL);
791 GNUNET_SCHEDULER_add_at (next_timestamp, &update_flood_message, NULL);
796 * Count the leading zeroes in hash.
798 * @param hash to count leading zeros in
799 * @return the number of leading zero bits.
802 count_leading_zeroes (const struct GNUNET_HashCode *hash)
804 unsigned int hash_count;
807 while (0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count))
814 * Check whether the given public key and integer are a valid proof of
817 * @param pkey the public key
818 * @param val the integer
819 * @return #GNUNET_YES if valid, #GNUNET_NO if not
822 check_proof_of_work (const struct GNUNET_CRYPTO_EddsaPublicKey *pkey,
825 char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
826 sizeof (val)] GNUNET_ALIGN;
827 struct GNUNET_HashCode result;
829 GNUNET_memcpy (buf, &val, sizeof (val));
830 GNUNET_memcpy (&buf[sizeof (val)],
832 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
833 pow_hash (buf, sizeof (buf), &result);
834 return (count_leading_zeroes (&result) >= nse_work_required) ? GNUNET_YES
840 * Write our current proof to disk.
848 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
850 if (sizeof (my_proof) != GNUNET_DISK_fn_write (proof,
853 GNUNET_DISK_PERM_USER_READ |
854 GNUNET_DISK_PERM_USER_WRITE))
855 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof);
861 * Find our proof of work.
863 * @param cls closure (unused)
866 find_proof (void *cls)
868 #define ROUND_SIZE 10
870 char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
871 sizeof (uint64_t)] GNUNET_ALIGN;
872 struct GNUNET_HashCode result;
877 GNUNET_memcpy (&buf[sizeof (uint64_t)],
879 sizeof (struct GNUNET_PeerIdentity));
882 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
884 GNUNET_memcpy (buf, &counter, sizeof (uint64_t));
885 pow_hash (buf, sizeof (buf), &result);
886 if (nse_work_required <= count_leading_zeroes (&result))
889 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
890 "Proof of work found: %llu!\n",
891 (unsigned long long) GNUNET_ntohll (counter));
893 setup_flood_message (estimate_index, current_timestamp);
899 if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
901 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
902 "Testing proofs currently at %llu\n",
903 (unsigned long long) counter);
904 /* remember progress every 100 rounds */
913 GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
914 GNUNET_SCHEDULER_PRIORITY_IDLE,
921 * An incoming flood message has been received which claims
922 * to have more bits matching than any we know in this time
923 * period. Verify the signature and/or proof of work.
925 * @param incoming_flood the message to verify
926 * @return #GNUNET_YES if the message is verified
927 * #GNUNET_NO if the key/signature don't verify
930 verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood)
932 if (GNUNET_YES != check_proof_of_work (&incoming_flood->origin.public_key,
933 incoming_flood->proof_of_work))
935 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
936 "Proof of work invalid: %llu!\n",
937 (unsigned long long) GNUNET_ntohll (
938 incoming_flood->proof_of_work));
942 if ((nse_work_required > 0) &&
944 GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
945 &incoming_flood->purpose,
946 &incoming_flood->signature,
947 &incoming_flood->origin.public_key)))
957 * Update transmissions for the given peer for the current round based
958 * on updated proximity information.
960 * @param cls peer entry to exclude from updates
961 * @param key hash of peer identity
962 * @param value the `struct NSEPeerEntry *` of a peer to transmit to
963 * @return #GNUNET_OK (continue to iterate)
966 update_flood_times (void *cls,
967 const struct GNUNET_PeerIdentity *key,
970 struct NSEPeerEntry *exclude = cls;
971 struct NSEPeerEntry *peer_entry = value;
972 struct GNUNET_TIME_Relative delay;
975 if (peer_entry == exclude)
976 return GNUNET_OK; /* trigger of the update */
977 if (GNUNET_NO == peer_entry->previous_round)
979 /* still stuck in previous round, no point to update, check that
980 * we are active here though... */
981 if (NULL == peer_entry->transmit_task)
987 if (NULL != peer_entry->transmit_task)
989 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
990 peer_entry->transmit_task = NULL;
992 delay = get_transmit_delay (0);
993 peer_entry->transmit_task =
994 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
1000 * Core handler for size estimate flooding messages.
1002 * @param cls peer this message is from
1003 * @param incoming_flood received message
1006 handle_p2p_estimate (void *cls,
1007 const struct GNUNET_NSE_FloodMessage *incoming_flood)
1009 struct NSEPeerEntry *peer_entry = cls;
1010 struct GNUNET_TIME_Absolute ts;
1011 uint32_t matching_bits;
1014 #if ENABLE_NSE_HISTOGRAM
1018 t = GNUNET_TIME_absolute_get ().abs_value_us;
1020 GNUNET_TESTBED_LOGGER_write (lh, &t, sizeof (uint64_t));
1021 if (NULL != histogram)
1022 GNUNET_BIO_write_int64 (histogram, t);
1025 GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO);
1026 matching_bits = ntohl (incoming_flood->matching_bits);
1031 struct GNUNET_PeerIdentity os;
1033 GNUNET_snprintf (origin,
1036 GNUNET_i2s (&incoming_flood->origin));
1037 GNUNET_snprintf (pred, sizeof (pred), "%s", GNUNET_i2s (peer_entry->id));
1038 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1039 "Flood at %s from `%s' via `%s' at `%s' with bits %u\n",
1040 GNUNET_STRINGS_absolute_time_to_string (
1041 GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp)),
1044 GNUNET_i2s (&my_identity),
1045 (unsigned int) matching_bits);
1049 #if ENABLE_NSE_HISTOGRAM
1050 peer_entry->received_messages++;
1051 if (peer_entry->transmitted_messages > 0 &&
1052 peer_entry->last_transmitted_size >= matching_bits)
1053 GNUNET_STATISTICS_update (stats, "# cross messages", 1, GNUNET_NO);
1056 ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp);
1057 if (ts.abs_value_us == current_timestamp.abs_value_us)
1058 idx = estimate_index;
1059 else if (ts.abs_value_us ==
1060 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us)
1061 idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1062 else if (ts.abs_value_us == next_timestamp.abs_value_us)
1064 if (matching_bits <= ntohl (next_message.matching_bits))
1065 return; /* ignore, simply too early/late */
1066 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1068 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1069 "Peer %s is likely ill-configured!\n",
1070 GNUNET_i2s (peer_entry->id));
1071 GNUNET_break_op (0);
1074 next_message = *incoming_flood;
1079 GNUNET_STATISTICS_update (stats,
1080 "# flood messages discarded (clock skew too large)",
1085 if (0 == (GNUNET_memcmp (peer_entry->id, &my_identity)))
1087 /* send to self, update our own estimate IF this also comes from us! */
1088 if (0 == GNUNET_memcmp (&incoming_flood->origin, &my_identity))
1089 update_network_size_estimate ();
1092 if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits))
1094 /* Cancel transmission in the other direction, as this peer clearly has
1095 up-to-date information already. Even if we didn't talk to this peer in
1096 the previous round, we should no longer send it stale information as it
1097 told us about the current round! */
1098 peer_entry->previous_round = GNUNET_YES;
1099 if (idx != estimate_index)
1101 /* do not transmit information for the previous round to this peer
1102 anymore (but allow current round) */
1105 /* got up-to-date information for current round, cancel transmission to
1106 * this peer altogether */
1107 if (NULL != peer_entry->transmit_task)
1109 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1110 peer_entry->transmit_task = NULL;
1114 if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits))
1116 if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES))
1118 peer_entry->previous_round = GNUNET_NO;
1120 /* push back our result now, that peer is spreading bad information... */
1121 if (NULL != peer_entry->transmit_task)
1122 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1123 peer_entry->transmit_task =
1124 GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry);
1125 /* Not closer than our most recent message, no need to do work here */
1126 GNUNET_STATISTICS_update (stats,
1127 "# flood messages ignored (had closer already)",
1132 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1134 GNUNET_break_op (0);
1137 GNUNET_assert (matching_bits >
1138 ntohl (size_estimate_messages[idx].matching_bits));
1139 /* Cancel transmission in the other direction, as this peer clearly has
1140 * up-to-date information already.
1142 peer_entry->previous_round = GNUNET_YES;
1143 if (idx == estimate_index)
1145 /* cancel any activity for current round */
1146 if (NULL != peer_entry->transmit_task)
1148 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1149 peer_entry->transmit_task = NULL;
1152 size_estimate_messages[idx] = *incoming_flood;
1153 size_estimate_messages[idx].hop_count =
1154 htonl (ntohl (incoming_flood->hop_count) + 1);
1156 GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max);
1157 GNUNET_STATISTICS_set (stats,
1158 "# estimated network diameter",
1162 /* have a new, better size estimate, inform clients */
1163 update_network_size_estimate ();
1166 GNUNET_CONTAINER_multipeermap_iterate (peers,
1167 &update_flood_times,
1173 * Method called whenever a peer connects. Sets up the PeerEntry and
1174 * schedules the initial size info transmission to this peer.
1176 * @param cls closure
1177 * @param peer peer identity this notification is about
1180 handle_core_connect (void *cls,
1181 const struct GNUNET_PeerIdentity *peer,
1182 struct GNUNET_MQ_Handle *mq)
1184 struct NSEPeerEntry *peer_entry;
1187 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1188 "Peer `%s' connected to us\n",
1190 /* set our default transmission options */
1191 GNUNET_MQ_set_options (mq, NSE_PRIORITY);
1192 /* create our peer entry for this peer */
1193 peer_entry = GNUNET_new (struct NSEPeerEntry);
1194 peer_entry->id = peer;
1195 peer_entry->mq = mq;
1196 GNUNET_assert (GNUNET_OK ==
1197 GNUNET_CONTAINER_multipeermap_put (
1201 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1202 peer_entry->transmit_task =
1203 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1),
1206 GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO);
1212 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
1213 * any pending transmission requests to that peer.
1215 * @param cls closure
1216 * @param peer peer identity this notification is about
1217 * @parma internal_cls the `struct NSEPeerEntry` for the @a peer
1220 handle_core_disconnect (void *cls,
1221 const struct GNUNET_PeerIdentity *peer,
1224 struct NSEPeerEntry *pos = internal_cls;
1227 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1228 "Peer `%s' disconnected from us\n",
1230 GNUNET_assert (GNUNET_YES ==
1231 GNUNET_CONTAINER_multipeermap_remove (peers, peer, pos));
1232 if (NULL != pos->transmit_task)
1234 GNUNET_SCHEDULER_cancel (pos->transmit_task);
1235 pos->transmit_task = NULL;
1238 GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO);
1242 #if ENABLE_NSE_HISTOGRAM
1244 * Functions of this type are called to notify a successful transmission of the
1245 * message to the logger service
1248 * @param size the amount of data sent (ignored)
1251 flush_comp_cb (void *cls, size_t size)
1255 GNUNET_TESTBED_LOGGER_disconnect (lh);
1262 * Task run during shutdown.
1267 shutdown_task (void *cls)
1270 if (NULL != flood_task)
1272 GNUNET_SCHEDULER_cancel (flood_task);
1275 if (NULL != proof_task)
1277 GNUNET_SCHEDULER_cancel (proof_task);
1279 write_proof (); /* remember progress */
1283 GNUNET_notification_context_destroy (nc);
1286 if (NULL != core_api)
1288 GNUNET_CORE_disconnect (core_api);
1293 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1298 GNUNET_CONTAINER_multipeermap_destroy (peers);
1301 if (NULL != my_private_key)
1303 GNUNET_free (my_private_key);
1304 my_private_key = NULL;
1306 #if ENABLE_NSE_HISTOGRAM
1307 if (NULL != logger_test)
1309 GNUNET_CLIENT_service_test_cancel (logger_test);
1314 GNUNET_TESTBED_LOGGER_flush (lh, &flush_comp_cb, NULL);
1316 if (NULL != histogram)
1318 GNUNET_BIO_write_close (histogram);
1326 * Called on core init/fail.
1328 * @param cls service closure
1329 * @param identity the public identity of this peer
1332 core_init (void *cls, const struct GNUNET_PeerIdentity *identity)
1334 struct GNUNET_TIME_Absolute now;
1335 struct GNUNET_TIME_Absolute prev_time;
1338 if (NULL == identity)
1340 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n");
1341 GNUNET_SCHEDULER_shutdown ();
1344 GNUNET_assert (0 == GNUNET_memcmp (&my_identity, identity));
1345 now = GNUNET_TIME_absolute_get ();
1346 current_timestamp.abs_value_us =
1347 (now.abs_value_us / gnunet_nse_interval.rel_value_us) *
1348 gnunet_nse_interval.rel_value_us;
1350 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
1351 estimate_index = HISTORY_SIZE - 1;
1353 if (GNUNET_YES == check_proof_of_work (&my_identity.public_key, my_proof))
1355 int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1356 prev_time.abs_value_us =
1357 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us;
1358 setup_flood_message (idx, prev_time);
1359 setup_flood_message (estimate_index, current_timestamp);
1363 GNUNET_SCHEDULER_add_at (next_timestamp, &update_flood_message, NULL);
1367 #if ENABLE_NSE_HISTOGRAM
1369 * Function called with the status of the testbed logger service
1372 * @param status #GNUNET_YES if the service is running,
1373 * #GNUNET_NO if the service is not running
1374 * #GNUNET_SYSERR if the configuration is invalid
1377 status_cb (void *cls, int status)
1381 if (GNUNET_YES != status)
1383 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Testbed logger not running\n");
1386 if (NULL == (lh = GNUNET_TESTBED_LOGGER_connect (cfg)))
1388 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1389 "Cannot connect to the testbed logger. Exiting.\n");
1390 GNUNET_SCHEDULER_shutdown ();
1397 * Handle network size estimate clients.
1399 * @param cls closure
1400 * @param c configuration to use
1401 * @param service the initialized service
1405 const struct GNUNET_CONFIGURATION_Handle *c,
1406 struct GNUNET_SERVICE_Handle *service)
1408 struct GNUNET_MQ_MessageHandler core_handlers[] =
1409 {GNUNET_MQ_hd_fixed_size (p2p_estimate,
1410 GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1411 struct GNUNET_NSE_FloodMessage,
1413 GNUNET_MQ_handler_end ()};
1415 struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
1420 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
1423 &gnunet_nse_interval))
1425 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "INTERVAL");
1426 GNUNET_SCHEDULER_shutdown ();
1429 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
1434 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKDELAY");
1435 GNUNET_SCHEDULER_shutdown ();
1438 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
1441 &nse_work_required))
1443 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKBITS");
1444 GNUNET_SCHEDULER_shutdown ();
1447 if (nse_work_required >= sizeof (struct GNUNET_HashCode) * 8)
1449 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1452 _ ("Value is too large.\n"));
1453 GNUNET_SCHEDULER_shutdown ();
1457 #if ENABLE_NSE_HISTOGRAM
1459 char *histogram_dir;
1462 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg,
1468 0 < GNUNET_asprintf (&histogram_fn, "%s/timestamps", histogram_dir));
1469 GNUNET_free (histogram_dir);
1470 histogram = GNUNET_BIO_write_open (histogram_fn);
1471 if (NULL == histogram)
1472 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1473 "Unable to open histogram file `%s'\n",
1475 GNUNET_free (histogram_fn);
1477 logger_test = GNUNET_CLIENT_service_test ("testbed-logger",
1479 GNUNET_TIME_UNIT_SECONDS,
1485 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
1486 pk = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
1487 GNUNET_assert (NULL != pk);
1488 my_private_key = pk;
1489 GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key);
1491 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
1493 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "PROOFFILE");
1494 GNUNET_free (my_private_key);
1495 my_private_key = NULL;
1496 GNUNET_SCHEDULER_shutdown ();
1499 if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) ||
1500 (sizeof (my_proof) !=
1501 GNUNET_DISK_fn_read (proof, &my_proof, sizeof (my_proof))))
1503 GNUNET_free (proof);
1505 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1509 peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES);
1510 nc = GNUNET_notification_context_create (1);
1511 /* Connect to core service and register core handlers */
1513 GNUNET_CORE_connect (cfg, /* Main configuration */
1514 NULL, /* Closure passed to functions */
1515 &core_init, /* Call core_init once connected */
1516 &handle_core_connect, /* Handle connects */
1517 &handle_core_disconnect, /* Handle disconnects */
1518 core_handlers); /* Register these handlers */
1519 if (NULL == core_api)
1521 GNUNET_SCHEDULER_shutdown ();
1524 stats = GNUNET_STATISTICS_create ("nse", cfg);
1529 * Callback called when a client connects to the service.
1531 * @param cls closure for the service
1532 * @param c the new client that connected to the service
1533 * @param mq the message queue used to send messages to the client
1537 client_connect_cb (void *cls,
1538 struct GNUNET_SERVICE_Client *c,
1539 struct GNUNET_MQ_Handle *mq)
1548 * Callback called when a client disconnected from the service
1550 * @param cls closure for the service
1551 * @param c the client that disconnected
1552 * @param internal_cls should be equal to @a c
1555 client_disconnect_cb (void *cls,
1556 struct GNUNET_SERVICE_Client *c,
1560 GNUNET_assert (c == internal_cls);
1565 * Define "main" method using service macro.
1567 GNUNET_SERVICE_MAIN ("nse",
1568 GNUNET_SERVICE_OPTION_NONE,
1571 &client_disconnect_cb,
1573 GNUNET_MQ_hd_fixed_size (start,
1574 GNUNET_MESSAGE_TYPE_NSE_START,
1575 struct GNUNET_MessageHeader,
1577 GNUNET_MQ_handler_end ());
1580 #if defined(LINUX) && defined(__GLIBC__)
1584 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1586 void __attribute__ ((constructor)) GNUNET_ARM_memory_init ()
1588 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1589 mallopt (M_TOP_PAD, 1 * 1024);
1595 /* end of gnunet-service-nse.c */