error handling
[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 it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
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  * @param cls closure
56  */
57 static void
58 shutdown_task (void *cls)
59 {
60   (void) cls;
61   if (sizeof(proof) != GNUNET_DISK_fn_write (pwfn,
62                                              &proof,
63                                              sizeof(proof),
64                                              GNUNET_DISK_PERM_USER_READ
65                                              | GNUNET_DISK_PERM_USER_WRITE))
66     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", pwfn);
67 }
68
69
70 /**
71  * Count the leading zeroes in hash.
72  *
73  * @param hash to count leading zeros in
74  * @return the number of leading zero bits.
75  */
76 static unsigned int
77 count_leading_zeroes (const struct GNUNET_HashCode *hash)
78 {
79   unsigned int hash_count;
80
81   hash_count = 0;
82   while (0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count))
83     hash_count++;
84   return hash_count;
85 }
86
87
88 /**
89  * Find our proof of work.
90  *
91  * @param cls closure (unused)
92  * @param tc task context
93  */
94 static void
95 find_proof (void *cls)
96 {
97 #define ROUND_SIZE 10
98   uint64_t counter;
99   char buf[sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)
100            + sizeof(uint64_t)] GNUNET_ALIGN;
101   struct GNUNET_HashCode result;
102   unsigned int i;
103   struct GNUNET_TIME_Absolute timestamp;
104   struct GNUNET_TIME_Relative elapsed;
105
106   (void) cls;
107   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
108               "Got Proof of Work %llu\n",
109               (unsigned long long) proof);
110   proof_task = NULL;
111   GNUNET_memcpy (&buf[sizeof(uint64_t)],
112                  &pub,
113                  sizeof(struct GNUNET_CRYPTO_EddsaPublicKey));
114   i = 0;
115   counter = proof;
116   timestamp = GNUNET_TIME_absolute_get ();
117   while ((counter != UINT64_MAX) && (i < ROUND_SIZE))
118   {
119     GNUNET_memcpy (buf, &counter, sizeof(uint64_t));
120     GNUNET_CRYPTO_pow_hash ("gnunet-nse-proof-of-work",
121                             buf,
122                             sizeof(buf),
123                             &result);
124     if (nse_work_required <= count_leading_zeroes (&result))
125     {
126       proof = counter;
127       fprintf (stdout,
128                "Proof of work found: %llu!\n",
129                (unsigned long long) proof);
130       GNUNET_SCHEDULER_shutdown ();
131       return;
132     }
133     counter++;
134     i++;
135   }
136   elapsed = GNUNET_TIME_absolute_get_duration (timestamp);
137   elapsed = GNUNET_TIME_relative_divide (elapsed, ROUND_SIZE);
138   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
139               "Current: %llu [%s/proof]\n",
140               (unsigned long long) counter,
141               GNUNET_STRINGS_relative_time_to_string (elapsed, 0));
142   if (proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
143   {
144     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
145                 "Testing proofs currently at %llu\n",
146                 (unsigned long long) counter);
147     /* remember progress every 100 rounds */
148     proof = counter;
149     shutdown_task (NULL);
150   }
151   else
152   {
153     proof = counter;
154   }
155   proof_task =
156     GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
157                                                 GNUNET_SCHEDULER_PRIORITY_IDLE,
158                                                 &find_proof,
159                                                 NULL);
160 }
161
162
163 /**
164  * Main function that will be run by the scheduler.
165  *
166  * @param cls closure
167  * @param args remaining command-line arguments
168  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
169  * @param cfg configuration
170  */
171 static void
172 run (void *cls,
173      char *const *args,
174      const char *cfgfile,
175      const struct GNUNET_CONFIGURATION_Handle *config)
176 {
177   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
178   char *pids;
179
180   (void) cls;
181   (void) args;
182   (void) cfgfile;
183   cfg = config;
184   /* load proof of work */
185   if (NULL == pwfn)
186   {
187     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
188                                                               "NSE",
189                                                               "PROOFFILE",
190                                                               &pwfn))
191     {
192       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "PROOFFILE");
193       GNUNET_SCHEDULER_shutdown ();
194       return;
195     }
196   }
197   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Proof of Work file: %s\n", pwfn);
198   if ((GNUNET_YES != GNUNET_DISK_file_test (pwfn)) ||
199       (sizeof(proof) != GNUNET_DISK_fn_read (pwfn, &proof, sizeof(proof))))
200     proof = 0;
201
202   /* load private key */
203   if (NULL == pkfn)
204   {
205     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
206                                                               "PEER",
207                                                               "PRIVATE_KEY",
208                                                               &pkfn))
209     {
210       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
211                                  "PEER",
212                                  "PRIVATE_KEY");
213       return;
214     }
215   }
216   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Private Key file: %s\n", pkfn);
217   if (NULL == (pk = GNUNET_CRYPTO_eddsa_key_create_from_file (pkfn)))
218   {
219     fprintf (stderr, _ ("Loading hostkey from `%s' failed.\n"), pkfn);
220     GNUNET_free (pkfn);
221     return;
222   }
223   GNUNET_free (pkfn);
224   GNUNET_CRYPTO_eddsa_key_get_public (pk, &pub);
225   GNUNET_free (pk);
226   pids = GNUNET_CRYPTO_eddsa_public_key_to_string (&pub);
227   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer ID: %s\n", pids);
228   GNUNET_free (pids);
229
230   /* get target bit amount */
231   if (0 == nse_work_required)
232   {
233     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
234                                                             "NSE",
235                                                             "WORKBITS",
236                                                             &nse_work_required))
237     {
238       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKBITS");
239       GNUNET_SCHEDULER_shutdown ();
240       return;
241     }
242     if (nse_work_required >= sizeof(struct GNUNET_HashCode) * 8)
243     {
244       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
245                                  "NSE",
246                                  "WORKBITS",
247                                  _ ("Value is too large.\n"));
248       GNUNET_SCHEDULER_shutdown ();
249       return;
250     }
251     else if (0 == nse_work_required)
252     {
253       GNUNET_SCHEDULER_shutdown ();
254       return;
255     }
256   }
257   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Bits: %llu\n", nse_work_required);
258
259   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
260               "Delay between tries: %s\n",
261               GNUNET_STRINGS_relative_time_to_string (proof_find_delay, 1));
262   proof_task =
263     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
264                                         &find_proof,
265                                         NULL);
266   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
267 }
268
269
270 /**
271  * Program to manipulate ECC key files.
272  *
273  * @param argc number of arguments from the command line
274  * @param argv command line arguments
275  * @return 0 ok, 1 on error
276  */
277 int
278 main (int argc, char *const *argv)
279 {
280   struct GNUNET_GETOPT_CommandLineOption options[] =
281   { GNUNET_GETOPT_option_ulong (
282       'b',
283       "bits",
284       "BITS",
285       gettext_noop ("number of bits to require for the proof of work"),
286       &nse_work_required),
287     GNUNET_GETOPT_option_filename (
288       'k',
289       "keyfile",
290       "FILE",
291       gettext_noop ("file with private key, otherwise default is used"),
292       &pkfn),
293     GNUNET_GETOPT_option_filename (
294       'o',
295       "outfile",
296       "FILE",
297       gettext_noop ("file with proof of work, otherwise default is used"),
298       &pwfn),
299     GNUNET_GETOPT_option_relative_time ('t',
300                                         "timeout",
301                                         "TIME",
302                                         gettext_noop (
303                                           "time to wait between calculations"),
304                                         &proof_find_delay),
305     GNUNET_GETOPT_OPTION_END };
306   int ret;
307
308   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
309     return 2;
310
311   ret =
312     (GNUNET_OK ==
313      GNUNET_PROGRAM_run (argc,
314                          argv,
315                          "gnunet-scrypt [OPTIONS] prooffile",
316                          gettext_noop ("Manipulate GNUnet proof of work files"),
317                          options,
318                          &run,
319                          NULL))
320     ? 0
321     : 1;
322   GNUNET_free ((void *) argv);
323   GNUNET_free_non_null (pwfn);
324   return ret;
325 }
326
327
328 /* end of gnunet-scrypt.c */