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.
120 struct NSEPeerEntry {
122 * Core handle for sending messages to this peer.
124 struct GNUNET_MQ_Handle *mq;
127 * What is the identity of the peer?
129 const struct GNUNET_PeerIdentity *id;
132 * Task scheduled to send message to this peer.
134 struct GNUNET_SCHEDULER_Task *transmit_task;
137 * Did we receive or send a message about the previous round
138 * to this peer yet? #GNUNET_YES if the previous round has
139 * been taken care of.
143 #if ENABLE_NSE_HISTOGRAM
145 * Amount of messages received from this peer on this round.
147 unsigned int received_messages;
150 * Amount of messages transmitted to this peer on this round.
152 unsigned int transmitted_messages;
155 * Which size did we tell the peer the network is?
157 unsigned int last_transmitted_size;
162 GNUNET_NETWORK_STRUCT_BEGIN
165 * Network size estimate reply; sent when "this"
166 * peer's timer has run out before receiving a
167 * valid reply from another peer.
169 struct GNUNET_NSE_FloodMessage {
171 * Type: #GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD
173 struct GNUNET_MessageHeader header;
176 * Number of hops this message has taken so far.
178 uint32_t hop_count GNUNET_PACKED;
183 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
186 * The current timestamp value (which all
187 * peers should agree on).
189 struct GNUNET_TIME_AbsoluteNBO timestamp;
192 * Number of matching bits between the hash
193 * of timestamp and the initiator's public
196 uint32_t matching_bits GNUNET_PACKED;
199 * Public key of the originator.
201 struct GNUNET_PeerIdentity origin;
204 * Proof of work, causing leading zeros when hashed with pkey.
206 uint64_t proof_of_work GNUNET_PACKED;
209 * Signature (over range specified in purpose).
211 struct GNUNET_CRYPTO_EddsaSignature signature;
213 GNUNET_NETWORK_STRUCT_END
216 * Handle to our current configuration.
218 static const struct GNUNET_CONFIGURATION_Handle *cfg;
221 * Handle to the statistics service.
223 static struct GNUNET_STATISTICS_Handle *stats;
226 * Handle to the core service.
228 static struct GNUNET_CORE_Handle *core_api;
231 * Map of all connected peers.
233 static struct GNUNET_CONTAINER_MultiPeerMap *peers;
236 * The current network size estimate. Number of bits matching on
239 static double current_size_estimate;
242 * The standard deviation of the last #HISTORY_SIZE network
245 static double current_std_dev = NAN;
248 * Current hop counter estimate (estimate for network diameter).
250 static uint32_t hop_count_max;
253 * Message for the next round, if we got any.
255 static struct GNUNET_NSE_FloodMessage next_message;
258 * Array of recent size estimate messages.
260 static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE];
263 * Index of most recent estimate.
265 static unsigned int estimate_index;
268 * Number of valid entries in the history.
270 static unsigned int estimate_count;
273 * Task scheduled to update our flood message for the next round.
275 static struct GNUNET_SCHEDULER_Task *flood_task;
278 * Task scheduled to compute our proof.
280 static struct GNUNET_SCHEDULER_Task *proof_task;
283 * Notification context, simplifies client broadcasts.
285 static struct GNUNET_NotificationContext *nc;
288 * The next major time.
290 static struct GNUNET_TIME_Absolute next_timestamp;
293 * The current major time.
295 static struct GNUNET_TIME_Absolute current_timestamp;
298 * The private key of this peer.
300 static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
303 * The peer identity of this peer.
305 static struct GNUNET_PeerIdentity my_identity;
308 * Proof of work for this peer.
310 static uint64_t my_proof;
314 * Initialize a message to clients with the current network
317 * @param em message to fill in
320 setup_estimate_message(struct GNUNET_NSE_ClientMessage *em)
330 /* Weighted incremental algorithm for stddev according to West (1979) */
342 for (unsigned int i = 0; i < estimate_count; i++)
344 unsigned int j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
346 val = htonl(size_estimate_messages[j].matching_bits);
347 weight = estimate_count + 1 - i;
349 temp = weight + sumweight;
351 r = q * weight / temp;
353 sum += sumweight * q * r;
356 if (estimate_count > 0)
357 variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0);
359 /* trivial version for debugging */
362 /* non-weighted trivial version */
368 for (unsigned int i = 0; i < estimate_count; i++)
370 unsigned int j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
372 val = htonl(size_estimate_messages[j].matching_bits);
376 if (0 != estimate_count)
378 mean = sum / estimate_count;
379 variance = (vsq - mean * sum) /
380 (estimate_count - 1.0); // terrible for numerical stability...
384 std_dev = sqrt(variance);
386 std_dev = variance; /* must be infinity due to estimate_count == 0 */
387 current_std_dev = std_dev;
388 current_size_estimate = mean;
390 em->header.size = htons(sizeof(struct GNUNET_NSE_ClientMessage));
391 em->header.type = htons(GNUNET_MESSAGE_TYPE_NSE_ESTIMATE);
392 em->reserved = htonl(0);
393 em->timestamp = GNUNET_TIME_absolute_hton(GNUNET_TIME_absolute_get());
395 double se = mean - 0.332747;
396 unsigned int j = GNUNET_CONTAINER_multipeermap_size(peers);
398 j = 1; /* Avoid log2(0); can only happen if CORE didn't report
399 connection to self yet */
401 em->size_estimate = GNUNET_hton_double(GNUNET_MAX(se, nsize));
402 em->std_deviation = GNUNET_hton_double(std_dev);
403 GNUNET_STATISTICS_set(stats,
404 "# nodes in the network (estimate)",
405 (uint64_t)pow(2, GNUNET_MAX(se, nsize)),
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, const struct GNUNET_MessageHeader *message)
423 struct GNUNET_SERVICE_Client *client = cls;
424 struct GNUNET_MQ_Handle *mq;
425 struct GNUNET_NSE_ClientMessage em;
426 struct GNUNET_MQ_Envelope *env;
429 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Received START message from client\n");
430 mq = GNUNET_SERVICE_client_get_mq(client);
431 GNUNET_notification_context_add(nc, mq);
432 setup_estimate_message(&em);
433 env = GNUNET_MQ_msg_copy(&em.header);
434 GNUNET_MQ_send(mq, env);
435 GNUNET_SERVICE_client_continue(client);
440 * How long should we delay a message to go the given number of
443 * @param matching_bits number of matching bits to consider
446 get_matching_bits_delay(uint32_t matching_bits)
448 /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */
449 // S is next_timestamp (ignored in return value)
450 // f is frequency (gnunet_nse_interval)
451 // x is matching_bits
452 // p' is current_size_estimate
453 return ((double)gnunet_nse_interval.rel_value_us / (double)2.0) -
454 ((gnunet_nse_interval.rel_value_us / M_PI) *
455 atan(matching_bits - current_size_estimate));
460 * What delay randomization should we apply for a given number of matching bits?
462 * @param matching_bits number of matching bits
463 * @return random delay to apply
465 static struct GNUNET_TIME_Relative
466 get_delay_randomization(uint32_t matching_bits)
468 #if USE_RANDOM_DELAYS
469 struct GNUNET_TIME_Relative ret;
473 d = get_matching_bits_delay(matching_bits);
474 i = (uint32_t)(d / (double)(hop_count_max + 1));
475 ret.rel_value_us = i;
476 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
477 "Randomizing flood using latencies up to %s\n",
478 GNUNET_STRINGS_relative_time_to_string(ret, GNUNET_YES));
480 GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, i + 1);
483 return GNUNET_TIME_UNIT_ZERO;
489 * Calculate the 'proof-of-work' hash (an expensive hash).
491 * @param buf data to hash
492 * @param buf_len number of bytes in @a buf
493 * @param result where to write the resulting hash
496 pow_hash(const void *buf, size_t buf_len, struct GNUNET_HashCode *result)
499 0 == gcry_kdf_derive(buf,
503 "gnunet-proof-of-work",
504 strlen("gnunet-proof-of-work"),
505 2 /* iterations; keep cost of individual op small */,
506 sizeof(struct GNUNET_HashCode),
512 * Get the number of matching bits that the given timestamp has to the given peer ID.
514 * @param timestamp time to generate key
515 * @param id peer identity to compare with
516 * @return number of matching bits
519 get_matching_bits(struct GNUNET_TIME_Absolute timestamp,
520 const struct GNUNET_PeerIdentity *id)
522 struct GNUNET_HashCode timestamp_hash;
523 struct GNUNET_HashCode pid_hash;
525 GNUNET_CRYPTO_hash(×tamp.abs_value_us,
526 sizeof(timestamp.abs_value_us),
528 GNUNET_CRYPTO_hash(id, sizeof(struct GNUNET_PeerIdentity), &pid_hash);
529 return GNUNET_CRYPTO_hash_matching_bits(×tamp_hash, &pid_hash);
534 * Get the transmission delay that should be applied for a
537 * @param round_offset -1 for the previous round (random delay between 0 and 50ms)
538 * 0 for the current round (based on our proximity to time key)
539 * @return delay that should be applied
541 static struct GNUNET_TIME_Relative
542 get_transmit_delay(int round_offset)
544 struct GNUNET_TIME_Relative ret;
545 struct GNUNET_TIME_Absolute tgt;
547 uint32_t matching_bits;
549 switch (round_offset)
552 /* previous round is randomized between 0 and 50 ms */
553 #if USE_RANDOM_DELAYS
555 GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK, 50);
557 ret = GNUNET_TIME_UNIT_ZERO;
559 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
560 "Transmitting previous round behind schedule in %s\n",
561 GNUNET_STRINGS_relative_time_to_string(ret, GNUNET_YES));
565 /* current round is based on best-known matching_bits */
567 ntohl(size_estimate_messages[estimate_index].matching_bits);
568 dist_delay = get_matching_bits_delay(matching_bits);
569 dist_delay += get_delay_randomization(matching_bits).rel_value_us;
570 ret.rel_value_us = (uint64_t)dist_delay;
571 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
572 "For round %s, delay for %u matching bits is %s\n",
573 GNUNET_STRINGS_absolute_time_to_string(current_timestamp),
574 (unsigned int)matching_bits,
575 GNUNET_STRINGS_relative_time_to_string(ret, GNUNET_YES));
576 /* now consider round start time and add delay to it */
577 tgt = GNUNET_TIME_absolute_add(current_timestamp, ret);
578 return GNUNET_TIME_absolute_get_remaining(tgt);
581 return GNUNET_TIME_UNIT_FOREVER_REL;
586 * Task that triggers a NSE P2P transmission.
588 * @param cls the `struct NSEPeerEntry *`
591 transmit_task_cb(void *cls)
593 struct NSEPeerEntry *peer_entry = cls;
595 struct GNUNET_MQ_Envelope *env;
597 peer_entry->transmit_task = NULL;
598 idx = estimate_index;
599 if (GNUNET_NO == peer_entry->previous_round)
601 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
602 peer_entry->previous_round = GNUNET_YES;
603 peer_entry->transmit_task =
604 GNUNET_SCHEDULER_add_delayed(get_transmit_delay(0),
608 if ((0 == ntohl(size_estimate_messages[idx].hop_count)) &&
609 (NULL != proof_task))
611 GNUNET_STATISTICS_update(stats,
612 "# flood messages not generated (no proof yet)",
617 if (0 == ntohs(size_estimate_messages[idx].header.size))
619 GNUNET_STATISTICS_update(stats,
620 "# flood messages not generated (lack of history)",
625 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
626 "In round %s, sending to `%s' estimate with %u bits\n",
627 GNUNET_STRINGS_absolute_time_to_string(
628 GNUNET_TIME_absolute_ntoh(
629 size_estimate_messages[idx].timestamp)),
630 GNUNET_i2s(peer_entry->id),
631 (unsigned int)ntohl(size_estimate_messages[idx].matching_bits));
632 if (0 == ntohl(size_estimate_messages[idx].hop_count))
633 GNUNET_STATISTICS_update(stats, "# flood messages started", 1, GNUNET_NO);
634 GNUNET_STATISTICS_update(stats,
635 "# flood messages transmitted",
638 #if ENABLE_NSE_HISTOGRAM
639 peer_entry->transmitted_messages++;
640 peer_entry->last_transmitted_size =
641 ntohl(size_estimate_messages[idx].matching_bits);
643 env = GNUNET_MQ_msg_copy(&size_estimate_messages[idx].header);
644 GNUNET_MQ_send(peer_entry->mq, env);
649 * We've sent on our flood message or one that we received which was
650 * validated and closer than ours. Update the global list of recent
651 * messages and the average. Also re-broadcast the message to any
655 update_network_size_estimate()
657 struct GNUNET_NSE_ClientMessage em;
659 setup_estimate_message(&em);
660 GNUNET_notification_context_broadcast(nc, &em.header, GNUNET_YES);
665 * Setup a flood message in our history array at the given
666 * slot offset for the given timestamp.
668 * @param slot index to use
669 * @param ts timestamp to use
672 setup_flood_message(unsigned int slot, struct GNUNET_TIME_Absolute ts)
674 struct GNUNET_NSE_FloodMessage *fm;
675 uint32_t matching_bits;
677 matching_bits = get_matching_bits(ts, &my_identity);
678 fm = &size_estimate_messages[slot];
679 fm->header.size = htons(sizeof(struct GNUNET_NSE_FloodMessage));
680 fm->header.type = htons(GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
681 fm->hop_count = htonl(0);
682 fm->purpose.purpose = htonl(GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
684 htonl(sizeof(struct GNUNET_NSE_FloodMessage) -
685 sizeof(struct GNUNET_MessageHeader) - sizeof(uint32_t) -
686 sizeof(struct GNUNET_CRYPTO_EddsaSignature));
687 fm->matching_bits = htonl(matching_bits);
688 fm->timestamp = GNUNET_TIME_absolute_hton(ts);
689 fm->origin = my_identity;
690 fm->proof_of_work = my_proof;
691 if (nse_work_required > 0)
692 GNUNET_assert(GNUNET_OK == GNUNET_CRYPTO_eddsa_sign(my_private_key,
696 memset(&fm->signature, 0, sizeof(fm->signature));
701 * Schedule transmission for the given peer for the current round based
702 * on what we know about the desired delay.
705 * @param key hash of peer identity
706 * @param value the `struct NSEPeerEntry`
707 * @return #GNUNET_OK (continue to iterate)
710 schedule_current_round(void *cls,
711 const struct GNUNET_PeerIdentity *key,
714 struct NSEPeerEntry *peer_entry = value;
715 struct GNUNET_TIME_Relative delay;
719 if (NULL != peer_entry->transmit_task)
721 GNUNET_SCHEDULER_cancel(peer_entry->transmit_task);
722 peer_entry->previous_round = GNUNET_NO;
724 #if ENABLE_NSE_HISTOGRAM
725 if (peer_entry->received_messages > 1)
726 GNUNET_STATISTICS_update(stats,
728 peer_entry->received_messages - 1,
730 peer_entry->transmitted_messages = 0;
731 peer_entry->last_transmitted_size = 0;
732 peer_entry->received_messages = 0;
735 get_transmit_delay((GNUNET_NO == peer_entry->previous_round) ? -1 : 0);
736 peer_entry->transmit_task =
737 GNUNET_SCHEDULER_add_delayed(delay, &transmit_task_cb, peer_entry);
743 * Update our flood message to be sent (and our timestamps).
748 update_flood_message(void *cls)
750 struct GNUNET_TIME_Relative offset;
754 offset = GNUNET_TIME_absolute_get_remaining(next_timestamp);
755 if (0 != offset.rel_value_us)
757 /* somehow run early, delay more */
759 GNUNET_SCHEDULER_add_delayed(offset, &update_flood_message, NULL);
762 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
763 if (estimate_count < HISTORY_SIZE)
765 current_timestamp = next_timestamp;
767 GNUNET_TIME_absolute_add(current_timestamp, gnunet_nse_interval);
768 if ((current_timestamp.abs_value_us ==
769 GNUNET_TIME_absolute_ntoh(next_message.timestamp).abs_value_us) &&
770 (get_matching_bits(current_timestamp, &my_identity) <
771 ntohl(next_message.matching_bits)))
773 /* we received a message for this round way early, use it! */
774 size_estimate_messages[estimate_index] = next_message;
775 size_estimate_messages[estimate_index].hop_count =
776 htonl(1 + ntohl(next_message.hop_count));
779 setup_flood_message(estimate_index, current_timestamp);
780 next_message.matching_bits = htonl(0); /* reset for 'next' round */
782 for (unsigned int i = 0; i < HISTORY_SIZE; i++)
784 GNUNET_MAX(ntohl(size_estimate_messages[i].hop_count), hop_count_max);
785 GNUNET_CONTAINER_multipeermap_iterate(peers, &schedule_current_round, NULL);
787 GNUNET_SCHEDULER_add_at(next_timestamp, &update_flood_message, NULL);
792 * Count the leading zeroes in hash.
794 * @param hash to count leading zeros in
795 * @return the number of leading zero bits.
798 count_leading_zeroes(const struct GNUNET_HashCode *hash)
800 unsigned int hash_count;
803 while (0 == GNUNET_CRYPTO_hash_get_bit(hash, hash_count))
810 * Check whether the given public key and integer are a valid proof of
813 * @param pkey the public key
814 * @param val the integer
815 * @return #GNUNET_YES if valid, #GNUNET_NO if not
818 check_proof_of_work(const struct GNUNET_CRYPTO_EddsaPublicKey *pkey,
821 char buf[sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) +
822 sizeof(val)] GNUNET_ALIGN;
823 struct GNUNET_HashCode result;
825 GNUNET_memcpy(buf, &val, sizeof(val));
826 GNUNET_memcpy(&buf[sizeof(val)],
828 sizeof(struct GNUNET_CRYPTO_EddsaPublicKey));
829 pow_hash(buf, sizeof(buf), &result);
830 return (count_leading_zeroes(&result) >= nse_work_required) ? GNUNET_YES
836 * Write our current proof to disk.
844 GNUNET_CONFIGURATION_get_value_filename(cfg, "NSE", "PROOFFILE", &proof))
846 if (sizeof(my_proof) != GNUNET_DISK_fn_write(proof,
849 GNUNET_DISK_PERM_USER_READ |
850 GNUNET_DISK_PERM_USER_WRITE))
851 GNUNET_log_strerror_file(GNUNET_ERROR_TYPE_WARNING, "write", proof);
857 * Find our proof of work.
859 * @param cls closure (unused)
862 find_proof(void *cls)
864 #define ROUND_SIZE 10
866 char buf[sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) +
867 sizeof(uint64_t)] GNUNET_ALIGN;
868 struct GNUNET_HashCode result;
873 GNUNET_memcpy(&buf[sizeof(uint64_t)],
875 sizeof(struct GNUNET_PeerIdentity));
878 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
880 GNUNET_memcpy(buf, &counter, sizeof(uint64_t));
881 pow_hash(buf, sizeof(buf), &result);
882 if (nse_work_required <= count_leading_zeroes(&result))
885 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
886 "Proof of work found: %llu!\n",
887 (unsigned long long)GNUNET_ntohll(counter));
889 setup_flood_message(estimate_index, current_timestamp);
895 if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
897 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
898 "Testing proofs currently at %llu\n",
899 (unsigned long long)counter);
900 /* remember progress every 100 rounds */
909 GNUNET_SCHEDULER_add_delayed_with_priority(proof_find_delay,
910 GNUNET_SCHEDULER_PRIORITY_IDLE,
917 * An incoming flood message has been received which claims
918 * to have more bits matching than any we know in this time
919 * period. Verify the signature and/or proof of work.
921 * @param incoming_flood the message to verify
922 * @return #GNUNET_YES if the message is verified
923 * #GNUNET_NO if the key/signature don't verify
926 verify_message_crypto(const struct GNUNET_NSE_FloodMessage *incoming_flood)
928 if (GNUNET_YES != check_proof_of_work(&incoming_flood->origin.public_key,
929 incoming_flood->proof_of_work))
931 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
932 "Proof of work invalid: %llu!\n",
933 (unsigned long long)GNUNET_ntohll(
934 incoming_flood->proof_of_work));
938 if ((nse_work_required > 0) &&
940 GNUNET_CRYPTO_eddsa_verify(GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
941 &incoming_flood->purpose,
942 &incoming_flood->signature,
943 &incoming_flood->origin.public_key)))
953 * Update transmissions for the given peer for the current round based
954 * on updated proximity information.
956 * @param cls peer entry to exclude from updates
957 * @param key hash of peer identity
958 * @param value the `struct NSEPeerEntry *` of a peer to transmit to
959 * @return #GNUNET_OK (continue to iterate)
962 update_flood_times(void *cls,
963 const struct GNUNET_PeerIdentity *key,
966 struct NSEPeerEntry *exclude = cls;
967 struct NSEPeerEntry *peer_entry = value;
968 struct GNUNET_TIME_Relative delay;
971 if (peer_entry == exclude)
972 return GNUNET_OK; /* trigger of the update */
973 if (GNUNET_NO == peer_entry->previous_round)
975 /* still stuck in previous round, no point to update, check that
976 * we are active here though... */
977 if (NULL == peer_entry->transmit_task)
983 if (NULL != peer_entry->transmit_task)
985 GNUNET_SCHEDULER_cancel(peer_entry->transmit_task);
986 peer_entry->transmit_task = NULL;
988 delay = get_transmit_delay(0);
989 peer_entry->transmit_task =
990 GNUNET_SCHEDULER_add_delayed(delay, &transmit_task_cb, peer_entry);
996 * Core handler for size estimate flooding messages.
998 * @param cls peer this message is from
999 * @param incoming_flood received message
1002 handle_p2p_estimate(void *cls,
1003 const struct GNUNET_NSE_FloodMessage *incoming_flood)
1005 struct NSEPeerEntry *peer_entry = cls;
1006 struct GNUNET_TIME_Absolute ts;
1007 uint32_t matching_bits;
1010 #if ENABLE_NSE_HISTOGRAM
1014 t = GNUNET_TIME_absolute_get().abs_value_us;
1016 GNUNET_TESTBED_LOGGER_write(lh, &t, sizeof(uint64_t));
1017 if (NULL != histogram)
1018 GNUNET_BIO_write_int64(histogram, t);
1021 GNUNET_STATISTICS_update(stats, "# flood messages received", 1, GNUNET_NO);
1022 matching_bits = ntohl(incoming_flood->matching_bits);
1027 struct GNUNET_PeerIdentity os;
1029 GNUNET_snprintf(origin,
1032 GNUNET_i2s(&incoming_flood->origin));
1033 GNUNET_snprintf(pred, sizeof(pred), "%s", GNUNET_i2s(peer_entry->id));
1034 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1035 "Flood at %s from `%s' via `%s' at `%s' with bits %u\n",
1036 GNUNET_STRINGS_absolute_time_to_string(
1037 GNUNET_TIME_absolute_ntoh(incoming_flood->timestamp)),
1040 GNUNET_i2s(&my_identity),
1041 (unsigned int)matching_bits);
1045 #if ENABLE_NSE_HISTOGRAM
1046 peer_entry->received_messages++;
1047 if (peer_entry->transmitted_messages > 0 &&
1048 peer_entry->last_transmitted_size >= matching_bits)
1049 GNUNET_STATISTICS_update(stats, "# cross messages", 1, GNUNET_NO);
1052 ts = GNUNET_TIME_absolute_ntoh(incoming_flood->timestamp);
1053 if (ts.abs_value_us == current_timestamp.abs_value_us)
1054 idx = estimate_index;
1055 else if (ts.abs_value_us ==
1056 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us)
1057 idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1058 else if (ts.abs_value_us == next_timestamp.abs_value_us)
1060 if (matching_bits <= ntohl(next_message.matching_bits))
1061 return; /* ignore, simply too early/late */
1062 if (GNUNET_YES != verify_message_crypto(incoming_flood))
1064 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1065 "Peer %s is likely ill-configured!\n",
1066 GNUNET_i2s(peer_entry->id));
1070 next_message = *incoming_flood;
1075 GNUNET_STATISTICS_update(stats,
1076 "# flood messages discarded (clock skew too large)",
1081 if (0 == (GNUNET_memcmp(peer_entry->id, &my_identity)))
1083 /* send to self, update our own estimate IF this also comes from us! */
1084 if (0 == GNUNET_memcmp(&incoming_flood->origin, &my_identity))
1085 update_network_size_estimate();
1088 if (matching_bits == ntohl(size_estimate_messages[idx].matching_bits))
1090 /* Cancel transmission in the other direction, as this peer clearly has
1091 up-to-date information already. Even if we didn't talk to this peer in
1092 the previous round, we should no longer send it stale information as it
1093 told us about the current round! */
1094 peer_entry->previous_round = GNUNET_YES;
1095 if (idx != estimate_index)
1097 /* do not transmit information for the previous round to this peer
1098 anymore (but allow current round) */
1101 /* got up-to-date information for current round, cancel transmission to
1102 * this peer altogether */
1103 if (NULL != peer_entry->transmit_task)
1105 GNUNET_SCHEDULER_cancel(peer_entry->transmit_task);
1106 peer_entry->transmit_task = NULL;
1110 if (matching_bits < ntohl(size_estimate_messages[idx].matching_bits))
1112 if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES))
1114 peer_entry->previous_round = GNUNET_NO;
1116 /* push back our result now, that peer is spreading bad information... */
1117 if (NULL != peer_entry->transmit_task)
1118 GNUNET_SCHEDULER_cancel(peer_entry->transmit_task);
1119 peer_entry->transmit_task =
1120 GNUNET_SCHEDULER_add_now(&transmit_task_cb, peer_entry);
1121 /* Not closer than our most recent message, no need to do work here */
1122 GNUNET_STATISTICS_update(stats,
1123 "# flood messages ignored (had closer already)",
1128 if (GNUNET_YES != verify_message_crypto(incoming_flood))
1133 GNUNET_assert(matching_bits >
1134 ntohl(size_estimate_messages[idx].matching_bits));
1135 /* Cancel transmission in the other direction, as this peer clearly has
1136 * up-to-date information already.
1138 peer_entry->previous_round = GNUNET_YES;
1139 if (idx == estimate_index)
1141 /* cancel any activity for current round */
1142 if (NULL != peer_entry->transmit_task)
1144 GNUNET_SCHEDULER_cancel(peer_entry->transmit_task);
1145 peer_entry->transmit_task = NULL;
1148 size_estimate_messages[idx] = *incoming_flood;
1149 size_estimate_messages[idx].hop_count =
1150 htonl(ntohl(incoming_flood->hop_count) + 1);
1152 GNUNET_MAX(ntohl(incoming_flood->hop_count) + 1, hop_count_max);
1153 GNUNET_STATISTICS_set(stats,
1154 "# estimated network diameter",
1158 /* have a new, better size estimate, inform clients */
1159 update_network_size_estimate();
1162 GNUNET_CONTAINER_multipeermap_iterate(peers,
1163 &update_flood_times,
1169 * Method called whenever a peer connects. Sets up the PeerEntry and
1170 * schedules the initial size info transmission to this peer.
1172 * @param cls closure
1173 * @param peer peer identity this notification is about
1176 handle_core_connect(void *cls,
1177 const struct GNUNET_PeerIdentity *peer,
1178 struct GNUNET_MQ_Handle *mq)
1180 struct NSEPeerEntry *peer_entry;
1183 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1184 "Peer `%s' connected to us\n",
1186 /* set our default transmission options */
1187 GNUNET_MQ_set_options(mq, NSE_PRIORITY);
1188 /* create our peer entry for this peer */
1189 peer_entry = GNUNET_new(struct NSEPeerEntry);
1190 peer_entry->id = peer;
1191 peer_entry->mq = mq;
1192 GNUNET_assert(GNUNET_OK ==
1193 GNUNET_CONTAINER_multipeermap_put(
1197 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1198 peer_entry->transmit_task =
1199 GNUNET_SCHEDULER_add_delayed(get_transmit_delay(-1),
1202 GNUNET_STATISTICS_update(stats, "# peers connected", 1, GNUNET_NO);
1208 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
1209 * any pending transmission requests to that peer.
1211 * @param cls closure
1212 * @param peer peer identity this notification is about
1213 * @parma internal_cls the `struct NSEPeerEntry` for the @a peer
1216 handle_core_disconnect(void *cls,
1217 const struct GNUNET_PeerIdentity *peer,
1220 struct NSEPeerEntry *pos = internal_cls;
1223 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1224 "Peer `%s' disconnected from us\n",
1226 GNUNET_assert(GNUNET_YES ==
1227 GNUNET_CONTAINER_multipeermap_remove(peers, peer, pos));
1228 if (NULL != pos->transmit_task)
1230 GNUNET_SCHEDULER_cancel(pos->transmit_task);
1231 pos->transmit_task = NULL;
1234 GNUNET_STATISTICS_update(stats, "# peers connected", -1, GNUNET_NO);
1238 #if ENABLE_NSE_HISTOGRAM
1240 * Functions of this type are called to notify a successful transmission of the
1241 * message to the logger service
1244 * @param size the amount of data sent (ignored)
1247 flush_comp_cb(void *cls, size_t size)
1251 GNUNET_TESTBED_LOGGER_disconnect(lh);
1258 * Task run during shutdown.
1263 shutdown_task(void *cls)
1266 if (NULL != flood_task)
1268 GNUNET_SCHEDULER_cancel(flood_task);
1271 if (NULL != proof_task)
1273 GNUNET_SCHEDULER_cancel(proof_task);
1275 write_proof(); /* remember progress */
1279 GNUNET_notification_context_destroy(nc);
1282 if (NULL != core_api)
1284 GNUNET_CORE_disconnect(core_api);
1289 GNUNET_STATISTICS_destroy(stats, GNUNET_NO);
1294 GNUNET_CONTAINER_multipeermap_destroy(peers);
1297 if (NULL != my_private_key)
1299 GNUNET_free(my_private_key);
1300 my_private_key = NULL;
1302 #if ENABLE_NSE_HISTOGRAM
1303 if (NULL != logger_test)
1305 GNUNET_CLIENT_service_test_cancel(logger_test);
1310 GNUNET_TESTBED_LOGGER_flush(lh, &flush_comp_cb, NULL);
1312 if (NULL != histogram)
1314 GNUNET_BIO_write_close(histogram);
1322 * Called on core init/fail.
1324 * @param cls service closure
1325 * @param identity the public identity of this peer
1328 core_init(void *cls, const struct GNUNET_PeerIdentity *identity)
1330 struct GNUNET_TIME_Absolute now;
1331 struct GNUNET_TIME_Absolute prev_time;
1334 if (NULL == identity)
1336 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n");
1337 GNUNET_SCHEDULER_shutdown();
1340 GNUNET_assert(0 == GNUNET_memcmp(&my_identity, identity));
1341 now = GNUNET_TIME_absolute_get();
1342 current_timestamp.abs_value_us =
1343 (now.abs_value_us / gnunet_nse_interval.rel_value_us) *
1344 gnunet_nse_interval.rel_value_us;
1346 GNUNET_TIME_absolute_add(current_timestamp, gnunet_nse_interval);
1347 estimate_index = HISTORY_SIZE - 1;
1349 if (GNUNET_YES == check_proof_of_work(&my_identity.public_key, my_proof))
1351 int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1352 prev_time.abs_value_us =
1353 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us;
1354 setup_flood_message(idx, prev_time);
1355 setup_flood_message(estimate_index, current_timestamp);
1359 GNUNET_SCHEDULER_add_at(next_timestamp, &update_flood_message, NULL);
1363 #if ENABLE_NSE_HISTOGRAM
1365 * Function called with the status of the testbed logger service
1368 * @param status #GNUNET_YES if the service is running,
1369 * #GNUNET_NO if the service is not running
1370 * #GNUNET_SYSERR if the configuration is invalid
1373 status_cb(void *cls, int status)
1377 if (GNUNET_YES != status)
1379 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Testbed logger not running\n");
1382 if (NULL == (lh = GNUNET_TESTBED_LOGGER_connect(cfg)))
1384 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
1385 "Cannot connect to the testbed logger. Exiting.\n");
1386 GNUNET_SCHEDULER_shutdown();
1393 * Handle network size estimate clients.
1395 * @param cls closure
1396 * @param c configuration to use
1397 * @param service the initialized service
1401 const struct GNUNET_CONFIGURATION_Handle *c,
1402 struct GNUNET_SERVICE_Handle *service)
1404 struct GNUNET_MQ_MessageHandler core_handlers[] =
1405 { GNUNET_MQ_hd_fixed_size(p2p_estimate,
1406 GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1407 struct GNUNET_NSE_FloodMessage,
1409 GNUNET_MQ_handler_end() };
1411 struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
1416 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time(cfg,
1419 &gnunet_nse_interval))
1421 GNUNET_log_config_missing(GNUNET_ERROR_TYPE_ERROR, "NSE", "INTERVAL");
1422 GNUNET_SCHEDULER_shutdown();
1425 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time(cfg,
1430 GNUNET_log_config_missing(GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKDELAY");
1431 GNUNET_SCHEDULER_shutdown();
1434 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number(cfg,
1437 &nse_work_required))
1439 GNUNET_log_config_missing(GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKBITS");
1440 GNUNET_SCHEDULER_shutdown();
1443 if (nse_work_required >= sizeof(struct GNUNET_HashCode) * 8)
1445 GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR,
1448 _("Value is too large.\n"));
1449 GNUNET_SCHEDULER_shutdown();
1453 #if ENABLE_NSE_HISTOGRAM
1455 char *histogram_dir;
1458 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename(cfg,
1464 0 < GNUNET_asprintf(&histogram_fn, "%s/timestamps", histogram_dir));
1465 GNUNET_free(histogram_dir);
1466 histogram = GNUNET_BIO_write_open(histogram_fn);
1467 if (NULL == histogram)
1468 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
1469 "Unable to open histogram file `%s'\n",
1471 GNUNET_free(histogram_fn);
1473 logger_test = GNUNET_CLIENT_service_test("testbed-logger",
1475 GNUNET_TIME_UNIT_SECONDS,
1481 GNUNET_SCHEDULER_add_shutdown(&shutdown_task, NULL);
1482 pk = GNUNET_CRYPTO_eddsa_key_create_from_configuration(cfg);
1483 GNUNET_assert(NULL != pk);
1484 my_private_key = pk;
1485 GNUNET_CRYPTO_eddsa_key_get_public(my_private_key, &my_identity.public_key);
1487 GNUNET_CONFIGURATION_get_value_filename(cfg, "NSE", "PROOFFILE", &proof))
1489 GNUNET_log_config_missing(GNUNET_ERROR_TYPE_ERROR, "NSE", "PROOFFILE");
1490 GNUNET_free(my_private_key);
1491 my_private_key = NULL;
1492 GNUNET_SCHEDULER_shutdown();
1495 if ((GNUNET_YES != GNUNET_DISK_file_test(proof)) ||
1496 (sizeof(my_proof) !=
1497 GNUNET_DISK_fn_read(proof, &my_proof, sizeof(my_proof))))
1501 GNUNET_SCHEDULER_add_with_priority(GNUNET_SCHEDULER_PRIORITY_IDLE,
1505 peers = GNUNET_CONTAINER_multipeermap_create(128, GNUNET_YES);
1506 nc = GNUNET_notification_context_create(1);
1507 /* Connect to core service and register core handlers */
1509 GNUNET_CORE_connect(cfg, /* Main configuration */
1510 NULL, /* Closure passed to functions */
1511 &core_init, /* Call core_init once connected */
1512 &handle_core_connect, /* Handle connects */
1513 &handle_core_disconnect, /* Handle disconnects */
1514 core_handlers); /* Register these handlers */
1515 if (NULL == core_api)
1517 GNUNET_SCHEDULER_shutdown();
1520 stats = GNUNET_STATISTICS_create("nse", cfg);
1525 * Callback called when a client connects to the service.
1527 * @param cls closure for the service
1528 * @param c the new client that connected to the service
1529 * @param mq the message queue used to send messages to the client
1533 client_connect_cb(void *cls,
1534 struct GNUNET_SERVICE_Client *c,
1535 struct GNUNET_MQ_Handle *mq)
1544 * Callback called when a client disconnected from the service
1546 * @param cls closure for the service
1547 * @param c the client that disconnected
1548 * @param internal_cls should be equal to @a c
1551 client_disconnect_cb(void *cls,
1552 struct GNUNET_SERVICE_Client *c,
1556 GNUNET_assert(c == internal_cls);
1561 * Define "main" method using service macro.
1563 GNUNET_SERVICE_MAIN("nse",
1564 GNUNET_SERVICE_OPTION_NONE,
1567 &client_disconnect_cb,
1569 GNUNET_MQ_hd_fixed_size(start,
1570 GNUNET_MESSAGE_TYPE_NSE_START,
1571 struct GNUNET_MessageHeader,
1573 GNUNET_MQ_handler_end());
1576 #if defined(LINUX) && defined(__GLIBC__)
1580 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1582 void __attribute__ ((constructor)) GNUNET_ARM_memory_init()
1584 mallopt(M_TRIM_THRESHOLD, 4 * 1024);
1585 mallopt(M_TOP_PAD, 1 * 1024);
1591 /* end of gnunet-service-nse.c */