2 error_reporting(E_ALL | E_STRICT);
4 define('START_TIME' , microtime(TRUE));
5 define('CHECK_POINT' , 'chash.pos');
7 // Hashes needed to complete a "block"
8 $GLOBALS['block_size'] = 100;
9 $GLOBALS['none_increment'] = (1 / pow(10, 20));
12 $GLOBALS['hash_algo'] = MHASH_SHA256;
14 // Automatic saving interval in seconds
15 $GLOBALS['flush_file_time'] = 30;
18 * How long (in seconds) to try to find a proper hash until the best root hash
21 $GLOBALS['restart_search_time'] = 1800;
24 $GLOBALS['hash_cycles'] = 5;
27 $GLOBALS['total_restarts'] = 0;
30 $GLOBALS['found_hashes'] = array(0 => array());
35 * @author Roland Haeder <roland@mxchange.org>
36 * @copyright Copyright (c) 2013 by Core Developer Team
37 * @license See LICENSE (public-domain)
41 * Calculates a simple but stronger hash from given string. No salts are being
44 * @param $str The string to be hashed
45 * @return $hash The hash from string $str
47 function hashString ($str) {
48 // Calculate strong hash from given string
49 $hash = mhash($GLOBALS['hash_algo'], $str);
51 // Return it hexadecimal-encoded
52 return bin2hex($hash);
56 * Multiple-hashes given string. This is done by hashing the given string and
57 * then hashing the generated hash again.
59 * @param $str The string to be hashed 4 times
60 * @return $hash The generated hash
62 function multipleHashString ($str) {
63 // Generate hash from given hash
64 $hash = hashString($str);
67 for ($idx = 0; $idx < ($GLOBALS['hash_cycles'] - 1); $idx++) {
68 // Over-hash the given hash
69 $hash = hashString($hash);
77 * Calculates a "modula-hash" based given two hashes.
79 * @param $hash1 Hash 1
80 * @param $hash2 Hash 2
82 function modulaHash ($hash1, $hash2) {
83 // Both must have same length
84 assert(strlen($hash1) === strlen($hash2));
86 // Init propability array with 256 zeros
87 $propability = array_fill(0, 256, 0);
92 // "Walk" trough first hash and get every 2 byte of both hashes
93 for ($idx = 0; $idx < strlen($hash1); $idx += 2) {
97 // Get both hash parts and convert to ASCII number
98 $part1 = hexdec(substr($hash1, $idx, 2));
99 $part2 = hexdec(substr($hash2, $idx, 2));
102 //* NOISY-DEBUG: */ print 'part1=' . $part1 . ',part2=' . $part2 . PHP_EOL;
105 * If part1 is larget part2, part1 is divident and vise-versa. But don't do it
108 if (($part1 > $part2) && ($part2 > 0)) {
109 // 'part1' is larger than 'part2'
110 $mod = $part1 % $part2;
111 } elseif (($part2 > $part1) && ($part1 > 0)) {
112 // 'part2' is larger than 'part1'
113 $mod = $part2 % $part1;
116 // $mod is now mostly a small number so try to "improve" it
117 //* NOISY-DEBUG: */ print 'mod[' . gettype($mod) . ']=' . $mod . ' - BEFORE!' . PHP_EOL;
118 $mod = (int) round(sqrt($mod * ($part1 + $part2 + $mod ^ 7) / 3));
119 //* NOISY-DEBUG: */ print 'mod[' . gettype($mod) . ']=' . $mod . ' - AFTER!' . PHP_EOL;
121 // Make sure it is valid
125 // "Invert" the result against 255 as zeros are not good for later calculations
128 // Add it to propability array for debugging
129 $propability[$mod]++;
131 // Encode to hex, pre-pad it with zeros and add to new hash
132 $modulaHash .= padHex($mod);
135 // Debug propability array
137 foreach ($propability as $value) {
138 // Is the value larger than one, means the number has been found at least once?
146 //* NOISY-DEBUG: */ print('cnt=' . $cnt . '/' . strlen($hash1) / 2 . PHP_EOL);
148 // Modula hash must have same length as input hash
149 assert(strlen($modulaHash) === strlen($hash1));
151 // Return modula hash
156 * Calculates a "sqrt-hash" based given two hashes and single-hash it
158 * @param $hash1 Hash 1
159 * @param $hash2 Hash 2
161 function sqrtHash ($hash1, $hash2) {
162 // Both must have same length
163 assert(strlen($hash1) === strlen($hash2));
168 // "Walk" trough first hash and get every 2 byte of both hashes
169 for ($idx = 0; $idx < strlen($hash1); $idx += 2) {
173 // Get both hash parts and convert to ASCII number
174 $part1 = hexdec(substr($hash1, $idx, 2));
175 $part2 = hexdec(substr($hash2, $idx, 2));
177 // Calculate square root of both parts being multiplied and round up, then "invert" it against 255
178 $sqrt = intval(255 - ceil(sqrt($part1 * $part2)));
180 // Encode to hex, pre-pad it with zeros and add to new hash
181 $sqrtHash .= padHex($sqrt);
184 // "sqrt-hash" must have same length as input hash
185 assert(strlen($sqrtHash) === strlen($hash1));
187 // Hash reversed "sqrt-hash" again and return it
188 return hashString(strrev($sqrtHash));
192 * Converts a number between 0 and 255 into a zero-padded hexadecimal string
194 * @param $num Number between 0 and 255
195 * @return $hex Hexadecimal string, padded with zeros
197 function padHex ($num) {
198 // Must be a integer number and between 0 and 255
199 assert(is_int($num));
204 $hex = str_pad(dechex($num), 2, '0', STR_PAD_LEFT);
211 * Calculates sum from given hash
213 * @param $hash Hash to calculate sum from
214 * @return $sum Sum from given hash
216 function calculateSumFromHash ($hash) {
217 // Everything starts with zero ...
221 for ($idx = 0; $idx < (strlen($hash) / 2); $idx++) {
223 $sum = $sum + hexdec(substr($hash, $idx, 2));
231 * Calculates new nonce
235 function calculateNonce () {
236 // Linear incrementation
237 $GLOBALS['nonce'] += $GLOBALS['none_increment'];
241 * Writes/flushes check-point file
243 * @param $hash Modula hash (or hash to save)
246 function flushCheckPointFile ($hash) {
248 print ('FLUSHING: Writing ' . count($GLOBALS['found_hashes']) . ' blocks ...' . PHP_EOL);
251 $timer = microtime(TRUE);
256 $GLOBALS['total_blocks'] . ':' .
257 $GLOBALS['total_reward'] . ':' .
258 $GLOBALS['total_hashes'] . ':' .
259 $GLOBALS['total_found'] . ':' .
260 $GLOBALS['total_restarts'] . ':' .
261 $GLOBALS['hash_cycles'] . ':' .
262 base64_encode((float) $GLOBALS['nonce']) . ':' .
264 $GLOBALS['root_hash'] . ':' .
265 base64_encode(gzcompress(json_encode($GLOBALS['found_hashes'])))
269 $GLOBALS['time_flush'] = microtime(TRUE);
270 print ('FLUSHING: Took ' . ($GLOBALS['time_flush'] - $timer) . ' seconds.' . PHP_EOL);
274 * Adds a found hash and flushes the checkpoint file
276 * @param $hash Hash to save
278 function addFoundHash ($hash) {
280 $GLOBALS['total_found']++;
283 array_push($GLOBALS['found_hashes'][$GLOBALS['total_blocks']], array(
284 'modula_hash' => $GLOBALS['modula_hash'],
285 'genesis_hash' => $GLOBALS['genesis_hash'],
286 'root_hash' => $GLOBALS['root_hash'],
287 'nonce' => (float) $GLOBALS['nonce'],
288 'iter' => $GLOBALS['iteration'],
289 'hashes_block' => $GLOBALS['hashes_block'],
290 'hash_cycles' => $GLOBALS['hash_cycles'],
291 'nonce_hash' => $hash
295 print ('FOUND: hash=' . $hash . ',nonce=' . $GLOBALS['nonce'] . ',total_found=' . $GLOBALS['total_found'] . PHP_EOL);
297 // Set time as a new hash was found
298 $GLOBALS['found_time'] = microtime(TRUE);
300 // Flush check-point file after new hash is found
301 flushCheckPointFile($hash);
303 // Use nonceHash as next modula hash
304 setModulaHash($hash);
312 function initNonce () {
313 $GLOBALS['nonce'] = 1 / (mt_rand() ^ pi());
314 print (__FUNCTION__ . ': nonce=' . $GLOBALS['nonce'] . PHP_EOL);
318 * Sets modula hash and calculates sum of it
320 * @param $hash Hash to set as "modula hash"
323 function setModulaHash ($hash) {
324 $GLOBALS['modula_hash'] = $hash;
325 $GLOBALS['sum_modula'] = calculateSumFromHash($GLOBALS['modula_hash']);
329 * Calculate "genesis" hashes, please note that these "genesis strings" are now
330 * known to the public as you can read them here in source code and therefore I
331 * will not use them for the real genesis hashes.
333 $gensisHashes = array(
334 // A famous quote from Deus Ex 2 - Invisible War
335 multiplehashString('"Informations must be free." - AI Helios from Deus Ex'),
336 // My name + URL of my first StatusNet instance
337 multipleHashString('Roland Haeder, https://status.mxchange.org'),
338 // A famous quote from Linus Torwalds
339 multipleHashString('"Software is like sex. Its better when its free." - Linus Torwalds'),
341 multipleHashString('September 11 is a big lie.'),
344 multipleHashString('GNU is Not Uni*.'),
345 // WINE is not an emulator
346 multipleHashString('WINE Is Not an Emulator.'),
347 // FlightGear - Fly free!
348 multipleHashString('FlightGear - Fly free!'),
349 // Quote from Linus Torwalds
350 multipleHashString('Your code is shit. Your argument is shit.'),
353 // Calculate "modula hash" from 1st/4th and 2nd/3rd
354 $modulaHashes = array(
356 modulaHash($gensisHashes[0], $gensisHashes[3]),
357 modulaHash($gensisHashes[1], $gensisHashes[2]),
360 modulaHash($gensisHashes[4], $gensisHashes[7]),
361 modulaHash($gensisHashes[5], $gensisHashes[6]),
364 // Calculate "sqrt hash"
366 sqrtHash($modulaHashes[0], $modulaHashes[1]),
367 sqrtHash($modulaHashes[2], $modulaHashes[3])
370 // Calulcate modula hash
371 setModulaHash(multipleHashString(modulaHash($sqrtHashes[0], $sqrtHashes[1])));
373 // This is also the "genesis" hash and first root hash
374 $GLOBALS['genesis_hash'] = $GLOBALS['modula_hash'];
375 $GLOBALS['root_hash'] = $GLOBALS['modula_hash'];
378 print ('hashes=' . print_r($gensisHashes, TRUE));
379 print ('modulaHashes=' . print_r($modulaHashes, TRUE));
380 print ('sqrtHashes=' . print_r($sqrtHashes, TRUE));
381 print ('modulaHash=' . $GLOBALS['modula_hash'] . PHP_EOL);
383 // Total reward + hashes
384 $GLOBALS['total_reward'] = 0;
385 $GLOBALS['total_hashes'] = 0;
386 $GLOBALS['total_found'] = 0;
387 $GLOBALS['total_blocks'] = 0;
388 $GLOBALS['found_time'] = microtime(TRUE);
390 // Is the check point there?
391 if (is_readable(CHECK_POINT)) {
393 $checkPoint = file_get_contents(CHECK_POINT);
396 $data = explode(':', $checkPoint);
399 assert(count($data) == 10);
401 // 1st element is nonce, 2nd hash, 3rd found hashes
402 $GLOBALS['total_blocks'] = $data[0];
403 $GLOBALS['total_reward'] = $data[1];
404 $GLOBALS['total_hashes'] = $data[2];
405 $GLOBALS['total_found'] = $data[3];
406 $GLOBALS['total_restarts'] = $data[4];
407 $GLOBALS['hash_cycles'] = intval($data[5]);
408 $GLOBALS['nonce'] = (float) base64_decode($data[6]);
409 $GLOBALS['root_hash'] = $data[8];
410 $GLOBALS['found_hashes'] = json_decode(gzuncompress(base64_decode($data[9])));
413 setModulaHash($data[7]);
415 // Create nonce (small)
420 print ('modulaHash=' . $GLOBALS['modula_hash'] . PHP_EOL);
421 print ('nonce=' . $GLOBALS['nonce'] . PHP_EOL);
422 print ('found=' . count($GLOBALS['found_hashes'][$GLOBALS['total_blocks']]) . PHP_EOL);
426 // Init hash-per-block counter and hashrate
427 $GLOBALS['hashes_block'] = 0;
430 // Wait for block_size iterations (= found hashes). This is one block
431 $timeBlock = microtime(TRUE);
432 $timeDisplay = $timeBlock;
433 $GLOBALS['time_flush'] = $timeBlock;
435 // Time waited for a good block again (no iteration)
438 while (count($GLOBALS['found_hashes'][$GLOBALS['total_blocks']]) <= $GLOBALS['block_size']) {
439 // Create hash from modulaHash ("genesis hash") and nonce
440 $nonceHash = multipleHashString($GLOBALS['nonce'] . $GLOBALS['modula_hash']);
443 $sumNonce = calculateSumFromHash($nonceHash);
446 $GLOBALS['iteration'] = 0;
447 $GLOBALS['iteration_second'] = 0;
449 // Now start the "mining" ...
450 $timeHash = microtime(TRUE);
451 while ($sumNonce < $GLOBALS['sum_modula']) {
452 // Calculate new nonce
456 $nonceHash = multipleHashString($GLOBALS['nonce'] . $GLOBALS['modula_hash']);
459 $sumNonce = calculateSumFromHash($nonceHash);
461 // Time spend in loop
462 $testTime = abs(microtime(TRUE) - $timeDisplay);
464 // Calculate hashrate/sec
465 $hashrate = 1 / $testTime * $GLOBALS['iteration_second'] * $GLOBALS['hash_cycles'];
468 if ($testTime >= 1) {
470 print ('hashrate=' . round($hashrate) . ' hashes/sec,iterSecond=' . $GLOBALS['iteration_second'] . ' iterations/sec' . PHP_EOL);
473 $timeDisplay = microtime(TRUE);
474 $GLOBALS['iteration_second'] = 0;
477 // Time spend from last flush
478 $testTime = abs(microtime(TRUE) - $GLOBALS['time_flush']);
480 // Only once per 10 seconds
481 if ($testTime >= $GLOBALS['flush_file_time']) {
482 // Flush check-point file
483 flushCheckPointFile($GLOBALS['modula_hash']);
486 // Time spend from last found block
487 $testTime = abs(microtime(TRUE) - $GLOBALS['found_time']);
489 // Is the last found time to far away?
490 if ($testTime >= $GLOBALS['restart_search_time']) {
492 $GLOBALS['total_restarts']++;
495 print('total_restarts=' . $GLOBALS['total_restarts'] . ' - Restarting ...');
497 // Count all root (genesis) hashes
498 $rootHashes = array();
499 foreach ($GLOBALS['found_hashes'] as $block) {
500 // "Walk" through all blocks
501 foreach ($block as $hash) {
502 if (!isset($hash['root_hash'])) {
504 die('INCONSISTENCY: hash=' . print_r($hash, TRUE));
507 if (isset($rootHashes[$hash['root_hash']])) {
509 $rootHashes[$hash['root_hash']]++;
512 $rootHashes[$hash['root_hash']] = 1;
517 // Find best root hash
520 foreach ($rootHashes as $hash => $count) {
522 //* NOISY-DEBUG: */ print ('hash=' . $hash . ',count=' . $count . ',bestRootHash=' . $bestRootHash . ',bestRootCount=' . $bestRootCount . PHP_EOL);
524 // Is a better one found?
525 if ($count > $bestRootCount) {
527 $bestRootHash = $hash;
528 $bestRootCount = $count;
533 print ('bestRootHash=' . $bestRootHash . ',bestRootCount=' . $bestRootCount . PHP_EOL);
535 // Search for latest best root hash
536 foreach ($GLOBALS['found_hashes'] as $block) {
537 // "Walk" through whole block and search for first appearance of best root hash
538 foreach ($block as $idx => $hash) {
539 // Is the root hash there?
540 if ($hash['root_hash'] == $bestRootHash) {
541 // Set found modula hash as new root and current modula hash
542 $GLOBALS['root_hash'] = $hash['nonce_hash'];
543 setModulaHash($hash['nonce_hash']);
544 print ('idx=' . $idx . ',modulaHash=' . $GLOBALS['root_hash'] . ' - Is now new root hash!' . PHP_EOL);
546 // Reset "found time" (when a hash was found)
547 $GLOBALS['found_time'] = microtime(TRUE);
549 // Re-initialize nonce
560 $GLOBALS['iteration']++;
561 $GLOBALS['iteration_second']++;
562 //print ('nonce=' . $GLOBALS['nonce'] . ',iteration=' . $GLOBALS['iteration'] . PHP_EOL);
563 //print ('nonceHash=' . $nonceHash . PHP_EOL);
564 //print ('sumNonce=' . $sumNonce . PHP_EOL);
565 //print ('sumModula=' . $GLOBALS['sum_modula'] . PHP_EOL);
568 // If the iteration is zero, then no hash is found
569 if ($GLOBALS['iteration'] == 0) {
571 $timeBadHashes += abs(microtime(TRUE) - $timeHash);
574 print('BAD:nonce=' . $GLOBALS['nonce'] . PHP_EOL);
576 // Nothing found, so calculate new nonce
581 // Add amount of hashes per block (multiple-hash)
582 $GLOBALS['hashes_block'] += $GLOBALS['iteration'] * $GLOBALS['hash_cycles'] + $GLOBALS['hash_cycles'];
585 addFoundHash($nonceHash);
588 // Time taken for one
589 $timeBlock = abs(microtime(TRUE) - $timeBlock);
592 $reward = abs($timeBlock - $timeBadHashes) / $hashrate * $GLOBALS['hashes_block'] / $GLOBALS['block_size'] * 1000;
593 print ('timeBlock=' . $timeBlock . ',timeBadHashes=' . $timeBadHashes . ',hashesPerBlock=' . $GLOBALS['hashes_block'] .',reward=' . $reward . PHP_EOL);
596 $GLOBALS['total_hashes'] += $GLOBALS['hashes_block'];
597 $GLOBALS['total_blocks']++;
598 $GLOBALS['hashes_block'] = 0;
601 $GLOBALS['found_hashes'][$GLOBALS['total_blocks']] = array();
603 // Calculate new nonce
606 // Add reward to total
607 $GLOBALS['total_reward'] += $reward;
609 // Calculate average block value
610 $blockValue = $GLOBALS['total_reward'] / $GLOBALS['total_blocks'] * $GLOBALS['total_hashes'] / ($GLOBALS['block_size'] * $GLOBALS['total_blocks']);
612 // Calculate reward per hour (= 3600 seconds)
613 $rewardPerHour = $GLOBALS['total_reward'] / abs(microtime(TRUE) - START_TIME) * 3600;
615 print ('totalReward=' . $GLOBALS['total_reward'] . ',blockValue=' . $blockValue . ',rewardPerHour=' . $rewardPerHour . PHP_EOL);