push back
[oweals/gnunet.git] / src / nse / gnunet-service-nse.c
index 2d323a36af62a092cc5040c6aa7b3bf47f426779..3d980ca26ae04eed46f6671d496b9c8a54c1ea1c 100644 (file)
  * those peer from sending their messages at a later duration.  So
  * every peer should receive the same nearest peer message, and from
  * this can calculate the expected number of peers in the network.
- *
- * TODO:
- * - generate proof-of-work asynchronously, store it on disk & load it back
- * - handle messages for future round (one into the future, see FIXME)
  */
 #include "platform.h"
-#include "gnunet_client_lib.h"
+#include "gnunet_util_lib.h"
 #include "gnunet_constants.h"
-#include "gnunet_container_lib.h"
 #include "gnunet_protocols.h"
 #include "gnunet_signatures.h"
-#include "gnunet_service_lib.h"
-#include "gnunet_server_lib.h"
 #include "gnunet_statistics_service.h"
 #include "gnunet_core_service.h"
-#include "gnunet_time_lib.h"
 #include "gnunet_nse_service.h"
 #include "nse.h"
 
+/**
+ * Send messages on connect.
+ */
+#define SEND_ON_CONNECT GNUNET_YES
+
+/**
+ * Should we generate a histogram with the time stamps of when we received
+ * NSE messages to disk? (for performance evaluation only, not useful in
+ * production)
+ */
+#define ENABLE_HISTOGRAM GNUNET_YES
+
 /**
  * Over how many values do we calculate the weighted average?
  */
 /**
  * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits.
  */
-#define NSE_WORK_REQUIRED 0
+static unsigned long long nse_work_required;
 
 /**
  * Interval for sending network size estimation flood requests.
  */
-#define GNUNET_NSE_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
+static struct GNUNET_TIME_Relative gnunet_nse_interval;
+
+/**
+ * Interval between proof find runs.
+ */
+static struct GNUNET_TIME_Relative proof_find_delay;
+
+
+#if ENABLE_HISTOGRAM
+static struct GNUNET_BIO_WriteHandle *wh;
+#endif
 
 
 /**
@@ -107,7 +121,8 @@ struct NSEPeerEntry
 
   /**
    * Did we receive or send a message about the previous round
-   * to this peer yet?  
+   * to this peer yet?   GNUNET_YES if the previous round has
+   * been taken care of.
    */
   int previous_round;
 };
@@ -202,6 +217,11 @@ static double current_std_dev = NAN;
  */
 static uint32_t hop_count_max;
 
+/**
+ * Message for the next round, if we got any.
+ */
+static struct GNUNET_NSE_FloodMessage next_message;
+
 /**
  * Array of recent size estimate messages.
  */
@@ -212,11 +232,21 @@ static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE];
  */
 static unsigned int estimate_index;
 
+/**
+ * Number of valid entries in the history.
+ */
+static unsigned int estimate_count;
+
 /**
  * Task scheduled to update our flood message for the next round.
  */
 static GNUNET_SCHEDULER_TaskIdentifier flood_task;
 
+/**
+ * Task scheduled to compute our proof.
+ */
+static GNUNET_SCHEDULER_TaskIdentifier proof_task;
+
 /**
  * Notification context, simplifies client broadcasts.
  */
@@ -278,10 +308,11 @@ setup_estimate_message (struct GNUNET_NSE_ClientMessage *em)
   mean = 0.0;
   sum = 0.0;
   sumweight = 0.0;
-  for (i=0; i<HISTORY_SIZE; i++)
+  for (i = 0; i < estimate_count; i++)
     {
-      val = htonl (size_estimate_messages[i].matching_bits);
-      weight = HISTORY_SIZE - ((estimate_index + HISTORY_SIZE - i) % HISTORY_SIZE);
+      val = htonl (size_estimate_messages[(estimate_index - i + HISTORY_SIZE)
+          % HISTORY_SIZE].matching_bits);
+      weight = 1; /* was: estimate_count + 1 - i; */
 
       temp = weight + sumweight;
       q = val - mean;
@@ -301,11 +332,12 @@ setup_estimate_message (struct GNUNET_NSE_ClientMessage *em)
   em->header.type
     = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE);
   em->reserved = htonl (0);
