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.
123 * Core handle for sending messages to this peer.
125 struct GNUNET_MQ_Handle *mq;
128 * What is the identity of the peer?
130 const struct GNUNET_PeerIdentity *id;
133 * Task scheduled to send message to this peer.
135 struct GNUNET_SCHEDULER_Task *transmit_task;
138 * Did we receive or send a message about the previous round
139 * to this peer yet? #GNUNET_YES if the previous round has
140 * been taken care of.
144 #if ENABLE_NSE_HISTOGRAM
146 * Amount of messages received from this peer on this round.
148 unsigned int received_messages;
151 * Amount of messages transmitted to this peer on this round.
153 unsigned int transmitted_messages;
156 * Which size did we tell the peer the network is?
158 unsigned int last_transmitted_size;
163 GNUNET_NETWORK_STRUCT_BEGIN
166 * Network size estimate reply; sent when "this"
167 * peer's timer has run out before receiving a
168 * valid reply from another peer.
170 struct GNUNET_NSE_FloodMessage
173 * Type: #GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD
175 struct GNUNET_MessageHeader header;
178 * Number of hops this message has taken so far.
180 uint32_t hop_count GNUNET_PACKED;
185 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
188 * The current timestamp value (which all
189 * peers should agree on).
191 struct GNUNET_TIME_AbsoluteNBO timestamp;
194 * Number of matching bits between the hash
195 * of timestamp and the initiator's public
198 uint32_t matching_bits GNUNET_PACKED;
201 * Public key of the originator.
203 struct GNUNET_PeerIdentity origin;
206 * Proof of work, causing leading zeros when hashed with pkey.
208 uint64_t proof_of_work GNUNET_PACKED;
211 * Signature (over range specified in purpose).
213 struct GNUNET_CRYPTO_EddsaSignature signature;
215 GNUNET_NETWORK_STRUCT_END
218 * Handle to our current configuration.
220 static const struct GNUNET_CONFIGURATION_Handle *cfg;
223 * Handle to the statistics service.
225 static struct GNUNET_STATISTICS_Handle *stats;
228 * Handle to the core service.
230 static struct GNUNET_CORE_Handle *core_api;
233 * Map of all connected peers.
235 static struct GNUNET_CONTAINER_MultiPeerMap *peers;
238 * The current network size estimate. Number of bits matching on
241 static double current_size_estimate;
244 * The standard deviation of the last #HISTORY_SIZE network
247 static double current_std_dev = NAN;
250 * Current hop counter estimate (estimate for network diameter).
252 static uint32_t hop_count_max;
255 * Message for the next round, if we got any.
257 static struct GNUNET_NSE_FloodMessage next_message;
260 * Array of recent size estimate messages.
262 static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE];
265 * Index of most recent estimate.
267 static unsigned int estimate_index;
270 * Number of valid entries in the history.
272 static unsigned int estimate_count;
275 * Task scheduled to update our flood message for the next round.
277 static struct GNUNET_SCHEDULER_Task *flood_task;
280 * Task scheduled to compute our proof.
282 static struct GNUNET_SCHEDULER_Task *proof_task;
285 * Notification context, simplifies client broadcasts.
287 static struct GNUNET_NotificationContext *nc;
290 * The next major time.
292 static struct GNUNET_TIME_Absolute next_timestamp;
295 * The current major time.
297 static struct GNUNET_TIME_Absolute current_timestamp;
300 * The private key of this peer.
302 static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
305 * The peer identity of this peer.
307 static struct GNUNET_PeerIdentity my_identity;
310 * Proof of work for this peer.
312 static uint64_t my_proof;
316 * Initialize a message to clients with the current network
319 * @param em message to fill in
322 setup_estimate_message (struct GNUNET_NSE_ClientMessage *em)
332 /* Weighted incremental algorithm for stddev according to West (1979) */
344 for (unsigned int i = 0; i < estimate_count; i++)
346 unsigned int j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
348 val = htonl (size_estimate_messages[j].matching_bits);
349 weight = estimate_count + 1 - i;
351 temp = weight + sumweight;
353 r = q * weight / temp;
355 sum += sumweight * q * r;
358 if (estimate_count > 0)
359 variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0);
361 /* trivial version for debugging */
364 /* non-weighted trivial version */
370 for (unsigned int i = 0; i < estimate_count; i++)
372 unsigned int j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
374 val = htonl (size_estimate_messages[j].matching_bits);
378 if (0 != estimate_count)
380 mean = sum / estimate_count;
381 variance = (vsq - mean * sum)
382 / (estimate_count - 1.0); // terrible for numerical stability...
386 std_dev = sqrt (variance);
388 std_dev = variance; /* must be infinity due to estimate_count == 0 */
389 current_std_dev = std_dev;
390 current_size_estimate = mean;
392 em->header.size = htons (sizeof(struct GNUNET_NSE_ClientMessage));
393 em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE);
394 em->reserved = htonl (0);
395 em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
397 double se = mean - 0.332747;
398 unsigned int j = GNUNET_CONTAINER_multipeermap_size (peers);
400 j = 1; /* Avoid log2(0); can only happen if CORE didn't report
401 connection to self yet */
403 em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se, nsize));
404 em->std_deviation = GNUNET_hton_double (std_dev);
405 GNUNET_STATISTICS_set (stats,
406 "# nodes in the network (estimate)",
407 (uint64_t) pow (2, GNUNET_MAX (se, nsize)),
414 * Handler for START message from client, triggers an
415 * immediate current network estimate notification.
416 * Also, we remember the client for updates upon future
417 * estimate measurements.
419 * @param cls client who sent the message
420 * @param message the message received
423 handle_start (void *cls, const struct GNUNET_MessageHeader *message)
425 struct GNUNET_SERVICE_Client *client = cls;
426 struct GNUNET_MQ_Handle *mq;
427 struct GNUNET_NSE_ClientMessage em;
428 struct GNUNET_MQ_Envelope *env;
431 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received START message from client\n");
432 mq = GNUNET_SERVICE_client_get_mq (client);
433 GNUNET_notification_context_add (nc, mq);
434 setup_estimate_message (&em);
435 env = GNUNET_MQ_msg_copy (&em.header);
436 GNUNET_MQ_send (mq, env);
437 GNUNET_SERVICE_client_continue (client);
442 * How long should we delay a message to go the given number of
445 * @param matching_bits number of matching bits to consider
448 get_matching_bits_delay (uint32_t matching_bits)
450 /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */
451 // S is next_timestamp (ignored in return value)
452 // f is frequency (gnunet_nse_interval)
453 // x is matching_bits
454 // p' is current_size_estimate
455 return ((double) gnunet_nse_interval.rel_value_us / (double) 2.0)
456 - ((gnunet_nse_interval.rel_value_us / M_PI)
457 * atan (matching_bits - current_size_estimate));
462 * What delay randomization should we apply for a given number of matching bits?
464 * @param matching_bits number of matching bits
465 * @return random delay to apply
467 static struct GNUNET_TIME_Relative
468 get_delay_randomization (uint32_t matching_bits)
470 #if USE_RANDOM_DELAYS
471 struct GNUNET_TIME_Relative ret;
475 d = get_matching_bits_delay (matching_bits);
476 i = (uint32_t) (d / (double) (hop_count_max + 1));
477 ret.rel_value_us = i;
478 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
479 "Randomizing flood using latencies up to %s\n",
480 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
482 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1);
485 return GNUNET_TIME_UNIT_ZERO;
491 * Calculate the 'proof-of-work' hash (an expensive hash).
493 * @param buf data to hash
494 * @param buf_len number of bytes in @a buf
495 * @param result where to write the resulting hash
498 pow_hash (const void *buf, size_t buf_len, struct GNUNET_HashCode *result)
501 0 == gcry_kdf_derive (buf,
505 "gnunet-proof-of-work",
506 strlen ("gnunet-proof-of-work"),
507 2 /* iterations; keep cost of individual op small */,
508 sizeof(struct GNUNET_HashCode),
514 * Get the number of matching bits that the given timestamp has to the given peer ID.
516 * @param timestamp time to generate key
517 * @param id peer identity to compare with
518 * @return number of matching bits
521 get_matching_bits (struct GNUNET_TIME_Absolute timestamp,
522 const struct GNUNET_PeerIdentity *id)
524 struct GNUNET_HashCode timestamp_hash;
525 struct GNUNET_HashCode pid_hash;
527 GNUNET_CRYPTO_hash (×tamp.abs_value_us,
528 sizeof(timestamp.abs_value_us),
530 GNUNET_CRYPTO_hash (id, sizeof(struct GNUNET_PeerIdentity), &pid_hash);
531 return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash, &pid_hash);
536 * Get the transmission delay that should be applied for a
539 * @param round_offset -1 for the previous round (random delay between 0 and 50ms)
540 * 0 for the current round (based on our proximity to time key)
541 * @return delay that should be applied
543 static struct GNUNET_TIME_Relative
544 get_transmit_delay (int round_offset)
546 struct GNUNET_TIME_Relative ret;
547 struct GNUNET_TIME_Absolute tgt;
549 uint32_t matching_bits;
551 switch (round_offset)
554 /* previous round is randomized between 0 and 50 ms */
555 #if USE_RANDOM_DELAYS
557 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50);
559 ret = GNUNET_TIME_UNIT_ZERO;
561 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
562 "Transmitting previous round behind schedule in %s\n",
563 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
567 /* current round is based on best-known matching_bits */
569 ntohl (size_estimate_messages[estimate_index].matching_bits);
570 dist_delay = get_matching_bits_delay (matching_bits);
571 dist_delay += get_delay_randomization (matching_bits).rel_value_us;
572 ret.rel_value_us = (uint64_t) dist_delay;
573 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
574 "For round %s, delay for %u matching bits is %s\n",
575 GNUNET_STRINGS_absolute_time_to_string (current_timestamp),
576 (unsigned int) matching_bits,
577 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
578 /* now consider round start time and add delay to it */
579 tgt = GNUNET_TIME_absolute_add (current_timestamp, ret);
580 return GNUNET_TIME_absolute_get_remaining (tgt);
583 return GNUNET_TIME_UNIT_FOREVER_REL;
588 * Task that triggers a NSE P2P transmission.
590 * @param cls the `struct NSEPeerEntry *`
593 transmit_task_cb (void *cls)
595 struct NSEPeerEntry *peer_entry = cls;
597 struct GNUNET_MQ_Envelope *env;
599 peer_entry->transmit_task = NULL;
600 idx = estimate_index;
601 if (GNUNET_NO == peer_entry->previous_round)
603 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
604 peer_entry->previous_round = GNUNET_YES;
605 peer_entry->transmit_task =
606 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0),
610 if ((0 == ntohl (size_estimate_messages[idx].hop_count)) &&
611 (NULL != proof_task))
613 GNUNET_STATISTICS_update (stats,
614 "# flood messages not generated (no proof yet)",
619 if (0 == ntohs (size_estimate_messages[idx].header.size))
621 GNUNET_STATISTICS_update (stats,
622 "# 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 (
630 GNUNET_TIME_absolute_ntoh (
631 size_estimate_messages[idx].timestamp)),
632 GNUNET_i2s (peer_entry->id),
633 (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
634 if (0 == ntohl (size_estimate_messages[idx].hop_count))
635 GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO);
636 GNUNET_STATISTICS_update (stats,
637 "# flood messages transmitted",
640 #if ENABLE_NSE_HISTOGRAM
641 peer_entry->transmitted_messages++;
642 peer_entry->last_transmitted_size =
643 ntohl (size_estimate_messages[idx].matching_bits);
645 env = GNUNET_MQ_msg_copy (&size_estimate_messages[idx].header);
646 GNUNET_MQ_send (peer_entry->mq, env);
651 * We've sent on our flood message or one that we received which was
652 * validated and closer than ours. Update the global list of recent
653 * messages and the average. Also re-broadcast the message to any
657 update_network_size_estimate ()
659 struct GNUNET_NSE_ClientMessage em;
661 setup_estimate_message (&em);
662 GNUNET_notification_context_broadcast (nc, &em.header, GNUNET_YES);
667 * Setup a flood message in our history array at the given
668 * slot offset for the given timestamp.
670 * @param slot index to use
671 * @param ts timestamp to use
674 setup_flood_message (unsigned int slot, struct GNUNET_TIME_Absolute ts)
676 struct GNUNET_NSE_FloodMessage *fm;
677 uint32_t matching_bits;
679 matching_bits = get_matching_bits (ts, &my_identity);
680 fm = &size_estimate_messages[slot];
681 fm->header.size = htons (sizeof(struct GNUNET_NSE_FloodMessage));
682 fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
683 fm->hop_count = htonl (0);
684 fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
686 htonl (sizeof(struct GNUNET_NSE_FloodMessage)
687 - sizeof(struct GNUNET_MessageHeader) - sizeof(uint32_t)
688 - sizeof(struct GNUNET_CRYPTO_EddsaSignature));
689 fm->matching_bits = htonl (matching_bits);
690 fm->timestamp = GNUNET_TIME_absolute_hton (ts);
691 fm->origin = my_identity;
692 fm->proof_of_work = my_proof;
693 if (nse_work_required > 0)
694 GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign (my_private_key,
698 memset (&fm->signature, 0, sizeof(fm->signature));
703 * Schedule transmission for the given peer for the current round based
704 * on what we know about the desired delay.
707 * @param key hash of peer identity
708 * @param value the `struct NSEPeerEntry`
709 * @return #GNUNET_OK (continue to iterate)
712 schedule_current_round (void *cls,
713 const struct GNUNET_PeerIdentity *key,
716 struct NSEPeerEntry *peer_entry = value;
717 struct GNUNET_TIME_Relative delay;
721 if (NULL != peer_entry->transmit_task)
723 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
724 peer_entry->previous_round = GNUNET_NO;
726 #if ENABLE_NSE_HISTOGRAM
727 if (peer_entry->received_messages > 1)
728 GNUNET_STATISTICS_update (stats,
730 peer_entry->received_messages - 1,
732 peer_entry->transmitted_messages = 0;
733 peer_entry->last_transmitted_size = 0;
734 peer_entry->received_messages = 0;
737 get_transmit_delay ((GNUNET_NO == peer_entry->previous_round) ? -1 : 0);
738 peer_entry->transmit_task =
739 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
745 * Update our flood message to be sent (and our timestamps).
750 update_flood_message (void *cls)
752 struct GNUNET_TIME_Relative offset;
756 offset = GNUNET_TIME_absolute_get_remaining (next_timestamp);
757 if (0 != offset.rel_value_us)
759 /* somehow run early, delay more */
761 GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL);
764 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
765 if (estimate_count < HISTORY_SIZE)
767 current_timestamp = next_timestamp;
769 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
770 if ((current_timestamp.abs_value_us ==
771 GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value_us) &&
772 (get_matching_bits (current_timestamp, &my_identity) <
773 ntohl (next_message.matching_bits)))
775 /* we received a message for this round way early, use it! */
776 size_estimate_messages[estimate_index] = next_message;
777 size_estimate_messages[estimate_index].hop_count =
778 htonl (1 + ntohl (next_message.hop_count));
781 setup_flood_message (estimate_index, current_timestamp);
782 next_message.matching_bits = htonl (0); /* reset for 'next' round */
784 for (unsigned int i = 0; i < HISTORY_SIZE; i++)
786 GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max);
787 GNUNET_CONTAINER_multipeermap_iterate (peers, &schedule_current_round, NULL);
789 GNUNET_SCHEDULER_add_at (next_timestamp, &update_flood_message, NULL);
794 * Count the leading zeroes in hash.
796 * @param hash to count leading zeros in
797 * @return the number of leading zero bits.
800 count_leading_zeroes (const struct GNUNET_HashCode *hash)
802 unsigned int hash_count;
805 while (0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count))
812 * Check whether the given public key and integer are a valid proof of
815 * @param pkey the public key
816 * @param val the integer
817 * @return #GNUNET_YES if valid, #GNUNET_NO if not
820 check_proof_of_work (const struct GNUNET_CRYPTO_EddsaPublicKey *pkey,
823 char buf[sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)
824 + sizeof(val)] GNUNET_ALIGN;
825 struct GNUNET_HashCode result;
827 GNUNET_memcpy (buf, &val, sizeof(val));
828 GNUNET_memcpy (&buf[sizeof(val)],
830 sizeof(struct GNUNET_CRYPTO_EddsaPublicKey));
831 pow_hash (buf, sizeof(buf), &result);
832 return (count_leading_zeroes (&result) >= nse_work_required) ? GNUNET_YES
838 * Write our current proof to disk.
846 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
848 if (sizeof(my_proof) != GNUNET_DISK_fn_write (proof,
851 GNUNET_DISK_PERM_USER_READ
852 | GNUNET_DISK_PERM_USER_WRITE))
853 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof);
859 * Find our proof of work.
861 * @param cls closure (unused)
864 find_proof (void *cls)
866 #define ROUND_SIZE 10
868 char buf[sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)
869 + sizeof(uint64_t)] GNUNET_ALIGN;
870 struct GNUNET_HashCode result;
875 GNUNET_memcpy (&buf[sizeof(uint64_t)],
877 sizeof(struct GNUNET_PeerIdentity));
880 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
882 GNUNET_memcpy (buf, &counter, sizeof(uint64_t));
883 pow_hash (buf, sizeof(buf), &result);
884 if (nse_work_required <= count_leading_zeroes (&result))
887 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
888 "Proof of work found: %llu!\n",
889 (unsigned long long) GNUNET_ntohll (counter));
891 setup_flood_message (estimate_index, current_timestamp);
897 if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
899 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
900 "Testing proofs currently at %llu\n",
901 (unsigned long long) counter);
902 /* remember progress every 100 rounds */
911 GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
912 GNUNET_SCHEDULER_PRIORITY_IDLE,
919 * An incoming flood message has been received which claims
920 * to have more bits matching than any we know in this time
921 * period. Verify the signature and/or proof of work.
923 * @param incoming_flood the message to verify
924 * @return #GNUNET_YES if the message is verified
925 * #GNUNET_NO if the key/signature don't verify
928 verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood)
930 if (GNUNET_YES != check_proof_of_work (&incoming_flood->origin.public_key,
931 incoming_flood->proof_of_work))
933 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
934 "Proof of work invalid: %llu!\n",
935 (unsigned long long) GNUNET_ntohll (
936 incoming_flood->proof_of_work));
940 if ((nse_work_required > 0) &&
942 GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
943 &incoming_flood->purpose,
944 &incoming_flood->signature,
945 &incoming_flood->origin.public_key)))
955 * Update transmissions for the given peer for the current round based
956 * on updated proximity information.
958 * @param cls peer entry to exclude from updates
959 * @param key hash of peer identity
960 * @param value the `struct NSEPeerEntry *` of a peer to transmit to
961 * @return #GNUNET_OK (continue to iterate)
964 update_flood_times (void *cls,
965 const struct GNUNET_PeerIdentity *key,
968 struct NSEPeerEntry *exclude = cls;
969 struct NSEPeerEntry *peer_entry = value;
970 struct GNUNET_TIME_Relative delay;
973 if (peer_entry == exclude)
974 return GNUNET_OK; /* trigger of the update */
975 if (GNUNET_NO == peer_entry->previous_round)
977 /* still stuck in previous round, no point to update, check that
978 * we are active here though... */
979 if (NULL == peer_entry->transmit_task)
985 if (NULL != peer_entry->transmit_task)
987 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
988 peer_entry->transmit_task = NULL;
990 delay = get_transmit_delay (0);
991 peer_entry->transmit_task =
992 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
998 * Core handler for size estimate flooding messages.
1000 * @param cls peer this message is from
1001 * @param incoming_flood received message
1004 handle_p2p_estimate (void *cls,
1005 const struct GNUNET_NSE_FloodMessage *incoming_flood)
1007 struct NSEPeerEntry *peer_entry = cls;
1008 struct GNUNET_TIME_Absolute ts;
1009 uint32_t matching_bits;
1012 #if ENABLE_NSE_HISTOGRAM
1016 t = GNUNET_TIME_absolute_get ().abs_value_us;
1018 GNUNET_TESTBED_LOGGER_write (lh, &t, sizeof(uint64_t));
1019 if (NULL != histogram)
1020 GNUNET_BIO_write_int64 (histogram, t);
1023 GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO);
1024 matching_bits = ntohl (incoming_flood->matching_bits);
1029 struct GNUNET_PeerIdentity os;
1031 GNUNET_snprintf (origin,
1034 GNUNET_i2s (&incoming_flood->origin));
1035 GNUNET_snprintf (pred, sizeof(pred), "%s", GNUNET_i2s (peer_entry->id));
1036 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1037 "Flood at %s from `%s' via `%s' at `%s' with bits %u\n",
1038 GNUNET_STRINGS_absolute_time_to_string (
1039 GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp)),
1042 GNUNET_i2s (&my_identity),
1043 (unsigned int) matching_bits);
1047 #if ENABLE_NSE_HISTOGRAM
1048 peer_entry->received_messages++;
1049 if ((peer_entry->transmitted_messages > 0)&&
1050 (peer_entry->last_transmitted_size >= matching_bits) )
1051 GNUNET_STATISTICS_update (stats, "# cross messages", 1, GNUNET_NO);
1054 ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp);
1055 if (ts.abs_value_us == current_timestamp.abs_value_us)
1056 idx = estimate_index;
1057 else if (ts.abs_value_us ==
1058 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us)
1059 idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1060 else if (ts.abs_value_us == next_timestamp.abs_value_us)
1062 if (matching_bits <= ntohl (next_message.matching_bits))
1063 return; /* ignore, simply too early/late */
1064 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1066 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1067 "Peer %s is likely ill-configured!\n",
1068 GNUNET_i2s (peer_entry->id));
1069 GNUNET_break_op (0);
1072 next_message = *incoming_flood;
1077 GNUNET_STATISTICS_update (stats,
1078 "# flood messages discarded (clock skew too large)",
1083 if (0 == (GNUNET_memcmp (peer_entry->id, &my_identity)))
1085 /* send to self, update our own estimate IF this also comes from us! */
1086 if (0 == GNUNET_memcmp (&incoming_flood->origin, &my_identity))
1087 update_network_size_estimate ();
1090 if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits))
1092 /* Cancel transmission in the other direction, as this peer clearly has
1093 up-to-date information already. Even if we didn't talk to this peer in
1094 the previous round, we should no longer send it stale information as it
1095 told us about the current round! */
1096 peer_entry->previous_round = GNUNET_YES;
1097 if (idx != estimate_index)
1099 /* do not transmit information for the previous round to this peer
1100 anymore (but allow current round) */
1103 /* got up-to-date information for current round, cancel transmission to
1104 * this peer altogether */
1105 if (NULL != peer_entry->transmit_task)
1107 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1108 peer_entry->transmit_task = NULL;
1112 if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits))
1114 if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES))
1116 peer_entry->previous_round = GNUNET_NO;
1118 /* push back our result now, that peer is spreading bad information... */
1119 if (NULL != peer_entry->transmit_task)
1120 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1121 peer_entry->transmit_task =
1122 GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry);
1123 /* Not closer than our most recent message, no need to do work here */
1124 GNUNET_STATISTICS_update (stats,
1125 "# flood messages ignored (had closer already)",
1130 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1132 GNUNET_break_op (0);
1135 GNUNET_assert (matching_bits >
1136 ntohl (size_estimate_messages[idx].matching_bits));
1137 /* Cancel transmission in the other direction, as this peer clearly has
1138 * up-to-date information already.
1140 peer_entry->previous_round = GNUNET_YES;
1141 if (idx == estimate_index)
1143 /* cancel any activity for current round */
1144 if (NULL != peer_entry->transmit_task)
1146 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1147 peer_entry->transmit_task = NULL;
1150 size_estimate_messages[idx] = *incoming_flood;
1151 size_estimate_messages[idx].hop_count =
1152 htonl (ntohl (incoming_flood->hop_count) + 1);
1154 GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max);
1155 GNUNET_STATISTICS_set (stats,
1156 "# estimated network diameter",
1160 /* have a new, better size estimate, inform clients */
1161 update_network_size_estimate ();
1164 GNUNET_CONTAINER_multipeermap_iterate (peers,
1165 &update_flood_times,
1171 * Method called whenever a peer connects. Sets up the PeerEntry and
1172 * schedules the initial size info transmission to this peer.
1174 * @param cls closure
1175 * @param peer peer identity this notification is about
1178 handle_core_connect (void *cls,
1179 const struct GNUNET_PeerIdentity *peer,
1180 struct GNUNET_MQ_Handle *mq)
1182 struct NSEPeerEntry *peer_entry;
1185 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1186 "Peer `%s' connected to us\n",
1188 /* set our default transmission options */
1189 GNUNET_MQ_set_options (mq, NSE_PRIORITY);
1190 /* create our peer entry for this peer */
1191 peer_entry = GNUNET_new (struct NSEPeerEntry);
1192 peer_entry->id = peer;
1193 peer_entry->mq = mq;
1194 GNUNET_assert (GNUNET_OK ==
1195 GNUNET_CONTAINER_multipeermap_put (
1199 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1200 peer_entry->transmit_task =
1201 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1),
1204 GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO);
1210 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
1211 * any pending transmission requests to that peer.
1213 * @param cls closure
1214 * @param peer peer identity this notification is about
1215 * @parma internal_cls the `struct NSEPeerEntry` for the @a peer
1218 handle_core_disconnect (void *cls,
1219 const struct GNUNET_PeerIdentity *peer,
1222 struct NSEPeerEntry *pos = internal_cls;
1225 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1226 "Peer `%s' disconnected from us\n",
1228 GNUNET_assert (GNUNET_YES ==
1229 GNUNET_CONTAINER_multipeermap_remove (peers, peer, pos));
1230 if (NULL != pos->transmit_task)
1232 GNUNET_SCHEDULER_cancel (pos->transmit_task);
1233 pos->transmit_task = NULL;
1236 GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO);
1240 #if ENABLE_NSE_HISTOGRAM
1242 * Functions of this type are called to notify a successful transmission of the
1243 * message to the logger service
1246 * @param size the amount of data sent (ignored)
1249 flush_comp_cb (void *cls, size_t size)
1253 GNUNET_TESTBED_LOGGER_disconnect (lh);
1260 * Task run during shutdown.
1265 shutdown_task (void *cls)
1268 if (NULL != flood_task)
1270 GNUNET_SCHEDULER_cancel (flood_task);
1273 if (NULL != proof_task)
1275 GNUNET_SCHEDULER_cancel (proof_task);
1277 write_proof (); /* remember progress */
1281 GNUNET_notification_context_destroy (nc);
1284 if (NULL != core_api)
1286 GNUNET_CORE_disconnect (core_api);
1291 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1296 GNUNET_CONTAINER_multipeermap_destroy (peers);
1299 if (NULL != my_private_key)
1301 GNUNET_free (my_private_key);
1302 my_private_key = NULL;
1304 #if ENABLE_NSE_HISTOGRAM
1305 if (NULL != logger_test)
1307 GNUNET_CLIENT_service_test_cancel (logger_test);
1312 GNUNET_TESTBED_LOGGER_flush (lh, &flush_comp_cb, NULL);
1314 if (NULL != histogram)
1316 GNUNET_BIO_write_close (histogram);
1324 * Called on core init/fail.
1326 * @param cls service closure
1327 * @param identity the public identity of this peer
1330 core_init (void *cls, const struct GNUNET_PeerIdentity *identity)
1332 struct GNUNET_TIME_Absolute now;
1333 struct GNUNET_TIME_Absolute prev_time;
1336 if (NULL == identity)
1338 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n");
1339 GNUNET_SCHEDULER_shutdown ();
1342 GNUNET_assert (0 == GNUNET_memcmp (&my_identity, identity));
1343 now = GNUNET_TIME_absolute_get ();
1344 current_timestamp.abs_value_us =
1345 (now.abs_value_us / gnunet_nse_interval.rel_value_us)
1346 * gnunet_nse_interval.rel_value_us;
1348 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
1349 estimate_index = HISTORY_SIZE - 1;
1351 if (GNUNET_YES == check_proof_of_work (&my_identity.public_key, my_proof))
1353 int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1354 prev_time.abs_value_us =
1355 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us;
1356 setup_flood_message (idx, prev_time);
1357 setup_flood_message (estimate_index, current_timestamp);
1361 GNUNET_SCHEDULER_add_at (next_timestamp, &update_flood_message, NULL);
1365 #if ENABLE_NSE_HISTOGRAM
1367 * Function called with the status of the testbed logger service
1370 * @param status #GNUNET_YES if the service is running,
1371 * #GNUNET_NO if the service is not running
1372 * #GNUNET_SYSERR if the configuration is invalid
1375 status_cb (void *cls, int status)
1379 if (GNUNET_YES != status)
1381 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Testbed logger not running\n");
1384 if (NULL == (lh = GNUNET_TESTBED_LOGGER_connect (cfg)))
1386 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1387 "Cannot connect to the testbed logger. Exiting.\n");
1388 GNUNET_SCHEDULER_shutdown ();
1395 * Handle network size estimate clients.
1397 * @param cls closure
1398 * @param c configuration to use
1399 * @param service the initialized service
1403 const struct GNUNET_CONFIGURATION_Handle *c,
1404 struct GNUNET_SERVICE_Handle *service)
1406 struct GNUNET_MQ_MessageHandler core_handlers[] =
1407 { GNUNET_MQ_hd_fixed_size (p2p_estimate,
1408 GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1409 struct GNUNET_NSE_FloodMessage,
1411 GNUNET_MQ_handler_end () };
1413 struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
1418 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
1421 &gnunet_nse_interval))
1423 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "INTERVAL");
1424 GNUNET_SCHEDULER_shutdown ();
1427 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
1432 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKDELAY");
1433 GNUNET_SCHEDULER_shutdown ();
1436 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
1439 &nse_work_required))
1441 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKBITS");
1442 GNUNET_SCHEDULER_shutdown ();
1445 if (nse_work_required >= sizeof(struct GNUNET_HashCode) * 8)
1447 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1450 _ ("Value is too large.\n"));
1451 GNUNET_SCHEDULER_shutdown ();
1455 #if ENABLE_NSE_HISTOGRAM
1457 char *histogram_dir;
1460 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg,
1466 0 < GNUNET_asprintf (&histogram_fn, "%s/timestamps", histogram_dir));
1467 GNUNET_free (histogram_dir);
1468 histogram = GNUNET_BIO_write_open (histogram_fn);
1469 if (NULL == histogram)
1470 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1471 "Unable to open histogram file `%s'\n",
1473 GNUNET_free (histogram_fn);
1475 logger_test = GNUNET_CLIENT_service_test ("testbed-logger",
1477 GNUNET_TIME_UNIT_SECONDS,
1483 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
1484 pk = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
1485 GNUNET_assert (NULL != pk);
1486 my_private_key = pk;
1487 GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key);
1489 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
1491 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "PROOFFILE");
1492 GNUNET_free (my_private_key);
1493 my_private_key = NULL;
1494 GNUNET_SCHEDULER_shutdown ();
1497 if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) ||
1498 (sizeof(my_proof) !=
1499 GNUNET_DISK_fn_read (proof, &my_proof, sizeof(my_proof))))
1501 GNUNET_free (proof);
1503 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1507 peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES);
1508 nc = GNUNET_notification_context_create (1);
1509 /* Connect to core service and register core handlers */
1511 GNUNET_CORE_connect (cfg, /* Main configuration */
1512 NULL, /* Closure passed to functions */
1513 &core_init, /* Call core_init once connected */
1514 &handle_core_connect, /* Handle connects */
1515 &handle_core_disconnect, /* Handle disconnects */
1516 core_handlers); /* Register these handlers */
1517 if (NULL == core_api)
1519 GNUNET_SCHEDULER_shutdown ();
1522 stats = GNUNET_STATISTICS_create ("nse", cfg);
1527 * Callback called when a client connects to the service.
1529 * @param cls closure for the service
1530 * @param c the new client that connected to the service
1531 * @param mq the message queue used to send messages to the client
1535 client_connect_cb (void *cls,
1536 struct GNUNET_SERVICE_Client *c,
1537 struct GNUNET_MQ_Handle *mq)
1546 * Callback called when a client disconnected from the service
1548 * @param cls closure for the service
1549 * @param c the client that disconnected
1550 * @param internal_cls should be equal to @a c
1553 client_disconnect_cb (void *cls,
1554 struct GNUNET_SERVICE_Client *c,
1558 GNUNET_assert (c == internal_cls);
1563 * Define "main" method using service macro.
1565 GNUNET_SERVICE_MAIN ("nse",
1566 GNUNET_SERVICE_OPTION_NONE,
1569 &client_disconnect_cb,
1571 GNUNET_MQ_hd_fixed_size (start,
1572 GNUNET_MESSAGE_TYPE_NSE_START,
1573 struct GNUNET_MessageHeader,
1575 GNUNET_MQ_handler_end ());
1578 #if defined(LINUX) && defined(__GLIBC__)
1582 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1584 void __attribute__ ((constructor)) GNUNET_ARM_memory_init ()
1586 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1587 mallopt (M_TOP_PAD, 1 * 1024);
1593 /* end of gnunet-service-nse.c */