2 This file is part of GNUnet.
3 (C) 2009, 2010, 2011, 2012, 2013 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, 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"
55 * Should messages be delayed randomly? This option should be set to
56 * GNUNET_NO only for experiments, not in production. It should also
57 * be removed once the initial experiments have been completed.
59 #define USE_RANDOM_DELAYS GNUNET_YES
62 * Over how many values do we calculate the weighted average?
64 #define HISTORY_SIZE 64
67 * Message priority to use.
69 #define NSE_PRIORITY 5
72 #define log2(a) (log(a)/log(2))
76 * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits.
78 static unsigned long long nse_work_required;
81 * Interval for sending network size estimation flood requests.
83 static struct GNUNET_TIME_Relative gnunet_nse_interval;
86 * Interval between proof find runs.
88 static struct GNUNET_TIME_Relative proof_find_delay;
90 #if ENABLE_NSE_HISTOGRAM
92 * Handle for writing when we received messages to disk.
94 static struct GNUNET_TESTBED_LOGGER_Handle *lh;
99 * Per-peer information.
105 * Core handle for sending messages to this peer.
107 struct GNUNET_CORE_TransmitHandle *th;
110 * What is the identity of the peer?
112 struct GNUNET_PeerIdentity id;
115 * Task scheduled to send message to this peer.
117 GNUNET_SCHEDULER_TaskIdentifier transmit_task;
120 * Did we receive or send a message about the previous round
121 * to this peer yet? GNUNET_YES if the previous round has
122 * been taken care of.
126 #if ENABLE_NSE_HISTOGRAM
129 * Amount of messages received from this peer on this round.
131 unsigned int received_messages;
134 * Amount of messages transmitted to this peer on this round.
136 unsigned int transmitted_messages;
139 * Which size did we tell the peer the network is?
141 unsigned int last_transmitted_size;
148 GNUNET_NETWORK_STRUCT_BEGIN
151 * Network size estimate reply; sent when "this"
152 * peer's timer has run out before receiving a
153 * valid reply from another peer.
155 struct GNUNET_NSE_FloodMessage
158 * Type: GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD
160 struct GNUNET_MessageHeader header;
163 * Number of hops this message has taken so far.
165 uint32_t hop_count GNUNET_PACKED;
170 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
173 * The current timestamp value (which all
174 * peers should agree on).
176 struct GNUNET_TIME_AbsoluteNBO timestamp;
179 * Number of matching bits between the hash
180 * of timestamp and the initiator's public
183 uint32_t matching_bits GNUNET_PACKED;
186 * Public key of the originator.
188 struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded pkey;
191 * Proof of work, causing leading zeros when hashed with pkey.
193 uint64_t proof_of_work GNUNET_PACKED;
196 * Signature (over range specified in purpose).
198 struct GNUNET_CRYPTO_EccSignature signature;
200 GNUNET_NETWORK_STRUCT_END
203 * Handle to our current configuration.
205 static const struct GNUNET_CONFIGURATION_Handle *cfg;
208 * Handle to the statistics service.
210 static struct GNUNET_STATISTICS_Handle *stats;
213 * Handle to the core service.
215 static struct GNUNET_CORE_Handle *coreAPI;
218 * Map of all connected peers.
220 static struct GNUNET_CONTAINER_MultiHashMap *peers;
223 * The current network size estimate. Number of bits matching on
226 static double current_size_estimate;
229 * The standard deviation of the last HISTORY_SIZE network
232 static double current_std_dev = NAN;
235 * Current hop counter estimate (estimate for network diameter).
237 static uint32_t hop_count_max;
240 * Message for the next round, if we got any.
242 static struct GNUNET_NSE_FloodMessage next_message;
245 * Array of recent size estimate messages.
247 static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE];
250 * Index of most recent estimate.
252 static unsigned int estimate_index;
255 * Number of valid entries in the history.
257 static unsigned int estimate_count;
260 * Task scheduled to update our flood message for the next round.
262 static GNUNET_SCHEDULER_TaskIdentifier flood_task;
265 * Task scheduled to compute our proof.
267 static GNUNET_SCHEDULER_TaskIdentifier proof_task;
270 * Notification context, simplifies client broadcasts.
272 static struct GNUNET_SERVER_NotificationContext *nc;
275 * The next major time.
277 static struct GNUNET_TIME_Absolute next_timestamp;
280 * The current major time.
282 static struct GNUNET_TIME_Absolute current_timestamp;
285 * The public key of this peer.
287 static struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded my_public_key;
290 * The private key of this peer.
292 static struct GNUNET_CRYPTO_EccPrivateKey *my_private_key;
295 * The peer identity of this peer.
297 static struct GNUNET_PeerIdentity my_identity;
300 * Proof of work for this peer.
302 static uint64_t my_proof;
305 * Handle to this serivce's server.
307 static struct GNUNET_SERVER_Handle *srv;
310 * Hostkey generation context
312 static struct GNUNET_CRYPTO_EccKeyGenerationContext *keygen;
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)
334 /* Weighted incremental algorithm for stddev according to West (1979) */
346 for (i = 0; i < estimate_count; i++)
348 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
349 val = htonl (size_estimate_messages[j].matching_bits);
350 weight = estimate_count + 1 - i;
352 temp = weight + sumweight;
354 r = q * weight / temp;
356 sum += sumweight * q * r;
359 if (estimate_count > 0)
360 variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0);
362 /* trivial version for debugging */
365 /* non-weighted trivial version */
371 for (i = 0; i < estimate_count; i++)
373 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) / (estimate_count - 1.0); // terrible for numerical stability...
385 std_dev = sqrt (variance);
387 std_dev = variance; /* must be infinity due to estimate_count == 0 */
388 current_std_dev = std_dev;
389 current_size_estimate = mean;
391 em->header.size = htons (sizeof (struct GNUNET_NSE_ClientMessage));
392 em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE);
393 em->reserved = htonl (0);
394 em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
395 double se = mean - 0.332747;
396 nsize = log2 (GNUNET_CONTAINER_multihashmap_size (peers) + 1);
397 em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se, nsize));
398 em->std_deviation = GNUNET_hton_double (std_dev);
399 GNUNET_STATISTICS_set (stats, "# nodes in the network (estimate)",
400 (uint64_t) pow (2, mean - 1.0 / 3.0), GNUNET_NO);
405 * Handler for START message from client, triggers an
406 * immediate current network estimate notification.
407 * Also, we remember the client for updates upon future
408 * estimate measurements.
411 * @param client who sent the message
412 * @param message the message received
415 handle_start_message (void *cls, struct GNUNET_SERVER_Client *client,
416 const struct GNUNET_MessageHeader *message)
418 struct GNUNET_NSE_ClientMessage em;
420 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received START message from client\n");
421 GNUNET_SERVER_notification_context_add (nc, client);
422 setup_estimate_message (&em);
423 GNUNET_SERVER_notification_context_unicast (nc, client, &em.header,
425 GNUNET_SERVER_receive_done (client, GNUNET_OK);
430 * How long should we delay a message to go the given number of
433 * @param matching_bits number of matching bits to consider
436 get_matching_bits_delay (uint32_t matching_bits)
438 /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */
439 // S is next_timestamp (ignored in return value)
440 // f is frequency (gnunet_nse_interval)
441 // x is matching_bits
442 // p' is current_size_estimate
443 return ((double) gnunet_nse_interval.rel_value / (double) 2.0) -
444 ((gnunet_nse_interval.rel_value / M_PI) *
445 atan (matching_bits - current_size_estimate));
450 * What delay randomization should we apply for a given number of matching bits?
452 * @param matching_bits number of matching bits
453 * @return random delay to apply
455 static struct GNUNET_TIME_Relative
456 get_delay_randomization (uint32_t matching_bits)
458 #if USE_RANDOM_DELAYS
459 struct GNUNET_TIME_Relative ret;
463 d = get_matching_bits_delay (matching_bits);
464 i = (uint32_t) (d / (double) (hop_count_max + 1));
465 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
466 "Randomizing flood using latencies up to %u ms\n",
468 ret.rel_value = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1);
471 return GNUNET_TIME_UNIT_ZERO;
477 * Calculate the 'proof-of-work' hash (an expensive hash).
479 * @param buf data to hash
480 * @param buf_len number of bytes in 'buf'
481 * @param result where to write the resulting hash
484 pow_hash (const void *buf,
486 struct GNUNET_HashCode *result)
489 gcry_kdf_derive (buf, buf_len,
492 "gnunet-proof-of-work", strlen ("gnunet-proof-of-work"),
493 2 /* iterations; keep cost of individual op small */,
494 sizeof (struct GNUNET_HashCode), result));
499 * Get the number of matching bits that the given timestamp has to the given peer ID.
501 * @param timestamp time to generate key
502 * @param id peer identity to compare with
503 * @return number of matching bits
506 get_matching_bits (struct GNUNET_TIME_Absolute timestamp,
507 const struct GNUNET_PeerIdentity *id)
509 struct GNUNET_HashCode timestamp_hash;
511 GNUNET_CRYPTO_hash (×tamp.abs_value, sizeof (timestamp.abs_value),
513 return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash, &id->hashPubKey);
518 * Get the transmission delay that should be applied for a
521 * @param round_offset -1 for the previous round (random delay between 0 and 50ms)
522 * 0 for the current round (based on our proximity to time key)
523 * @return delay that should be applied
525 static struct GNUNET_TIME_Relative
526 get_transmit_delay (int round_offset)
528 struct GNUNET_TIME_Relative ret;
529 struct GNUNET_TIME_Absolute tgt;
531 uint32_t matching_bits;
533 switch (round_offset)
536 /* previous round is randomized between 0 and 50 ms */
537 #if USE_RANDOM_DELAYS
538 ret.rel_value = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50);
540 ret = GNUNET_TIME_UNIT_ZERO;
542 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
543 "Transmitting previous round behind schedule in %llu ms\n",
544 (unsigned long long) ret.rel_value);
547 /* current round is based on best-known matching_bits */
549 ntohl (size_estimate_messages[estimate_index].matching_bits);
550 dist_delay = get_matching_bits_delay (matching_bits);
551 dist_delay += get_delay_randomization (matching_bits).rel_value;
552 ret.rel_value = (uint64_t) dist_delay;
553 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
554 "For round %llu, delay for %u matching bits is %llu ms\n",
555 (unsigned long long) current_timestamp.abs_value,
556 (unsigned int) matching_bits,
557 (unsigned long long) ret.rel_value);
558 /* now consider round start time and add delay to it */
559 tgt = GNUNET_TIME_absolute_add (current_timestamp, ret);
560 return GNUNET_TIME_absolute_get_remaining (tgt);
563 return GNUNET_TIME_UNIT_FOREVER_REL;
568 * Task that triggers a NSE P2P transmission.
570 * @param cls the 'struct NSEPeerEntry'
571 * @param tc scheduler context
574 transmit_task_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
578 * Called when core is ready to send a message we asked for
579 * out to the destination.
581 * @param cls closure (NULL)
582 * @param size number of bytes available in buf
583 * @param buf where the callee should write the message
584 * @return number of bytes written to buf
587 transmit_ready (void *cls, size_t size, void *buf)
589 struct NSEPeerEntry *peer_entry = cls;
592 peer_entry->th = NULL;
595 /* client disconnected */
598 GNUNET_assert (size >= sizeof (struct GNUNET_NSE_FloodMessage));
599 idx = estimate_index;
600 if (GNUNET_NO == peer_entry->previous_round)
602 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
603 peer_entry->previous_round = GNUNET_YES;
604 peer_entry->transmit_task =
605 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0), &transmit_task_cb,
608 if ((0 == ntohl (size_estimate_messages[idx].hop_count)) &&
609 (GNUNET_SCHEDULER_NO_TASK != proof_task))
611 GNUNET_STATISTICS_update (stats,
612 "# flood messages not generated (no proof yet)",
616 if (0 == ntohs (size_estimate_messages[idx].header.size))
618 GNUNET_STATISTICS_update (stats,
619 "# flood messages not generated (lack of history)",
623 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
624 "In round %llu, sending to `%s' estimate with %u bits\n",
626 GNUNET_TIME_absolute_ntoh (size_estimate_messages[idx].
627 timestamp).abs_value,
628 GNUNET_i2s (&peer_entry->id),
629 (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
630 if (ntohl (size_estimate_messages[idx].hop_count) == 0)
631 GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO);
632 GNUNET_STATISTICS_update (stats, "# flood messages transmitted", 1,
634 #if ENABLE_NSE_HISTOGRAM
635 peer_entry->transmitted_messages++;
636 peer_entry->last_transmitted_size =
637 ntohl(size_estimate_messages[idx].matching_bits);
639 memcpy (buf, &size_estimate_messages[idx],
640 sizeof (struct GNUNET_NSE_FloodMessage));
641 return sizeof (struct GNUNET_NSE_FloodMessage);
646 * Task that triggers a NSE P2P transmission.
648 * @param cls the 'struct NSEPeerEntry'
649 * @param tc scheduler context
652 transmit_task_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
654 struct NSEPeerEntry *peer_entry = cls;
656 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
658 GNUNET_assert (NULL == peer_entry->th);
660 GNUNET_CORE_notify_transmit_ready (coreAPI, GNUNET_NO, NSE_PRIORITY,
661 GNUNET_TIME_UNIT_FOREVER_REL,
664 GNUNET_NSE_FloodMessage),
665 &transmit_ready, peer_entry);
670 * We've sent on our flood message or one that we received which was
671 * validated and closer than ours. Update the global list of recent
672 * messages and the average. Also re-broadcast the message to any
676 update_network_size_estimate ()
678 struct GNUNET_NSE_ClientMessage em;
680 setup_estimate_message (&em);
681 GNUNET_SERVER_notification_context_broadcast (nc, &em.header, GNUNET_YES);
686 * Setup a flood message in our history array at the given
687 * slot offset for the given timestamp.
689 * @param slot index to use
690 * @param ts timestamp to use
693 setup_flood_message (unsigned int slot,
694 struct GNUNET_TIME_Absolute ts)
696 struct GNUNET_NSE_FloodMessage *fm;
697 uint32_t matching_bits;
699 matching_bits = get_matching_bits (ts, &my_identity);
700 fm = &size_estimate_messages[slot];
701 fm->header.size = htons (sizeof (struct GNUNET_NSE_FloodMessage));
702 fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
703 fm->hop_count = htonl (0);
704 fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
706 htonl (sizeof (struct GNUNET_NSE_FloodMessage) -
707 sizeof (struct GNUNET_MessageHeader) - sizeof (uint32_t) -
708 sizeof (struct GNUNET_CRYPTO_EccSignature));
709 fm->matching_bits = htonl (matching_bits);
710 fm->timestamp = GNUNET_TIME_absolute_hton (ts);
711 fm->pkey = my_public_key;
712 fm->proof_of_work = my_proof;
713 if (nse_work_required > 0)
714 GNUNET_assert (GNUNET_OK ==
715 GNUNET_CRYPTO_ecc_sign (my_private_key, &fm->purpose,
718 memset (&fm->signature, 0, sizeof (fm->signature));
723 * Schedule transmission for the given peer for the current round based
724 * on what we know about the desired delay.
727 * @param key hash of peer identity
728 * @param value the 'struct NSEPeerEntry'
729 * @return GNUNET_OK (continue to iterate)
732 schedule_current_round (void *cls,
733 const struct GNUNET_HashCode * key,
736 struct NSEPeerEntry *peer_entry = value;
737 struct GNUNET_TIME_Relative delay;
739 if (NULL != peer_entry->th)
741 peer_entry->previous_round = GNUNET_NO;
744 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
746 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
747 peer_entry->previous_round = GNUNET_NO;
749 #if ENABLE_NSE_HISTOGRAM
750 if (peer_entry->received_messages > 1)
751 GNUNET_STATISTICS_update(stats, "# extra messages",
752 peer_entry->received_messages - 1, GNUNET_NO);
753 peer_entry->transmitted_messages = 0;
754 peer_entry->last_transmitted_size = 0;
755 peer_entry->received_messages = 0;
758 get_transmit_delay ((peer_entry->previous_round == GNUNET_NO) ? -1 : 0);
759 peer_entry->transmit_task =
760 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
766 * Update our flood message to be sent (and our timestamps).
769 * @param tc context for this message
772 update_flood_message (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
774 struct GNUNET_TIME_Relative offset;
777 flood_task = GNUNET_SCHEDULER_NO_TASK;
778 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
780 offset = GNUNET_TIME_absolute_get_remaining (next_timestamp);
781 if (0 != offset.rel_value)
783 /* somehow run early, delay more */
785 GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL);
788 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
789 if (estimate_count < HISTORY_SIZE)
791 current_timestamp = next_timestamp;
793 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
794 if ((current_timestamp.abs_value ==
795 GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value) &&
796 (get_matching_bits (current_timestamp, &my_identity) <
797 ntohl(next_message.matching_bits)))
799 /* we received a message for this round way early, use it! */
800 size_estimate_messages[estimate_index] = next_message;
801 size_estimate_messages[estimate_index].hop_count =
802 htonl (1 + ntohl (next_message.hop_count));
805 setup_flood_message (estimate_index, current_timestamp);
806 next_message.matching_bits = htonl (0); /* reset for 'next' round */
808 for (i = 0; i < HISTORY_SIZE; i++)
810 GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max);
811 GNUNET_CONTAINER_multihashmap_iterate (peers, &schedule_current_round, NULL);
813 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
814 (next_timestamp), &update_flood_message,
820 * Count the leading zeroes in hash.
823 * @return the number of leading zero bits.
826 count_leading_zeroes (const struct GNUNET_HashCode * hash)
828 unsigned int hash_count;
831 while ((0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count)))
838 * Check whether the given public key
839 * and integer are a valid proof of work.
841 * @param pkey the public key
842 * @param val the integer
844 * @return GNUNET_YES if valid, GNUNET_NO if not
847 check_proof_of_work (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pkey,
850 char buf[sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded) +
851 sizeof (val)] GNUNET_ALIGN;
852 struct GNUNET_HashCode result;
854 memcpy (buf, &val, sizeof (val));
855 memcpy (&buf[sizeof (val)], pkey,
856 sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded));
857 pow_hash (buf, sizeof (buf), &result);
858 return (count_leading_zeroes (&result) >=
859 nse_work_required) ? GNUNET_YES : GNUNET_NO;
864 * Write our current proof to disk.
872 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
874 if (sizeof (my_proof) !=
875 GNUNET_DISK_fn_write (proof, &my_proof, sizeof (my_proof),
876 GNUNET_DISK_PERM_USER_READ |
877 GNUNET_DISK_PERM_USER_WRITE))
878 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof);
885 * Find our proof of work.
887 * @param cls closure (unused)
888 * @param tc task context
891 find_proof (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
893 #define ROUND_SIZE 10
895 char buf[sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded) +
896 sizeof (uint64_t)] GNUNET_ALIGN;
897 struct GNUNET_HashCode result;
900 proof_task = GNUNET_SCHEDULER_NO_TASK;
901 memcpy (&buf[sizeof (uint64_t)], &my_public_key,
902 sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded));
905 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
907 memcpy (buf, &counter, sizeof (uint64_t));
908 pow_hash (buf, sizeof (buf), &result);
909 if (nse_work_required <= count_leading_zeroes (&result))
912 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work found: %llu!\n",
913 (unsigned long long) GNUNET_ntohll (counter));
915 setup_flood_message (estimate_index, current_timestamp);
921 if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
923 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing proofs currently at %llu\n",
924 (unsigned long long) counter);
925 /* remember progress every 100 rounds */
934 GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
935 GNUNET_SCHEDULER_PRIORITY_IDLE,
941 * An incoming flood message has been received which claims
942 * to have more bits matching than any we know in this time
943 * period. Verify the signature and/or proof of work.
945 * @param incoming_flood the message to verify
947 * @return GNUNET_YES if the message is verified
948 * GNUNET_NO if the key/signature don't verify
951 verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood)
954 check_proof_of_work (&incoming_flood->pkey,
955 incoming_flood->proof_of_work))
957 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work invalid: %llu!\n",
959 GNUNET_ntohll (incoming_flood->proof_of_work));
963 if ((nse_work_required > 0) &&
965 GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
966 &incoming_flood->purpose,
967 &incoming_flood->signature,
968 &incoming_flood->pkey)))
978 * Update transmissions for the given peer for the current round based
979 * on updated proximity information.
981 * @param cls peer entry to exclude from updates
982 * @param key hash of peer identity
983 * @param value the 'struct NSEPeerEntry'
984 * @return GNUNET_OK (continue to iterate)
987 update_flood_times (void *cls, const struct GNUNET_HashCode * key, void *value)
989 struct NSEPeerEntry *exclude = cls;
990 struct NSEPeerEntry *peer_entry = value;
991 struct GNUNET_TIME_Relative delay;
993 if (NULL != peer_entry->th)
994 return GNUNET_OK; /* already active */
995 if (peer_entry == exclude)
996 return GNUNET_OK; /* trigger of the update */
997 if (peer_entry->previous_round == GNUNET_NO)
999 /* still stuck in previous round, no point to update, check that
1000 * we are active here though... */
1001 if ( (GNUNET_SCHEDULER_NO_TASK == peer_entry->transmit_task) &&
1002 (NULL == peer_entry->th) )
1008 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1010 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1011 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1013 delay = get_transmit_delay (0);
1014 peer_entry->transmit_task =
1015 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
1021 * Core handler for size estimate flooding messages.
1023 * @param cls closure unused
1024 * @param message message
1025 * @param peer peer identity this message is from (ignored)
1028 handle_p2p_size_estimate (void *cls, const struct GNUNET_PeerIdentity *peer,
1029 const struct GNUNET_MessageHeader *message)
1031 const struct GNUNET_NSE_FloodMessage *incoming_flood;
1032 struct GNUNET_TIME_Absolute ts;
1033 struct NSEPeerEntry *peer_entry;
1034 uint32_t matching_bits;
1037 #if ENABLE_NSE_HISTOGRAM
1041 t = GNUNET_TIME_absolute_get().abs_value;
1042 GNUNET_TESTBED_LOGGER_write (lh, &t, sizeof (uint64_t));
1045 incoming_flood = (const struct GNUNET_NSE_FloodMessage *) message;
1046 GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO);
1047 matching_bits = ntohl (incoming_flood->matching_bits);
1052 struct GNUNET_PeerIdentity os;
1054 GNUNET_CRYPTO_hash (&incoming_flood->pkey,
1055 sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded),
1057 GNUNET_snprintf (origin, sizeof (origin), "%s", GNUNET_i2s (&os));
1058 GNUNET_snprintf (pred, sizeof (pred), "%s", GNUNET_i2s (peer));
1059 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1060 "Flood at %llu from `%s' via `%s' at `%s' with bits %u\n",
1061 (unsigned long long)
1062 GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp).abs_value,
1063 origin, pred, GNUNET_i2s (&my_identity),
1064 (unsigned int) matching_bits);
1068 peer_entry = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
1069 if (NULL == peer_entry)
1074 #if ENABLE_NSE_HISTOGRAM
1075 peer_entry->received_messages++;
1076 if (peer_entry->transmitted_messages > 0 &&
1077 peer_entry->last_transmitted_size >= matching_bits)
1078 GNUNET_STATISTICS_update(stats, "# cross messages", 1, GNUNET_NO);
1081 ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp);
1082 if (ts.abs_value == current_timestamp.abs_value)
1083 idx = estimate_index;
1084 else if (ts.abs_value ==
1085 current_timestamp.abs_value - gnunet_nse_interval.rel_value)
1086 idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1087 else if (ts.abs_value == next_timestamp.abs_value)
1089 if (matching_bits <= ntohl (next_message.matching_bits))
1090 return GNUNET_OK; /* ignore, simply too early/late */
1091 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1093 GNUNET_break_op (0);
1096 next_message = *incoming_flood;
1101 GNUNET_STATISTICS_update (stats,
1102 "# flood messages discarded (clock skew too large)",
1106 if (0 == (memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))))
1108 /* send to self, update our own estimate IF this also comes from us! */
1110 memcmp (&incoming_flood->pkey, &my_public_key, sizeof (my_public_key)))
1111 update_network_size_estimate ();
1114 if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits))
1116 /* Cancel transmission in the other direction, as this peer clearly has
1117 up-to-date information already. Even if we didn't talk to this peer in
1118 the previous round, we should no longer send it stale information as it
1119 told us about the current round! */
1120 peer_entry->previous_round = GNUNET_YES;
1121 if (idx != estimate_index)
1123 /* do not transmit information for the previous round to this peer
1124 anymore (but allow current round) */
1127 /* got up-to-date information for current round, cancel transmission to
1128 * this peer altogether */
1129 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1131 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1132 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1134 if (NULL != peer_entry->th)
1136 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1137 peer_entry->th = NULL;
1141 if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits))
1143 if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES)) {
1144 peer_entry->previous_round = GNUNET_NO;
1146 /* push back our result now, that peer is spreading bad information... */
1147 if (NULL == peer_entry->th)
1149 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1150 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1151 peer_entry->transmit_task =
1152 GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry);
1154 /* Not closer than our most recent message, no need to do work here */
1155 GNUNET_STATISTICS_update (stats,
1156 "# flood messages ignored (had closer already)",
1160 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1162 GNUNET_break_op (0);
1165 GNUNET_assert (matching_bits >
1166 ntohl (size_estimate_messages[idx].matching_bits));
1167 /* Cancel transmission in the other direction, as this peer clearly has
1168 * up-to-date information already.
1170 peer_entry->previous_round = GNUNET_YES;
1171 if (idx == estimate_index)
1173 /* cancel any activity for current round */
1174 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1176 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1177 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1179 if (peer_entry->th != NULL)
1181 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1182 peer_entry->th = NULL;
1185 size_estimate_messages[idx] = *incoming_flood;
1186 size_estimate_messages[idx].hop_count =
1187 htonl (ntohl (incoming_flood->hop_count) + 1);
1189 GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max);
1190 GNUNET_STATISTICS_set (stats,
1191 "# estimated network diameter",
1192 hop_count_max, GNUNET_NO);
1194 /* have a new, better size estimate, inform clients */
1195 update_network_size_estimate ();
1198 GNUNET_CONTAINER_multihashmap_iterate (peers, &update_flood_times,
1206 * Method called whenever a peer connects. Sets up the PeerEntry and
1207 * schedules the initial size info transmission to this peer.
1209 * @param cls closure
1210 * @param peer peer identity this notification is about
1213 handle_core_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
1215 struct NSEPeerEntry *peer_entry;
1217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' connected to us\n",
1219 peer_entry = GNUNET_malloc (sizeof (struct NSEPeerEntry));
1220 peer_entry->id = *peer;
1221 GNUNET_assert (GNUNET_OK ==
1222 GNUNET_CONTAINER_multihashmap_put (peers, &peer->hashPubKey,
1224 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1225 peer_entry->transmit_task =
1226 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1), &transmit_task_cb,
1228 GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO);
1233 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
1234 * any pending transmission requests to that peer.
1236 * @param cls closure
1237 * @param peer peer identity this notification is about
1240 handle_core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
1242 struct NSEPeerEntry *pos;
1244 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' disconnected from us\n",
1246 pos = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
1252 GNUNET_assert (GNUNET_YES ==
1253 GNUNET_CONTAINER_multihashmap_remove (peers, &peer->hashPubKey,
1255 if (pos->transmit_task != GNUNET_SCHEDULER_NO_TASK) {
1256 GNUNET_SCHEDULER_cancel (pos->transmit_task);
1257 pos->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1259 if (NULL != pos->th)
1261 GNUNET_CORE_notify_transmit_ready_cancel (pos->th);
1265 GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO);
1269 #if ENABLE_NSE_HISTOGRAM
1271 * Functions of this type are called to notify a successful transmission of the
1272 * message to the logger service
1275 * @param size the amount of data sent
1278 flush_comp_cb (void *cls, size_t size)
1280 GNUNET_TESTBED_LOGGER_disconnect (lh);
1287 * Task run during shutdown.
1293 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1295 if (GNUNET_SCHEDULER_NO_TASK != flood_task)
1297 GNUNET_SCHEDULER_cancel (flood_task);
1298 flood_task = GNUNET_SCHEDULER_NO_TASK;
1300 if (GNUNET_SCHEDULER_NO_TASK != proof_task)
1302 GNUNET_SCHEDULER_cancel (proof_task);
1303 proof_task = GNUNET_SCHEDULER_NO_TASK;
1304 write_proof (); /* remember progress */
1308 GNUNET_CRYPTO_ecc_key_create_stop (keygen);
1313 GNUNET_SERVER_notification_context_destroy (nc);
1316 if (NULL != coreAPI)
1318 GNUNET_CORE_disconnect (coreAPI);
1323 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1328 GNUNET_CONTAINER_multihashmap_destroy (peers);
1331 if (NULL != my_private_key)
1333 GNUNET_CRYPTO_ecc_key_free (my_private_key);
1334 my_private_key = NULL;
1336 #if ENABLE_NSE_HISTOGRAM
1337 struct GNUNET_TIME_Relative timeout;
1338 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30);
1339 GNUNET_TESTBED_LOGGER_flush (lh, timeout, &flush_comp_cb, NULL);
1345 * Called on core init/fail.
1347 * @param cls service closure
1348 * @param server handle to the server for this service
1349 * @param identity the public identity of this peer
1352 core_init (void *cls, struct GNUNET_CORE_Handle *server,
1353 const struct GNUNET_PeerIdentity *identity)
1355 struct GNUNET_TIME_Absolute now;
1356 struct GNUNET_TIME_Absolute prev_time;
1360 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n");
1361 GNUNET_SCHEDULER_shutdown ();
1365 memcmp (&my_identity, identity,
1366 sizeof (struct GNUNET_PeerIdentity)));
1367 now = GNUNET_TIME_absolute_get ();
1368 current_timestamp.abs_value =
1369 (now.abs_value / gnunet_nse_interval.rel_value) *
1370 gnunet_nse_interval.rel_value;
1372 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
1373 estimate_index = HISTORY_SIZE - 1;
1375 if (GNUNET_YES == check_proof_of_work (&my_public_key, my_proof))
1377 int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1378 prev_time.abs_value =
1379 current_timestamp.abs_value - gnunet_nse_interval.rel_value;
1380 setup_flood_message (idx, prev_time);
1381 setup_flood_message (estimate_index, current_timestamp);
1385 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
1386 (next_timestamp), &update_flood_message,
1392 * Callback for hostkey read/generation
1395 * @param pk the private key
1396 * @param emsg error message
1399 key_generation_cb (void *cls,
1400 struct GNUNET_CRYPTO_EccPrivateKey *pk,
1403 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1404 {&handle_start_message, NULL, GNUNET_MESSAGE_TYPE_NSE_START,
1405 sizeof (struct GNUNET_MessageHeader)},
1408 static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
1409 {&handle_p2p_size_estimate, GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1410 sizeof (struct GNUNET_NSE_FloodMessage)},
1418 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1419 _("NSE service could not access hostkey: %s\n"),
1421 GNUNET_SCHEDULER_shutdown ();
1424 my_private_key = pk;
1425 GNUNET_CRYPTO_ecc_key_get_public (my_private_key, &my_public_key);
1426 GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key),
1427 &my_identity.hashPubKey);
1429 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
1431 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1433 ("NSE service is lacking key configuration settings. Exiting.\n"));
1434 GNUNET_CRYPTO_ecc_key_free (my_private_key);
1435 my_private_key = NULL;
1436 GNUNET_SCHEDULER_shutdown ();
1439 if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) ||
1440 (sizeof (my_proof) !=
1441 GNUNET_DISK_fn_read (proof, &my_proof, sizeof (my_proof))))
1443 GNUNET_free (proof);
1445 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1448 peers = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_NO);
1449 GNUNET_SERVER_add_handlers (srv, handlers);
1450 nc = GNUNET_SERVER_notification_context_create (srv, 1);
1451 /* Connect to core service and register core handlers */
1452 coreAPI = GNUNET_CORE_connect (cfg, /* Main configuration */
1453 NULL, /* Closure passed to functions */
1454 &core_init, /* Call core_init once connected */
1455 &handle_core_connect, /* Handle connects */
1456 &handle_core_disconnect, /* Handle disconnects */
1457 NULL, /* Don't want notified about all incoming messages */
1458 GNUNET_NO, /* For header only inbound notification */
1459 NULL, /* Don't want notified about all outbound messages */
1460 GNUNET_NO, /* For header only outbound notification */
1461 core_handlers); /* Register these handlers */
1462 if (NULL == coreAPI)
1464 GNUNET_SCHEDULER_shutdown ();
1467 stats = GNUNET_STATISTICS_create ("nse", cfg);
1468 GNUNET_SERVER_resume (srv);
1473 * Handle network size estimate clients.
1475 * @param cls closure
1476 * @param server the initialized server
1477 * @param c configuration to use
1481 struct GNUNET_SERVER_Handle *server,
1482 const struct GNUNET_CONFIGURATION_Handle *c)
1489 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "INTERVAL",
1490 &gnunet_nse_interval)) ||
1492 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "WORKDELAY",
1493 &proof_find_delay)) ||
1495 GNUNET_CONFIGURATION_get_value_number (cfg, "NSE", "WORKBITS",
1496 &nse_work_required)))
1498 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1500 ("%s service is lacking key configuration settings (%s). Exiting.\n"),
1501 "NSE", "interval/workdelay/workbits");
1502 GNUNET_SCHEDULER_shutdown ();
1505 if (nse_work_required >= sizeof (struct GNUNET_HashCode) * 8)
1507 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1508 _("Invalid work requirement for NSE service. Exiting.\n"));
1509 GNUNET_SCHEDULER_shutdown ();
1513 GNUNET_CONFIGURATION_get_value_filename (c, "PEER", "PRIVATE_KEY",
1516 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1518 ("%s service is lacking key configuration settings (%s). Exiting.\n"),
1519 "NSE", "peer/privatekey");
1520 GNUNET_SCHEDULER_shutdown ();
1523 #if ENABLE_NSE_HISTOGRAM
1524 if (NULL == (lh = GNUNET_TESTBED_LOGGER_connect (cfg)))
1526 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1527 "Cannot connect to the testbed logger. Exiting.\n");
1528 GNUNET_SCHEDULER_shutdown ();
1533 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1535 GNUNET_SERVER_suspend (srv);
1536 keygen = GNUNET_CRYPTO_ecc_key_create_start (keyfile, &key_generation_cb, NULL);
1537 GNUNET_free (keyfile);
1542 * The main function for the network size estimation service.
1544 * @param argc number of arguments from the command line
1545 * @param argv command line arguments
1546 * @return 0 ok, 1 on error
1549 main (int argc, char *const *argv)
1551 return (GNUNET_OK ==
1552 GNUNET_SERVICE_run (argc, argv, "nse", GNUNET_SERVICE_OPTION_NONE,
1553 &run, NULL)) ? 0 : 1;
1561 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1563 void __attribute__ ((constructor)) GNUNET_ARM_memory_init ()
1565 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1566 mallopt (M_TOP_PAD, 1 * 1024);
1573 /* end of gnunet-service-nse.c */