-  em->size_estimate = mean - 0.5;
+  em->timestamp = GNUNET_TIME_absolute_hton(GNUNET_TIME_absolute_get());
+  em->size_estimate = mean - 1.0/3.0;
   em->std_deviation = std_dev;
   GNUNET_STATISTICS_set (stats, 
-                        "Current network size estimate",
-                        (uint64_t) pow (2, mean - 0.5), GNUNET_NO);
+                        "# nodes in the network (estimate)",
+                        (uint64_t) pow (2, mean - 1.0/3.0), GNUNET_NO);
 }
 
 
@@ -324,6 +356,7 @@ handle_start_message(void *cls, struct GNUNET_SERVER_Client *client,
                      const struct GNUNET_MessageHeader *message)
 {
   struct GNUNET_NSE_ClientMessage em;
+
 #if DEBUG_NSE
   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, 
             "Received START message from client\n");
@@ -345,12 +378,12 @@ static double
 get_matching_bits_delay (uint32_t matching_bits)
 {
   /* Calculated as: S + f/2 - (f / pi) * (atan(x - p'))*/  
-  // S is next_timestamp
-  // f is frequency (GNUNET_NSE_INTERVAL)
+  // S is next_timestamp (ignored in return value)
+  // f is frequency (gnunet_nse_interval)
   // x is matching_bits
   // p' is current_size_estimate
-  return ((double) GNUNET_NSE_INTERVAL.rel_value / (double) 2)
-    - ((GNUNET_NSE_INTERVAL.rel_value / M_PI) * atan (matching_bits - current_size_estimate));
+  return ((double) gnunet_nse_interval.rel_value / (double) 2.0)
+    - ((gnunet_nse_interval.rel_value / M_PI) * atan (matching_bits - current_size_estimate));
 }
 
 
@@ -416,6 +449,11 @@ get_transmit_delay (int round_offset)
       /* previous round is randomized between 0 and 50 ms */
       ret.rel_value = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
                                                50);
+#if DEBUG_NSE
+      GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, 
+                "Transmitting previous round behind schedule in %llu ms\n",
+                (unsigned long long) ret.rel_value);
+#endif
       return ret;
     case 0:
       /* current round is based on best-known matching_bits */
@@ -423,6 +461,13 @@ get_transmit_delay (int round_offset)
       dist_delay = get_matching_bits_delay (matching_bits);
       dist_delay += get_delay_randomization (matching_bits).rel_value;
       ret.rel_value = (uint64_t) dist_delay;
+#if DEBUG_NSE
+      GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, 
+                "For round %llu, delay for %u matching bits is %llu ms\n",
+                (unsigned long long) current_timestamp.abs_value,
+                (unsigned int) matching_bits,
+                (unsigned long long) ret.rel_value);
+#endif
       /* now consider round start time and add delay to it */
       tgt = GNUNET_TIME_absolute_add (current_timestamp, ret);
       return GNUNET_TIME_absolute_get_remaining (tgt);
@@ -465,20 +510,40 @@ transmit_ready (void *cls, size_t size, void *buf)
       return 0;
     }
   GNUNET_assert (size >= sizeof (struct GNUNET_NSE_FloodMessage));
-#if DEBUG_NSE > 1
-  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 
-             "Sending size estimate to `%s'\n",
-             GNUNET_i2s (&peer_entry->id));
-#endif
   idx = estimate_index;
-  if (peer_entry->previous_round == GNUNET_YES)
+  if (peer_entry->previous_round == GNUNET_NO)
     {
-      idx = (idx + HISTORY_SIZE -1) % HISTORY_SIZE;
-      peer_entry->previous_round = GNUNET_NO;
+      idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE;
+      peer_entry->previous_round = GNUNET_YES;
       peer_entry->transmit_task = GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0),
                                                                &transmit_task,
                                                                peer_entry);
     }
