Added counter for total restarts
[core.git] / contrib / chash / chash.php
1 <?php
2 error_reporting(E_ALL | E_STRICT);
3
4 define('START_TIME'            , microtime(TRUE));
5 define('CHECK_POINT'           , 'chash.pos');
6
7 $GLOBALS['block_size']          = 100;
8 $GLOBALS['none_increment']      = (1 / pow(10, 20));
9 $GLOBALS['hash_algo']           = MHASH_RIPEMD320;
10 $GLOBALS['flush_file_time']     = 30;
11 $GLOBALS['restart_search_time'] = 1800;
12
13 // Hashes per call
14 $GLOBALS['hash_cycles'] = 3;
15
16 // Total restarts
17 $GLOBALS['total_restarts'] = 0;
18
19 // Found hashes
20 $GLOBALS['found_hashes'] = array(0 => array());
21
22 /**
23  * Continued-hashing
24  *
25  * @author              Roland Haeder <roland@mxchange.org>
26  * @copyright   Copyright (c) 2013 by Core Developer Team
27  * @license             See LICENSE (public-domain)
28  */
29
30 /**
31  * Calculates a simple but stronger hash from given string. No salts are being
32  * added here.
33  *
34  * @param       $str    The string to be hashed
35  * @return      $hash   The hash from string $str
36  */
37 function hashString ($str) {
38         // Calculate strong hash from given string
39         $hash = mhash($GLOBALS['hash_algo'], $str);
40
41         // Return it hexadecimal-encoded
42         return bin2hex($hash);
43 }
44
45 /**
46  * Multiple-hashes given string. This is done by hashing the given string and
47  * then hashing the generated hash again.
48  *
49  * @param       $str    The string to be hashed 4 times
50  * @return      $hash   The generated hash
51  */
52 function multipleHashString ($str) {
53         // Generate hash from given hash
54         $hash = hashString($str);
55
56         // Now over-hash it
57         for ($idx = 0; $idx < ($GLOBALS['hash_cycles'] - 1); $idx++) {
58                 // Over-hash the given hash
59                 $hash = hashString($hash);
60         } // END - for
61
62         // Return it
63         return $hash;
64 }
65
66 /**
67  * Calculates a "modula-hash" based given two hashes.
68  *
69  * @param       $hash1  Hash 1
70  * @param       $hash2  Hash 2
71  */
72 function modulaHash ($hash1, $hash2) {
73         // Both must have same length
74         assert(strlen($hash1) === strlen($hash2));
75
76         // Init new hash
77         $modulaHash = '';
78
79         // "Walk" trough first hash and get every 2 byte of both hashes
80         for ($idx = 0; $idx < strlen($hash1); $idx += 2) {
81                 // Init modula value
82                 $mod = 0;
83
84                 // Get both hash parts and convert to ASCII number
85                 $part1 = hexdec(substr($hash1, $idx, 2));
86                 $part2 = hexdec(substr($hash2, $idx, 2));
87
88                 /*
89                  * If part1 is larget part2, part1 is divident and vise-versa. But don't do it
90                  * if one is zero
91                  */
92                 if (($part1 > $part2) && ($part2 > 0)) {
93                         // 'part1' is larger than 'part2'
94                         $mod = $part1 % $part2;
95                 } elseif (($part1 < $part2) && ($part1 > 0)) {
96                         // 'part2' is larger than 'part1'
97                         $mod = $part2 % $part1;
98                 }
99
100                 // "Invert" the result against 255
101                 $mod = 255 - $mod;
102
103                 // Encode to hex, pre-pad it with zeros and add to new hash
104                 $modulaHash .= padHex($mod);
105         } // END - for
106
107         // Modula hash must have same length as input hash
108         assert(strlen($modulaHash) === strlen($hash1));
109
110         // Return modula hash
111         return $modulaHash;
112 }
113
114 /**
115  * Calculates a "sqrt-hash" based given two hashes and single-hash it
116  *
117  * @param       $hash1  Hash 1
118  * @param       $hash2  Hash 2
119  */
120 function sqrtHash ($hash1, $hash2) {
121         // Both must have same length
122         assert(strlen($hash1) === strlen($hash2));
123
124         // Init new hash
125         $sqrtHash = '';
126
127         // "Walk" trough first hash and get every 2 byte of both hashes
128         for ($idx = 0; $idx < strlen($hash1); $idx += 2) {
129                 // Init modula value
130                 $mod = 0;
131
132                 // Get both hash parts and convert to ASCII number
133                 $part1 = hexdec(substr($hash1, $idx, 2));
134                 $part2 = hexdec(substr($hash2, $idx, 2));
135
136                 // Calculate square root of both parts being multiplied and round up, then "invert" it against 255
137                 $sqrt = intval(255 - ceil(sqrt($part1 * $part2)));
138
139                 // Encode to hex, pre-pad it with zeros and add to new hash
140                 $sqrtHash .= padHex($sqrt);
141         } // END - for
142
143         // "sqrt-hash" must have same length as input hash
144         assert(strlen($sqrtHash) === strlen($hash1));
145
146         // Hash reversed "sqrt-hash" again and return it
147         return hashString(strrev($sqrtHash));
148 }
149
150 /**
151  * Converts a number between 0 and 255 into a zero-padded hexadecimal string
152  *
153  * @param       $num    Number between 0 and 255
154  * @return      $hex    Hexadecimal string, padded with zeros
155  */
156 function padHex ($num) {
157         // Must be a integer number and between 0 and 255
158         assert(is_int($num));
159         assert($num >= 0);
160         assert($num <= 255);
161
162         // Convert it
163         $hex = str_pad(dechex($num), 2, '0', STR_PAD_LEFT);
164
165         // ... and return it
166         return $hex;
167 }
168
169 /**
170  * Calculates sum from given hash
171  *
172  * @param       $hash   Hash to calculate sum from
173  * @return      $sum    Sum from given hash
174  */
175 function calculateSumFromHash ($hash) {
176         // Everything starts with zero ...
177         $sum = 0;
178
179         // Loop through hash
180         for ($idx = 0; $idx < (strlen($hash) / 2); $idx++) {
181                 // And add it
182                 $sum = $sum + hexdec(substr($hash, $idx, 2));
183         } // END - for
184
185         // And return it
186         return $sum;
187 }
188
189 /**
190  * Calculates new nonce
191  *
192  * @return      void
193  */
194 function calculateNonce () {
195         // Linear incrementation
196         $GLOBALS['nonce'] += $GLOBALS['none_increment'];
197 }
198
199 /**
200  * Writes/flushes check-point file
201  *
202  * @param       $hash   Modula hash (or hash to save)
203  * @return      void
204  */
205 function flushCheckPointFile ($hash) {
206         // Display message
207         print ('FLUSHING: Writing ' . count($GLOBALS['found_hashes']) . ' blocks ...' . PHP_EOL);
208
209         // Start timer
210         $timer = microtime(TRUE);
211
212         // Flush data
213         file_put_contents(
214                 CHECK_POINT,
215                 $GLOBALS['total_blocks'] . ':' .
216                 $GLOBALS['total_reward'] . ':' .
217                 $GLOBALS['total_hashes'] . ':' .
218                 $GLOBALS['total_found'] . ':' .
219                 $GLOBALS['total_restarts'] . ':' .
220                 $GLOBALS['hash_cycles'] . ':' .
221                 base64_encode($GLOBALS['nonce']) . ':' .
222                 $hash . ':' .
223                 $GLOBALS['root_hash'] . ':' .
224                 base64_encode(gzcompress(serialize($GLOBALS['found_hashes'])))
225         );
226
227         // Set time
228         $GLOBALS['time_flush'] = microtime(TRUE);
229         print ('FLUSHING: Took ' . ($GLOBALS['time_flush'] - $timer) . ' seconds.' . PHP_EOL);
230 }
231
232 /**
233  * Adds a found hash and flushes the checkpoint file
234  *
235  * @param       $hash   Hash to save
236  */
237 function addFoundHash ($hash) {
238         // Increment counter
239         $GLOBALS['total_found']++;
240
241         // Add hash to array
242         array_push($GLOBALS['found_hashes'][$GLOBALS['total_blocks']], array(
243                 'modula_hash'  => $GLOBALS['modula_hash'],
244                 'genesis_hash' => $GLOBALS['genesis_hash'],
245                 'root_hash'    => $GLOBALS['root_hash'],
246                 'nonce'        => $GLOBALS['nonce'],
247                 'iter'         => $GLOBALS['iteration'],
248                 'hashes_block' => $GLOBALS['hashes_block'],
249                 'hash_cycles'  => $GLOBALS['hash_cycles'],
250                 'nonce_hash'   => $hash
251         ));
252
253         // Found hash:
254         print ('FOUND: hash=' . $hash . ',nonce=' . $GLOBALS['nonce'] . ',total_found=' . $GLOBALS['total_found'] . PHP_EOL);
255
256         // Set time as a new hash was found
257         $GLOBALS['found_time'] = microtime(TRUE);
258
259         // Flush check-point file after new hash is found
260         flushCheckPointFile($hash);
261
262         // Use nonceHash as next modula hash
263         setModulaHash($hash);
264 }
265
266 /**
267  * Initializes nonce
268  *
269  * @return      void
270  */
271 function initNonce () {
272         $GLOBALS['nonce'] = 1 / (mt_rand() ^ pi());
273         print (__FUNCTION__ . ': nonce=' . $GLOBALS['nonce'] . PHP_EOL);
274 }
275
276 /**
277  * Sets modula hash and calculates sum of it
278  *
279  * @param       $hash   Hash to set as "modula hash"
280  * @return      void
281  */
282 function setModulaHash ($hash) {
283         $GLOBALS['modula_hash'] = $hash;
284         $GLOBALS['sum_modula']  = calculateSumFromHash($GLOBALS['modula_hash']);
285 }
286
287 /*
288  * Calculate "genesis" hashes, please note that these "genesis strings" are now
289  * known to the public as you can read them here in source code and therefore I
290  * will not use them for the real genesis hashes.
291  */
292 $gensisHashes = array(
293         // A famous quote from Deus Ex 2 - Invisible War
294         multiplehashString('"Informations must be free." - AI Helios from Deus Ex'),
295         // My name + URL of my first StatusNet instance
296         multipleHashString('Roland Haeder, https://status.mxchange.org'),
297         // A famous quote from Linus Torwalds
298         multipleHashString('"Software is like sex. Its better when its free." - Linus Torwalds'),
299         // Well ...
300         multipleHashString('September 11 is a big lie.'),
301
302         // GNU is not Uni*
303         multipleHashString('GNU is Not Uni*.'),
304         // WINE is not an emulator
305         multipleHashString('WINE Is Not an Emulator.'),
306         // FlightGear - Fly free!
307         multipleHashString('FlightGear - Fly free!'),
308         // Quote from Linus Torwalds
309         multipleHashString('Your code is shit. Your argument is shit.'),
310 );
311
312 // Calculate "modula hash" from 1st/4th and 2nd/3rd
313 $modulaHashes = array(
314         // "Block" 0
315         modulaHash($gensisHashes[0], $gensisHashes[3]),
316         modulaHash($gensisHashes[1], $gensisHashes[2]),
317
318         // "Block" 1
319         modulaHash($gensisHashes[4], $gensisHashes[7]),
320         modulaHash($gensisHashes[5], $gensisHashes[6]),
321 );
322
323 // Calculate "sqrt hash"
324 $sqrtHashes = array(
325         sqrtHash($modulaHashes[0], $modulaHashes[1]),
326         sqrtHash($modulaHashes[2], $modulaHashes[3])
327 );
328
329 // Calulcate modula hash
330 setModulaHash(multipleHashString(modulaHash($sqrtHashes[0], $sqrtHashes[1])));
331
332 // This is also the "genesis" hash and first root hash
333 $GLOBALS['genesis_hash'] = $GLOBALS['modula_hash'];
334 $GLOBALS['root_hash']    = $GLOBALS['modula_hash'];
335
336 // Output results
337 print ('hashes=' . print_r($gensisHashes, TRUE));
338 print ('modulaHashes=' . print_r($modulaHashes, TRUE));
339 print ('sqrtHashes=' . print_r($sqrtHashes, TRUE));
340 print ('modulaHash=' . $GLOBALS['modula_hash'] . PHP_EOL);
341
342 // Total reward + hashes
343 $GLOBALS['total_reward']   = 0;
344 $GLOBALS['total_hashes']   = 0;
345 $GLOBALS['total_found']    = 0;
346 $GLOBALS['total_blocks']   = 0;
347 $GLOBALS['found_time']     = microtime(TRUE);
348
349 // Is the check point there?
350 if (is_readable(CHECK_POINT)) {
351         // Then load it
352         $checkPoint = file_get_contents(CHECK_POINT);
353
354         // Explode it
355         $data = explode(':', $checkPoint);
356
357         // Assert on count
358         assert(count($data) == 10);
359
360         // 1st element is nonce, 2nd hash, 3rd found hashes
361         $GLOBALS['total_blocks']   = $data[0];
362         $GLOBALS['total_reward']   = $data[1];
363         $GLOBALS['total_hashes']   = $data[2];
364         $GLOBALS['total_found']    = $data[3];
365         $GLOBALS['total_restarts'] = $data[4];
366         $GLOBALS['hash_cycles']    = intval($data[5]);
367         $GLOBALS['nonce']          = base64_decode($data[7]);
368         $GLOBALS['root_hash']      = $data[8];
369         $GLOBALS['found_hashes']   = unserialize(gzuncompress(base64_decode($data[9])));
370
371         // Set modula hash
372         setModulaHash($data[6]);
373 } else {
374         // Create nonce (small)
375         initNonce();
376 }
377
378 // Output again
379 print ('modulaHash=' . $GLOBALS['modula_hash'] . PHP_EOL);
380 print ('nonce=' . $GLOBALS['nonce'] . PHP_EOL);
381 print ('found=' . count($GLOBALS['found_hashes'][$GLOBALS['total_blocks']]) . PHP_EOL);
382
383 // Start "mining"
384 while (TRUE) {
385         // Init hash-per-block counter and hashrate
386         $GLOBALS['hashes_block'] = 0;
387         $hashrate = 0;
388
389         // Wait for block_size iterations (= found hashes). This is one block
390         $timeBlock   = microtime(TRUE);
391         $timeDisplay = $timeBlock;
392         $GLOBALS['time_flush'] = $timeBlock;
393
394         // Time waited for a good block again (no iteration)
395         $timeBadHashes = 0;
396
397         while (count($GLOBALS['found_hashes'][$GLOBALS['total_blocks']]) <= $GLOBALS['block_size']) {
398                 // Create hash from modulaHash ("genesis hash") and nonce
399                 $nonceHash = multipleHashString($GLOBALS['nonce'] . $GLOBALS['modula_hash']);
400
401                 // Calculate sums
402                 $sumNonce  = calculateSumFromHash($nonceHash);
403
404                 // Init counter
405                 $GLOBALS['iteration'] = 0;
406                 $GLOBALS['iteration_second'] = 0;
407
408                 // Now start the "mining" ...
409                 $timeHash = microtime(TRUE);
410                 while ($sumNonce < $GLOBALS['sum_modula']) {
411                         // Calculate new nonce
412                         calculateNonce();
413
414                         // And hash again
415                         $nonceHash = multipleHashString($GLOBALS['nonce'] . $GLOBALS['modula_hash']);
416
417                         // Calculate sums
418                         $sumNonce  = calculateSumFromHash($nonceHash);
419
420                         // Time spend in loop
421                         $testTime = abs(microtime(TRUE) - $timeDisplay);
422
423                         // Calculate hashrate/sec
424                         $hashrate = 1 / $testTime * $GLOBALS['iteration_second'] * $GLOBALS['hash_cycles'];
425
426                         // Only every second
427                         if ($testTime >= 1) {
428                                 // Display hash rate
429                                 print ('hashrate=' . round($hashrate) . ' hashes/sec,iterSecond=' . $GLOBALS['iteration_second'] . ' iterations/sec' . PHP_EOL);
430
431                                 // Reset timer
432                                 $timeDisplay = microtime(TRUE);
433                                 $GLOBALS['iteration_second']  = 0;
434                         } // END - if
435
436                         // Time spend from last flush
437                         $testTime = abs(microtime(TRUE) - $GLOBALS['time_flush']);
438
439                         // Only once per 10 seconds
440                         if ($testTime >= $GLOBALS['flush_file_time']) {
441                                 // Flush check-point file
442                                 flushCheckPointFile($GLOBALS['modula_hash']);
443                         } // END - if
444
445                         // Time spend from last found block
446                         $testTime = abs(microtime(TRUE) - $GLOBALS['found_time']);
447
448                         // Is the last found time to far away?
449                         if ($testTime >= $GLOBALS['restart_search_time']) {
450                                 // Count up restart
451                                 $GLOBALS['total_restarts']++;
452
453                                 // Output message
454                                 print('total_restarts=' . $GLOBALS['total_restarts'] . ' - Restarting ...');
455
456                                 // Count all root (genesis) hashes
457                                 $rootHashes = array();
458                                 foreach ($GLOBALS['found_hashes'] as $block) {
459                                         // "Walk" through all blocks
460                                         foreach ($block as $hash) {
461                                                 if (!isset($hash['root_hash'])) {
462                                                         // Bad file
463                                                         die('INCONSISTENCY: hash=' . print_r($hash, TRUE));
464                                                 } // END - if
465
466                                                 if (isset($rootHashes[$hash['root_hash']])) {
467                                                         // Count up
468                                                         $rootHashes[$hash['root_hash']]++;
469                                                 } else {
470                                                         // First entry found
471                                                         $rootHashes[$hash['root_hash']] = 1;
472                                                 }
473                                         } // END - foreach
474                                 } // END - foreach
475
476                                 // Find best root hash
477                                 $bestRootHash = '';
478                                 $bestRootCount = 0;
479                                 foreach ($rootHashes as $hash => $count) {
480                                         // Debug message
481                                         //* NOISY-DEBUG: */ print ('hash=' . $hash . ',count=' . $count . ',bestRootHash=' . $bestRootHash . ',bestRootCount=' . $bestRootCount . PHP_EOL);
482
483                                         // Is a better one found?
484                                         if ($count > $bestRootCount) {
485                                                 // Remember it
486                                                 $bestRootHash  = $hash;
487                                                 $bestRootCount = $count;
488                                         } // END - if
489                                 } // END - foreach
490
491                                 // Output message
492                                 print ('bestRootHash=' . $bestRootHash . ',bestRootCount=' . $bestRootCount . PHP_EOL);
493
494                                 // Search for latest best root hash
495                                 foreach ($GLOBALS['found_hashes'] as $block) {
496                                         // "Walk" through whole block and search for first appearance of best root hash
497                                         foreach ($block as $idx => $hash) {
498                                                 // Is the root hash there?
499                                                 if ($hash['root_hash'] == $bestRootHash) {
500                                                         // Set found modula hash as new root and current modula hash
501                                                         $GLOBALS['root_hash']   = $hash['nonce_hash'];
502                                                         setModulaHash($hash['nonce_hash']);
503                                                         print ('idx=' . $idx . ',modulaHash=' . $GLOBALS['root_hash'] . ' - Is now new root hash!' . PHP_EOL);
504
505                                                         // Reset "found time" (when a hash was found)
506                                                         $GLOBALS['found_time'] = microtime(TRUE);
507
508                                                         // Re-initialize nonce
509                                                         initNonce();
510
511                                                         // Abort search
512                                                         break;
513                                                 } // END - if
514                                         } // END - for
515                                 } // END - foreach
516                         } // END - if
517
518                         // Next round
519                         $GLOBALS['iteration']++;
520                         $GLOBALS['iteration_second']++;
521                         //print ('nonce=' . $GLOBALS['nonce'] . ',iteration=' . $GLOBALS['iteration'] . PHP_EOL);
522                         //print ('nonceHash=' . $nonceHash . PHP_EOL);
523                         //print ('sumNonce=' . $sumNonce . PHP_EOL);
524                         //print ('sumModula=' . $GLOBALS['sum_modula'] . PHP_EOL);
525                 } // END - while
526
527                 // If the iteration is zero, then no hash is found
528                 if ($GLOBALS['iteration'] == 0) {
529                         // Bad hash found
530                         $timeBadHashes += abs(microtime(TRUE) - $timeHash);
531
532                         // And next round
533                         print('BAD:nonce=' . $GLOBALS['nonce'] . PHP_EOL);
534
535                         // Nothing found, so calculate new nonce
536                         calculateNonce();
537                         continue;
538                 } // END - if
539
540                 // Add amount of hashes per block (multiple-hash)
541                 $GLOBALS['hashes_block'] += $GLOBALS['iteration'] * $GLOBALS['hash_cycles'] + $GLOBALS['hash_cycles'];
542
543                 // Push found hash
544                 addFoundHash($nonceHash);
545         } // END - while
546
547         // Time taken for one
548         $timeBlock = abs(microtime(TRUE) - $timeBlock);
549
550         // Calculate reward
551         $reward = abs($timeBlock - $timeBadHashes) / $hashrate * $GLOBALS['hashes_block'] / $GLOBALS['block_size'] * 1000;
552         print ('timeBlock=' . $timeBlock . ',timeBadHashes=' . $timeBadHashes . ',hashesPerBlock=' . $GLOBALS['hashes_block'] .',reward=' . $reward . PHP_EOL);
553
554         // Block completed
555         $GLOBALS['total_hashes'] += $GLOBALS['hashes_block'];
556         $GLOBALS['total_blocks']++;
557         $GLOBALS['hashes_block'] = 0;
558
559         // Init next block
560         $GLOBALS['found_hashes'][$GLOBALS['total_blocks']] = array();
561
562         // Calculate new nonce
563         calculateNonce();
564
565         // Add reward to total
566         $GLOBALS['total_reward'] += $reward;
567
568         // Calculate average block value
569         $blockValue = $GLOBALS['total_reward'] / $GLOBALS['total_blocks'] * $GLOBALS['total_hashes'] / ($GLOBALS['block_size'] * $GLOBALS['total_blocks']);
570
571         // Calculate reward per hour (= 3600 seconds)
572         $rewardPerHour = $GLOBALS['total_reward'] / abs(microtime(TRUE) - START_TIME) * 3600;
573
574         print ('totalReward=' . $GLOBALS['total_reward'] . ',blockValue=' . $blockValue . ',rewardPerHour=' . $rewardPerHour . PHP_EOL);
575 } // END - while
576
577 // [EOF]
578 ?>