small API change: do no longer pass rarely needed GNUNET_SCHEDULER_TaskContext to...
[oweals/gnunet.git] / src / util / gnunet-scrypt.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2014 GNUnet e.V.
4
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.
9
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.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file util/gnunet-scrypt.c
22  * @brief tool to manipulate SCRYPT proofs of work.
23  * @author Bart Polot
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include <gcrypt.h>
28
29 /**
30  * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits.
31  */
32 static unsigned long long nse_work_required;
33
34 /**
35  * Interval between proof find runs.
36  */
37 static struct GNUNET_TIME_Relative proof_find_delay;
38
39 static struct GNUNET_CRYPTO_EddsaPublicKey pub;
40
41 static uint64_t proof;
42
43 static struct GNUNET_SCHEDULER_Task * proof_task;
44
45 static const struct GNUNET_CONFIGURATION_Handle *cfg;
46
47 static char *pkfn;
48
49 static char *pwfn;
50
51
52 /**
53  * Write our current proof to disk.
54  */
55 static void
56 write_proof ()
57 {
58   if (sizeof (proof) !=
59       GNUNET_DISK_fn_write (pwfn, &proof, sizeof (proof),
60                             GNUNET_DISK_PERM_USER_READ |
61                             GNUNET_DISK_PERM_USER_WRITE))
62     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", proof);
63 }
64
65
66 /**
67  * Calculate the 'proof-of-work' hash (an expensive hash).
68  *
69  * @param buf data to hash
70  * @param buf_len number of bytes in @a buf
71  * @param result where to write the resulting hash
72  */
73 static void
74 pow_hash (const void *buf,
75           size_t buf_len,
76           struct GNUNET_HashCode *result)
77 {
78   GNUNET_break (0 ==
79   gcry_kdf_derive (buf, buf_len,
80                    GCRY_KDF_SCRYPT,
81                    1 /* subalgo */,
82                    "gnunet-proof-of-work", strlen ("gnunet-proof-of-work"),
83                    2 /* iterations; keep cost of individual op small */,
84                    sizeof (struct GNUNET_HashCode), result));
85 }
86
87
88 /**
89  * Count the leading zeroes in hash.
90  *
91  * @param hash to count leading zeros in
92  * @return the number of leading zero bits.
93  */
94 static unsigned int
95 count_leading_zeroes (const struct GNUNET_HashCode *hash)
96 {
97   unsigned int hash_count;
98
99   hash_count = 0;
100   while (0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count))
101     hash_count++;
102   return hash_count;
103 }
104
105
106 /**
107  * Find our proof of work.
108  *
109  * @param cls closure (unused)
110  * @param tc task context
111  */
112 static void
113 find_proof (void *cls)
114 {
115   #define ROUND_SIZE 10
116   uint64_t counter;
117   char buf[sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
118            sizeof (uint64_t)] GNUNET_ALIGN;
119   struct GNUNET_HashCode result;
120   unsigned int i;
121   struct GNUNET_TIME_Absolute timestamp;
122   struct GNUNET_TIME_Relative elapsed;
123   const struct GNUNET_SCHEDULER_TaskContext *tc;
124
125   tc = GNUNET_SCHEDULER_get_task_context ();
126   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
127   {
128     write_proof ();
129     return;
130   }
131   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got Proof of Work %llu\n", proof);
132   proof_task = NULL;
133   memcpy (&buf[sizeof (uint64_t)], &pub,
134           sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
135   i = 0;
136   counter = proof;
137   timestamp = GNUNET_TIME_absolute_get ();
138   while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
139   {
140     memcpy (buf, &counter, sizeof (uint64_t));
141     pow_hash (buf, sizeof (buf), &result);
142     if (nse_work_required <= count_leading_zeroes (&result))
143     {
144       proof = counter;
145       FPRINTF (stdout, "Proof of work found: %llu!\n",
146                (unsigned long long) proof);
147       write_proof ();
148       return;
149     }
150     counter++;
151     i++;
152   }
153   elapsed = GNUNET_TIME_absolute_get_duration (timestamp);
154   elapsed = GNUNET_TIME_relative_divide (elapsed, ROUND_SIZE);
155   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
156               "Current: %llu [%s/proof]\n",
157               (unsigned long long) counter,
158               GNUNET_STRINGS_relative_time_to_string (elapsed, 0));
159   if (proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
160   {
161     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing proofs currently at %llu\n",
162                 (unsigned long long) counter);
163     /* remember progress every 100 rounds */
164     proof = counter;
165     write_proof ();
166   }
167   else
168   {
169     proof = counter;
170   }
171   proof_task =
172   GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
173                                               GNUNET_SCHEDULER_PRIORITY_IDLE,
174                                               &find_proof, NULL);
175 }
176
177
178 /**
179  * Main function that will be run by the scheduler.
180  *
181  * @param cls closure
182  * @param args remaining command-line arguments
183  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
184  * @param cfg configuration
185  */
186 static void
187 run (void *cls,
188      char *const *args,
189      const char *cfgfile,
190      const struct GNUNET_CONFIGURATION_Handle *config)
191 {
192   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
193   char *pids;
194
195   cfg = config;
196
197   /* load proof of work */
198   if (NULL == pwfn)
199   {
200     if (GNUNET_OK !=
201         GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE",
202                                                  "PROOFFILE",
203                                                  &pwfn))
204     {
205       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
206                                  "NSE", "PROOFFILE");
207       GNUNET_SCHEDULER_shutdown ();
208       return;
209     }
210   }
211   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
212               "Proof of Work file: %s\n",
213               pwfn);
214   if ( (GNUNET_YES != GNUNET_DISK_file_test (pwfn)) ||
215        (sizeof (proof) !=
216         GNUNET_DISK_fn_read (pwfn, &proof, sizeof (proof))))
217     proof = 0;
218
219   /* load private key */
220   if (NULL == pkfn)
221   {
222     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "PEER",
223                                                               "PRIVATE_KEY",
224                                                               &pkfn))
225     {
226       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
227                                  "PEER", "PRIVATE_KEY");
228       return;
229     }
230   }
231   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Private Key file: %s\n", pkfn);
232   if (NULL == (pk = GNUNET_CRYPTO_eddsa_key_create_from_file (pkfn)))
233   {
234     FPRINTF (stderr, _("Loading hostkey from `%s' failed.\n"), pkfn);
235     GNUNET_free (pkfn);
236     return;
237   }
238   GNUNET_free (pkfn);
239   GNUNET_CRYPTO_eddsa_key_get_public (pk, &pub);
240   GNUNET_free (pk);
241   pids = GNUNET_CRYPTO_eddsa_public_key_to_string (&pub);
242   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
243               "Peer ID: %s\n",
244               pids);
245   GNUNET_free (pids);
246
247   /* get target bit amount */
248   if (0 == nse_work_required)
249   {
250     if (GNUNET_OK !=
251         GNUNET_CONFIGURATION_get_value_number (cfg, "NSE", "WORKBITS",
252                                                &nse_work_required))
253     {
254       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKBITS");
255       GNUNET_SCHEDULER_shutdown ();
256       return;
257     }
258     if (nse_work_required >= sizeof (struct GNUNET_HashCode) * 8)
259     {
260       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKBITS",
261                                 _("Value is too large.\n"));
262       GNUNET_SCHEDULER_shutdown ();
263       return;
264     } else if (0 == nse_work_required)
265     {
266       write_proof ();
267       GNUNET_SCHEDULER_shutdown ();
268       return;
269     }
270   }
271   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
272               "Bits: %llu\n",
273               nse_work_required);
274
275   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
276               "Delay between tries: %s\n",
277               GNUNET_STRINGS_relative_time_to_string (proof_find_delay, 1));
278   GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
279                                       &find_proof, NULL);
280 }
281
282
283 /**
284  * Program to manipulate ECC key files.
285  *
286  * @param argc number of arguments from the command line
287  * @param argv command line arguments
288  * @return 0 ok, 1 on error
289  */
290 int
291 main (int argc, char *const *argv)
292 {
293   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
294     { 'b', "bits", "BITS",
295       gettext_noop ("number of bits to require for the proof of work"),
296       1, &GNUNET_GETOPT_set_ulong, &nse_work_required },
297     { 'k', "keyfile", "FILE",
298       gettext_noop ("file with private key, otherwise default is used"),
299       1, &GNUNET_GETOPT_set_filename, &pkfn },
300     { 'o', "outfile", "FILE",
301       gettext_noop ("file with proof of work, otherwise default is used"),
302       1, &GNUNET_GETOPT_set_filename, &pwfn },
303     { 't', "timeout", "TIME",
304       gettext_noop ("time to wait between calculations"),
305       1, &GNUNET_GETOPT_set_relative_time, &proof_find_delay },
306     GNUNET_GETOPT_OPTION_END
307   };
308   int ret;
309
310   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
311     return 2;
312
313   ret = (GNUNET_OK ==
314          GNUNET_PROGRAM_run (argc, argv,
315                              "gnunet-scrypt [OPTIONS] prooffile",
316                              gettext_noop ("Manipulate GNUnet proof of work files"),
317                              options, &run, NULL)) ? 0 : 1;
318   GNUNET_free ((void*) argv);
319   GNUNET_free_non_null (pwfn);
320   return ret;
321 }
322
323 /* end of gnunet-scrypt.c */