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