+  if ( (ntohl (size_estimate_messages[idx].hop_count) == 0) &&
+       (GNUNET_SCHEDULER_NO_TASK != proof_task) )
+    {
+      GNUNET_STATISTICS_update (stats, 
+                               "# flood messages not generated (no proof yet)", 
+                               1,
+                               GNUNET_NO);
+      return 0; 
+    }
+#if DEBUG_NSE
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
+             "In round %llu, sending to `%s' estimate with %u bits\n",
+             (unsigned long long) GNUNET_TIME_absolute_ntoh (size_estimate_messages[idx].timestamp).abs_value,
+             GNUNET_i2s (&peer_entry->id),
+             (unsigned int) ntohl (size_estimate_messages[idx].matching_bits));
+#endif
+  if (ntohl (size_estimate_messages[idx].hop_count) == 0) 
+    GNUNET_STATISTICS_update (stats, 
+                             "# flood messages started", 
+                             1,
+                             GNUNET_NO);
+  GNUNET_STATISTICS_update (stats, 
+                           "# flood messages transmitted", 
+                           1,
+                           GNUNET_NO);
   memcpy (buf,
          &size_estimate_messages[idx],
          sizeof (struct GNUNET_NSE_FloodMessage));
@@ -627,11 +692,23 @@ update_flood_message(void *cls,
     }
   current_timestamp = next_timestamp;
   next_timestamp = GNUNET_TIME_absolute_add (current_timestamp,
-                                            GNUNET_NSE_INTERVAL);
+                                            gnunet_nse_interval);
   estimate_index = (estimate_index + 1) % HISTORY_SIZE;
-  setup_flood_message (estimate_index, current_timestamp);
+  if (estimate_count < HISTORY_SIZE)
+    estimate_count++;
+  if (next_timestamp.abs_value == 
+      GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value)
+    {
+      /* we received a message for this round way early, use it! */
+      size_estimate_messages[estimate_index] = next_message;
+      size_estimate_messages[estimate_index].hop_count 
+       = htonl (1 + ntohl (next_message.hop_count));
+    }
+  else
+    setup_flood_message (estimate_index, current_timestamp);
+  next_message.matching_bits = htonl (0); /* reset for 'next' round */
   hop_count_max = 0;
-  for (i=0;i<HISTORY_SIZE;i++)
+  for (i = 0; i < HISTORY_SIZE; i++)
     hop_count_max = GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count),
                                hop_count_max);
   GNUNET_CONTAINER_multihashmap_iterate (peers,
@@ -683,40 +760,104 @@ check_proof_of_work(const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pkey,
          pkey, 
          sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
   GNUNET_CRYPTO_hash (buf, sizeof (buf), &result);
-  return (count_leading_zeroes (&result) >= NSE_WORK_REQUIRED) ? GNUNET_YES : GNUNET_NO;
+  return (count_leading_zeroes (&result) >= nse_work_required) ? GNUNET_YES : GNUNET_NO;
+}
+
+
+/**
+ * Write our current proof to disk.
+ */
+static void
+write_proof ()
+{
+  char *proof;
+
+  if (GNUNET_OK != 
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                              "NSE", "PROOFFILE",
+                                              &proof))
+    return;    
+  if (sizeof (my_proof) !=
+      GNUNET_DISK_fn_write (proof,
+                           &my_proof,
+                           sizeof (my_proof),
+                           GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE))
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                             "write",
+                             proof);   
+  GNUNET_free (proof);
+
 }
 
 
 /**
- * Given a public key, find an integer such that the hash of the key
- * concatenated with the integer has NSE_WORK_REQUIRED leading 0
- * bits.  FIXME: this is a synchronous function... bad
+ * Find our proof of work.
  *
- * @param pkey the public key
- * @return 64 bit number that satisfies the requirements
+ * @param cls closure (unused)
+ * @param tc task context
  */
-static uint64_t 
-find_proof_of_work(const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pkey)
+static void
+find_proof (void *cls,
+           const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
+#define ROUND_SIZE 10
   uint64_t counter;
   char buf[sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) + sizeof(uint64_t)];
   GNUNET_HashCode result;
+  unsigned int i;  
   
