2 error_reporting(E_ALL | E_STRICT);
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 define('FLUSH_BLOCKS_FILE_TIME', 10);
10 define('RESTART_SEARCH_TIME' , 3600);
13 $GLOBALS['cycles'] = 3;
16 $GLOBALS['found_hashes'] = array(0 => array());
21 * @author Roland Haeder <roland@mxchange.org>
22 * @copyright Copyright (c) 2013 by Core Developer Team
23 * @license See LICENSE (public-domain)
27 * Calculates a simple but stronger hash from given string. No salts are being
30 * @param $str The string to be hashed
31 * @return $hash The hash from string $str
33 function hashString ($str) {
34 // Calculate strong hash from given string
35 $hash = mhash(HASH_ALGO, $str);
37 // Return it hexadecimal-encoded
38 return bin2hex($hash);
42 * Multiple-hashes given string. This is done by hashing the given string and
43 * then hashing the generated hash again.
45 * @param $str The string to be hashed 4 times
46 * @return $hash The generated hash
48 function multipleHashString ($str) {
49 // One less to go (see below)
50 $totalHashes = $GLOBALS['cycles'] - 1;
52 // Generate hash from given hash
53 $hash = hashString($str);
56 for ($idx = 0; $idx < $totalHashes; $idx++) {
57 // Over-hash the given hash
58 $hash = hashString($hash);
66 * Calculates a "modula-hash" based given two hashes.
68 * @param $hash1 Hash 1
69 * @param $hash2 Hash 2
71 function modulaHash ($hash1, $hash2) {
72 // Both must have same length
73 assert(strlen($hash1) === strlen($hash2));
78 // "Walk" trough first hash and get every 2 byte of both hashes
79 for ($idx = 0; $idx < strlen($hash1); $idx += 2) {
83 // Get both hash parts and convert to ASCII number
84 $part1 = hexdec(substr($hash1, $idx, 2));
85 $part2 = hexdec(substr($hash2, $idx, 2));
88 * If part1 is larget part2, part1 is divident and vise-versa. But don't do it
91 if (($part1 > $part2) && ($part2 > 0)) {
92 // 'part1' is larger than 'part2'
93 $mod = $part1 % $part2;
94 } elseif (($part1 < $part2) && ($part1 > 0)) {
95 // 'part2' is larger than 'part1'
96 $mod = $part2 % $part1;
99 // "Invert" the result against 255
102 // Encode to hex, pre-pad it with zeros and add to new hash
103 $modulaHash .= padHex($mod);
106 // Modula hash must have same length as input hash
107 assert(strlen($modulaHash) === strlen($hash1));
109 // Return modula hash
114 * Calculates a "sqrt-hash" based given two hashes and single-hash it
116 * @param $hash1 Hash 1
117 * @param $hash2 Hash 2
119 function sqrtHash ($hash1, $hash2) {
120 // Both must have same length
121 assert(strlen($hash1) === strlen($hash2));
126 // "Walk" trough first hash and get every 2 byte of both hashes
127 for ($idx = 0; $idx < strlen($hash1); $idx += 2) {
131 // Get both hash parts and convert to ASCII number
132 $part1 = hexdec(substr($hash1, $idx, 2));
133 $part2 = hexdec(substr($hash2, $idx, 2));
135 // Calculate square root of both parts being multiplied and round up, then "invert" it against 255
136 $sqrt = intval(255 - ceil(sqrt($part1 * $part2)));
138 // Encode to hex, pre-pad it with zeros and add to new hash
139 $sqrtHash .= padHex($sqrt);
142 // "sqrt-hash" must have same length as input hash
143 assert(strlen($sqrtHash) === strlen($hash1));
145 // Hash reversed "sqrt-hash" again and return it
146 return hashString(strrev($sqrtHash));
150 * Converts a number between 0 and 255 into a zero-padded hexadecimal string
152 * @param $num Number between 0 and 255
153 * @return $hex Hexadecimal string, padded with zeros
155 function padHex ($num) {
156 // Must be a integer number and between 0 and 255
157 assert(is_int($num));
162 $hex = str_pad(dechex($num), 2, '0', STR_PAD_LEFT);
169 * Calculates sum from given hash
171 * @param $hash Hash to calculate sum from
172 * @return $sum Sum from given hash
174 function calculateSumFromHash ($hash) {
175 // Everything starts with zero ...
179 for ($idx = 0; $idx < (strlen($hash) / 2); $idx++) {
181 $sum = $sum + (hexdec(substr($hash, $idx, 2)) * $idx & 256);
189 * Calculates new nonce
193 function calculateNonce () {
194 // Linear incrementation
195 $GLOBALS['nonce'] = $GLOBALS['nonce'] + NONCE_INCREMENT;
199 * Writes/flushes check-point file
201 * @param $hash Modula hash (or hash to save)
204 function flushCheckPointFile ($hash) {
206 print ('FLUSHING: Writing ' . count($GLOBALS['found_hashes']) . ' blocks ...' . PHP_EOL);
209 $timer = microtime(TRUE);
212 file_put_contents(CHECK_POINT, $GLOBALS['total_blocks'] . ':' . $GLOBALS['total_reward'] . ':' . $GLOBALS['total_hashes'] . ':' . $GLOBALS['cycles'] . ':' . base64_encode($GLOBALS['nonce']) . ':' . $hash . ':' . $GLOBALS['root_hash'] . ':' . base64_encode(gzcompress(serialize($GLOBALS['found_hashes']))));
215 $GLOBALS['time_flush'] = microtime(TRUE);
216 print ('FLUSHING: Took ' . ($GLOBALS['time_flush'] - $timer) . ' seconds.' . PHP_EOL);
220 * Adds a found hash and flushes the checkpoint file
222 * @param $hash Hash to save
224 function addFoundHash ($hash) {
226 array_push($GLOBALS['found_hashes'][$GLOBALS['total_blocks']], array(
227 'modula_hash' => $GLOBALS['modula_hash'],
228 'genesis_hash' => $GLOBALS['genesis_hash'],
229 'root_hash' => $GLOBALS['root_hash'],
230 'nonce' => $GLOBALS['nonce'],
231 'iter' => $GLOBALS['iteration'],
232 'hashes_block' => $GLOBALS['hashes_block'],
233 'nonce_hash' => $hash
237 print ('FOUND: hash=' . $hash . ',nonce=' . $GLOBALS['nonce'] . ',iteration=' . $GLOBALS['iteration'] . ',foundTime()=' . (microtime(TRUE) - $GLOBALS['found_time'] ) . PHP_EOL);
239 // Set time as a new hash was found
240 $GLOBALS['found_time'] = microtime(TRUE);
242 // Flush check-point file after new hash is found
243 flushCheckPointFile($hash);
245 // Use nonceHash as next modula hash
246 $GLOBALS['modula_hash'] = $hash;
254 function initNonce () {
255 $GLOBALS['nonce'] = 1 / (mt_rand() ^ pi());
256 print (__FUNCTION__ . ': nonce=' . $GLOBALS['nonce'] . PHP_EOL);
260 * Calculate "genesis" hashes, please note that these "genesis strings" are now
261 * known to the public as you can read them here in source code and therefore I
262 * will not use them for the real genesis hashes.
264 $gensisHashes = array(
265 // A famous quote from Deus Ex 2 - Invisible War
266 multiplehashString('"Informations must be free." - AI Helios from Deus Ex'),
267 // My name + URL of my first StatusNet instance
268 multipleHashString('Roland Haeder, https://status.mxchange.org'),
269 // A famous quote from Linus Torwalds
270 multipleHashString('"Software is like sex. Its better when its free." - Linus Torwalds'),
272 multipleHashString('September 11 is a big lie.'),
275 multipleHashString('GNU is Not Uni*.'),
276 // WINE is not an emulator
277 multipleHashString('WINE Is Not an Emulator.'),
278 // FlightGear - Fly free!
279 multipleHashString('FlightGear - Fly free!'),
280 // Quote from Linus Torwalds
281 multipleHashString('Your code is shit.. your argument is shit.'),
284 // Calculate "modula hash" from 1st/4th and 2nd/3rd
285 $modulaHashes = array(
287 modulaHash($gensisHashes[0], $gensisHashes[3]),
288 modulaHash($gensisHashes[1], $gensisHashes[2]),
291 modulaHash($gensisHashes[4], $gensisHashes[7]),
292 modulaHash($gensisHashes[5], $gensisHashes[6]),
295 // Calculate "sqrt hash"
297 sqrtHash($modulaHashes[0], $modulaHashes[1]),
298 sqrtHash($modulaHashes[2], $modulaHashes[3])
301 // Calulcate modula hash
302 $GLOBALS['modula_hash'] = multipleHashString(modulaHash($sqrtHashes[0], $sqrtHashes[1]));
304 // This is also the "genesis" hash and first root hash
305 $GLOBALS['genesis_hash'] = $GLOBALS['modula_hash'];
306 $GLOBALS['root_hash'] = $GLOBALS['modula_hash'];
309 print ('hashes=' . print_r($gensisHashes, TRUE));
310 print ('modulaHashes=' . print_r($modulaHashes, TRUE));
311 print ('sqrtHashes=' . print_r($sqrtHashes, TRUE));
312 print ('modulaHash=' . $GLOBALS['modula_hash'] . PHP_EOL);
314 // Total reward + hashes
315 $GLOBALS['total_reward'] = 0;
316 $GLOBALS['total_hashes'] = 0;
317 $GLOBALS['total_blocks'] = 0;
318 $GLOBALS['found_time'] = microtime(TRUE);
320 // Is the check point there?
321 if (is_readable(CHECK_POINT)) {
323 $checkPoint = file_get_contents(CHECK_POINT);
326 $data = explode(':', $checkPoint);
329 assert(count($data) == 8);
331 // 1st element is nonce, 2nd hash, 3rd found hashes
332 $GLOBALS['total_blocks'] = $data[0];
333 $GLOBALS['total_reward'] = $data[1];
334 $GLOBALS['total_hashes'] = $data[2];
335 $GLOBALS['cycles'] = intval($data[3]);
336 $GLOBALS['nonce'] = base64_decode($data[4]);
337 $GLOBALS['modula_hash'] = $data[5];
338 $GLOBALS['root_hash'] = $data[6];
339 $GLOBALS['found_hashes'] = unserialize(gzuncompress(base64_decode($data[7])));
341 // Create nonce (small)
346 print ('modulaHash=' . $GLOBALS['modula_hash'] . PHP_EOL);
347 print ('nonce=' . $GLOBALS['nonce'] . PHP_EOL);
348 print ('found=' . count($GLOBALS['found_hashes'][$GLOBALS['total_blocks']]) . PHP_EOL);
352 // Init hash-per-block counter and hashrate
353 $GLOBALS['hashes_block'] = 0;
356 // Wait for BLOCK_SIZE iterations (= found hashes). This is one block
357 $timeBlock = microtime(TRUE);
358 $timeDisplay = $timeBlock;
359 $GLOBALS['time_flush'] = $timeBlock;
361 // Time waited for a good block again (no iteration)
364 while (count($GLOBALS['found_hashes'][$GLOBALS['total_blocks']]) <= BLOCK_SIZE) {
365 // Create hash from modulaHash ("genesis hash") and nonce
366 $nonceHash = multipleHashString($GLOBALS['modula_hash'] . $GLOBALS['nonce']);
369 $sumNonce = calculateSumFromHash($nonceHash);
370 $sumModula = calculateSumFromHash($GLOBALS['modula_hash']);
373 $GLOBALS['iteration'] = 0;
374 $GLOBALS['iteration_second'] = 0;
376 // Now start the "mining" ...
377 $timeHash = microtime(TRUE);
378 while ($sumNonce >= $sumModula) {
379 // Calculate new nonce
383 $nonceHash = multipleHashString($GLOBALS['modula_hash'] . $GLOBALS['nonce']);
386 $sumNonce = calculateSumFromHash($nonceHash);
388 // Time spend in loop
389 $testTime = abs(microtime(TRUE) - $timeDisplay);
391 // Calculate hashrate/sec
392 $hashrate = 1 / $testTime * $GLOBALS['iteration_second'] * $GLOBALS['cycles'];
395 if ($testTime >= 1) {
397 print ('hashrate=' . round($hashrate) . ' hashes/sec,iterSecond=' . $GLOBALS['iteration_second'] . ' iterations/sec' . PHP_EOL);
400 $timeDisplay = microtime(TRUE);
401 $GLOBALS['iteration_second'] = 0;
404 // Time spend from last flush
405 $testTime = abs(microtime(TRUE) - $GLOBALS['time_flush']);
407 // Only once per 10 seconds
408 if ($testTime >= FLUSH_BLOCKS_FILE_TIME) {
409 // Flush check-point file
410 flushCheckPointFile($GLOBALS['modula_hash']);
413 // Time spend from last found block
414 $testTime = abs(microtime(TRUE) - $GLOBALS['found_time']);
416 // Is the last found time to far away?
417 if ($testTime >= RESTART_SEARCH_TIME) {
418 // Count all root (genesis) hashes
419 $rootHashes = array();
420 foreach ($GLOBALS['found_hashes'] as $block) {
421 // "Walk" through all blocks
422 foreach ($block as $hash) {
423 if (!isset($hash['root_hash'])) {
425 die('INCONSISTENCY: hash=' . print_r($hash, TRUE));
428 if (isset($rootHashes[$hash['root_hash']])) {
430 $rootHashes[$hash['root_hash']]++;
433 $rootHashes[$hash['root_hash']] = 1;
438 // Find best root hash
441 foreach ($rootHashes as $hash => $count) {
443 //* NOISY-DEBUG: */ print ('hash=' . $hash . ',count=' . $count . ',bestRootHash=' . $bestRootHash . ',bestRootCount=' . $bestRootCount . PHP_EOL);
445 // Is a better one found?
446 if ($count > $bestRootCount) {
448 $bestRootHash = $hash;
449 $bestRootCount = $count;
454 print ('bestRootHash=' . $bestRootHash . ',bestRootCount=' . $bestRootCount . PHP_EOL);
456 // Search for latest best root hash
457 foreach ($GLOBALS['found_hashes'] as $block) {
458 // "Walk" through whole block counter-wise
459 for ($idx = (count($block) - 1); $idx > 0; $idx--) {
460 // Is the root hash there?
461 if ($block[$idx]['root_hash'] == $bestRootHash) {
462 // Set found modula hash as new root and current modula hash
463 $GLOBALS['root_hash'] = $block[$idx]['modula_hash'];
464 $GLOBALS['modula_hash'] = $block[$idx]['modula_hash'];
465 print ('idx=' . $idx . ',modulaHash=' . $GLOBALS['root_hash'] . ' - Is now new root hash!' . PHP_EOL);
467 // Reset "found time" (when a hash was found)
468 $GLOBALS['found_time'] = microtime(TRUE);
470 // Re-initialize nonce
481 $GLOBALS['iteration']++;
482 $GLOBALS['iteration_second']++;
483 //print ('nonce=' . $GLOBALS['nonce'] . ',iteration=' . $GLOBALS['iteration'] . PHP_EOL);
484 //print ('nonceHash=' . $nonceHash . PHP_EOL);
485 //print ('sumNonce=' . $sumNonce . PHP_EOL);
486 //print ('sumModula=' . $sumModula . PHP_EOL);
489 // If the iteration is zero, then no hash is found
490 if ($GLOBALS['iteration'] == 0) {
492 $timeBadHashes += abs(microtime(TRUE) - $timeHash);
495 print('BAD:nonce=' . $GLOBALS['nonce'] . PHP_EOL);
497 // Nothing found, so calculate new nonce
502 // Add amount of hashes per block (multiple-hash)
503 $GLOBALS['hashes_block'] += $GLOBALS['iteration'] * $GLOBALS['cycles'] + $GLOBALS['cycles'];
506 addFoundHash($nonceHash);
509 // Time taken for one
510 $timeBlock = abs(microtime(TRUE) - $timeBlock);
513 $reward = abs($timeBlock - $timeBadHashes) / $hashrate * $GLOBALS['hashes_block'] / BLOCK_SIZE * 1000;
514 print ('timeBlock=' . $timeBlock . ',timeBadHashes=' . $timeBadHashes . ',hashesPerBlock=' . $GLOBALS['hashes_block'] .',reward=' . $reward . PHP_EOL);
517 $GLOBALS['total_hashes'] += $GLOBALS['hashes_block'];
518 $GLOBALS['total_blocks']++;
519 $GLOBALS['hashes_block'] = 0;
522 $GLOBALS['found_hashes'][$GLOBALS['total_blocks']] = array();
524 // Calculate new nonce
527 // Add reward to total
528 $GLOBALS['total_reward'] += $reward;
530 // Calculate average block value
531 $blockValue = $GLOBALS['total_reward'] / $GLOBALS['total_blocks'] * $GLOBALS['total_hashes'] / (BLOCK_SIZE * $GLOBALS['total_blocks']);
533 // Calculate reward per hour (= 3600 seconds)
534 $rewardPerHour = $GLOBALS['total_reward'] / abs(microtime(TRUE) - START_TIME) * 3600;
536 print ('totalReward=' . $GLOBALS['total_reward'] . ',blockValue=' . $blockValue . ',rewardPerHour=' . $rewardPerHour . PHP_EOL);