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