+  proof_task = GNUNET_SCHEDULER_NO_TASK;
   memcpy (&buf[sizeof(uint64_t)],
-         pkey, 
+         &my_public_key, 
          sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
-  counter = 0;
-  while (counter != UINT64_MAX)
+  i = 0;
+  counter = my_proof;
+  while ( (counter != UINT64_MAX) && (i < ROUND_SIZE) )
     {
       memcpy (buf,
              &counter, 
              sizeof(uint64_t));
       GNUNET_CRYPTO_hash (buf, sizeof (buf), &result);
-      if (NSE_WORK_REQUIRED <= count_leading_zeroes(&result))
-        break;
+      if (nse_work_required <= count_leading_zeroes(&result))
+       {
+         my_proof = counter;
+#if DEBUG_NSE
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "Proof of work found: %llu!\n",
+                     (unsigned long long) GNUNET_ntohll (counter));
+#endif
+         for (i=0;i<HISTORY_SIZE;i++)      
+           if (ntohl (size_estimate_messages[i].hop_count) == 0) 
+             {
+               size_estimate_messages[i].proof_of_work = my_proof;
+               GNUNET_CRYPTO_rsa_sign (my_private_key, 
+                                       &size_estimate_messages[i].purpose,
+                                       &size_estimate_messages[i].signature);
+             }
+         write_proof ();
+         return;
+       }
       counter++;
+      i++;
+    }
+  if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
+    {
+#if DEBUG_NSE
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Testing proofs currently at %llu\n",
+                 (unsigned long long) counter);
+#endif
+      /* remember progress every 100 rounds */
+      my_proof = counter;
+      write_proof (); 
+    }
+  else
+    {
+      my_proof = counter;
     }
-  return counter;
+  proof_task = GNUNET_SCHEDULER_add_delayed (proof_find_delay,
+                                            &find_proof,
+                                            NULL);
 }
 
 
@@ -737,6 +878,9 @@ verify_message_crypto(const struct GNUNET_NSE_FloodMessage *incoming_flood)
       check_proof_of_work (&incoming_flood->pkey,
                           incoming_flood->proof_of_work))
     {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                 _("Proof of work invalid: %llu!\n"),
+                 (unsigned long long) GNUNET_ntohll (incoming_flood->proof_of_work));
       GNUNET_break_op (0);
       return GNUNET_NO;
     }
@@ -775,11 +919,13 @@ update_flood_times (void *cls,
     return GNUNET_OK; /* already active */
   if (peer_entry == exclude)
     return GNUNET_OK; /* trigger of the update */
-  if (peer_entry->previous_round == GNUNET_YES)
+  if (peer_entry->previous_round == GNUNET_NO)
     {
       /* still stuck in previous round, no point to update, check that 
         we are active here though... */
+#if SEND_ON_CONNECT
       GNUNET_break (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK);
+#endif
       return GNUNET_OK; 
     }
   if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
@@ -816,33 +962,65 @@ handle_p2p_size_estimate(void *cls,
   uint32_t matching_bits;  
   unsigned int idx;
 
+#if ENABLE_HISTOGRAM
+  if (NULL != wh)
+    GNUNET_BIO_write_int64 (wh, GNUNET_TIME_absolute_get ().abs_value);
+#endif
   incoming_flood = (const struct GNUNET_NSE_FloodMessage *) message;
   GNUNET_STATISTICS_update (stats, 
                            "# flood messages received", 
                            1,
                            GNUNET_NO);
   matching_bits = ntohl (incoming_flood->matching_bits);
+#if DEBUG_NSE
+  {
+    char origin[5];
+    char pred[5];
+    struct GNUNET_PeerIdentity os;
+
+    GNUNET_CRYPTO_hash (&incoming_flood->pkey,
+                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                       &os.hashPubKey);
+    GNUNET_snprintf (origin, sizeof (origin),
+                    "%s",
+                    GNUNET_i2s (&os));
+    GNUNET_snprintf (pred, sizeof (pred),
+                    "%s",
+                    GNUNET_i2s (peer));
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Flood at %llu from `%s' via `%s' at `%s' with bits %u\n",
+               (unsigned long long) GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp).abs_value,
+               origin,
+               pred,
+               GNUNET_i2s (&my_identity),
+               (unsigned int) matching_bits);
+  }
+#endif  
+
   peer_entry = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
   if (NULL == peer_entry)
     {
       GNUNET_break (0);
       return GNUNET_OK;
     }
