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
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 * @file nse/gnunet-service-nse.c
23 * @brief network size estimation service
24 * @author Nathan Evans
25 * @author Christian Grothoff
27 * The purpose of this service is to estimate the size of the network.
28 * Given a specified interval, each peer hashes the most recent
29 * timestamp which is evenly divisible by that interval. This hash is
30 * compared in distance to the peer identity to choose an offset. The
31 * closer the peer identity to the hashed timestamp, the earlier the
32 * peer sends out a "nearest peer" message. The closest peer's
33 * message should thus be received before any others, which stops
34 * those peer from sending their messages at a later duration. So
35 * every peer should receive the same nearest peer message, and from
36 * this can calculate the expected number of peers in the network.
40 #include "gnunet_util_lib.h"
41 #include "gnunet_constants.h"
42 #include "gnunet_protocols.h"
43 #include "gnunet_signatures.h"
44 #include "gnunet_statistics_service.h"
45 #include "gnunet_core_service.h"
46 #include "gnunet_nse_service.h"
47 #if ENABLE_NSE_HISTOGRAM
48 #include "gnunet_testbed_logger_service.h"
56 * Should messages be delayed randomly? This option should be set to
57 * #GNUNET_NO only for experiments, not in production.
59 #define USE_RANDOM_DELAYS GNUNET_YES
62 * Generate extensive debug-level log messages?
64 #define DEBUG_NSE GNUNET_NO
67 * Over how many values do we calculate the weighted average?
69 #define HISTORY_SIZE 64
72 * Message priority to use.
74 #define NSE_PRIORITY GNUNET_CORE_PRIO_CRITICAL_CONTROL
77 #define log2(a) (log(a)/log(2))
81 * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits.
83 static unsigned long long nse_work_required;
86 * Interval for sending network size estimation flood requests.
88 static struct GNUNET_TIME_Relative gnunet_nse_interval;
91 * Interval between proof find runs.
93 static struct GNUNET_TIME_Relative proof_find_delay;
95 #if ENABLE_NSE_HISTOGRAM
98 * Handle to test if testbed logger service is running or not
100 struct GNUNET_CLIENT_TestHandle *logger_test;
103 * Handle for writing when we received messages to disk.
105 static struct GNUNET_TESTBED_LOGGER_Handle *lh;
108 * Handle for writing message received timestamp information to disk.
110 static struct GNUNET_BIO_WriteHandle *histogram;
116 * Per-peer information.
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
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;
165 GNUNET_NETWORK_STRUCT_BEGIN
168 * Network size estimate reply; sent when "this"
169 * peer's timer has run out before receiving a
170 * valid reply from another peer.
172 struct GNUNET_NSE_FloodMessage
175 * Type: #GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD
177 struct GNUNET_MessageHeader header;
180 * Number of hops this message has taken so far.
182 uint32_t hop_count GNUNET_PACKED;
187 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
190 * The current timestamp value (which all
191 * peers should agree on).
193 struct GNUNET_TIME_AbsoluteNBO timestamp;
196 * Number of matching bits between the hash
197 * of timestamp and the initiator's public
200 uint32_t matching_bits GNUNET_PACKED;
203 * Public key of the originator.
205 struct GNUNET_PeerIdentity origin;
208 * Proof of work, causing leading zeros when hashed with pkey.
210 uint64_t proof_of_work GNUNET_PACKED;
213 * Signature (over range specified in purpose).
215 struct GNUNET_CRYPTO_EddsaSignature signature;
217 GNUNET_NETWORK_STRUCT_END
220 * Handle to our current configuration.
222 static const struct GNUNET_CONFIGURATION_Handle *cfg;
225 * Handle to the statistics service.
227 static struct GNUNET_STATISTICS_Handle *stats;
230 * Handle to the core service.
232 static struct GNUNET_CORE_Handle *core_api;
235 * Map of all connected peers.
237 static struct GNUNET_CONTAINER_MultiPeerMap *peers;
240 * The current network size estimate. Number of bits matching on
243 static double current_size_estimate;
246 * The standard deviation of the last #HISTORY_SIZE network
249 static double current_std_dev = NAN;
252 * Current hop counter estimate (estimate for network diameter).
254 static uint32_t hop_count_max;
257 * Message for the next round, if we got any.
259 static struct GNUNET_NSE_FloodMessage next_message;
262 * Array of recent size estimate messages.
264 static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE];
267 * Index of most recent estimate.
269 static unsigned int estimate_index;
272 * Number of valid entries in the history.
274 static unsigned int estimate_count;
277 * Task scheduled to update our flood message for the next round.
279 static struct GNUNET_SCHEDULER_Task *flood_task;
282 * Task scheduled to compute our proof.
284 static struct GNUNET_SCHEDULER_Task *proof_task;
287 * Notification context, simplifies client broadcasts.
289 static struct GNUNET_NotificationContext *nc;
292 * The next major time.
294 static struct GNUNET_TIME_Absolute next_timestamp;
297 * The current major time.
299 static struct GNUNET_TIME_Absolute current_timestamp;
302 * The private key of this peer.
304 static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
307 * The peer identity of this peer.
309 static struct GNUNET_PeerIdentity my_identity;
312 * Proof of work for this peer.
314 static uint64_t my_proof;
318 * Initialize a message to clients with the current network
321 * @param em message to fill in
324 setup_estimate_message (struct GNUNET_NSE_ClientMessage *em)
334 /* Weighted incremental algorithm for stddev according to West (1979) */
346 for (unsigned int i = 0; i < estimate_count; i++)
348 unsigned int j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
350 val = htonl (size_estimate_messages[j].matching_bits);
351 weight = estimate_count + 1 - i;
353 temp = weight + sumweight;
355 r = q * weight / temp;
357 sum += sumweight * q * r;
360 if (estimate_count > 0)
361 variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0);
363 /* trivial version for debugging */
366 /* non-weighted trivial version */
372 for (unsigned int i = 0; i < estimate_count; i++)
374 unsigned int j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
376 val = htonl (size_estimate_messages[j].matching_bits);
380 if (0 != estimate_count)
382 mean = sum / estimate_count;
383 variance = (vsq - mean * sum) / (estimate_count - 1.0); // terrible for numerical stability...
387 std_dev = sqrt (variance);
389 std_dev = variance; /* must be infinity due to estimate_count == 0 */
390 current_std_dev = std_dev;
391 current_size_estimate = mean;
393 em->header.size = htons (sizeof (struct GNUNET_NSE_ClientMessage));
394 em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE);
395 em->reserved = htonl (0);
396 em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
398 double se = mean - 0.332747;
399 unsigned int j = GNUNET_CONTAINER_multipeermap_size (peers);
401 j = 1; /* Avoid log2(0); can only happen if CORE didn't report
402 connection to self yet */
404 em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se,
406 em->std_deviation = GNUNET_hton_double (std_dev);
407 GNUNET_STATISTICS_set (stats,
408 "# nodes in the network (estimate)",
409 (uint64_t) pow (2, GNUNET_MAX (se,
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,
427 const struct GNUNET_MessageHeader *message)
429 struct GNUNET_SERVICE_Client *client = cls;
430 struct GNUNET_MQ_Handle *mq;
431 struct GNUNET_NSE_ClientMessage em;
432 struct GNUNET_MQ_Envelope *env;
434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
435 "Received START message from client\n");
436 mq = GNUNET_SERVICE_client_get_mq (client);
437 GNUNET_notification_context_add (nc,
439 setup_estimate_message (&em);
440 env = GNUNET_MQ_msg_copy (&em.header);
443 GNUNET_SERVICE_client_continue (client);
448 * How long should we delay a message to go the given number of
451 * @param matching_bits number of matching bits to consider
454 get_matching_bits_delay (uint32_t matching_bits)
456 /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */
457 // S is next_timestamp (ignored in return value)
458 // f is frequency (gnunet_nse_interval)
459 // x is matching_bits
460 // p' is current_size_estimate
461 return ((double) gnunet_nse_interval.rel_value_us / (double) 2.0) -
462 ((gnunet_nse_interval.rel_value_us / M_PI) *
463 atan (matching_bits - current_size_estimate));
468 * What delay randomization should we apply for a given number of matching bits?
470 * @param matching_bits number of matching bits
471 * @return random delay to apply
473 static struct GNUNET_TIME_Relative
474 get_delay_randomization (uint32_t matching_bits)
476 #if USE_RANDOM_DELAYS
477 struct GNUNET_TIME_Relative ret;
481 d = get_matching_bits_delay (matching_bits);
482 i = (uint32_t) (d / (double) (hop_count_max + 1));
483 ret.rel_value_us = i;
484 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
485 "Randomizing flood using latencies up to %s\n",
486 GNUNET_STRINGS_relative_time_to_string (ret,
488 ret.rel_value_us = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1);
491 return GNUNET_TIME_UNIT_ZERO;
497 * Calculate the 'proof-of-work' hash (an expensive hash).
499 * @param buf data to hash
500 * @param buf_len number of bytes in @a buf
501 * @param result where to write the resulting hash
504 pow_hash (const void *buf,
506 struct GNUNET_HashCode *result)
509 gcry_kdf_derive (buf, buf_len,
512 "gnunet-proof-of-work",
513 strlen ("gnunet-proof-of-work"),
514 2 /* iterations; keep cost of individual op small */,
515 sizeof (struct GNUNET_HashCode),
521 * Get the number of matching bits that the given timestamp has to the given peer ID.
523 * @param timestamp time to generate key
524 * @param id peer identity to compare with
525 * @return number of matching bits
528 get_matching_bits (struct GNUNET_TIME_Absolute timestamp,
529 const struct GNUNET_PeerIdentity *id)
531 struct GNUNET_HashCode timestamp_hash;
532 struct GNUNET_HashCode pid_hash;
534 GNUNET_CRYPTO_hash (×tamp.abs_value_us,
535 sizeof (timestamp.abs_value_us),
537 GNUNET_CRYPTO_hash (id,
538 sizeof (struct GNUNET_PeerIdentity),
540 return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash,
546 * Get the transmission delay that should be applied for a
549 * @param round_offset -1 for the previous round (random delay between 0 and 50ms)
550 * 0 for the current round (based on our proximity to time key)
551 * @return delay that should be applied
553 static struct GNUNET_TIME_Relative
554 get_transmit_delay (int round_offset)
556 struct GNUNET_TIME_Relative ret;
557 struct GNUNET_TIME_Absolute tgt;
559 uint32_t matching_bits;
561 switch (round_offset)
564 /* previous round is randomized between 0 and 50 ms */
565 #if USE_RANDOM_DELAYS
566 ret.rel_value_us = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
569 ret = GNUNET_TIME_UNIT_ZERO;
571 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
572 "Transmitting previous round behind schedule in %s\n",
573 GNUNET_STRINGS_relative_time_to_string (ret,
577 /* current round is based on best-known matching_bits */
579 ntohl (size_estimate_messages[estimate_index].matching_bits);
580 dist_delay = get_matching_bits_delay (matching_bits);
581 dist_delay += get_delay_randomization (matching_bits).rel_value_us;
582 ret.rel_value_us = (uint64_t) dist_delay;
583 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
584 "For round %s, delay for %u matching bits is %s\n",
585 GNUNET_STRINGS_absolute_time_to_string (current_timestamp),
586 (unsigned int) matching_bits,
587 GNUNET_STRINGS_relative_time_to_string (ret,
589 /* now consider round start time and add delay to it */
590 tgt = GNUNET_TIME_absolute_add (current_timestamp,
592 return GNUNET_TIME_absolute_get_remaining (tgt);
595 return GNUNET_TIME_UNIT_FOREVER_REL;
600 * Task that triggers a NSE P2P transmission.
602 * @param cls the `struct NSEPeerEntry *`
605 transmit_task_cb (void *cls)
607 struct NSEPeerEntry *peer_entry = cls;
609 struct GNUNET_MQ_Envelope *env;
611 peer_entry->transmit_task = NULL;
612 idx = estimate_index;
613 if (GNUNET_NO == peer_entry->previous_round)
615 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
616 peer_entry->previous_round = GNUNET_YES;
617 peer_entry->transmit_task
618 = GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0),
622 if ((0 == ntohl (size_estimate_messages[idx].hop_count)) &&
623 (NULL != proof_task))
625 GNUNET_STATISTICS_update (stats,
626 "# flood messages not generated (no proof yet)",
631 if (0 == ntohs (size_estimate_messages[idx].header.size))
633 GNUNET_STATISTICS_update (stats,
634 "# flood messages not generated (lack of history)",
639 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
640 "In round %s, sending to `%s' estimate with %u bits\n",
641 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (size_estimate_messages[idx].timestamp)),
642 GNUNET_i2s (peer_entry->id),
643 (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
644 if (0 == ntohl (size_estimate_messages[idx].hop_count))
645 GNUNET_STATISTICS_update (stats,
646 "# flood messages started",
649 GNUNET_STATISTICS_update (stats,
650 "# flood messages transmitted",
653 #if ENABLE_NSE_HISTOGRAM
654 peer_entry->transmitted_messages++;
655 peer_entry->last_transmitted_size
656 = ntohl(size_estimate_messages[idx].matching_bits);
658 env = GNUNET_MQ_msg_copy (&size_estimate_messages[idx].header);
659 GNUNET_MQ_send (peer_entry->mq,
665 * We've sent on our flood message or one that we received which was
666 * validated and closer than ours. Update the global list of recent
667 * messages and the average. Also re-broadcast the message to any
671 update_network_size_estimate ()
673 struct GNUNET_NSE_ClientMessage em;
675 setup_estimate_message (&em);
676 GNUNET_notification_context_broadcast (nc,
683 * Setup a flood message in our history array at the given
684 * slot offset for the given timestamp.
686 * @param slot index to use
687 * @param ts timestamp to use
690 setup_flood_message (unsigned int slot,
691 struct GNUNET_TIME_Absolute ts)
693 struct GNUNET_NSE_FloodMessage *fm;
694 uint32_t matching_bits;
696 matching_bits = get_matching_bits (ts,
698 fm = &size_estimate_messages[slot];
699 fm->header.size = htons (sizeof (struct GNUNET_NSE_FloodMessage));
700 fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
701 fm->hop_count = htonl (0);
702 fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
704 htonl (sizeof (struct GNUNET_NSE_FloodMessage) -
705 sizeof (struct GNUNET_MessageHeader) - sizeof (uint32_t) -
706 sizeof (struct GNUNET_CRYPTO_EddsaSignature));
707 fm->matching_bits = htonl (matching_bits);
708 fm->timestamp = GNUNET_TIME_absolute_hton (ts);
709 fm->origin = my_identity;
710 fm->proof_of_work = my_proof;
711 if (nse_work_required > 0)
712 GNUNET_assert (GNUNET_OK ==
713 GNUNET_CRYPTO_eddsa_sign (my_private_key,
717 memset (&fm->signature,
719 sizeof (fm->signature));
724 * Schedule transmission for the given peer for the current round based
725 * on what we know about the desired delay.
728 * @param key hash of peer identity
729 * @param value the `struct NSEPeerEntry`
730 * @return #GNUNET_OK (continue to iterate)
733 schedule_current_round (void *cls,
734 const struct GNUNET_PeerIdentity * key,
737 struct NSEPeerEntry *peer_entry = value;
738 struct GNUNET_TIME_Relative delay;
740 if (NULL != peer_entry->transmit_task)
742 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
743 peer_entry->previous_round = GNUNET_NO;
745 #if ENABLE_NSE_HISTOGRAM
746 if (peer_entry->received_messages > 1)
747 GNUNET_STATISTICS_update(stats,
749 peer_entry->received_messages - 1,
751 peer_entry->transmitted_messages = 0;
752 peer_entry->last_transmitted_size = 0;
753 peer_entry->received_messages = 0;
756 get_transmit_delay ((GNUNET_NO == peer_entry->previous_round) ? -1 : 0);
757 peer_entry->transmit_task =
758 GNUNET_SCHEDULER_add_delayed (delay,
766 * Update our flood message to be sent (and our timestamps).
771 update_flood_message (void *cls)
773 struct GNUNET_TIME_Relative offset;
777 offset = GNUNET_TIME_absolute_get_remaining (next_timestamp);
778 if (0 != offset.rel_value_us)
780 /* somehow run early, delay more */
782 GNUNET_SCHEDULER_add_delayed (offset,
783 &update_flood_message,
787 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
788 if (estimate_count < HISTORY_SIZE)
790 current_timestamp = next_timestamp;
792 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
793 if ( (current_timestamp.abs_value_us ==
794 GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value_us) &&
795 (get_matching_bits (current_timestamp, &my_identity) <
796 ntohl(next_message.matching_bits)) )
798 /* we received a message for this round way early, use it! */
799 size_estimate_messages[estimate_index] = next_message;
800 size_estimate_messages[estimate_index].hop_count =
801 htonl (1 + ntohl (next_message.hop_count));
804 setup_flood_message (estimate_index,
806 next_message.matching_bits = htonl (0); /* reset for 'next' round */
808 for (i = 0; i < HISTORY_SIZE; i++)
809 hop_count_max = GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count),
811 GNUNET_CONTAINER_multipeermap_iterate (peers,
812 &schedule_current_round,
815 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
817 &update_flood_message,
823 * Count the leading zeroes in hash.
825 * @param hash to count leading zeros in
826 * @return the number of leading zero bits.
829 count_leading_zeroes (const struct GNUNET_HashCode *hash)
831 unsigned int hash_count;
834 while (0 == GNUNET_CRYPTO_hash_get_bit (hash,
842 * Check whether the given public key and integer are a valid proof of
845 * @param pkey the public key
846 * @param val the integer
847 * @return #GNUNET_YES if valid, #GNUNET_NO if not
850 check_proof_of_work (const struct GNUNET_CRYPTO_EddsaPublicKey *pkey,
853 char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
854 sizeof (val)] GNUNET_ALIGN;
855 struct GNUNET_HashCode result;
860 GNUNET_memcpy (&buf[sizeof (val)],
862 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
866 return (count_leading_zeroes (&result) >=
867 nse_work_required) ? GNUNET_YES : GNUNET_NO;
872 * Write our current proof to disk.
880 GNUNET_CONFIGURATION_get_value_filename (cfg,
885 if (sizeof (my_proof) !=
886 GNUNET_DISK_fn_write (proof,
889 GNUNET_DISK_PERM_USER_READ |
890 GNUNET_DISK_PERM_USER_WRITE))
891 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
899 * Find our proof of work.
901 * @param cls closure (unused)
904 find_proof (void *cls)
906 #define ROUND_SIZE 10
908 char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
909 sizeof (uint64_t)] GNUNET_ALIGN;
910 struct GNUNET_HashCode result;
914 GNUNET_memcpy (&buf[sizeof (uint64_t)], &my_identity,
915 sizeof (struct GNUNET_PeerIdentity));
918 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
920 GNUNET_memcpy (buf, &counter, sizeof (uint64_t));
921 pow_hash (buf, sizeof (buf), &result);
922 if (nse_work_required <= count_leading_zeroes (&result))
925 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work found: %llu!\n",
926 (unsigned long long) GNUNET_ntohll (counter));
928 setup_flood_message (estimate_index, current_timestamp);
934 if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
936 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
937 "Testing proofs currently at %llu\n",
938 (unsigned long long) counter);
939 /* remember progress every 100 rounds */
948 GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
949 GNUNET_SCHEDULER_PRIORITY_IDLE,
955 * An incoming flood message has been received which claims
956 * to have more bits matching than any we know in this time
957 * period. Verify the signature and/or proof of work.
959 * @param incoming_flood the message to verify
960 * @return #GNUNET_YES if the message is verified
961 * #GNUNET_NO if the key/signature don't verify
964 verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood)
967 check_proof_of_work (&incoming_flood->origin.public_key,
968 incoming_flood->proof_of_work))
970 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
971 "Proof of work invalid: %llu!\n",
973 GNUNET_ntohll (incoming_flood->proof_of_work));
977 if ((nse_work_required > 0) &&
979 GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
980 &incoming_flood->purpose,
981 &incoming_flood->signature,
982 &incoming_flood->origin.public_key)))
992 * Update transmissions for the given peer for the current round based
993 * on updated proximity information.
995 * @param cls peer entry to exclude from updates
996 * @param key hash of peer identity
997 * @param value the `struct NSEPeerEntry *` of a peer to transmit to
998 * @return #GNUNET_OK (continue to iterate)
1001 update_flood_times (void *cls,
1002 const struct GNUNET_PeerIdentity *key,
1005 struct NSEPeerEntry *exclude = cls;
1006 struct NSEPeerEntry *peer_entry = value;
1007 struct GNUNET_TIME_Relative delay;
1009 if (peer_entry == exclude)
1010 return GNUNET_OK; /* trigger of the update */
1011 if (GNUNET_NO == peer_entry->previous_round)
1013 /* still stuck in previous round, no point to update, check that
1014 * we are active here though... */
1015 if (NULL == peer_entry->transmit_task)
1021 if (NULL != peer_entry->transmit_task)
1023 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1024 peer_entry->transmit_task = NULL;
1026 delay = get_transmit_delay (0);
1027 peer_entry->transmit_task =
1028 GNUNET_SCHEDULER_add_delayed (delay,
1029 &transmit_task_cb, peer_entry);
1035 * Core handler for size estimate flooding messages.
1037 * @param cls peer this message is from
1038 * @param incoming_flood received message
1041 handle_p2p_estimate (void *cls,
1042 const struct GNUNET_NSE_FloodMessage *incoming_flood)
1044 struct NSEPeerEntry *peer_entry = cls;
1045 struct GNUNET_TIME_Absolute ts;
1046 uint32_t matching_bits;
1049 #if ENABLE_NSE_HISTOGRAM
1053 t = GNUNET_TIME_absolute_get().abs_value_us;
1055 GNUNET_TESTBED_LOGGER_write (lh, &t, sizeof (uint64_t));
1056 if (NULL != histogram)
1057 GNUNET_BIO_write_int64 (histogram, t);
1060 GNUNET_STATISTICS_update (stats,
1061 "# flood messages received",
1064 matching_bits = ntohl (incoming_flood->matching_bits);
1069 struct GNUNET_PeerIdentity os;
1071 GNUNET_snprintf (origin,
1074 GNUNET_i2s (&incoming_flood->origin));
1075 GNUNET_snprintf (pred,
1078 GNUNET_i2s (peer_entry->id));
1079 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1080 "Flood at %s from `%s' via `%s' at `%s' with bits %u\n",
1081 GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp)),
1084 GNUNET_i2s (&my_identity),
1085 (unsigned int) matching_bits);
1089 #if ENABLE_NSE_HISTOGRAM
1090 peer_entry->received_messages++;
1091 if (peer_entry->transmitted_messages > 0 &&
1092 peer_entry->last_transmitted_size >= matching_bits)
1093 GNUNET_STATISTICS_update(stats,
1099 ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp);
1100 if (ts.abs_value_us == current_timestamp.abs_value_us)
1101 idx = estimate_index;
1102 else if (ts.abs_value_us ==
1103 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us)
1104 idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1105 else if (ts.abs_value_us == next_timestamp.abs_value_us)
1107 if (matching_bits <= ntohl (next_message.matching_bits))
1108 return; /* ignore, simply too early/late */
1110 verify_message_crypto (incoming_flood))
1112 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1113 "Peer %s is likely ill-configured!\n",
1114 GNUNET_i2s (peer_entry->id));
1115 GNUNET_break_op (0);
1118 next_message = *incoming_flood;
1123 GNUNET_STATISTICS_update (stats,
1124 "# flood messages discarded (clock skew too large)",
1128 if (0 == (memcmp (peer_entry->id,
1130 sizeof (struct GNUNET_PeerIdentity))))
1132 /* send to self, update our own estimate IF this also comes from us! */
1134 memcmp (&incoming_flood->origin,
1135 &my_identity, sizeof (my_identity)))
1136 update_network_size_estimate ();
1139 if (matching_bits ==
1140 ntohl (size_estimate_messages[idx].matching_bits))
1142 /* Cancel transmission in the other direction, as this peer clearly has
1143 up-to-date information already. Even if we didn't talk to this peer in
1144 the previous round, we should no longer send it stale information as it
1145 told us about the current round! */
1146 peer_entry->previous_round = GNUNET_YES;
1147 if (idx != estimate_index)
1149 /* do not transmit information for the previous round to this peer
1150 anymore (but allow current round) */
1153 /* got up-to-date information for current round, cancel transmission to
1154 * this peer altogether */
1155 if (NULL != peer_entry->transmit_task)
1157 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1158 peer_entry->transmit_task = NULL;
1162 if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits))
1164 if ( (idx < estimate_index) &&
1165 (peer_entry->previous_round == GNUNET_YES))
1167 peer_entry->previous_round = GNUNET_NO;
1169 /* push back our result now, that peer is spreading bad information... */
1170 if (NULL != peer_entry->transmit_task)
1171 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1172 peer_entry->transmit_task
1173 = GNUNET_SCHEDULER_add_now (&transmit_task_cb,
1175 /* Not closer than our most recent message, no need to do work here */
1176 GNUNET_STATISTICS_update (stats,
1177 "# flood messages ignored (had closer already)",
1183 verify_message_crypto (incoming_flood))
1185 GNUNET_break_op (0);
1188 GNUNET_assert (matching_bits >
1189 ntohl (size_estimate_messages[idx].matching_bits));
1190 /* Cancel transmission in the other direction, as this peer clearly has
1191 * up-to-date information already.
1193 peer_entry->previous_round = GNUNET_YES;
1194 if (idx == estimate_index)
1196 /* cancel any activity for current round */
1197 if (NULL != peer_entry->transmit_task)
1199 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1200 peer_entry->transmit_task = NULL;
1203 size_estimate_messages[idx] = *incoming_flood;
1204 size_estimate_messages[idx].hop_count =
1205 htonl (ntohl (incoming_flood->hop_count) + 1);
1207 GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1,
1209 GNUNET_STATISTICS_set (stats,
1210 "# estimated network diameter",
1211 hop_count_max, GNUNET_NO);
1213 /* have a new, better size estimate, inform clients */
1214 update_network_size_estimate ();
1217 GNUNET_CONTAINER_multipeermap_iterate (peers,
1218 &update_flood_times,
1224 * Method called whenever a peer connects. Sets up the PeerEntry and
1225 * schedules the initial size info transmission to this peer.
1227 * @param cls closure
1228 * @param peer peer identity this notification is about
1231 handle_core_connect (void *cls,
1232 const struct GNUNET_PeerIdentity *peer,
1233 struct GNUNET_MQ_Handle *mq)
1235 struct NSEPeerEntry *peer_entry;
1239 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1240 "Peer `%s' connected to us\n",
1242 /* set our default transmission options */
1243 extra = GNUNET_CORE_get_mq_options (GNUNET_NO,
1246 GNUNET_MQ_set_options (mq,
1249 /* create our peer entry for this peer */
1250 peer_entry = GNUNET_new (struct NSEPeerEntry);
1251 peer_entry->id = peer;
1252 peer_entry->mq = mq;
1253 GNUNET_assert (GNUNET_OK ==
1254 GNUNET_CONTAINER_multipeermap_put (peers,
1257 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1258 peer_entry->transmit_task =
1259 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1),
1262 GNUNET_STATISTICS_update (stats,
1263 "# peers connected",
1271 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
1272 * any pending transmission requests to that peer.
1274 * @param cls closure
1275 * @param peer peer identity this notification is about
1276 * @parma internal_cls the `struct NSEPeerEntry` for the @a peer
1279 handle_core_disconnect (void *cls,
1280 const struct GNUNET_PeerIdentity *peer,
1283 struct NSEPeerEntry *pos = internal_cls;
1285 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1286 "Peer `%s' disconnected from us\n",
1288 GNUNET_assert (GNUNET_YES ==
1289 GNUNET_CONTAINER_multipeermap_remove (peers,
1292 if (NULL != pos->transmit_task)
1294 GNUNET_SCHEDULER_cancel (pos->transmit_task);
1295 pos->transmit_task = NULL;
1298 GNUNET_STATISTICS_update (stats,
1299 "# peers connected",
1305 #if ENABLE_NSE_HISTOGRAM
1307 * Functions of this type are called to notify a successful transmission of the
1308 * message to the logger service
1311 * @param size the amount of data sent (ignored)
1314 flush_comp_cb (void *cls,
1317 GNUNET_TESTBED_LOGGER_disconnect (lh);
1324 * Task run during shutdown.
1329 shutdown_task (void *cls)
1331 if (NULL != flood_task)
1333 GNUNET_SCHEDULER_cancel (flood_task);
1336 if (NULL != proof_task)
1338 GNUNET_SCHEDULER_cancel (proof_task);
1340 write_proof (); /* remember progress */
1344 GNUNET_notification_context_destroy (nc);
1347 if (NULL != core_api)
1349 GNUNET_CORE_disconnecT (core_api);
1354 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1359 GNUNET_CONTAINER_multipeermap_destroy (peers);
1362 if (NULL != my_private_key)
1364 GNUNET_free (my_private_key);
1365 my_private_key = NULL;
1367 #if ENABLE_NSE_HISTOGRAM
1368 if (NULL != logger_test)
1370 GNUNET_CLIENT_service_test_cancel (logger_test);
1375 GNUNET_TESTBED_LOGGER_flush (lh,
1379 if (NULL != histogram)
1381 GNUNET_BIO_write_close (histogram);
1389 * Called on core init/fail.
1391 * @param cls service closure
1392 * @param identity the public identity of this peer
1395 core_init (void *cls,
1396 const struct GNUNET_PeerIdentity *identity)
1398 struct GNUNET_TIME_Absolute now;
1399 struct GNUNET_TIME_Absolute prev_time;
1401 if (NULL == identity)
1403 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1404 "Connection to core FAILED!\n");
1405 GNUNET_SCHEDULER_shutdown ();
1409 memcmp (&my_identity,
1411 sizeof (struct GNUNET_PeerIdentity)));
1412 now = GNUNET_TIME_absolute_get ();
1413 current_timestamp.abs_value_us =
1414 (now.abs_value_us / gnunet_nse_interval.rel_value_us) *
1415 gnunet_nse_interval.rel_value_us;
1417 GNUNET_TIME_absolute_add (current_timestamp,
1418 gnunet_nse_interval);
1419 estimate_index = HISTORY_SIZE - 1;
1422 check_proof_of_work (&my_identity.public_key,
1425 int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1426 prev_time.abs_value_us =
1427 current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us;
1428 setup_flood_message (idx,
1430 setup_flood_message (estimate_index,
1435 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
1437 &update_flood_message,
1442 #if ENABLE_NSE_HISTOGRAM
1444 * Function called with the status of the testbed logger service
1447 * @param status #GNUNET_YES if the service is running,
1448 * #GNUNET_NO if the service is not running
1449 * #GNUNET_SYSERR if the configuration is invalid
1452 status_cb (void *cls,
1456 if (GNUNET_YES != status)
1458 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1459 "Testbed logger not running\n");
1462 if (NULL == (lh = GNUNET_TESTBED_LOGGER_connect (cfg)))
1464 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1465 "Cannot connect to the testbed logger. Exiting.\n");
1466 GNUNET_SCHEDULER_shutdown ();
1473 * Handle network size estimate clients.
1475 * @param cls closure
1476 * @param c configuration to use
1477 * @param service the initialized service
1481 const struct GNUNET_CONFIGURATION_Handle *c,
1482 struct GNUNET_SERVICE_Handle *service)
1484 struct GNUNET_MQ_MessageHandler core_handlers[] = {
1485 GNUNET_MQ_hd_fixed_size (p2p_estimate,
1486 GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1487 struct GNUNET_NSE_FloodMessage,
1489 GNUNET_MQ_handler_end ()
1492 struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
1496 GNUNET_CONFIGURATION_get_value_time (cfg,
1499 &gnunet_nse_interval))
1501 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1504 GNUNET_SCHEDULER_shutdown ();
1508 GNUNET_CONFIGURATION_get_value_time (cfg,
1513 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1516 GNUNET_SCHEDULER_shutdown ();
1520 GNUNET_CONFIGURATION_get_value_number (cfg,
1523 &nse_work_required))
1525 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1528 GNUNET_SCHEDULER_shutdown ();
1531 if (nse_work_required >= sizeof (struct GNUNET_HashCode) * 8)
1533 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1536 _("Value is too large.\n"));
1537 GNUNET_SCHEDULER_shutdown ();
1541 #if ENABLE_NSE_HISTOGRAM
1543 char *histogram_dir;
1547 GNUNET_CONFIGURATION_get_value_filename (cfg,
1552 GNUNET_assert (0 < GNUNET_asprintf (&histogram_fn,
1555 GNUNET_free (histogram_dir);
1556 histogram = GNUNET_BIO_write_open (histogram_fn);
1557 if (NULL == histogram)
1558 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1559 "Unable to open histogram file `%s'\n",
1561 GNUNET_free (histogram_fn);
1564 GNUNET_CLIENT_service_test ("testbed-logger",
1566 GNUNET_TIME_UNIT_SECONDS,
1573 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1575 pk = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
1576 GNUNET_assert (NULL != pk);
1577 my_private_key = pk;
1578 GNUNET_CRYPTO_eddsa_key_get_public (my_private_key,
1579 &my_identity.public_key);
1581 GNUNET_CONFIGURATION_get_value_filename (cfg,
1586 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1589 GNUNET_free (my_private_key);
1590 my_private_key = NULL;
1591 GNUNET_SCHEDULER_shutdown ();
1594 if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) ||
1595 (sizeof (my_proof) !=
1596 GNUNET_DISK_fn_read (proof,
1598 sizeof (my_proof))))
1600 GNUNET_free (proof);
1602 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1606 peers = GNUNET_CONTAINER_multipeermap_create (128,
1608 nc = GNUNET_notification_context_create (1);
1609 /* Connect to core service and register core handlers */
1610 core_api = GNUNET_CORE_connecT (cfg, /* Main configuration */
1611 NULL, /* Closure passed to functions */
1612 &core_init, /* Call core_init once connected */
1613 &handle_core_connect, /* Handle connects */
1614 &handle_core_disconnect, /* Handle disconnects */
1615 core_handlers); /* Register these handlers */
1616 if (NULL == core_api)
1618 GNUNET_SCHEDULER_shutdown ();
1621 stats = GNUNET_STATISTICS_create ("nse",
1627 * Callback called when a client connects to the service.
1629 * @param cls closure for the service
1630 * @param c the new client that connected to the service
1631 * @param mq the message queue used to send messages to the client
1635 client_connect_cb (void *cls,
1636 struct GNUNET_SERVICE_Client *c,
1637 struct GNUNET_MQ_Handle *mq)
1644 * Callback called when a client disconnected from the service
1646 * @param cls closure for the service
1647 * @param c the client that disconnected
1648 * @param internal_cls should be equal to @a c
1651 client_disconnect_cb (void *cls,
1652 struct GNUNET_SERVICE_Client *c,
1655 GNUNET_assert (c == internal_cls);
1660 * Define "main" method using service macro.
1664 GNUNET_SERVICE_OPTION_NONE,
1667 &client_disconnect_cb,
1669 GNUNET_MQ_hd_fixed_size (start,
1670 GNUNET_MESSAGE_TYPE_NSE_START,
1671 struct GNUNET_MessageHeader,
1673 GNUNET_MQ_handler_end ());
1676 #if defined(LINUX) && defined(__GLIBC__)
1680 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1682 void __attribute__ ((constructor))
1683 GNUNET_ARM_memory_init ()
1685 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1686 mallopt (M_TOP_PAD, 1 * 1024);
1693 /* end of gnunet-service-nse.c */