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"
52 * Should messages be delayed randomly? This option should be set to
53 * GNUNET_NO only for experiments, not in production. It should also
54 * be removed once the initial experiments have been completed.
56 #define USE_RANDOM_DELAYS GNUNET_YES
59 * Should we generate a histogram with the time stamps of when we received
60 * NSE messages to disk? (for performance evaluation only, not useful in
61 * production). The associated code should also probably be removed
62 * once we're done with experiments.
64 #define ENABLE_HISTOGRAM 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 5
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;
97 * Handle for writing when we received messages to disk.
99 static struct GNUNET_BIO_WriteHandle *wh;
104 * Per-peer information.
110 * Core handle for sending messages to this peer.
112 struct GNUNET_CORE_TransmitHandle *th;
115 * What is the identity of the peer?
117 struct GNUNET_PeerIdentity id;
120 * Task scheduled to send message to this peer.
122 GNUNET_SCHEDULER_TaskIdentifier transmit_task;
125 * Did we receive or send a message about the previous round
126 * to this peer yet? GNUNET_YES if the previous round has
127 * been taken care of.
134 * Amount of messages received from this peer on this round.
136 unsigned int received_messages;
139 * Amount of messages transmitted to this peer on this round.
141 unsigned int transmitted_messages;
144 * Which size did we tell the peer the network is?
146 unsigned int last_transmitted_size;
153 GNUNET_NETWORK_STRUCT_BEGIN
156 * Network size estimate reply; sent when "this"
157 * peer's timer has run out before receiving a
158 * valid reply from another peer.
160 struct GNUNET_NSE_FloodMessage
163 * Type: GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD
165 struct GNUNET_MessageHeader header;
168 * Number of hops this message has taken so far.
170 uint32_t hop_count GNUNET_PACKED;
175 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
178 * The current timestamp value (which all
179 * peers should agree on).
181 struct GNUNET_TIME_AbsoluteNBO timestamp;
184 * Number of matching bits between the hash
185 * of timestamp and the initiator's public
188 uint32_t matching_bits GNUNET_PACKED;
191 * Public key of the originator.
193 struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded pkey;
196 * Proof of work, causing leading zeros when hashed with pkey.
198 uint64_t proof_of_work GNUNET_PACKED;
201 * Signature (over range specified in purpose).
203 struct GNUNET_CRYPTO_EccSignature signature;
205 GNUNET_NETWORK_STRUCT_END
208 * Handle to our current configuration.
210 static const struct GNUNET_CONFIGURATION_Handle *cfg;
213 * Handle to the statistics service.
215 static struct GNUNET_STATISTICS_Handle *stats;
218 * Handle to the core service.
220 static struct GNUNET_CORE_Handle *coreAPI;
223 * Map of all connected peers.
225 static struct GNUNET_CONTAINER_MultiHashMap *peers;
228 * The current network size estimate. Number of bits matching on
231 static double current_size_estimate;
234 * The standard deviation of the last HISTORY_SIZE network
237 static double current_std_dev = NAN;
240 * Current hop counter estimate (estimate for network diameter).
242 static uint32_t hop_count_max;
245 * Message for the next round, if we got any.
247 static struct GNUNET_NSE_FloodMessage next_message;
250 * Array of recent size estimate messages.
252 static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE];
255 * Index of most recent estimate.
257 static unsigned int estimate_index;
260 * Number of valid entries in the history.
262 static unsigned int estimate_count;
265 * Task scheduled to update our flood message for the next round.
267 static GNUNET_SCHEDULER_TaskIdentifier flood_task;
270 * Task scheduled to compute our proof.
272 static GNUNET_SCHEDULER_TaskIdentifier proof_task;
275 * Notification context, simplifies client broadcasts.
277 static struct GNUNET_SERVER_NotificationContext *nc;
280 * The next major time.
282 static struct GNUNET_TIME_Absolute next_timestamp;
285 * The current major time.
287 static struct GNUNET_TIME_Absolute current_timestamp;
290 * The public key of this peer.
292 static struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded my_public_key;
295 * The private key of this peer.
297 static struct GNUNET_CRYPTO_EccPrivateKey *my_private_key;
300 * The peer identity of this peer.
302 static struct GNUNET_PeerIdentity my_identity;
305 * Proof of work for this peer.
307 static uint64_t my_proof;
310 * Handle to this serivce's server.
312 static struct GNUNET_SERVER_Handle *srv;
315 * Hostkey generation context
317 static struct GNUNET_CRYPTO_EccKeyGenerationContext *keygen;
321 * Initialize a message to clients with the current network
324 * @param em message to fill in
327 setup_estimate_message (struct GNUNET_NSE_ClientMessage *em)
339 /* Weighted incremental algorithm for stddev according to West (1979) */
351 for (i = 0; i < estimate_count; i++)
353 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
354 val = htonl (size_estimate_messages[j].matching_bits);
355 weight = estimate_count + 1 - i;
357 temp = weight + sumweight;
359 r = q * weight / temp;
361 sum += sumweight * q * r;
364 if (estimate_count > 0)
365 variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0);
367 /* trivial version for debugging */
370 /* non-weighted trivial version */
376 for (i = 0; i < estimate_count; i++)
378 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
379 val = htonl (size_estimate_messages[j].matching_bits);
383 if (0 != estimate_count)
385 mean = sum / estimate_count;
386 variance = (vsq - mean * sum) / (estimate_count - 1.0); // terrible for numerical stability...
390 std_dev = sqrt (variance);
392 std_dev = variance; /* must be infinity due to estimate_count == 0 */
393 current_std_dev = std_dev;
394 current_size_estimate = mean;
396 em->header.size = htons (sizeof (struct GNUNET_NSE_ClientMessage));
397 em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE);
398 em->reserved = htonl (0);
399 em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
400 double se = mean - 0.332747;
401 nsize = log2 (GNUNET_CONTAINER_multihashmap_size (peers) + 1);
402 em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se, nsize));
403 em->std_deviation = GNUNET_hton_double (std_dev);
404 GNUNET_STATISTICS_set (stats, "# nodes in the network (estimate)",
405 (uint64_t) pow (2, mean - 1.0 / 3.0), GNUNET_NO);
410 * Handler for START message from client, triggers an
411 * immediate current network estimate notification.
412 * Also, we remember the client for updates upon future
413 * estimate measurements.
416 * @param client who sent the message
417 * @param message the message received
420 handle_start_message (void *cls, struct GNUNET_SERVER_Client *client,
421 const struct GNUNET_MessageHeader *message)
423 struct GNUNET_NSE_ClientMessage em;
425 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received START message from client\n");
426 GNUNET_SERVER_notification_context_add (nc, client);
427 setup_estimate_message (&em);
428 GNUNET_SERVER_notification_context_unicast (nc, client, &em.header,
430 GNUNET_SERVER_receive_done (client, GNUNET_OK);
435 * How long should we delay a message to go the given number of
438 * @param matching_bits number of matching bits to consider
441 get_matching_bits_delay (uint32_t matching_bits)
443 /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */
444 // S is next_timestamp (ignored in return value)
445 // f is frequency (gnunet_nse_interval)
446 // x is matching_bits
447 // p' is current_size_estimate
448 return ((double) gnunet_nse_interval.rel_value / (double) 2.0) -
449 ((gnunet_nse_interval.rel_value / M_PI) *
450 atan (matching_bits - current_size_estimate));
455 * What delay randomization should we apply for a given number of matching bits?
457 * @param matching_bits number of matching bits
458 * @return random delay to apply
460 static struct GNUNET_TIME_Relative
461 get_delay_randomization (uint32_t matching_bits)
463 #if USE_RANDOM_DELAYS
464 struct GNUNET_TIME_Relative ret;
468 d = get_matching_bits_delay (matching_bits);
469 i = (uint32_t) (d / (double) (hop_count_max + 1));
470 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471 "Randomizing flood using latencies up to %u ms\n",
473 ret.rel_value = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1);
476 return GNUNET_TIME_UNIT_ZERO;
482 * Calculate the 'proof-of-work' hash (an expensive hash).
484 * @param buf data to hash
485 * @param buf_len number of bytes in 'buf'
486 * @param result where to write the resulting hash
489 pow_hash (const void *buf,
491 struct GNUNET_HashCode *result)
494 gcry_kdf_derive (buf, buf_len,
495 GCRY_KDF_PBKDF2 /* FIX: use SCRYPT! */,
497 "gnunet-proof-of-work", strlen ("gnunet-proof-of-work"),
498 2 /* iterations; keep cost of individual op small */,
499 sizeof (struct GNUNET_HashCode), result));
504 * Get the number of matching bits that the given timestamp has to the given peer ID.
506 * @param timestamp time to generate key
507 * @param id peer identity to compare with
508 * @return number of matching bits
511 get_matching_bits (struct GNUNET_TIME_Absolute timestamp,
512 const struct GNUNET_PeerIdentity *id)
514 struct GNUNET_HashCode timestamp_hash;
516 GNUNET_CRYPTO_hash (×tamp.abs_value, sizeof (timestamp.abs_value),
518 return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash, &id->hashPubKey);
523 * Get the transmission delay that should be applied for a
526 * @param round_offset -1 for the previous round (random delay between 0 and 50ms)
527 * 0 for the current round (based on our proximity to time key)
528 * @return delay that should be applied
530 static struct GNUNET_TIME_Relative
531 get_transmit_delay (int round_offset)
533 struct GNUNET_TIME_Relative ret;
534 struct GNUNET_TIME_Absolute tgt;
536 uint32_t matching_bits;
538 switch (round_offset)
541 /* previous round is randomized between 0 and 50 ms */
542 #if USE_RANDOM_DELAYS
543 ret.rel_value = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50);
545 ret = GNUNET_TIME_UNIT_ZERO;
547 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
548 "Transmitting previous round behind schedule in %llu ms\n",
549 (unsigned long long) ret.rel_value);
552 /* current round is based on best-known matching_bits */
554 ntohl (size_estimate_messages[estimate_index].matching_bits);
555 dist_delay = get_matching_bits_delay (matching_bits);
556 dist_delay += get_delay_randomization (matching_bits).rel_value;
557 ret.rel_value = (uint64_t) dist_delay;
558 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
559 "For round %llu, delay for %u matching bits is %llu ms\n",
560 (unsigned long long) current_timestamp.abs_value,
561 (unsigned int) matching_bits,
562 (unsigned long long) ret.rel_value);
563 /* now consider round start time and add delay to it */
564 tgt = GNUNET_TIME_absolute_add (current_timestamp, ret);
565 return GNUNET_TIME_absolute_get_remaining (tgt);
568 return GNUNET_TIME_UNIT_FOREVER_REL;
573 * Task that triggers a NSE P2P transmission.
575 * @param cls the 'struct NSEPeerEntry'
576 * @param tc scheduler context
579 transmit_task_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
583 * Called when core is ready to send a message we asked for
584 * out to the destination.
586 * @param cls closure (NULL)
587 * @param size number of bytes available in buf
588 * @param buf where the callee should write the message
589 * @return number of bytes written to buf
592 transmit_ready (void *cls, size_t size, void *buf)
594 struct NSEPeerEntry *peer_entry = cls;
597 peer_entry->th = NULL;
600 /* client disconnected */
603 GNUNET_assert (size >= sizeof (struct GNUNET_NSE_FloodMessage));
604 idx = estimate_index;
605 if (GNUNET_NO == peer_entry->previous_round)
607 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
608 peer_entry->previous_round = GNUNET_YES;
609 peer_entry->transmit_task =
610 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0), &transmit_task_cb,
613 if ((0 == ntohl (size_estimate_messages[idx].hop_count)) &&
614 (GNUNET_SCHEDULER_NO_TASK != proof_task))
616 GNUNET_STATISTICS_update (stats,
617 "# flood messages not generated (no proof yet)",
621 if (0 == ntohs (size_estimate_messages[idx].header.size))
623 GNUNET_STATISTICS_update (stats,
624 "# flood messages not generated (lack of history)",
628 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
629 "In round %llu, sending to `%s' estimate with %u bits\n",
631 GNUNET_TIME_absolute_ntoh (size_estimate_messages[idx].
632 timestamp).abs_value,
633 GNUNET_i2s (&peer_entry->id),
634 (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
635 if (ntohl (size_estimate_messages[idx].hop_count) == 0)
636 GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO);
637 GNUNET_STATISTICS_update (stats, "# flood messages transmitted", 1,
640 peer_entry->transmitted_messages++;
641 peer_entry->last_transmitted_size =
642 ntohl(size_estimate_messages[idx].matching_bits);
644 memcpy (buf, &size_estimate_messages[idx],
645 sizeof (struct GNUNET_NSE_FloodMessage));
646 return sizeof (struct GNUNET_NSE_FloodMessage);
651 * Task that triggers a NSE P2P transmission.
653 * @param cls the 'struct NSEPeerEntry'
654 * @param tc scheduler context
657 transmit_task_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
659 struct NSEPeerEntry *peer_entry = cls;
661 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
663 GNUNET_assert (NULL == peer_entry->th);
665 GNUNET_CORE_notify_transmit_ready (coreAPI, GNUNET_NO, NSE_PRIORITY,
666 GNUNET_TIME_UNIT_FOREVER_REL,
669 GNUNET_NSE_FloodMessage),
670 &transmit_ready, peer_entry);
675 * We've sent on our flood message or one that we received which was
676 * validated and closer than ours. Update the global list of recent
677 * messages and the average. Also re-broadcast the message to any
681 update_network_size_estimate ()
683 struct GNUNET_NSE_ClientMessage em;
685 setup_estimate_message (&em);
686 GNUNET_SERVER_notification_context_broadcast (nc, &em.header, GNUNET_YES);
691 * Setup a flood message in our history array at the given
692 * slot offset for the given timestamp.
694 * @param slot index to use
695 * @param ts timestamp to use
698 setup_flood_message (unsigned int slot,
699 struct GNUNET_TIME_Absolute ts)
701 struct GNUNET_NSE_FloodMessage *fm;
702 uint32_t matching_bits;
704 matching_bits = get_matching_bits (ts, &my_identity);
705 fm = &size_estimate_messages[slot];
706 fm->header.size = htons (sizeof (struct GNUNET_NSE_FloodMessage));
707 fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
708 fm->hop_count = htonl (0);
709 fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
711 htonl (sizeof (struct GNUNET_NSE_FloodMessage) -
712 sizeof (struct GNUNET_MessageHeader) - sizeof (uint32_t) -
713 sizeof (struct GNUNET_CRYPTO_EccSignature));
714 fm->matching_bits = htonl (matching_bits);
715 fm->timestamp = GNUNET_TIME_absolute_hton (ts);
716 fm->pkey = my_public_key;
717 fm->proof_of_work = my_proof;
718 if (nse_work_required > 0)
719 GNUNET_assert (GNUNET_OK ==
720 GNUNET_CRYPTO_ecc_sign (my_private_key, &fm->purpose,
723 memset (&fm->signature, 0, sizeof (fm->signature));
728 * Schedule transmission for the given peer for the current round based
729 * on what we know about the desired delay.
732 * @param key hash of peer identity
733 * @param value the 'struct NSEPeerEntry'
734 * @return GNUNET_OK (continue to iterate)
737 schedule_current_round (void *cls,
738 const struct GNUNET_HashCode * key,
741 struct NSEPeerEntry *peer_entry = value;
742 struct GNUNET_TIME_Relative delay;
744 if (NULL != peer_entry->th)
746 peer_entry->previous_round = GNUNET_NO;
749 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
751 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
752 peer_entry->previous_round = GNUNET_NO;
755 if (peer_entry->received_messages > 1)
756 GNUNET_STATISTICS_update(stats, "# extra messages",
757 peer_entry->received_messages - 1, GNUNET_NO);
758 peer_entry->transmitted_messages = 0;
759 peer_entry->last_transmitted_size = 0;
760 peer_entry->received_messages = 0;
763 get_transmit_delay ((peer_entry->previous_round == GNUNET_NO) ? -1 : 0);
764 peer_entry->transmit_task =
765 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
771 * Update our flood message to be sent (and our timestamps).
774 * @param tc context for this message
777 update_flood_message (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
779 struct GNUNET_TIME_Relative offset;
782 flood_task = GNUNET_SCHEDULER_NO_TASK;
783 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
785 offset = GNUNET_TIME_absolute_get_remaining (next_timestamp);
786 if (0 != offset.rel_value)
788 /* somehow run early, delay more */
790 GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL);
793 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
794 if (estimate_count < HISTORY_SIZE)
796 current_timestamp = next_timestamp;
798 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
799 if ((current_timestamp.abs_value ==
800 GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value) &&
801 (get_matching_bits (current_timestamp, &my_identity) <
802 ntohl(next_message.matching_bits)))
804 /* we received a message for this round way early, use it! */
805 size_estimate_messages[estimate_index] = next_message;
806 size_estimate_messages[estimate_index].hop_count =
807 htonl (1 + ntohl (next_message.hop_count));
810 setup_flood_message (estimate_index, current_timestamp);
811 next_message.matching_bits = htonl (0); /* reset for 'next' round */
813 for (i = 0; i < HISTORY_SIZE; i++)
815 GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max);
816 GNUNET_CONTAINER_multihashmap_iterate (peers, &schedule_current_round, NULL);
818 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
819 (next_timestamp), &update_flood_message,
825 * Count the leading zeroes in hash.
828 * @return the number of leading zero bits.
831 count_leading_zeroes (const struct GNUNET_HashCode * hash)
833 unsigned int hash_count;
836 while ((0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count)))
843 * Check whether the given public key
844 * and integer are a valid proof of work.
846 * @param pkey the public key
847 * @param val the integer
849 * @return GNUNET_YES if valid, GNUNET_NO if not
852 check_proof_of_work (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pkey,
855 char buf[sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded) +
856 sizeof (val)] GNUNET_ALIGN;
857 struct GNUNET_HashCode result;
859 memcpy (buf, &val, sizeof (val));
860 memcpy (&buf[sizeof (val)], pkey,
861 sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded));
862 pow_hash (buf, sizeof (buf), &result);
863 return (count_leading_zeroes (&result) >=
864 nse_work_required) ? GNUNET_YES : GNUNET_NO;
869 * Write our current proof to disk.
877 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
879 if (sizeof (my_proof) !=
880 GNUNET_DISK_fn_write (proof, &my_proof, sizeof (my_proof),
881 GNUNET_DISK_PERM_USER_READ |
882 GNUNET_DISK_PERM_USER_WRITE))
883 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof);
890 * Find our proof of work.
892 * @param cls closure (unused)
893 * @param tc task context
896 find_proof (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
898 #define ROUND_SIZE 10
900 char buf[sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded) +
901 sizeof (uint64_t)] GNUNET_ALIGN;
902 struct GNUNET_HashCode result;
905 proof_task = GNUNET_SCHEDULER_NO_TASK;
906 memcpy (&buf[sizeof (uint64_t)], &my_public_key,
907 sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded));
910 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
912 memcpy (buf, &counter, sizeof (uint64_t));
913 pow_hash (buf, sizeof (buf), &result);
914 if (nse_work_required <= count_leading_zeroes (&result))
917 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work found: %llu!\n",
918 (unsigned long long) GNUNET_ntohll (counter));
920 setup_flood_message (estimate_index, current_timestamp);
926 if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
928 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing proofs currently at %llu\n",
929 (unsigned long long) counter);
930 /* remember progress every 100 rounds */
939 GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
940 GNUNET_SCHEDULER_PRIORITY_IDLE,
946 * An incoming flood message has been received which claims
947 * to have more bits matching than any we know in this time
948 * period. Verify the signature and/or proof of work.
950 * @param incoming_flood the message to verify
952 * @return GNUNET_YES if the message is verified
953 * GNUNET_NO if the key/signature don't verify
956 verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood)
959 check_proof_of_work (&incoming_flood->pkey,
960 incoming_flood->proof_of_work))
962 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work invalid: %llu!\n",
964 GNUNET_ntohll (incoming_flood->proof_of_work));
968 if ((nse_work_required > 0) &&
970 GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
971 &incoming_flood->purpose,
972 &incoming_flood->signature,
973 &incoming_flood->pkey)))
983 * Update transmissions for the given peer for the current round based
984 * on updated proximity information.
986 * @param cls peer entry to exclude from updates
987 * @param key hash of peer identity
988 * @param value the 'struct NSEPeerEntry'
989 * @return GNUNET_OK (continue to iterate)
992 update_flood_times (void *cls, const struct GNUNET_HashCode * key, void *value)
994 struct NSEPeerEntry *exclude = cls;
995 struct NSEPeerEntry *peer_entry = value;
996 struct GNUNET_TIME_Relative delay;
998 if (NULL != peer_entry->th)
999 return GNUNET_OK; /* already active */
1000 if (peer_entry == exclude)
1001 return GNUNET_OK; /* trigger of the update */
1002 if (peer_entry->previous_round == GNUNET_NO)
1004 /* still stuck in previous round, no point to update, check that
1005 * we are active here though... */
1006 if ( (GNUNET_SCHEDULER_NO_TASK == peer_entry->transmit_task) &&
1007 (NULL == peer_entry->th) )
1013 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1015 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1016 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1018 delay = get_transmit_delay (0);
1019 peer_entry->transmit_task =
1020 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
1026 * Core handler for size estimate flooding messages.
1028 * @param cls closure unused
1029 * @param message message
1030 * @param peer peer identity this message is from (ignored)
1033 handle_p2p_size_estimate (void *cls, const struct GNUNET_PeerIdentity *peer,
1034 const struct GNUNET_MessageHeader *message)
1036 const struct GNUNET_NSE_FloodMessage *incoming_flood;
1037 struct GNUNET_TIME_Absolute ts;
1038 struct NSEPeerEntry *peer_entry;
1039 uint32_t matching_bits;
1042 #if ENABLE_HISTOGRAM
1044 GNUNET_break (GNUNET_OK == GNUNET_BIO_write_int64 (wh, GNUNET_TIME_absolute_get ().abs_value));
1046 incoming_flood = (const struct GNUNET_NSE_FloodMessage *) message;
1047 GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO);
1048 matching_bits = ntohl (incoming_flood->matching_bits);
1053 struct GNUNET_PeerIdentity os;
1055 GNUNET_CRYPTO_hash (&incoming_flood->pkey,
1056 sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded),
1058 GNUNET_snprintf (origin, sizeof (origin), "%s", GNUNET_i2s (&os));
1059 GNUNET_snprintf (pred, sizeof (pred), "%s", GNUNET_i2s (peer));
1060 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1061 "Flood at %llu from `%s' via `%s' at `%s' with bits %u\n",
1062 (unsigned long long)
1063 GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp).abs_value,
1064 origin, pred, GNUNET_i2s (&my_identity),
1065 (unsigned int) matching_bits);
1069 peer_entry = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
1070 if (NULL == peer_entry)
1075 #if ENABLE_HISTOGRAM
1076 peer_entry->received_messages++;
1077 if (peer_entry->transmitted_messages > 0 &&
1078 peer_entry->last_transmitted_size >= matching_bits)
1079 GNUNET_STATISTICS_update(stats, "# cross messages", 1, GNUNET_NO);
1082 ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp);
1083 if (ts.abs_value == current_timestamp.abs_value)
1084 idx = estimate_index;
1085 else if (ts.abs_value ==
1086 current_timestamp.abs_value - gnunet_nse_interval.rel_value)
1087 idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1088 else if (ts.abs_value == next_timestamp.abs_value)
1090 if (matching_bits <= ntohl (next_message.matching_bits))
1091 return GNUNET_OK; /* ignore, simply too early/late */
1092 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1094 GNUNET_break_op (0);
1097 next_message = *incoming_flood;
1102 GNUNET_STATISTICS_update (stats,
1103 "# flood messages discarded (clock skew too large)",
1107 if (0 == (memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))))
1109 /* send to self, update our own estimate IF this also comes from us! */
1111 memcmp (&incoming_flood->pkey, &my_public_key, sizeof (my_public_key)))
1112 update_network_size_estimate ();
1115 if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits))
1117 /* Cancel transmission in the other direction, as this peer clearly has
1118 up-to-date information already. Even if we didn't talk to this peer in
1119 the previous round, we should no longer send it stale information as it
1120 told us about the current round! */
1121 peer_entry->previous_round = GNUNET_YES;
1122 if (idx != estimate_index)
1124 /* do not transmit information for the previous round to this peer
1125 anymore (but allow current round) */
1128 /* got up-to-date information for current round, cancel transmission to
1129 * this peer altogether */
1130 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1132 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1133 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1135 if (NULL != peer_entry->th)
1137 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1138 peer_entry->th = NULL;
1142 if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits))
1144 if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES)) {
1145 peer_entry->previous_round = GNUNET_NO;
1147 /* push back our result now, that peer is spreading bad information... */
1148 if (NULL == peer_entry->th)
1150 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1151 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1152 peer_entry->transmit_task =
1153 GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry);
1155 /* Not closer than our most recent message, no need to do work here */
1156 GNUNET_STATISTICS_update (stats,
1157 "# flood messages ignored (had closer already)",
1161 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1163 GNUNET_break_op (0);
1166 GNUNET_assert (matching_bits >
1167 ntohl (size_estimate_messages[idx].matching_bits));
1168 /* Cancel transmission in the other direction, as this peer clearly has
1169 * up-to-date information already.
1171 peer_entry->previous_round = GNUNET_YES;
1172 if (idx == estimate_index)
1174 /* cancel any activity for current round */
1175 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1177 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1178 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1180 if (peer_entry->th != NULL)
1182 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1183 peer_entry->th = NULL;
1186 size_estimate_messages[idx] = *incoming_flood;
1187 size_estimate_messages[idx].hop_count =
1188 htonl (ntohl (incoming_flood->hop_count) + 1);
1190 GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max);
1191 GNUNET_STATISTICS_set (stats,
1192 "# estimated network diameter",
1193 hop_count_max, GNUNET_NO);
1195 /* have a new, better size estimate, inform clients */
1196 update_network_size_estimate ();
1199 GNUNET_CONTAINER_multihashmap_iterate (peers, &update_flood_times,
1207 * Method called whenever a peer connects. Sets up the PeerEntry and
1208 * schedules the initial size info transmission to this peer.
1210 * @param cls closure
1211 * @param peer peer identity this notification is about
1214 handle_core_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
1216 struct NSEPeerEntry *peer_entry;
1218 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' connected to us\n",
1220 peer_entry = GNUNET_malloc (sizeof (struct NSEPeerEntry));
1221 peer_entry->id = *peer;
1222 GNUNET_assert (GNUNET_OK ==
1223 GNUNET_CONTAINER_multihashmap_put (peers, &peer->hashPubKey,
1225 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1226 peer_entry->transmit_task =
1227 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1), &transmit_task_cb,
1229 GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO);
1234 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
1235 * any pending transmission requests to that peer.
1237 * @param cls closure
1238 * @param peer peer identity this notification is about
1241 handle_core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
1243 struct NSEPeerEntry *pos;
1245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' disconnected from us\n",
1247 pos = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
1253 GNUNET_assert (GNUNET_YES ==
1254 GNUNET_CONTAINER_multihashmap_remove (peers, &peer->hashPubKey,
1256 if (pos->transmit_task != GNUNET_SCHEDULER_NO_TASK) {
1257 GNUNET_SCHEDULER_cancel (pos->transmit_task);
1258 pos->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1260 if (NULL != pos->th)
1262 GNUNET_CORE_notify_transmit_ready_cancel (pos->th);
1266 GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO);
1271 * Task run during shutdown.
1277 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1279 if (GNUNET_SCHEDULER_NO_TASK != flood_task)
1281 GNUNET_SCHEDULER_cancel (flood_task);
1282 flood_task = GNUNET_SCHEDULER_NO_TASK;
1284 if (GNUNET_SCHEDULER_NO_TASK != proof_task)
1286 GNUNET_SCHEDULER_cancel (proof_task);
1287 proof_task = GNUNET_SCHEDULER_NO_TASK;
1288 write_proof (); /* remember progress */
1292 GNUNET_CRYPTO_ecc_key_create_stop (keygen);
1297 GNUNET_SERVER_notification_context_destroy (nc);
1300 if (NULL != coreAPI)
1302 GNUNET_CORE_disconnect (coreAPI);
1307 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1312 GNUNET_CONTAINER_multihashmap_destroy (peers);
1315 if (NULL != my_private_key)
1317 GNUNET_CRYPTO_ecc_key_free (my_private_key);
1318 my_private_key = NULL;
1320 #if ENABLE_HISTOGRAM
1323 GNUNET_break (GNUNET_OK == GNUNET_BIO_write_close (wh));
1331 * Called on core init/fail.
1333 * @param cls service closure
1334 * @param server handle to the server for this service
1335 * @param identity the public identity of this peer
1338 core_init (void *cls, struct GNUNET_CORE_Handle *server,
1339 const struct GNUNET_PeerIdentity *identity)
1341 struct GNUNET_TIME_Absolute now;
1342 struct GNUNET_TIME_Absolute prev_time;
1346 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n");
1347 GNUNET_SCHEDULER_shutdown ();
1351 memcmp (&my_identity, identity,
1352 sizeof (struct GNUNET_PeerIdentity)));
1353 now = GNUNET_TIME_absolute_get ();
1354 current_timestamp.abs_value =
1355 (now.abs_value / gnunet_nse_interval.rel_value) *
1356 gnunet_nse_interval.rel_value;
1358 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
1359 estimate_index = HISTORY_SIZE - 1;
1361 if (GNUNET_YES == check_proof_of_work (&my_public_key, my_proof))
1363 int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1364 prev_time.abs_value =
1365 current_timestamp.abs_value - gnunet_nse_interval.rel_value;
1366 setup_flood_message (idx, prev_time);
1367 setup_flood_message (estimate_index, current_timestamp);
1371 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
1372 (next_timestamp), &update_flood_message,
1378 * Callback for hostkey read/generation
1381 * @param pk the private key
1382 * @param emsg error message
1385 key_generation_cb (void *cls,
1386 struct GNUNET_CRYPTO_EccPrivateKey *pk,
1389 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1390 {&handle_start_message, NULL, GNUNET_MESSAGE_TYPE_NSE_START,
1391 sizeof (struct GNUNET_MessageHeader)},
1394 static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
1395 {&handle_p2p_size_estimate, GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1396 sizeof (struct GNUNET_NSE_FloodMessage)},
1404 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1405 _("NSE service could not access hostkey: %s\n"),
1407 GNUNET_SCHEDULER_shutdown ();
1410 my_private_key = pk;
1411 GNUNET_CRYPTO_ecc_key_get_public (my_private_key, &my_public_key);
1412 GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key),
1413 &my_identity.hashPubKey);
1415 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
1417 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1419 ("NSE service is lacking key configuration settings. Exiting.\n"));
1420 GNUNET_CRYPTO_ecc_key_free (my_private_key);
1421 my_private_key = NULL;
1422 GNUNET_SCHEDULER_shutdown ();
1425 if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) ||
1426 (sizeof (my_proof) !=
1427 GNUNET_DISK_fn_read (proof, &my_proof, sizeof (my_proof))))
1429 GNUNET_free (proof);
1431 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1434 peers = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_NO);
1435 GNUNET_SERVER_add_handlers (srv, handlers);
1436 nc = GNUNET_SERVER_notification_context_create (srv, 1);
1437 /* Connect to core service and register core handlers */
1438 coreAPI = GNUNET_CORE_connect (cfg, /* Main configuration */
1439 NULL, /* Closure passed to functions */
1440 &core_init, /* Call core_init once connected */
1441 &handle_core_connect, /* Handle connects */
1442 &handle_core_disconnect, /* Handle disconnects */
1443 NULL, /* Don't want notified about all incoming messages */
1444 GNUNET_NO, /* For header only inbound notification */
1445 NULL, /* Don't want notified about all outbound messages */
1446 GNUNET_NO, /* For header only outbound notification */
1447 core_handlers); /* Register these handlers */
1448 if (NULL == coreAPI)
1450 GNUNET_SCHEDULER_shutdown ();
1453 #if ENABLE_HISTOGRAM
1455 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "HISTOGRAM_DIR", &proof))
1459 unsigned long long peer_id;
1462 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_number (cfg, "TESTBED",
1463 "PEERID", &peer_id))
1464 (void) GNUNET_asprintf (&hgram_file, "%s/%llu.hist", proof, peer_id);
1467 hostname = GNUNET_malloc (GNUNET_OS_get_hostname_max_length ());
1468 if (0 == gethostname (hostname, HOST_NAME_MAX))
1469 (void) GNUNET_asprintf (&hgram_file, "%s/%s_%jd.hist",
1470 proof, hostname, (intmax_t) getpid());
1472 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "gethostname");
1473 GNUNET_free (hostname);
1475 if (NULL != hgram_file)
1477 wh = GNUNET_BIO_write_open (hgram_file);
1478 GNUNET_free (hgram_file);
1480 GNUNET_free (proof);
1483 stats = GNUNET_STATISTICS_create ("nse", cfg);
1484 GNUNET_SERVER_resume (srv);
1489 * Handle network size estimate clients.
1491 * @param cls closure
1492 * @param server the initialized server
1493 * @param c configuration to use
1497 struct GNUNET_SERVER_Handle *server,
1498 const struct GNUNET_CONFIGURATION_Handle *c)
1505 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "INTERVAL",
1506 &gnunet_nse_interval)) ||
1508 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "WORKDELAY",
1509 &proof_find_delay)) ||
1511 GNUNET_CONFIGURATION_get_value_number (cfg, "NSE", "WORKBITS",
1512 &nse_work_required)))
1514 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1516 ("%s service is lacking key configuration settings (%s). Exiting.\n"),
1517 "NSE", "interval/workdelay/workbits");
1518 GNUNET_SCHEDULER_shutdown ();
1521 if (nse_work_required >= sizeof (struct GNUNET_HashCode) * 8)
1523 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1524 _("Invalid work requirement for NSE service. Exiting.\n"));
1525 GNUNET_SCHEDULER_shutdown ();
1529 GNUNET_CONFIGURATION_get_value_filename (c, "PEER", "PRIVATE_KEY",
1532 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1534 ("%s service is lacking key configuration settings (%s). Exiting.\n"),
1535 "NSE", "peer/privatekey");
1536 GNUNET_SCHEDULER_shutdown ();
1539 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1541 GNUNET_SERVER_suspend (srv);
1542 keygen = GNUNET_CRYPTO_ecc_key_create_start (keyfile, &key_generation_cb, NULL);
1543 GNUNET_free (keyfile);
1548 * The main function for the network size estimation service.
1550 * @param argc number of arguments from the command line
1551 * @param argv command line arguments
1552 * @return 0 ok, 1 on error
1555 main (int argc, char *const *argv)
1557 return (GNUNET_OK ==
1558 GNUNET_SERVICE_run (argc, argv, "nse", GNUNET_SERVICE_OPTION_NONE,
1559 &run, NULL)) ? 0 : 1;
1567 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1569 void __attribute__ ((constructor)) GNUNET_ARM_memory_init ()
1571 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1572 mallopt (M_TOP_PAD, 1 * 1024);
1579 /* end of gnunet-service-nse.c */