+
   ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp);
+
   if (ts.abs_value == current_timestamp.abs_value)
     idx = estimate_index;
-  else if (ts.abs_value == current_timestamp.abs_value - GNUNET_NSE_INTERVAL.rel_value)
+  else if (ts.abs_value == current_timestamp.abs_value - gnunet_nse_interval.rel_value)
     idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE;
-  else if (ts.abs_value == next_timestamp.abs_value - GNUNET_NSE_INTERVAL.rel_value)
+  else if (ts.abs_value == next_timestamp.abs_value - gnunet_nse_interval.rel_value)
     {
+      if (matching_bits <= ntohl (next_message.matching_bits))
+       return GNUNET_OK; /* ignore, simply too early/late */
       if (GNUNET_YES !=
          verify_message_crypto (incoming_flood))
        {
          GNUNET_break_op (0);
          return GNUNET_OK;
        }
-      /* FIXME: keep in special 'future' buffer until next round starts for us! */
-      GNUNET_break (0); /* not implemented */
+      next_message = *incoming_flood;
       return GNUNET_OK;
     }
   else
@@ -851,7 +1029,6 @@ handle_p2p_size_estimate(void *cls,
                                 "# flood messages discarded (clock skew too large)",
                                1,
                                 GNUNET_NO);
-      GNUNET_break_op (0);
       return GNUNET_OK;
     }
   if (0 == (memcmp (peer, &my_identity, sizeof(struct GNUNET_PeerIdentity))))
