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, 20));
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['total_restarts'] = 0;
20 $GLOBALS['found_hashes'] = array(0 => array());
25 * @author Roland Haeder <roland@mxchange.org>
26 * @copyright Copyright (c) 2013 by Core Developer Team
27 * @license See LICENSE (public-domain)
31 * Calculates a simple but stronger hash from given string. No salts are being
34 * @param $str The string to be hashed
35 * @return $hash The hash from string $str
37 function hashString ($str) {
38 // Calculate strong hash from given string
39 $hash = mhash($GLOBALS['hash_algo'], $str);
41 // Return it hexadecimal-encoded
42 return bin2hex($hash);
46 * Multiple-hashes given string. This is done by hashing the given string and
47 * then hashing the generated hash again.
49 * @param $str The string to be hashed 4 times
50 * @return $hash The generated hash
52 function multipleHashString ($str) {
53 // Generate hash from given hash
54 $hash = hashString($str);
57 for ($idx = 0; $idx < ($GLOBALS['hash_cycles'] - 1); $idx++) {
58 // Over-hash the given hash
59 $hash = hashString($hash);
67 * Calculates a "modula-hash" based given two hashes.
69 * @param $hash1 Hash 1
70 * @param $hash2 Hash 2
72 function modulaHash ($hash1, $hash2) {
73 // Both must have same length
74 assert(strlen($hash1) === strlen($hash2));
79 // "Walk" trough first hash and get every 2 byte of both hashes
80 for ($idx = 0; $idx < strlen($hash1); $idx += 2) {
84 // Get both hash parts and convert to ASCII number
85 $part1 = hexdec(substr($hash1, $idx, 2));
86 $part2 = hexdec(substr($hash2, $idx, 2));
89 * If part1 is larget part2, part1 is divident and vise-versa. But don't do it
92 if (($part1 > $part2) && ($part2 > 0)) {
93 // 'part1' is larger than 'part2'
94 $mod = $part1 % $part2;
95 } elseif (($part1 < $part2) && ($part1 > 0)) {
96 // 'part2' is larger than 'part1'
97 $mod = $part2 % $part1;
100 // "Invert" the result against 255
103 // Encode to hex, pre-pad it with zeros and add to new hash
104 $modulaHash .= padHex($mod);
107 // Modula hash must have same length as input hash
108 assert(strlen($modulaHash) === strlen($hash1));
110 // Return modula hash
115 * Calculates a "sqrt-hash" based given two hashes and single-hash it
117 * @param $hash1 Hash 1
118 * @param $hash2 Hash 2
120 function sqrtHash ($hash1, $hash2) {
121 // Both must have same length
122 assert(strlen($hash1) === strlen($hash2));
127 // "Walk" trough first hash and get every 2 byte of both hashes
128 for ($idx = 0; $idx < strlen($hash1); $idx += 2) {
132 // Get both hash parts and convert to ASCII number
133 $part1 = hexdec(substr($hash1, $idx, 2));
134 $part2 = hexdec(substr($hash2, $idx, 2));
136 // Calculate square root of both parts being multiplied and round up, then "invert" it against 255
137 $sqrt = intval(255 - ceil(sqrt($part1 * $part2)));
139 // Encode to hex, pre-pad it with zeros and add to new hash
140 $sqrtHash .= padHex($sqrt);
143 // "sqrt-hash" must have same length as input hash
144 assert(strlen($sqrtHash) === strlen($hash1));
146 // Hash reversed "sqrt-hash" again and return it
147 return hashString(strrev($sqrtHash));
151 * Converts a number between 0 and 255 into a zero-padded hexadecimal string
153 * @param $num Number between 0 and 255
154 * @return $hex Hexadecimal string, padded with zeros
156 function padHex ($num) {
157 // Must be a integer number and between 0 and 255
158 assert(is_int($num));
163 $hex = str_pad(dechex($num), 2, '0', STR_PAD_LEFT);
170 * Calculates sum from given hash
172 * @param $hash Hash to calculate sum from
173 * @return $sum Sum from given hash
175 function calculateSumFromHash ($hash) {
176 // Everything starts with zero ...
180 for ($idx = 0; $idx < (strlen($hash) / 2); $idx++) {
182 $sum = $sum + hexdec(substr($hash, $idx, 2));
190 * Calculates new nonce
194 function calculateNonce () {
195 // Linear incrementation
196 $GLOBALS['nonce'] += $GLOBALS['none_increment'];
200 * Writes/flushes check-point file
202 * @param $hash Modula hash (or hash to save)
205 function flushCheckPointFile ($hash) {
207 print ('FLUSHING: Writing ' . count($GLOBALS['found_hashes']) . ' blocks ...' . PHP_EOL);
210 $timer = microtime(TRUE);
215 $GLOBALS['total_blocks'] . ':' .
216 $GLOBALS['total_reward'] . ':' .
217 $GLOBALS['total_hashes'] . ':' .
218 $GLOBALS['total_found'] . ':' .
219 $GLOBALS['total_restarts'] . ':' .
220 $GLOBALS['hash_cycles'] . ':' .
221 base64_encode($GLOBALS['nonce']) . ':' .
223 $GLOBALS['root_hash'] . ':' .
224 base64_encode(gzcompress(serialize($GLOBALS['found_hashes'])))
228 $GLOBALS['time_flush'] = microtime(TRUE);
229 print ('FLUSHING: Took ' . ($GLOBALS['time_flush'] - $timer) . ' seconds.' . PHP_EOL);
233 * Adds a found hash and flushes the checkpoint file
235 * @param $hash Hash to save
237 function addFoundHash ($hash) {
239 $GLOBALS['total_found']++;
242 array_push($GLOBALS['found_hashes'][$GLOBALS['total_blocks']], array(
243 'modula_hash' => $GLOBALS['modula_hash'],
244 'genesis_hash' => $GLOBALS['genesis_hash'],
245 'root_hash' => $GLOBALS['root_hash'],
246 'nonce' => $GLOBALS['nonce'],
247 'iter' => $GLOBALS['iteration'],
248 'hashes_block' => $GLOBALS['hashes_block'],
249 'hash_cycles' => $GLOBALS['hash_cycles'],
250 'nonce_hash' => $hash
254 print ('FOUND: hash=' . $hash . ',nonce=' . $GLOBALS['nonce'] . ',total_found=' . $GLOBALS['total_found'] . PHP_EOL);
256 // Set time as a new hash was found
257 $GLOBALS['found_time'] = microtime(TRUE);
259 // Flush check-point file after new hash is found
260 flushCheckPointFile($hash);
262 // Use nonceHash as next modula hash
263 setModulaHash($hash);
271 function initNonce () {
272 $GLOBALS['nonce'] = 1 / (mt_rand() ^ pi());
273 print (__FUNCTION__ . ': nonce=' . $GLOBALS['nonce'] . PHP_EOL);
277 * Sets modula hash and calculates sum of it
279 * @param $hash Hash to set as "modula hash"
282 function setModulaHash ($hash) {
283 $GLOBALS['modula_hash'] = $hash;
284 $GLOBALS['sum_modula'] = calculateSumFromHash($GLOBALS['modula_hash']);
288 * Calculate "genesis" hashes, please note that these "genesis strings" are now
289 * known to the public as you can read them here in source code and therefore I
290 * will not use them for the real genesis hashes.
292 $gensisHashes = array(
293 // A famous quote from Deus Ex 2 - Invisible War
294 multiplehashString('"Informations must be free." - AI Helios from Deus Ex'),
295 // My name + URL of my first StatusNet instance
296 multipleHashString('Roland Haeder, https://status.mxchange.org'),
297 // A famous quote from Linus Torwalds
298 multipleHashString('"Software is like sex. Its better when its free." - Linus Torwalds'),
300 multipleHashString('September 11 is a big lie.'),
303 multipleHashString('GNU is Not Uni*.'),
304 // WINE is not an emulator
305 multipleHashString('WINE Is Not an Emulator.'),
306 // FlightGear - Fly free!
307 multipleHashString('FlightGear - Fly free!'),
308 // Quote from Linus Torwalds
309 multipleHashString('Your code is shit. Your argument is shit.'),
312 // Calculate "modula hash" from 1st/4th and 2nd/3rd
313 $modulaHashes = array(
315 modulaHash($gensisHashes[0], $gensisHashes[3]),
316 modulaHash($gensisHashes[1], $gensisHashes[2]),
319 modulaHash($gensisHashes[4], $gensisHashes[7]),
320 modulaHash($gensisHashes[5], $gensisHashes[6]),
323 // Calculate "sqrt hash"
325 sqrtHash($modulaHashes[0], $modulaHashes[1]),
326 sqrtHash($modulaHashes[2], $modulaHashes[3])
329 // Calulcate modula hash
330 setModulaHash(multipleHashString(modulaHash($sqrtHashes[0], $sqrtHashes[1])));
332 // This is also the "genesis" hash and first root hash
333 $GLOBALS['genesis_hash'] = $GLOBALS['modula_hash'];
334 $GLOBALS['root_hash'] = $GLOBALS['modula_hash'];
337 print ('hashes=' . print_r($gensisHashes, TRUE));
338 print ('modulaHashes=' . print_r($modulaHashes, TRUE));
339 print ('sqrtHashes=' . print_r($sqrtHashes, TRUE));
340 print ('modulaHash=' . $GLOBALS['modula_hash'] . PHP_EOL);
342 // Total reward + hashes
343 $GLOBALS['total_reward'] = 0;
344 $GLOBALS['total_hashes'] = 0;
345 $GLOBALS['total_found'] = 0;
346 $GLOBALS['total_blocks'] = 0;
347 $GLOBALS['found_time'] = microtime(TRUE);
349 // Is the check point there?
350 if (is_readable(CHECK_POINT)) {
352 $checkPoint = file_get_contents(CHECK_POINT);
355 $data = explode(':', $checkPoint);
358 assert(count($data) == 10);
360 // 1st element is nonce, 2nd hash, 3rd found hashes
361 $GLOBALS['total_blocks'] = $data[0];
362 $GLOBALS['total_reward'] = $data[1];
363 $GLOBALS['total_hashes'] = $data[2];
364 $GLOBALS['total_found'] = $data[3];
365 $GLOBALS['total_restarts'] = $data[4];
366 $GLOBALS['hash_cycles'] = intval($data[5]);
367 $GLOBALS['nonce'] = base64_decode($data[7]);
368 $GLOBALS['root_hash'] = $data[8];
369 $GLOBALS['found_hashes'] = unserialize(gzuncompress(base64_decode($data[9])));
372 setModulaHash($data[6]);
374 // Create nonce (small)
379 print ('modulaHash=' . $GLOBALS['modula_hash'] . PHP_EOL);
380 print ('nonce=' . $GLOBALS['nonce'] . PHP_EOL);
381 print ('found=' . count($GLOBALS['found_hashes'][$GLOBALS['total_blocks']]) . PHP_EOL);
385 // Init hash-per-block counter and hashrate
386 $GLOBALS['hashes_block'] = 0;
389 // Wait for block_size iterations (= found hashes). This is one block
390 $timeBlock = microtime(TRUE);
391 $timeDisplay = $timeBlock;
392 $GLOBALS['time_flush'] = $timeBlock;
394 // Time waited for a good block again (no iteration)
397 while (count($GLOBALS['found_hashes'][$GLOBALS['total_blocks']]) <= $GLOBALS['block_size']) {
398 // Create hash from modulaHash ("genesis hash") and nonce
399 $nonceHash = multipleHashString($GLOBALS['nonce'] . $GLOBALS['modula_hash']);
402 $sumNonce = calculateSumFromHash($nonceHash);
405 $GLOBALS['iteration'] = 0;
406 $GLOBALS['iteration_second'] = 0;
408 // Now start the "mining" ...
409 $timeHash = microtime(TRUE);
410 while ($sumNonce < $GLOBALS['sum_modula']) {
411 // Calculate new nonce
415 $nonceHash = multipleHashString($GLOBALS['nonce'] . $GLOBALS['modula_hash']);
418 $sumNonce = calculateSumFromHash($nonceHash);
420 // Time spend in loop
421 $testTime = abs(microtime(TRUE) - $timeDisplay);
423 // Calculate hashrate/sec
424 $hashrate = 1 / $testTime * $GLOBALS['iteration_second'] * $GLOBALS['hash_cycles'];
427 if ($testTime >= 1) {
429 print ('hashrate=' . round($hashrate) . ' hashes/sec,iterSecond=' . $GLOBALS['iteration_second'] . ' iterations/sec' . PHP_EOL);
432 $timeDisplay = microtime(TRUE);
433 $GLOBALS['iteration_second'] = 0;
436 // Time spend from last flush
437 $testTime = abs(microtime(TRUE) - $GLOBALS['time_flush']);
439 // Only once per 10 seconds
440 if ($testTime >= $GLOBALS['flush_file_time']) {
441 // Flush check-point file
442 flushCheckPointFile($GLOBALS['modula_hash']);
445 // Time spend from last found block
446 $testTime = abs(microtime(TRUE) - $GLOBALS['found_time']);
448 // Is the last found time to far away?
449 if ($testTime >= $GLOBALS['restart_search_time']) {
451 $GLOBALS['total_restarts']++;
454 print('total_restarts=' . $GLOBALS['total_restarts'] . ' - Restarting ...');
456 // Count all root (genesis) hashes
457 $rootHashes = array();
458 foreach ($GLOBALS['found_hashes'] as $block) {
459 // "Walk" through all blocks
460 foreach ($block as $hash) {
461 if (!isset($hash['root_hash'])) {
463 die('INCONSISTENCY: hash=' . print_r($hash, TRUE));
466 if (isset($rootHashes[$hash['root_hash']])) {
468 $rootHashes[$hash['root_hash']]++;
471 $rootHashes[$hash['root_hash']] = 1;
476 // Find best root hash
479 foreach ($rootHashes as $hash => $count) {
481 //* NOISY-DEBUG: */ print ('hash=' . $hash . ',count=' . $count . ',bestRootHash=' . $bestRootHash . ',bestRootCount=' . $bestRootCount . PHP_EOL);
483 // Is a better one found?
484 if ($count > $bestRootCount) {
486 $bestRootHash = $hash;
487 $bestRootCount = $count;
492 print ('bestRootHash=' . $bestRootHash . ',bestRootCount=' . $bestRootCount . PHP_EOL);
494 // Search for latest best root hash
495 foreach ($GLOBALS['found_hashes'] as $block) {
496 // "Walk" through whole block and search for first appearance of best root hash
497 foreach ($block as $idx => $hash) {
498 // Is the root hash there?
499 if ($hash['root_hash'] == $bestRootHash) {
500 // Set found modula hash as new root and current modula hash
501 $GLOBALS['root_hash'] = $hash['nonce_hash'];
502 setModulaHash($hash['nonce_hash']);
503 print ('idx=' . $idx . ',modulaHash=' . $GLOBALS['root_hash'] . ' - Is now new root hash!' . PHP_EOL);
505 // Reset "found time" (when a hash was found)
506 $GLOBALS['found_time'] = microtime(TRUE);
508 // Re-initialize nonce
519 $GLOBALS['iteration']++;
520 $GLOBALS['iteration_second']++;
521 //print ('nonce=' . $GLOBALS['nonce'] . ',iteration=' . $GLOBALS['iteration'] . PHP_EOL);
522 //print ('nonceHash=' . $nonceHash . PHP_EOL);
523 //print ('sumNonce=' . $sumNonce . PHP_EOL);
524 //print ('sumModula=' . $GLOBALS['sum_modula'] . PHP_EOL);
527 // If the iteration is zero, then no hash is found
528 if ($GLOBALS['iteration'] == 0) {
530 $timeBadHashes += abs(microtime(TRUE) - $timeHash);
533 print('BAD:nonce=' . $GLOBALS['nonce'] . PHP_EOL);
535 // Nothing found, so calculate new nonce
540 // Add amount of hashes per block (multiple-hash)
541 $GLOBALS['hashes_block'] += $GLOBALS['iteration'] * $GLOBALS['hash_cycles'] + $GLOBALS['hash_cycles'];
544 addFoundHash($nonceHash);
547 // Time taken for one
548 $timeBlock = abs(microtime(TRUE) - $timeBlock);
551 $reward = abs($timeBlock - $timeBadHashes) / $hashrate * $GLOBALS['hashes_block'] / $GLOBALS['block_size'] * 1000;
552 print ('timeBlock=' . $timeBlock . ',timeBadHashes=' . $timeBadHashes . ',hashesPerBlock=' . $GLOBALS['hashes_block'] .',reward=' . $reward . PHP_EOL);
555 $GLOBALS['total_hashes'] += $GLOBALS['hashes_block'];
556 $GLOBALS['total_blocks']++;
557 $GLOBALS['hashes_block'] = 0;
560 $GLOBALS['found_hashes'][$GLOBALS['total_blocks']] = array();
562 // Calculate new nonce
565 // Add reward to total
566 $GLOBALS['total_reward'] += $reward;
568 // Calculate average block value
569 $blockValue = $GLOBALS['total_reward'] / $GLOBALS['total_blocks'] * $GLOBALS['total_hashes'] / ($GLOBALS['block_size'] * $GLOBALS['total_blocks']);
571 // Calculate reward per hour (= 3600 seconds)
572 $rewardPerHour = $GLOBALS['total_reward'] / abs(microtime(TRUE) - START_TIME) * 3600;
574 print ('totalReward=' . $GLOBALS['total_reward'] . ',blockValue=' . $blockValue . ',rewardPerHour=' . $rewardPerHour . PHP_EOL);