2 error_reporting(E_ALL | E_STRICT);
4 define('START_TIME' , microtime(TRUE));
5 define('CHECK_POINT' , 'chash.pos');
7 $GLOBALS['block_size'] = 100;
8 $GLOBALS['none_increment'] = (1 / pow(10, 12));
9 $GLOBALS['hash_algo'] = MHASH_RIPEMD320;
10 $GLOBALS['flush_file_time'] = 30;
11 $GLOBALS['restart_search_time'] = 1800;
14 $GLOBALS['hash_cycles'] = 3;
17 $GLOBALS['found_hashes'] = array(0 => array());
22 * @author Roland Haeder <roland@mxchange.org>
23 * @copyright Copyright (c) 2013 by Core Developer Team
24 * @license See LICENSE (public-domain)
28 * Calculates a simple but stronger hash from given string. No salts are being
31 * @param $str The string to be hashed
32 * @return $hash The hash from string $str
34 function hashString ($str) {
35 // Calculate strong hash from given string
36 $hash = mhash($GLOBALS['hash_algo'], $str);
38 // Return it hexadecimal-encoded
39 return bin2hex($hash);
43 * Multiple-hashes given string. This is done by hashing the given string and
44 * then hashing the generated hash again.
46 * @param $str The string to be hashed 4 times
47 * @return $hash The generated hash
49 function multipleHashString ($str) {
50 // Generate hash from given hash
51 $hash = hashString($str);
54 for ($idx = 0; $idx < ($GLOBALS['hash_cycles'] - 1); $idx++) {
55 // Over-hash the given hash
56 $hash = hashString($hash);
64 * Calculates a "modula-hash" based given two hashes.
66 * @param $hash1 Hash 1
67 * @param $hash2 Hash 2
69 function modulaHash ($hash1, $hash2) {
70 // Both must have same length
71 assert(strlen($hash1) === strlen($hash2));
76 // "Walk" trough first hash and get every 2 byte of both hashes
77 for ($idx = 0; $idx < strlen($hash1); $idx += 2) {
81 // Get both hash parts and convert to ASCII number
82 $part1 = hexdec(substr($hash1, $idx, 2));
83 $part2 = hexdec(substr($hash2, $idx, 2));
86 * If part1 is larget part2, part1 is divident and vise-versa. But don't do it
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;
97 // "Invert" the result against 255
100 // Encode to hex, pre-pad it with zeros and add to new hash
101 $modulaHash .= padHex($mod);
104 // Modula hash must have same length as input hash
105 assert(strlen($modulaHash) === strlen($hash1));
107 // Return modula hash
112 * Calculates a "sqrt-hash" based given two hashes and single-hash it
114 * @param $hash1 Hash 1
115 * @param $hash2 Hash 2
117 function sqrtHash ($hash1, $hash2) {
118 // Both must have same length
119 assert(strlen($hash1) === strlen($hash2));
124 // "Walk" trough first hash and get every 2 byte of both hashes
125 for ($idx = 0; $idx < strlen($hash1); $idx += 2) {
129 // Get both hash parts and convert to ASCII number
130 $part1 = hexdec(substr($hash1, $idx, 2));
131 $part2 = hexdec(substr($hash2, $idx, 2));
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)));
136 // Encode to hex, pre-pad it with zeros and add to new hash
137 $sqrtHash .= padHex($sqrt);
140 // "sqrt-hash" must have same length as input hash
141 assert(strlen($sqrtHash) === strlen($hash1));
143 // Hash reversed "sqrt-hash" again and return it
144 return hashString(strrev($sqrtHash));
148 * Converts a number between 0 and 255 into a zero-padded hexadecimal string
150 * @param $num Number between 0 and 255
151 * @return $hex Hexadecimal string, padded with zeros
153 function padHex ($num) {
154 // Must be a integer number and between 0 and 255
155 assert(is_int($num));
160 $hex = str_pad(dechex($num), 2, '0', STR_PAD_LEFT);
167 * Calculates sum from given hash
169 * @param $hash Hash to calculate sum from
170 * @return $sum Sum from given hash
172 function calculateSumFromHash ($hash) {
173 // Everything starts with zero ...
177 for ($idx = 0; $idx < (strlen($hash) / 2); $idx++) {
179 $sum = $sum + hexdec(substr($hash, $idx, 2));
187 * Calculates new nonce
191 function calculateNonce () {
192 // Linear incrementation
193 $GLOBALS['nonce'] += $GLOBALS['none_increment'];
197 * Writes/flushes check-point file
199 * @param $hash Modula hash (or hash to save)
202 function flushCheckPointFile ($hash) {
204 print ('FLUSHING: Writing ' . count($GLOBALS['found_hashes']) . ' blocks ...' . PHP_EOL);
207 $timer = microtime(TRUE);
212 $GLOBALS['total_blocks'] . ':' .
213 $GLOBALS['total_reward'] . ':' .
214 $GLOBALS['total_hashes'] . ':' .
215 $GLOBALS['total_found'] . ':' .
216 $GLOBALS['hash_cycles'] . ':' .
217 base64_encode($GLOBALS['nonce']) . ':' .
219 $GLOBALS['root_hash'] . ':' .
220 base64_encode(gzcompress(serialize($GLOBALS['found_hashes'])))
224 $GLOBALS['time_flush'] = microtime(TRUE);
225 print ('FLUSHING: Took ' . ($GLOBALS['time_flush'] - $timer) . ' seconds.' . PHP_EOL);
229 * Adds a found hash and flushes the checkpoint file
231 * @param $hash Hash to save
233 function addFoundHash ($hash) {
235 $GLOBALS['total_found']++;
238 array_push($GLOBALS['found_hashes'][$GLOBALS['total_blocks']], array(
239 'modula_hash' => $GLOBALS['modula_hash'],
240 'genesis_hash' => $GLOBALS['genesis_hash'],
241 'root_hash' => $GLOBALS['root_hash'],
242 'nonce' => $GLOBALS['nonce'],
243 'iter' => $GLOBALS['iteration'],
244 'hashes_block' => $GLOBALS['hashes_block'],
245 'hash_cycles' => $GLOBALS['hash_cycles'],
246 'nonce_hash' => $hash
250 print ('FOUND: hash=' . $hash . ',nonce=' . $GLOBALS['nonce'] . ',total_found=' . $GLOBALS['total_found'] . PHP_EOL);
252 // Set time as a new hash was found
253 $GLOBALS['found_time'] = microtime(TRUE);
255 // Flush check-point file after new hash is found
256 flushCheckPointFile($hash);
258 // Use nonceHash as next modula hash
259 setModulaHash($hash);
267 function initNonce () {
268 $GLOBALS['nonce'] = 1 / (mt_rand() ^ pi());
269 print (__FUNCTION__ . ': nonce=' . $GLOBALS['nonce'] . PHP_EOL);
273 * Sets modula hash and calculates sum of it
275 * @param $hash Hash to set as "modula hash"
278 function setModulaHash ($hash) {
279 $GLOBALS['modula_hash'] = $hash;
280 $GLOBALS['sum_modula'] = calculateSumFromHash($GLOBALS['modula_hash']);
284 * Calculate "genesis" hashes, please note that these "genesis strings" are now
285 * known to the public as you can read them here in source code and therefore I
286 * will not use them for the real genesis hashes.
288 $gensisHashes = array(
289 // A famous quote from Deus Ex 2 - Invisible War
290 multiplehashString('"Informations must be free." - AI Helios from Deus Ex'),
291 // My name + URL of my first StatusNet instance
292 multipleHashString('Roland Haeder, https://status.mxchange.org'),
293 // A famous quote from Linus Torwalds
294 multipleHashString('"Software is like sex. Its better when its free." - Linus Torwalds'),
296 multipleHashString('September 11 is a big lie.'),
299 multipleHashString('GNU is Not Uni*.'),
300 // WINE is not an emulator
301 multipleHashString('WINE Is Not an Emulator.'),
302 // FlightGear - Fly free!
303 multipleHashString('FlightGear - Fly free!'),
304 // Quote from Linus Torwalds
305 multipleHashString('Your code is shit. Your argument is shit.'),
308 // Calculate "modula hash" from 1st/4th and 2nd/3rd
309 $modulaHashes = array(
311 modulaHash($gensisHashes[0], $gensisHashes[3]),
312 modulaHash($gensisHashes[1], $gensisHashes[2]),
315 modulaHash($gensisHashes[4], $gensisHashes[7]),
316 modulaHash($gensisHashes[5], $gensisHashes[6]),
319 // Calculate "sqrt hash"
321 sqrtHash($modulaHashes[0], $modulaHashes[1]),
322 sqrtHash($modulaHashes[2], $modulaHashes[3])
325 // Calulcate modula hash
326 setModulaHash(multipleHashString(modulaHash($sqrtHashes[0], $sqrtHashes[1])));
328 // This is also the "genesis" hash and first root hash
329 $GLOBALS['genesis_hash'] = $GLOBALS['modula_hash'];
330 $GLOBALS['root_hash'] = $GLOBALS['modula_hash'];
333 print ('hashes=' . print_r($gensisHashes, TRUE));
334 print ('modulaHashes=' . print_r($modulaHashes, TRUE));
335 print ('sqrtHashes=' . print_r($sqrtHashes, TRUE));
336 print ('modulaHash=' . $GLOBALS['modula_hash'] . PHP_EOL);
338 // Total reward + hashes
339 $GLOBALS['total_reward'] = 0;
340 $GLOBALS['total_hashes'] = 0;
341 $GLOBALS['total_found'] = 0;
342 $GLOBALS['total_blocks'] = 0;
343 $GLOBALS['found_time'] = microtime(TRUE);
345 // Is the check point there?
346 if (is_readable(CHECK_POINT)) {
348 $checkPoint = file_get_contents(CHECK_POINT);
351 $data = explode(':', $checkPoint);
354 assert(count($data) == 9);
356 // 1st element is nonce, 2nd hash, 3rd found hashes
357 $GLOBALS['total_blocks'] = $data[0];
358 $GLOBALS['total_reward'] = $data[1];
359 $GLOBALS['total_hashes'] = $data[2];
360 $GLOBALS['total_found'] = $data[3];
361 $GLOBALS['hash_cycles'] = intval($data[4]);
362 $GLOBALS['nonce'] = base64_decode($data[5]);
363 $GLOBALS['root_hash'] = $data[7];
364 $GLOBALS['found_hashes'] = unserialize(gzuncompress(base64_decode($data[8])));
367 setModulaHash($data[6]);
369 // Create nonce (small)
374 print ('modulaHash=' . $GLOBALS['modula_hash'] . PHP_EOL);
375 print ('nonce=' . $GLOBALS['nonce'] . PHP_EOL);
376 print ('found=' . count($GLOBALS['found_hashes'][$GLOBALS['total_blocks']]) . PHP_EOL);
380 // Init hash-per-block counter and hashrate
381 $GLOBALS['hashes_block'] = 0;
384 // Wait for block_size iterations (= found hashes). This is one block
385 $timeBlock = microtime(TRUE);
386 $timeDisplay = $timeBlock;
387 $GLOBALS['time_flush'] = $timeBlock;
389 // Time waited for a good block again (no iteration)
392 while (count($GLOBALS['found_hashes'][$GLOBALS['total_blocks']]) <= $GLOBALS['block_size']) {
393 // Create hash from modulaHash ("genesis hash") and nonce
394 $nonceHash = multipleHashString($GLOBALS['nonce'] . $GLOBALS['modula_hash']);
397 $sumNonce = calculateSumFromHash($nonceHash);
400 $GLOBALS['iteration'] = 0;
401 $GLOBALS['iteration_second'] = 0;
403 // Now start the "mining" ...
404 $timeHash = microtime(TRUE);
405 while ($sumNonce < $GLOBALS['sum_modula']) {
406 // Calculate new nonce
410 $nonceHash = multipleHashString($GLOBALS['nonce'] . $GLOBALS['modula_hash']);
413 $sumNonce = calculateSumFromHash($nonceHash);
415 // Time spend in loop
416 $testTime = abs(microtime(TRUE) - $timeDisplay);
418 // Calculate hashrate/sec
419 $hashrate = 1 / $testTime * $GLOBALS['iteration_second'] * $GLOBALS['hash_cycles'];
422 if ($testTime >= 1) {
424 print ('hashrate=' . round($hashrate) . ' hashes/sec,iterSecond=' . $GLOBALS['iteration_second'] . ' iterations/sec' . PHP_EOL);
427 $timeDisplay = microtime(TRUE);
428 $GLOBALS['iteration_second'] = 0;
431 // Time spend from last flush
432 $testTime = abs(microtime(TRUE) - $GLOBALS['time_flush']);
434 // Only once per 10 seconds
435 if ($testTime >= $GLOBALS['flush_file_time']) {
436 // Flush check-point file
437 flushCheckPointFile($GLOBALS['modula_hash']);
440 // Time spend from last found block
441 $testTime = abs(microtime(TRUE) - $GLOBALS['found_time']);
443 // Is the last found time to far away?
444 if ($testTime >= $GLOBALS['restart_search_time']) {
445 // Count all root (genesis) hashes
446 $rootHashes = array();
447 foreach ($GLOBALS['found_hashes'] as $block) {
448 // "Walk" through all blocks
449 foreach ($block as $hash) {
450 if (!isset($hash['root_hash'])) {
452 die('INCONSISTENCY: hash=' . print_r($hash, TRUE));
455 if (isset($rootHashes[$hash['root_hash']])) {
457 $rootHashes[$hash['root_hash']]++;
460 $rootHashes[$hash['root_hash']] = 1;
465 // Find best root hash
468 foreach ($rootHashes as $hash => $count) {
470 //* NOISY-DEBUG: */ print ('hash=' . $hash . ',count=' . $count . ',bestRootHash=' . $bestRootHash . ',bestRootCount=' . $bestRootCount . PHP_EOL);
472 // Is a better one found?
473 if ($count > $bestRootCount) {
475 $bestRootHash = $hash;
476 $bestRootCount = $count;
481 print ('bestRootHash=' . $bestRootHash . ',bestRootCount=' . $bestRootCount . PHP_EOL);
483 // Search for latest best root hash
484 foreach ($GLOBALS['found_hashes'] as $block) {
485 // "Walk" through whole block and search for first appearance of best root hash
486 foreach ($block as $idx => $hash) {
487 // Is the root hash there?
488 if ($hash['root_hash'] == $bestRootHash) {
489 // Set found modula hash as new root and current modula hash
490 $GLOBALS['root_hash'] = $hash['nonce_hash'];
491 setModulaHash($hash['nonce_hash']);
492 print ('idx=' . $idx . ',modulaHash=' . $GLOBALS['root_hash'] . ' - Is now new root hash!' . PHP_EOL);
494 // Reset "found time" (when a hash was found)
495 $GLOBALS['found_time'] = microtime(TRUE);
497 // Re-initialize nonce
508 $GLOBALS['iteration']++;
509 $GLOBALS['iteration_second']++;
510 //print ('nonce=' . $GLOBALS['nonce'] . ',iteration=' . $GLOBALS['iteration'] . PHP_EOL);
511 //print ('nonceHash=' . $nonceHash . PHP_EOL);
512 //print ('sumNonce=' . $sumNonce . PHP_EOL);
513 //print ('sumModula=' . $GLOBALS['sum_modula'] . PHP_EOL);
516 // If the iteration is zero, then no hash is found
517 if ($GLOBALS['iteration'] == 0) {
519 $timeBadHashes += abs(microtime(TRUE) - $timeHash);
522 print('BAD:nonce=' . $GLOBALS['nonce'] . PHP_EOL);
524 // Nothing found, so calculate new nonce
529 // Add amount of hashes per block (multiple-hash)
530 $GLOBALS['hashes_block'] += $GLOBALS['iteration'] * $GLOBALS['hash_cycles'] + $GLOBALS['hash_cycles'];
533 addFoundHash($nonceHash);
536 // Time taken for one
537 $timeBlock = abs(microtime(TRUE) - $timeBlock);
540 $reward = abs($timeBlock - $timeBadHashes) / $hashrate * $GLOBALS['hashes_block'] / $GLOBALS['block_size'] * 1000;
541 print ('timeBlock=' . $timeBlock . ',timeBadHashes=' . $timeBadHashes . ',hashesPerBlock=' . $GLOBALS['hashes_block'] .',reward=' . $reward . PHP_EOL);
544 $GLOBALS['total_hashes'] += $GLOBALS['hashes_block'];
545 $GLOBALS['total_blocks']++;
546 $GLOBALS['hashes_block'] = 0;
549 $GLOBALS['found_hashes'][$GLOBALS['total_blocks']] = array();
551 // Calculate new nonce
554 // Add reward to total
555 $GLOBALS['total_reward'] += $reward;
557 // Calculate average block value
558 $blockValue = $GLOBALS['total_reward'] / $GLOBALS['total_blocks'] * $GLOBALS['total_hashes'] / ($GLOBALS['block_size'] * $GLOBALS['total_blocks']);
560 // Calculate reward per hour (= 3600 seconds)
561 $rewardPerHour = $GLOBALS['total_reward'] / abs(microtime(TRUE) - START_TIME) * 3600;
563 print ('totalReward=' . $GLOBALS['total_reward'] . ',blockValue=' . $blockValue . ',rewardPerHour=' . $rewardPerHour . PHP_EOL);