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