fix bit counting mess
[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_ltr (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 (GNUNET_SYSERR ==
218       GNUNET_CRYPTO_eddsa_key_from_file (pkfn,
219                                          GNUNET_YES,
220                                          &pk))
221   {
222     fprintf (stderr, _ ("Loading hostkey from `%s' failed.\n"), pkfn);
223     GNUNET_free (pkfn);
224     return;
225   }
226   GNUNET_free (pkfn);
227   GNUNET_CRYPTO_eddsa_key_get_public (&pk,
228                                       &pub);
229   pids = GNUNET_CRYPTO_eddsa_public_key_to_string (&pub);
230   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer ID: %s\n", pids);
231   GNUNET_free (pids);
232
233   /* get target bit amount */
234   if (0 == nse_work_required)
235   {
236     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
237                                                             "NSE",
238                                                             "WORKBITS",
239                                                             &nse_work_required))
240     {
241       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKBITS");
242       GNUNET_SCHEDULER_shutdown ();
243       return;
244     }
245     if (nse_work_required >= sizeof(struct GNUNET_HashCode) * 8)
246     {
247       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
248                                  "NSE",
249                                  "WORKBITS",
250                                  _ ("Value is too large.\n"));
251       GNUNET_SCHEDULER_shutdown ();
252       return;
253     }
254     else if (0 == nse_work_required)
255     {
256       GNUNET_SCHEDULER_shutdown ();
257       return;
258     }
259   }
260   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Bits: %llu\n", nse_work_required);
261
262   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
263               "Delay between tries: %s\n",
264               GNUNET_STRINGS_relative_time_to_string (proof_find_delay, 1));
265   proof_task =
266     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
267                                         &find_proof,
268                                         NULL);
269   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
270 }
271
272
273 /**
274  * Program to manipulate ECC key files.
275  *
276  * @param argc number of arguments from the command line
277  * @param argv command line arguments
278  * @return 0 ok, 1 on error
279  */
280 int
281 main (int argc, char *const *argv)
282 {
283   struct GNUNET_GETOPT_CommandLineOption options[] =
284   { GNUNET_GETOPT_option_ulong (
285       'b',
286       "bits",
287       "BITS",
288       gettext_noop ("number of bits to require for the proof of work"),
289       &nse_work_required),
290     GNUNET_GETOPT_option_filename (
291       'k',
292       "keyfile",
293       "FILE",
294       gettext_noop ("file with private key, otherwise default is used"),
295       &pkfn),
296     GNUNET_GETOPT_option_filename (
297       'o',
298       "outfile",
299       "FILE",
300       gettext_noop ("file with proof of work, otherwise default is used"),
301       &pwfn),
302     GNUNET_GETOPT_option_relative_time ('t',
303                                         "timeout",
304                                         "TIME",
305                                         gettext_noop (
306                                           "time to wait between calculations"),
307                                         &proof_find_delay),
308     GNUNET_GETOPT_OPTION_END };
309   int ret;
310
311   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
312     return 2;
313
314   ret =
315     (GNUNET_OK ==
316      GNUNET_PROGRAM_run (argc,
317                          argv,
318                          "gnunet-scrypt [OPTIONS] prooffile",
319                          gettext_noop ("Manipulate GNUnet proof of work files"),
320                          options,
321                          &run,
322                          NULL))
323     ? 0
324     : 1;
325   GNUNET_free_nz ((void *) argv);
326   GNUNET_free_non_null (pwfn);
327   return ret;
328 }
329
330
331 /* end of gnunet-scrypt.c */