@@ -868,13 +1045,13 @@ handle_p2p_size_estimate(void *cls,
       /* cancel transmission from us to this peer for this round */
       if (idx == estimate_index)
        {
-         if (peer_entry->previous_round == GNUNET_NO)
+         if (peer_entry->previous_round == GNUNET_YES)
            {
              /* cancel any activity for current round */
              if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
                {
                  GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
-                 peer_entry->previous_round = GNUNET_NO;
+                 peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
                }
              if (peer_entry->th != NULL)
                {
@@ -886,12 +1063,19 @@ handle_p2p_size_estimate(void *cls,
       else
        {
          /* cancel previous round only */
-         peer_entry->previous_round = GNUNET_NO;
-       }
+         peer_entry->previous_round = GNUNET_YES;
+       } 
     }
   if (matching_bits <= ntohl (size_estimate_messages[idx].matching_bits)) 
     {
+      if ( (idx < estimate_index) &&
+          (peer_entry->previous_round == GNUNET_YES) )
+       peer_entry->previous_round = GNUNET_NO;
+      /* push back our result now, that peer is spreading bad information... */
+      if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK)
+       GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
+      peer_entry->transmit_task = GNUNET_SCHEDULER_add_now (&transmit_task,
+                                                           peer_entry);          
       /* Not closer than our most recent message, no need to do work here */
       GNUNET_STATISTICS_update (stats,
                                 "# flood messages ignored (had closer already)",
@@ -935,15 +1119,22 @@ handle_core_connect(void *cls, const struct GNUNET_PeerIdentity *peer,
 {
   struct NSEPeerEntry *peer_entry;
 
+ #if DEBUG_NSE
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, 
+            "Peer `%s' connected to us\n",
+            GNUNET_i2s (peer));
+#endif
   peer_entry = GNUNET_malloc(sizeof(struct NSEPeerEntry));
   peer_entry->id = *peer;
   GNUNET_CONTAINER_multihashmap_put (peers,
                                     &peer->hashPubKey,
                                     peer_entry,
                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+#if SEND_ON_CONNECT
   peer_entry->transmit_task = GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1),
                                                            &transmit_task,
                                                            peer_entry);
+#endif
 }
 
 
@@ -958,7 +1149,12 @@ handle_core_disconnect(void *cls, const struct GNUNET_PeerIdentity *peer)
 {
   struct NSEPeerEntry *pos;
 
-  pos = GNUNET_CONTAINER_multihashmap_get (peers,
+ #if DEBUG_NSE
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, 
+            "Peer `%s' disconnected from us\n",
+            GNUNET_i2s (peer));
+#endif
+ pos = GNUNET_CONTAINER_multihashmap_get (peers,
                                           &peer->hashPubKey);
   if (NULL == pos)
     {
@@ -992,8 +1188,17 @@ shutdown_task(void *cls,
       GNUNET_SCHEDULER_cancel (flood_task);
       flood_task = GNUNET_SCHEDULER_NO_TASK;
     }
-  GNUNET_SERVER_notification_context_destroy (nc);
-  nc = NULL;
+  if (proof_task != GNUNET_SCHEDULER_NO_TASK)
+    {
+      GNUNET_SCHEDULER_cancel (proof_task);
+      proof_task = GNUNET_SCHEDULER_NO_TASK;
+      write_proof (); /* remember progress */
+    }
+  if (nc != NULL)
+    {
+      GNUNET_SERVER_notification_context_destroy (nc);
+      nc = NULL;
+    }
   if (coreAPI != NULL)
     {
       GNUNET_CORE_disconnect (coreAPI);
@@ -1004,6 +1209,23 @@ shutdown_task(void *cls,
       GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
       stats = NULL;
     }
+  if (peers != NULL)
+    {
+      GNUNET_CONTAINER_multihashmap_destroy (peers);
+      peers = NULL;
+    }
+  if (my_private_key != NULL)
+    {
+      GNUNET_CRYPTO_rsa_key_free (my_private_key);
+      my_private_key = NULL;
+    }
+#if ENABLE_HISTOGRAM
+  if (wh != NULL)
+    {
+      GNUNET_BIO_write_close (wh);
+      wh = NULL;
+    }
+#endif
 }
 
 
@@ -1015,10 +1237,10 @@ shutdown_task(void *cls,
  * @param identity the public identity of this peer
  * @param publicKey the public key of this peer
  */
-void
-core_init(void *cls, struct GNUNET_CORE_Handle *server,
-          const struct GNUNET_PeerIdentity *identity,
-          const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
+static void
+core_init (void *cls, struct GNUNET_CORE_Handle *server,
+          const struct GNUNET_PeerIdentity *identity,
+          const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
 {
   struct GNUNET_TIME_Absolute now;
   struct GNUNET_TIME_Absolute prev_time;
@@ -1030,26 +1252,24 @@ core_init(void *cls, struct GNUNET_CORE_Handle *server,
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
                  "Connection to core FAILED!\n");
 #endif
-      GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
+      GNUNET_SCHEDULER_shutdown ();
       return;
     }
-  my_identity = *identity;
-  my_public_key = *publicKey;
-
+  GNUNET_assert (0 == memcmp (&my_identity, identity, sizeof (struct GNUNET_PeerIdentity)));
   now = GNUNET_TIME_absolute_get ();
-  current_timestamp.abs_value = (now.abs_value / GNUNET_NSE_INTERVAL.rel_value) * GNUNET_NSE_INTERVAL.rel_value;
-  next_timestamp.abs_value = current_timestamp.abs_value + GNUNET_NSE_INTERVAL.rel_value;
+  current_timestamp.abs_value = (now.abs_value / gnunet_nse_interval.rel_value) * gnunet_nse_interval.rel_value;
+  next_timestamp.abs_value = current_timestamp.abs_value + gnunet_nse_interval.rel_value;
   
-  for (i=0;i<HISTORY_SIZE;i++)
+  for (i = 0; i < HISTORY_SIZE; i++)
     {
-      prev_time.abs_value = next_timestamp.abs_value - (HISTORY_SIZE - i - 1) * GNUNET_NSE_INTERVAL.rel_value;
+      prev_time.abs_value = current_timestamp.abs_value - (HISTORY_SIZE - i - 1) * gnunet_nse_interval.rel_value;
       setup_flood_message (i, prev_time);
     }
   estimate_index = HISTORY_SIZE - 1;
+  estimate_count = 2;
   flood_task
     = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining (next_timestamp),
                                    &update_flood_message, NULL);
-  my_proof = find_proof_of_work (&my_public_key);
 }
 
 
@@ -1065,6 +1285,7 @@ run(void *cls, struct GNUNET_SERVER_Handle *server,
     const struct GNUNET_CONFIGURATION_Handle *c)
 {
   char *keyfile;
+  char *proof;
 
   static const struct GNUNET_SERVER_MessageHandler handlers[] =
     {
@@ -1077,6 +1298,34 @@ run(void *cls, struct GNUNET_SERVER_Handle *server,
       { NULL, 0, 0 } 
     };
   cfg = c;
+
+  if ( (GNUNET_OK != 
+       GNUNET_CONFIGURATION_get_value_time (cfg,
+                                            "NSE", "INTERVAL",
+                                            &gnunet_nse_interval)) ||
+       (GNUNET_OK != 
+       GNUNET_CONFIGURATION_get_value_time (cfg,
+                                            "NSE", "WORKDELAY",
+                                            &proof_find_delay)) ||
+       (GNUNET_OK != 
+       GNUNET_CONFIGURATION_get_value_number (cfg,
+                                              "NSE", "WORKBITS",
+                                              &nse_work_required)) )       
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
+                 _ ("NSE service is lacking key configuration settings.  Exiting.\n"));
+      GNUNET_SCHEDULER_shutdown ();
+      return;
+    }
+  if (nse_work_required >= sizeof (GNUNET_HashCode) * 8)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
+                 _ ("Invalid work requirement for NSE service. Exiting.\n"));
+      GNUNET_SCHEDULER_shutdown ();
+      return;
+    }
+
+
   if (GNUNET_OK != 
       GNUNET_CONFIGURATION_get_value_filename (cfg,
                                               "GNUNETD", "HOSTKEY",
@@ -1096,6 +1345,35 @@ run(void *cls, struct GNUNET_SERVER_Handle *server,
       GNUNET_SCHEDULER_shutdown ();
       return;
     }
+  GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
+  GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), &my_identity.hashPubKey);
+  if (GNUNET_OK != 
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                              "NSE", "PROOFFILE",
+                                              &proof))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
+                 _ ("NSE service is lacking key configuration settings.  Exiting.\n"));
+      if (my_private_key != NULL)
+       {
+         GNUNET_CRYPTO_rsa_key_free (my_private_key);
+         my_private_key = NULL;
+       }
+      GNUNET_SCHEDULER_shutdown ();
+      return;
+    }
+  if ( (GNUNET_YES != GNUNET_DISK_file_test (proof)) ||
+       (sizeof (my_proof) !=
+       GNUNET_DISK_fn_read (proof,
+                            &my_proof,
+                            sizeof (my_proof))) )
+    my_proof = 0; 
+  GNUNET_free (proof);
+  proof_task = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
+                                                  &find_proof,
+                                                  NULL);
+
+  peers = GNUNET_CONTAINER_multihashmap_create (128);
   GNUNET_SERVER_add_handlers (server, handlers);
   nc = GNUNET_SERVER_notification_context_create (server, 1);
   /* Connect to core service and register core handlers */
@@ -1111,12 +1389,24 @@ run(void *cls, struct GNUNET_SERVER_Handle *server,
                                 NULL, /* Don't want notified about all outbound messages */
                                 GNUNET_NO, /* For header only outbound notification */
                                 core_handlers); /* Register these handlers */
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+                               &shutdown_task, NULL);
+#if ENABLE_HISTOGRAM
+  if (GNUNET_OK == 
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                              "NSE", "HISTOGRAM",
+                                              &proof))
+    {
+      wh = GNUNET_BIO_write_open (proof);
+      GNUNET_free (proof);
+    }
+#endif
   if (coreAPI == NULL)
     {
-      GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
+      GNUNET_SCHEDULER_shutdown ();
       return;
     }
-  stats = GNUNET_STATISTICS_create ("NSE", cfg);
+  stats = GNUNET_STATISTICS_create ("nse", cfg);
 }