create crypto_pow, in preparation for #3795
[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 (buf, sizeof(buf), &result);
121     if (nse_work_required <= count_leading_zeroes (&result))
122     {
123       proof = counter;
124       fprintf (stdout,
125                "Proof of work found: %llu!\n",
126                (unsigned long long) proof);
127       GNUNET_SCHEDULER_shutdown ();
128       return;
129     }
130     counter++;
131     i++;
132   }
133   elapsed = GNUNET_TIME_absolute_get_duration (timestamp);
134   elapsed = GNUNET_TIME_relative_divide (elapsed, ROUND_SIZE);
135   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
136               "Current: %llu [%s/proof]\n",
137               (unsigned long long) counter,
138               GNUNET_STRINGS_relative_time_to_string (elapsed, 0));
139   if (proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE))
140   {
141     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
142                 "Testing proofs currently at %llu\n",
143                 (unsigned long long) counter);
144     /* remember progress every 100 rounds */
145     proof = counter;
146     shutdown_task (NULL);
147   }
148   else
149   {
150     proof = counter;
151   }
152   proof_task =
153     GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay,
154                                                 GNUNET_SCHEDULER_PRIORITY_IDLE,
155                                                 &find_proof,
156                                                 NULL);
157 }
158
159
160 /**
161  * Main function that will be run by the scheduler.
162  *
163  * @param cls closure
164  * @param args remaining command-line arguments
165  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
166  * @param cfg configuration
167  */
168 static void
169 run (void *cls,
170      char *const *args,
171      const char *cfgfile,
172      const struct GNUNET_CONFIGURATION_Handle *config)
173 {
174   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
175   char *pids;
176
177   (void) cls;
178   (void) args;
179   (void) cfgfile;
180   cfg = config;
181   /* load proof of work */
182   if (NULL == pwfn)
183   {
184     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
185                                                               "NSE",
186                                                               "PROOFFILE",
187                                                               &pwfn))
188     {
189       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "PROOFFILE");
190       GNUNET_SCHEDULER_shutdown ();
191       return;
192     }
193   }
194   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Proof of Work file: %s\n", pwfn);
195   if ((GNUNET_YES != GNUNET_DISK_file_test (pwfn)) ||
196       (sizeof(proof) != GNUNET_DISK_fn_read (pwfn, &proof, sizeof(proof))))
197     proof = 0;
198
199   /* load private key */
200   if (NULL == pkfn)
201   {
202     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
203                                                               "PEER",
204                                                               "PRIVATE_KEY",
205                                                               &pkfn))
206     {
207       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
208                                  "PEER",
209                                  "PRIVATE_KEY");
210       return;
211     }
212   }
213   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Private Key file: %s\n", pkfn);
214   if (NULL == (pk = GNUNET_CRYPTO_eddsa_key_create_from_file (pkfn)))
215   {
216     fprintf (stderr, _ ("Loading hostkey from `%s' failed.\n"), pkfn);
217     GNUNET_free (pkfn);
218     return;
219   }
220   GNUNET_free (pkfn);
221   GNUNET_CRYPTO_eddsa_key_get_public (pk, &pub);
222   GNUNET_free (pk);
223   pids = GNUNET_CRYPTO_eddsa_public_key_to_string (&pub);
224   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer ID: %s\n", pids);
225   GNUNET_free (pids);
226
227   /* get target bit amount */
228   if (0 == nse_work_required)
229   {
230     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
231                                                             "NSE",
232                                                             "WORKBITS",
233                                                             &nse_work_required))
234     {
235       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKBITS");
236       GNUNET_SCHEDULER_shutdown ();
237       return;
238     }
239     if (nse_work_required >= sizeof(struct GNUNET_HashCode) * 8)
240     {
241       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
242                                  "NSE",
243                                  "WORKBITS",
244                                  _ ("Value is too large.\n"));
245       GNUNET_SCHEDULER_shutdown ();
246       return;
247     }
248     else if (0 == nse_work_required)
249     {
250       GNUNET_SCHEDULER_shutdown ();
251       return;
252     }
253   }
254   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Bits: %llu\n", nse_work_required);
255
256   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
257               "Delay between tries: %s\n",
258               GNUNET_STRINGS_relative_time_to_string (proof_find_delay, 1));
259   proof_task =
260     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
261                                         &find_proof,
262                                         NULL);
263   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
264 }
265
266
267 /**
268  * Program to manipulate ECC key files.
269  *
270  * @param argc number of arguments from the command line
271  * @param argv command line arguments
272  * @return 0 ok, 1 on error
273  */
274 int
275 main (int argc, char *const *argv)
276 {
277   struct GNUNET_GETOPT_CommandLineOption options[] =
278   { GNUNET_GETOPT_option_ulong (
279       'b',
280       "bits",
281       "BITS",
282       gettext_noop ("number of bits to require for the proof of work"),
283       &nse_work_required),
284     GNUNET_GETOPT_option_filename (
285       'k',
286       "keyfile",
287       "FILE",
288       gettext_noop ("file with private key, otherwise default is used"),
289       &pkfn),
290     GNUNET_GETOPT_option_filename (
291       'o',
292       "outfile",
293       "FILE",
294       gettext_noop ("file with proof of work, otherwise default is used"),
295       &pwfn),
296     GNUNET_GETOPT_option_relative_time ('t',
297                                         "timeout",
298                                         "TIME",
299                                         gettext_noop (
300                                           "time to wait between calculations"),
301                                         &proof_find_delay),
302     GNUNET_GETOPT_OPTION_END };
303   int ret;
304
305   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
306     return 2;
307
308   ret =
309     (GNUNET_OK ==
310      GNUNET_PROGRAM_run (argc,
311                          argv,
312                          "gnunet-scrypt [OPTIONS] prooffile",
313                          gettext_noop ("Manipulate GNUnet proof of work files"),
314                          options,
315                          &run,
316                          NULL))
317     ? 0
318     : 1;
319   GNUNET_free ((void *) argv);
320   GNUNET_free_non_null (pwfn);
321   return ret;
322 }
323
324
325 /* end of gnunet-scrypt.c */