2 This file is part of GNUnet.
3 (C) 2009, 2010, 2011 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"
50 * Should messages be delayed randomly? This option should be set to
51 * GNUNET_NO only for experiments, not in production. It should also
52 * be removed once the initial experiments have been completed.
54 #define USE_RANDOM_DELAYS GNUNET_YES
57 * Should we generate a histogram with the time stamps of when we received
58 * NSE messages to disk? (for performance evaluation only, not useful in
59 * production). The associated code should also probably be removed
60 * once we're done with experiments.
62 #define ENABLE_HISTOGRAM GNUNET_NO
65 * Over how many values do we calculate the weighted average?
67 #define HISTORY_SIZE 64
70 * Message priority to use.
72 #define NSE_PRIORITY 5
75 #define log2(a) (log(a)/log(2))
79 * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits.
81 static unsigned long long nse_work_required;
84 * Interval for sending network size estimation flood requests.
86 static struct GNUNET_TIME_Relative gnunet_nse_interval;
89 * Interval between proof find runs.
91 static struct GNUNET_TIME_Relative proof_find_delay;
95 * Handle for writing when we received messages to disk.
97 static struct GNUNET_BIO_WriteHandle *wh;
102 * Per-peer information.
108 * Core handle for sending messages to this peer.
110 struct GNUNET_CORE_TransmitHandle *th;
113 * What is the identity of the peer?
115 struct GNUNET_PeerIdentity id;
118 * Task scheduled to send message to this peer.
120 GNUNET_SCHEDULER_TaskIdentifier transmit_task;
123 * Did we receive or send a message about the previous round
124 * to this peer yet? GNUNET_YES if the previous round has
125 * been taken care of.
132 * Amount of messages received from this peer on this round.
134 unsigned int received_messages;
137 * Amount of messages transmitted to this peer on this round.
139 unsigned int transmitted_messages;
142 * Which size did we tell the peer the network is?
144 unsigned int last_transmitted_size;
151 GNUNET_NETWORK_STRUCT_BEGIN
154 * Network size estimate reply; sent when "this"
155 * peer's timer has run out before receiving a
156 * valid reply from another peer.
158 struct GNUNET_NSE_FloodMessage
161 * Type: GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD
163 struct GNUNET_MessageHeader header;
166 * Number of hops this message has taken so far.
168 uint32_t hop_count GNUNET_PACKED;
173 struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
176 * The current timestamp value (which all
177 * peers should agree on).
179 struct GNUNET_TIME_AbsoluteNBO timestamp;
182 * Number of matching bits between the hash
183 * of timestamp and the initiator's public
186 uint32_t matching_bits GNUNET_PACKED;
189 * Public key of the originator.
191 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
194 * Proof of work, causing leading zeros when hashed with pkey.
196 uint64_t proof_of_work GNUNET_PACKED;
199 * Signature (over range specified in purpose).
201 struct GNUNET_CRYPTO_RsaSignature signature;
203 GNUNET_NETWORK_STRUCT_END
206 * Handle to our current configuration.
208 static const struct GNUNET_CONFIGURATION_Handle *cfg;
211 * Handle to the statistics service.
213 static struct GNUNET_STATISTICS_Handle *stats;
216 * Handle to the core service.
218 static struct GNUNET_CORE_Handle *coreAPI;
221 * Map of all connected peers.
223 static struct GNUNET_CONTAINER_MultiHashMap *peers;
226 * The current network size estimate. Number of bits matching on
229 static double current_size_estimate;
232 * The standard deviation of the last HISTORY_SIZE network
235 static double current_std_dev = NAN;
238 * Current hop counter estimate (estimate for network diameter).
240 static uint32_t hop_count_max;
243 * Message for the next round, if we got any.
245 static struct GNUNET_NSE_FloodMessage next_message;
248 * Array of recent size estimate messages.
250 static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE];
253 * Index of most recent estimate.
255 static unsigned int estimate_index;
258 * Number of valid entries in the history.
260 static unsigned int estimate_count;
263 * Task scheduled to update our flood message for the next round.
265 static GNUNET_SCHEDULER_TaskIdentifier flood_task;
268 * Task scheduled to compute our proof.
270 static GNUNET_SCHEDULER_TaskIdentifier proof_task;
273 * Notification context, simplifies client broadcasts.
275 static struct GNUNET_SERVER_NotificationContext *nc;
278 * The next major time.
280 static struct GNUNET_TIME_Absolute next_timestamp;
283 * The current major time.
285 static struct GNUNET_TIME_Absolute current_timestamp;
288 * The public key of this peer.
290 static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
293 * The private key of this peer.
295 static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
298 * The peer identity of this peer.
300 static struct GNUNET_PeerIdentity my_identity;
303 * Proof of work for this peer.
305 static uint64_t my_proof;
309 * Initialize a message to clients with the current network
312 * @param em message to fill in
315 setup_estimate_message (struct GNUNET_NSE_ClientMessage *em)
327 /* Weighted incremental algorithm for stddev according to West (1979) */
339 for (i = 0; i < estimate_count; i++)
341 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
342 val = htonl (size_estimate_messages[j].matching_bits);
343 weight = estimate_count + 1 - i;
345 temp = weight + sumweight;
347 r = q * weight / temp;
349 sum += sumweight * q * r;
352 if (estimate_count > 0)
353 variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0);
355 /* trivial version for debugging */
358 /* non-weighted trivial version */
364 for (i = 0; i < estimate_count; i++)
366 j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE;
367 val = htonl (size_estimate_messages[j].matching_bits);
371 if (0 != estimate_count)
373 mean = sum / estimate_count;
374 variance = (vsq - mean * sum) / (estimate_count - 1.0); // terrible for numerical stability...
378 std_dev = sqrt (variance);
380 std_dev = variance; /* must be infinity due to estimate_count == 0 */
381 current_std_dev = std_dev;
382 current_size_estimate = mean;
384 em->header.size = htons (sizeof (struct GNUNET_NSE_ClientMessage));
385 em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE);
386 em->reserved = htonl (0);
387 em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
388 em->size_estimate = mean - 0.332747;
389 nsize = log2 (GNUNET_CONTAINER_multihashmap_size (peers) + 1);
390 if (em->size_estimate < nsize)
391 em->size_estimate = nsize;
392 em->std_deviation = std_dev;
393 GNUNET_STATISTICS_set (stats, "# nodes in the network (estimate)",
394 (uint64_t) pow (2, mean - 1.0 / 3.0), GNUNET_NO);
399 * Handler for START message from client, triggers an
400 * immediate current network estimate notification.
401 * Also, we remember the client for updates upon future
402 * estimate measurements.
405 * @param client who sent the message
406 * @param message the message received
409 handle_start_message (void *cls, struct GNUNET_SERVER_Client *client,
410 const struct GNUNET_MessageHeader *message)
412 struct GNUNET_NSE_ClientMessage em;
415 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received START message from client\n");
417 GNUNET_SERVER_notification_context_add (nc, client);
418 setup_estimate_message (&em);
419 GNUNET_SERVER_notification_context_unicast (nc, client, &em.header,
421 GNUNET_SERVER_receive_done (client, GNUNET_OK);
426 * How long should we delay a message to go the given number of
429 * @param matching_bits number of matching bits to consider
432 get_matching_bits_delay (uint32_t matching_bits)
434 /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */
435 // S is next_timestamp (ignored in return value)
436 // f is frequency (gnunet_nse_interval)
437 // x is matching_bits
438 // p' is current_size_estimate
439 return ((double) gnunet_nse_interval.rel_value / (double) 2.0) -
440 ((gnunet_nse_interval.rel_value / M_PI) *
441 atan (matching_bits - current_size_estimate));
446 * What delay randomization should we apply for a given number of matching bits?
448 * @param matching_bits number of matching bits
449 * @return random delay to apply
451 static struct GNUNET_TIME_Relative
452 get_delay_randomization (uint32_t matching_bits)
454 #if USE_RANDOM_DELAYS
455 struct GNUNET_TIME_Relative ret;
459 d = get_matching_bits_delay (matching_bits);
460 i = (uint32_t) (d / (double) (hop_count_max + 1));
462 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
463 "Randomizing flood using latencies up to %u ms\n",
466 ret.rel_value = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1);
469 return GNUNET_TIME_UNIT_ZERO;
475 * Get the number of matching bits that the given timestamp has to the given peer ID.
477 * @param timestamp time to generate key
478 * @param id peer identity to compare with
479 * @return number of matching bits
482 get_matching_bits (struct GNUNET_TIME_Absolute timestamp,
483 const struct GNUNET_PeerIdentity *id)
485 GNUNET_HashCode timestamp_hash;
487 GNUNET_CRYPTO_hash (×tamp.abs_value, sizeof (timestamp.abs_value),
489 return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash, &id->hashPubKey);
494 * Get the transmission delay that should be applied for a
497 * @param round_offset -1 for the previous round (random delay between 0 and 50ms)
498 * 0 for the current round (based on our proximity to time key)
499 * @return delay that should be applied
501 static struct GNUNET_TIME_Relative
502 get_transmit_delay (int round_offset)
504 struct GNUNET_TIME_Relative ret;
505 struct GNUNET_TIME_Absolute tgt;
507 uint32_t matching_bits;
509 switch (round_offset)
512 /* previous round is randomized between 0 and 50 ms */
513 #if USE_RANDOM_DELAYS
514 ret.rel_value = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50);
516 ret = GNUNET_TIME_UNIT_ZERO;
519 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
520 "Transmitting previous round behind schedule in %llu ms\n",
521 (unsigned long long) ret.rel_value);
525 /* current round is based on best-known matching_bits */
527 ntohl (size_estimate_messages[estimate_index].matching_bits);
528 dist_delay = get_matching_bits_delay (matching_bits);
529 dist_delay += get_delay_randomization (matching_bits).rel_value;
530 ret.rel_value = (uint64_t) dist_delay;
532 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
533 "For round %llu, delay for %u matching bits is %llu ms\n",
534 (unsigned long long) current_timestamp.abs_value,
535 (unsigned int) matching_bits,
536 (unsigned long long) ret.rel_value);
538 /* now consider round start time and add delay to it */
539 tgt = GNUNET_TIME_absolute_add (current_timestamp, ret);
540 return GNUNET_TIME_absolute_get_remaining (tgt);
543 return GNUNET_TIME_UNIT_FOREVER_REL;
548 * Task that triggers a NSE P2P transmission.
550 * @param cls the 'struct NSEPeerEntry'
551 * @param tc scheduler context
554 transmit_task_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
558 * Called when core is ready to send a message we asked for
559 * out to the destination.
561 * @param cls closure (NULL)
562 * @param size number of bytes available in buf
563 * @param buf where the callee should write the message
564 * @return number of bytes written to buf
567 transmit_ready (void *cls, size_t size, void *buf)
569 struct NSEPeerEntry *peer_entry = cls;
572 peer_entry->th = NULL;
575 /* client disconnected */
578 GNUNET_assert (size >= sizeof (struct GNUNET_NSE_FloodMessage));
579 idx = estimate_index;
580 if (peer_entry->previous_round == GNUNET_NO)
582 idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
583 peer_entry->previous_round = GNUNET_YES;
584 peer_entry->transmit_task =
585 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0), &transmit_task_cb,
588 if ((ntohl (size_estimate_messages[idx].hop_count) == 0) &&
589 (GNUNET_SCHEDULER_NO_TASK != proof_task))
591 GNUNET_STATISTICS_update (stats,
592 "# flood messages not generated (no proof yet)",
596 if (ntohs (size_estimate_messages[idx].header.size) == 0)
598 GNUNET_STATISTICS_update (stats,
599 "# flood messages not generated (lack of history)",
604 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
605 "In round %llu, sending to `%s' estimate with %u bits\n",
607 GNUNET_TIME_absolute_ntoh (size_estimate_messages[idx].
608 timestamp).abs_value,
609 GNUNET_i2s (&peer_entry->id),
610 (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
612 if (ntohl (size_estimate_messages[idx].hop_count) == 0)
613 GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO);
614 GNUNET_STATISTICS_update (stats, "# flood messages transmitted", 1,
617 peer_entry->transmitted_messages++;
618 peer_entry->last_transmitted_size =
619 ntohl(size_estimate_messages[idx].matching_bits);
621 memcpy (buf, &size_estimate_messages[idx],
622 sizeof (struct GNUNET_NSE_FloodMessage));
623 return sizeof (struct GNUNET_NSE_FloodMessage);
628 * Task that triggers a NSE P2P transmission.
630 * @param cls the 'struct NSEPeerEntry'
631 * @param tc scheduler context
634 transmit_task_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
636 struct NSEPeerEntry *peer_entry = cls;
638 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
639 GNUNET_assert (NULL == peer_entry->th);
641 GNUNET_CORE_notify_transmit_ready (coreAPI, GNUNET_NO, NSE_PRIORITY,
642 GNUNET_TIME_UNIT_FOREVER_REL,
645 GNUNET_NSE_FloodMessage),
646 &transmit_ready, peer_entry);
651 * We've sent on our flood message or one that we received which was
652 * validated and closer than ours. Update the global list of recent
653 * messages and the average. Also re-broadcast the message to any
657 update_network_size_estimate ()
659 struct GNUNET_NSE_ClientMessage em;
661 setup_estimate_message (&em);
662 GNUNET_SERVER_notification_context_broadcast (nc, &em.header, GNUNET_YES);
667 * Setup a flood message in our history array at the given
668 * slot offset for the given timestamp.
670 * @param slot index to use
671 * @param ts timestamp to use
674 setup_flood_message (unsigned int slot, struct GNUNET_TIME_Absolute ts)
676 struct GNUNET_NSE_FloodMessage *fm;
677 uint32_t matching_bits;
679 matching_bits = get_matching_bits (ts, &my_identity);
680 fm = &size_estimate_messages[slot];
681 fm->header.size = htons (sizeof (struct GNUNET_NSE_FloodMessage));
682 fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD);
683 fm->hop_count = htonl (0);
684 fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND);
686 htonl (sizeof (struct GNUNET_NSE_FloodMessage) -
687 sizeof (struct GNUNET_MessageHeader) - sizeof (uint32_t) -
688 sizeof (struct GNUNET_CRYPTO_RsaSignature));
689 fm->matching_bits = htonl (matching_bits);
690 fm->timestamp = GNUNET_TIME_absolute_hton (ts);
691 fm->pkey = my_public_key;
692 fm->proof_of_work = my_proof;
693 if (nse_work_required > 0)
694 GNUNET_assert (GNUNET_OK ==
695 GNUNET_CRYPTO_rsa_sign (my_private_key, &fm->purpose,
698 memset (&fm->signature, 0, sizeof (fm->signature));
703 * Schedule transmission for the given peer for the current round based
704 * on what we know about the desired delay.
707 * @param key hash of peer identity
708 * @param value the 'struct NSEPeerEntry'
709 * @return GNUNET_OK (continue to iterate)
712 schedule_current_round (void *cls, const GNUNET_HashCode * key, void *value)
714 struct NSEPeerEntry *peer_entry = value;
715 struct GNUNET_TIME_Relative delay;
717 if (peer_entry->th != NULL)
719 peer_entry->previous_round = GNUNET_NO;
722 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
724 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
725 peer_entry->previous_round = GNUNET_NO;
728 if (peer_entry->received_messages > 1)
729 GNUNET_STATISTICS_update(stats, "# extra messages",
730 peer_entry->received_messages - 1, GNUNET_NO);
731 peer_entry->transmitted_messages = 0;
732 peer_entry->last_transmitted_size = 0;
733 peer_entry->received_messages = 0;
736 get_transmit_delay ((peer_entry->previous_round == GNUNET_NO) ? -1 : 0);
737 peer_entry->transmit_task =
738 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
744 * Update our flood message to be sent (and our timestamps).
747 * @param tc context for this message
750 update_flood_message (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
752 struct GNUNET_TIME_Relative offset;
755 flood_task = GNUNET_SCHEDULER_NO_TASK;
756 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
758 offset = GNUNET_TIME_absolute_get_remaining (next_timestamp);
759 if (0 != offset.rel_value)
761 /* somehow run early, delay more */
763 GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL);
766 current_timestamp = next_timestamp;
768 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
769 estimate_index = (estimate_index + 1) % HISTORY_SIZE;
770 if (estimate_count < HISTORY_SIZE)
772 if (next_timestamp.abs_value ==
773 GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value)
775 /* we received a message for this round way early, use it! */
776 size_estimate_messages[estimate_index] = next_message;
777 size_estimate_messages[estimate_index].hop_count =
778 htonl (1 + ntohl (next_message.hop_count));
781 setup_flood_message (estimate_index, current_timestamp);
782 next_message.matching_bits = htonl (0); /* reset for 'next' round */
784 for (i = 0; i < HISTORY_SIZE; i++)
786 GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max);
787 GNUNET_CONTAINER_multihashmap_iterate (peers, &schedule_current_round, NULL);
789 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
790 (next_timestamp), &update_flood_message,
796 * Count the leading zeroes in hash.
799 * @return the number of leading zero bits.
802 count_leading_zeroes (const GNUNET_HashCode * hash)
804 unsigned int hash_count;
807 while ((0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count)))
814 * Check whether the given public key
815 * and integer are a valid proof of work.
817 * @param pkey the public key
818 * @param val the integer
820 * @return GNUNET_YES if valid, GNUNET_NO if not
823 check_proof_of_work (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pkey,
826 char buf[sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
828 GNUNET_HashCode result;
830 memcpy (buf, &val, sizeof (val));
831 memcpy (&buf[sizeof (val)], pkey,
832 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
833 GNUNET_CRYPTO_hash (buf, sizeof (buf), &result);
834 return (count_leading_zeroes (&result) >=
835 nse_work_required) ? GNUNET_YES : GNUNET_NO;
840 * Write our current proof to disk.
848 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
850 if (sizeof (my_proof) !=
851 GNUNET_DISK_fn_write (proof, &my_proof, sizeof (my_proof),
852 GNUNET_DISK_PERM_USER_READ |
853 GNUNET_DISK_PERM_USER_WRITE))
854 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof);
861 * Find our proof of work.
863 * @param cls closure (unused)
864 * @param tc task context
867 find_proof (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
869 #define ROUND_SIZE 10
871 char buf[sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
873 GNUNET_HashCode result;
876 proof_task = GNUNET_SCHEDULER_NO_TASK;
877 memcpy (&buf[sizeof (uint64_t)], &my_public_key,
878 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
881 while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
883 memcpy (buf, &counter, sizeof (uint64_t));
884 GNUNET_CRYPTO_hash (buf, sizeof (buf), &result);
885 if (nse_work_required <= count_leading_zeroes (&result))
889 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof of work found: %llu!\n",
890 (unsigned long long) GNUNET_ntohll (counter));
893 setup_flood_message (estimate_index, current_timestamp);
899 if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
902 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing proofs currently at %llu\n",
903 (unsigned long long) counter);
905 /* remember progress every 100 rounds */
914 GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
915 GNUNET_SCHEDULER_PRIORITY_IDLE,
921 * An incoming flood message has been received which claims
922 * to have more bits matching than any we know in this time
923 * period. Verify the signature and/or proof of work.
925 * @param incoming_flood the message to verify
927 * @return GNUNET_YES if the message is verified
928 * GNUNET_NO if the key/signature don't verify
931 verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood)
934 check_proof_of_work (&incoming_flood->pkey,
935 incoming_flood->proof_of_work))
937 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Proof of work invalid: %llu!\n"),
939 GNUNET_ntohll (incoming_flood->proof_of_work));
943 if ((nse_work_required > 0) &&
945 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND,
946 &incoming_flood->purpose,
947 &incoming_flood->signature,
948 &incoming_flood->pkey)))
958 * Update transmissions for the given peer for the current round based
959 * on updated proximity information.
961 * @param cls peer entry to exclude from updates
962 * @param key hash of peer identity
963 * @param value the 'struct NSEPeerEntry'
964 * @return GNUNET_OK (continue to iterate)
967 update_flood_times (void *cls, const GNUNET_HashCode * key, void *value)
969 struct NSEPeerEntry *exclude = cls;
970 struct NSEPeerEntry *peer_entry = value;
971 struct GNUNET_TIME_Relative delay;
973 if (peer_entry->th != NULL)
974 return GNUNET_OK; /* already active */
975 if (peer_entry == exclude)
976 return GNUNET_OK; /* trigger of the update */
977 if (peer_entry->previous_round == GNUNET_NO)
979 /* still stuck in previous round, no point to update, check that
980 * we are active here though... */
981 GNUNET_break (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK);
984 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
986 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
987 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
989 delay = get_transmit_delay (0);
990 peer_entry->transmit_task =
991 GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry);
997 * Core handler for size estimate flooding messages.
999 * @param cls closure unused
1000 * @param message message
1001 * @param peer peer identity this message is from (ignored)
1002 * @param atsi performance data (ignored)
1003 * @param atsi_count number of records in 'atsi'
1006 handle_p2p_size_estimate (void *cls, const struct GNUNET_PeerIdentity *peer,
1007 const struct GNUNET_MessageHeader *message,
1008 const struct GNUNET_ATS_Information *atsi,
1009 unsigned int atsi_count)
1011 const struct GNUNET_NSE_FloodMessage *incoming_flood;
1012 struct GNUNET_TIME_Absolute ts;
1013 struct NSEPeerEntry *peer_entry;
1014 uint32_t matching_bits;
1017 #if ENABLE_HISTOGRAM
1019 GNUNET_BIO_write_int64 (wh, GNUNET_TIME_absolute_get ().abs_value);
1021 incoming_flood = (const struct GNUNET_NSE_FloodMessage *) message;
1022 GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO);
1023 matching_bits = ntohl (incoming_flood->matching_bits);
1028 struct GNUNET_PeerIdentity os;
1030 GNUNET_CRYPTO_hash (&incoming_flood->pkey,
1031 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1033 GNUNET_snprintf (origin, sizeof (origin), "%s", GNUNET_i2s (&os));
1034 GNUNET_snprintf (pred, sizeof (pred), "%s", GNUNET_i2s (peer));
1035 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1036 "Flood at %llu from `%s' via `%s' at `%s' with bits %u\n",
1037 (unsigned long long)
1038 GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp).abs_value,
1039 origin, pred, GNUNET_i2s (&my_identity),
1040 (unsigned int) matching_bits);
1044 peer_entry = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
1045 if (NULL == peer_entry)
1050 #if ENABLE_HISTOGRAM
1051 peer_entry->received_messages++;
1052 if (peer_entry->transmitted_messages > 0 &&
1053 peer_entry->last_transmitted_size >= matching_bits)
1054 GNUNET_STATISTICS_update(stats, "# cross messages", 1, GNUNET_NO);
1057 ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp);
1059 if (ts.abs_value == current_timestamp.abs_value)
1060 idx = estimate_index;
1061 else if (ts.abs_value ==
1062 current_timestamp.abs_value - gnunet_nse_interval.rel_value)
1063 idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
1064 else if (ts.abs_value ==
1065 next_timestamp.abs_value - gnunet_nse_interval.rel_value)
1067 if (matching_bits <= ntohl (next_message.matching_bits))
1068 return GNUNET_OK; /* ignore, simply too early/late */
1069 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1071 GNUNET_break_op (0);
1074 next_message = *incoming_flood;
1079 GNUNET_STATISTICS_update (stats,
1080 "# flood messages discarded (clock skew too large)",
1084 if (0 == (memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))))
1086 /* send to self, update our own estimate IF this also comes from us! */
1088 memcmp (&incoming_flood->pkey, &my_public_key, sizeof (my_public_key)))
1089 update_network_size_estimate ();
1092 if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits))
1094 /* Cancel transmission in the other direction, as this peer clearly has
1095 up-to-date information already. Even if we didn't talk to this peer in
1096 the previous round, we should no longer send it stale information as it
1097 told us about the current round! */
1098 peer_entry->previous_round = GNUNET_YES;
1099 if (idx != estimate_index)
1101 /* do not transmit information for the previous round to this peer
1102 anymore (but allow current round) */
1105 /* got up-to-date information for current round, cancel transmission to
1106 * this peer altogether */
1107 if (GNUNET_SCHEDULER_NO_TASK != peer_entry->transmit_task)
1109 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1110 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1112 if (peer_entry->th != NULL)
1114 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1115 peer_entry->th = NULL;
1119 if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits))
1121 if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES))
1122 peer_entry->previous_round = GNUNET_NO;
1123 /* push back our result now, that peer is spreading bad information... */
1124 if (NULL == peer_entry->th)
1126 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1127 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1128 peer_entry->transmit_task =
1129 GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry);
1131 /* Not closer than our most recent message, no need to do work here */
1132 GNUNET_STATISTICS_update (stats,
1133 "# flood messages ignored (had closer already)",
1137 if (GNUNET_YES != verify_message_crypto (incoming_flood))
1139 GNUNET_break_op (0);
1142 GNUNET_assert (matching_bits >
1143 ntohl (size_estimate_messages[idx].matching_bits));
1144 /* cancel transmission from us to this peer for this round */
1145 if (idx == estimate_index)
1147 /* cancel any activity for current round */
1148 if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1150 GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
1151 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
1153 if (peer_entry->th != NULL)
1155 GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th);
1156 peer_entry->th = NULL;
1161 /* cancel previous round only */
1162 peer_entry->previous_round = GNUNET_YES;
1164 size_estimate_messages[idx] = *incoming_flood;
1165 size_estimate_messages[idx].hop_count =
1166 htonl (ntohl (incoming_flood->hop_count) + 1);
1168 GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max);
1169 GNUNET_STATISTICS_set (stats,
1170 "# estimated network diameter",
1171 hop_count_max, GNUNET_NO);
1173 /* have a new, better size estimate, inform clients */
1174 update_network_size_estimate ();
1177 GNUNET_CONTAINER_multihashmap_iterate (peers, &update_flood_times,
1185 * Method called whenever a peer connects.
1187 * @param cls closure
1188 * @param peer peer identity this notification is about
1189 * @param atsi performance data
1190 * @param atsi_count number of records in 'atsi'
1193 handle_core_connect (void *cls, const struct GNUNET_PeerIdentity *peer,
1194 const struct GNUNET_ATS_Information *atsi,
1195 unsigned int atsi_count)
1197 struct NSEPeerEntry *peer_entry;
1200 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' connected to us\n",
1203 peer_entry = GNUNET_malloc (sizeof (struct NSEPeerEntry));
1204 peer_entry->id = *peer;
1205 GNUNET_assert (GNUNET_OK ==
1206 GNUNET_CONTAINER_multihashmap_put (peers, &peer->hashPubKey,
1208 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1209 peer_entry->transmit_task =
1210 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1), &transmit_task_cb,
1212 GNUNET_STATISTICS_update (stats, "# peers", 1, GNUNET_NO);
1217 * Method called whenever a peer disconnects.
1219 * @param cls closure
1220 * @param peer peer identity this notification is about
1223 handle_core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
1225 struct NSEPeerEntry *pos;
1228 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' disconnected from us\n",
1231 pos = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
1237 GNUNET_assert (GNUNET_YES ==
1238 GNUNET_CONTAINER_multihashmap_remove (peers, &peer->hashPubKey,
1240 if (pos->transmit_task != GNUNET_SCHEDULER_NO_TASK)
1241 GNUNET_SCHEDULER_cancel (pos->transmit_task);
1242 if (pos->th != NULL)
1244 GNUNET_CORE_notify_transmit_ready_cancel (pos->th);
1248 GNUNET_STATISTICS_update (stats, "# peers", -1, GNUNET_NO);
1253 * Task run during shutdown.
1259 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1261 if (flood_task != GNUNET_SCHEDULER_NO_TASK)
1263 GNUNET_SCHEDULER_cancel (flood_task);
1264 flood_task = GNUNET_SCHEDULER_NO_TASK;
1266 if (proof_task != GNUNET_SCHEDULER_NO_TASK)
1268 GNUNET_SCHEDULER_cancel (proof_task);
1269 proof_task = GNUNET_SCHEDULER_NO_TASK;
1270 write_proof (); /* remember progress */
1274 GNUNET_SERVER_notification_context_destroy (nc);
1277 if (coreAPI != NULL)
1279 GNUNET_CORE_disconnect (coreAPI);
1284 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1289 GNUNET_CONTAINER_multihashmap_destroy (peers);
1292 if (my_private_key != NULL)
1294 GNUNET_CRYPTO_rsa_key_free (my_private_key);
1295 my_private_key = NULL;
1297 #if ENABLE_HISTOGRAM
1300 GNUNET_BIO_write_close (wh);
1308 * Called on core init/fail.
1310 * @param cls service closure
1311 * @param server handle to the server for this service
1312 * @param identity the public identity of this peer
1315 core_init (void *cls, struct GNUNET_CORE_Handle *server,
1316 const struct GNUNET_PeerIdentity *identity)
1318 struct GNUNET_TIME_Absolute now;
1319 struct GNUNET_TIME_Absolute prev_time;
1324 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection to core FAILED!\n");
1326 GNUNET_SCHEDULER_shutdown ();
1330 memcmp (&my_identity, identity,
1331 sizeof (struct GNUNET_PeerIdentity)));
1332 now = GNUNET_TIME_absolute_get ();
1333 current_timestamp.abs_value =
1334 (now.abs_value / gnunet_nse_interval.rel_value) *
1335 gnunet_nse_interval.rel_value;
1337 GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval);
1338 estimate_index = HISTORY_SIZE - 1;
1340 if (GNUNET_YES == check_proof_of_work (&my_public_key, my_proof))
1342 prev_time.abs_value =
1343 current_timestamp.abs_value - gnunet_nse_interval.rel_value;
1344 setup_flood_message (estimate_index, prev_time);
1348 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
1349 (next_timestamp), &update_flood_message,
1355 * Handle network size estimate clients.
1357 * @param cls closure
1358 * @param server the initialized server
1359 * @param c configuration to use
1362 run (void *cls, struct GNUNET_SERVER_Handle *server,
1363 const struct GNUNET_CONFIGURATION_Handle *c)
1368 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1369 {&handle_start_message, NULL, GNUNET_MESSAGE_TYPE_NSE_START,
1370 sizeof (struct GNUNET_MessageHeader)},
1373 static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
1374 {&handle_p2p_size_estimate, GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD,
1375 sizeof (struct GNUNET_NSE_FloodMessage)},
1381 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "INTERVAL",
1382 &gnunet_nse_interval)) ||
1384 GNUNET_CONFIGURATION_get_value_time (cfg, "NSE", "WORKDELAY",
1385 &proof_find_delay)) ||
1387 GNUNET_CONFIGURATION_get_value_number (cfg, "NSE", "WORKBITS",
1388 &nse_work_required)))
1390 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1392 ("NSE service is lacking key configuration settings. Exiting.\n"));
1393 GNUNET_SCHEDULER_shutdown ();
1396 if (nse_work_required >= sizeof (GNUNET_HashCode) * 8)
1398 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1399 _("Invalid work requirement for NSE service. Exiting.\n"));
1400 GNUNET_SCHEDULER_shutdown ();
1406 GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY",
1409 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1411 ("NSE service is lacking key configuration settings. Exiting.\n"));
1412 GNUNET_SCHEDULER_shutdown ();
1415 my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
1416 GNUNET_free (keyfile);
1417 if (my_private_key == NULL)
1419 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1420 _("NSE service could not access hostkey. Exiting.\n"));
1421 GNUNET_SCHEDULER_shutdown ();
1424 GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
1425 GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key),
1426 &my_identity.hashPubKey);
1428 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof))
1430 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1432 ("NSE service is lacking key configuration settings. Exiting.\n"));
1433 if (my_private_key != NULL)
1435 GNUNET_CRYPTO_rsa_key_free (my_private_key);
1436 my_private_key = NULL;
1438 GNUNET_SCHEDULER_shutdown ();
1441 if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) ||
1442 (sizeof (my_proof) !=
1443 GNUNET_DISK_fn_read (proof, &my_proof, sizeof (my_proof))))
1445 GNUNET_free (proof);
1447 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1450 peers = GNUNET_CONTAINER_multihashmap_create (128);
1451 GNUNET_SERVER_add_handlers (server, handlers);
1452 nc = GNUNET_SERVER_notification_context_create (server, 1);
1453 /* Connect to core service and register core handlers */
1454 coreAPI = GNUNET_CORE_connect (cfg, /* Main configuration */
1455 1, NULL, /* Closure passed to functions */
1456 &core_init, /* Call core_init once connected */
1457 &handle_core_connect, /* Handle connects */
1458 &handle_core_disconnect, /* Handle disconnects */
1459 NULL, /* Don't want notified about all incoming messages */
1460 GNUNET_NO, /* For header only inbound notification */
1461 NULL, /* Don't want notified about all outbound messages */
1462 GNUNET_NO, /* For header only outbound notification */
1463 core_handlers); /* Register these handlers */
1464 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1466 #if ENABLE_HISTOGRAM
1468 GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "HISTOGRAM", &proof))
1470 wh = GNUNET_BIO_write_open (proof);
1471 GNUNET_free (proof);
1474 if (coreAPI == NULL)
1476 GNUNET_SCHEDULER_shutdown ();
1479 stats = GNUNET_STATISTICS_create ("nse", cfg);
1484 * The main function for the statistics service.
1486 * @param argc number of arguments from the command line
1487 * @param argv command line arguments
1488 * @return 0 ok, 1 on error
1491 main (int argc, char *const *argv)
1493 return (GNUNET_OK ==
1494 GNUNET_SERVICE_run (argc, argv, "nse", GNUNET_SERVICE_OPTION_NONE,
1495 &run, NULL)) ? 0 : 1;
1498 /* end of gnunet-service-nse.c */