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'] = 10;
11 $GLOBALS['restart_search_time'] = 3600 * 12;
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['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);
210 file_put_contents(CHECK_POINT, $GLOBALS['total_blocks'] . ':' . $GLOBALS['total_reward'] . ':' . $GLOBALS['total_hashes'] . ':' . $GLOBALS['hash_cycles'] . ':' . base64_encode($GLOBALS['nonce']) . ':' . $hash . ':' . $GLOBALS['root_hash'] . ':' . base64_encode(gzcompress(serialize($GLOBALS['found_hashes']))));
213 $GLOBALS['time_flush'] = microtime(TRUE);
214 print ('FLUSHING: Took ' . ($GLOBALS['time_flush'] - $timer) . ' seconds.' . PHP_EOL);
218 * Adds a found hash and flushes the checkpoint file
220 * @param $hash Hash to save
222 function addFoundHash ($hash) {
224 $GLOBALS['current_hashes']++;
227 array_push($GLOBALS['found_hashes'][$GLOBALS['total_blocks']], array(
228 'modula_hash' => $GLOBALS['modula_hash'],
229 'genesis_hash' => $GLOBALS['genesis_hash'],
230 'root_hash' => $GLOBALS['root_hash'],
231 'nonce' => $GLOBALS['nonce'],
232 'iter' => $GLOBALS['iteration'],
233 'hashes_block' => $GLOBALS['hashes_block'],
234 'hash_cycles' => $GLOBALS['hash_cycles'],
235 'nonce_hash' => $hash
239 print ('FOUND: hash=' . $hash . ',nonce=' . $GLOBALS['nonce'] . ',current_hashes=' . $GLOBALS['current_hashes'] . PHP_EOL);
241 // Set time as a new hash was found
242 $GLOBALS['found_time'] = microtime(TRUE);
244 // Flush check-point file after new hash is found
245 flushCheckPointFile($hash);
247 // Use nonceHash as next modula hash
248 setModulaHash($hash);
256 function initNonce () {
257 $GLOBALS['nonce'] = 1 / (mt_rand() ^ pi());
258 print (__FUNCTION__ . ': nonce=' . $GLOBALS['nonce'] . PHP_EOL);
262 * Sets modula hash and calculates sum of it
264 * @param $hash Hash to set as "modula hash"
267 function setModulaHash ($hash) {
268 $GLOBALS['modula_hash'] = $hash;
269 $GLOBALS['sum_modula'] = calculateSumFromHash($GLOBALS['modula_hash']);
273 * Calculate "genesis" hashes, please note that these "genesis strings" are now
274 * known to the public as you can read them here in source code and therefore I
275 * will not use them for the real genesis hashes.
277 $gensisHashes = array(
278 // A famous quote from Deus Ex 2 - Invisible War
279 multiplehashString('"Informations must be free." - AI Helios from Deus Ex'),
280 // My name + URL of my first StatusNet instance
281 multipleHashString('Roland Haeder, https://status.mxchange.org'),
282 // A famous quote from Linus Torwalds
283 multipleHashString('"Software is like sex. Its better when its free." - Linus Torwalds'),
285 multipleHashString('September 11 is a big lie.'),
288 multipleHashString('GNU is Not Uni*.'),
289 // WINE is not an emulator
290 multipleHashString('WINE Is Not an Emulator.'),
291 // FlightGear - Fly free!
292 multipleHashString('FlightGear - Fly free!'),
293 // Quote from Linus Torwalds
294 multipleHashString('Your code is shit.. your argument is shit.'),
297 // Calculate "modula hash" from 1st/4th and 2nd/3rd
298 $modulaHashes = array(
300 modulaHash($gensisHashes[0], $gensisHashes[3]),
301 modulaHash($gensisHashes[1], $gensisHashes[2]),
304 modulaHash($gensisHashes[4], $gensisHashes[7]),
305 modulaHash($gensisHashes[5], $gensisHashes[6]),
308 // Calculate "sqrt hash"
310 sqrtHash($modulaHashes[0], $modulaHashes[1]),
311 sqrtHash($modulaHashes[2], $modulaHashes[3])
314 // Calulcate modula hash
315 setModulaHash(multipleHashString(modulaHash($sqrtHashes[0], $sqrtHashes[1])));
317 // This is also the "genesis" hash and first root hash
318 $GLOBALS['genesis_hash'] = $GLOBALS['modula_hash'];
319 $GLOBALS['root_hash'] = $GLOBALS['modula_hash'];
322 print ('hashes=' . print_r($gensisHashes, TRUE));
323 print ('modulaHashes=' . print_r($modulaHashes, TRUE));
324 print ('sqrtHashes=' . print_r($sqrtHashes, TRUE));
325 print ('modulaHash=' . $GLOBALS['modula_hash'] . PHP_EOL);
327 // Total reward + hashes
328 $GLOBALS['total_reward'] = 0;
329 $GLOBALS['total_hashes'] = 0;
330 $GLOBALS['total_blocks'] = 0;
331 $GLOBALS['current_hashes'] = 0;
332 $GLOBALS['found_time'] = microtime(TRUE);
334 // Is the check point there?
335 if (is_readable(CHECK_POINT)) {
337 $checkPoint = file_get_contents(CHECK_POINT);
340 $data = explode(':', $checkPoint);
343 assert(count($data) == 8);
345 // 1st element is nonce, 2nd hash, 3rd found hashes
346 $GLOBALS['total_blocks'] = $data[0];
347 $GLOBALS['total_reward'] = $data[1];
348 $GLOBALS['total_hashes'] = $data[2];
349 $GLOBALS['hash_cycles'] = intval($data[3]);
350 $GLOBALS['nonce'] = base64_decode($data[4]);
351 $GLOBALS['root_hash'] = $data[6];
352 $GLOBALS['found_hashes'] = unserialize(gzuncompress(base64_decode($data[7])));
355 setModulaHash($data[5]);
357 // Create nonce (small)
362 print ('modulaHash=' . $GLOBALS['modula_hash'] . PHP_EOL);
363 print ('nonce=' . $GLOBALS['nonce'] . PHP_EOL);
364 print ('found=' . count($GLOBALS['found_hashes'][$GLOBALS['total_blocks']]) . PHP_EOL);
368 // Init hash-per-block counter and hashrate
369 $GLOBALS['hashes_block'] = 0;
372 // Wait for block_size iterations (= found hashes). This is one block
373 $timeBlock = microtime(TRUE);
374 $timeDisplay = $timeBlock;
375 $GLOBALS['time_flush'] = $timeBlock;
377 // Time waited for a good block again (no iteration)
380 while (count($GLOBALS['found_hashes'][$GLOBALS['total_blocks']]) <= $GLOBALS['block_size']) {
381 // Create hash from modulaHash ("genesis hash") and nonce
382 $nonceHash = multipleHashString($GLOBALS['nonce'] . $GLOBALS['modula_hash']);
385 $sumNonce = calculateSumFromHash($nonceHash);
388 $GLOBALS['iteration'] = 0;
389 $GLOBALS['iteration_second'] = 0;
391 // Now start the "mining" ...
392 $timeHash = microtime(TRUE);
393 while ($sumNonce < $GLOBALS['sum_modula']) {
394 // Calculate new nonce
398 $nonceHash = multipleHashString($GLOBALS['nonce'] . $GLOBALS['modula_hash']);
401 $sumNonce = calculateSumFromHash($nonceHash);
403 // Time spend in loop
404 $testTime = abs(microtime(TRUE) - $timeDisplay);
406 // Calculate hashrate/sec
407 $hashrate = 1 / $testTime * $GLOBALS['iteration_second'] * $GLOBALS['hash_cycles'];
410 if ($testTime >= 1) {
412 print ('hashrate=' . round($hashrate) . ' hashes/sec,iterSecond=' . $GLOBALS['iteration_second'] . ' iterations/sec' . PHP_EOL);
415 $timeDisplay = microtime(TRUE);
416 $GLOBALS['iteration_second'] = 0;
419 // Time spend from last flush
420 $testTime = abs(microtime(TRUE) - $GLOBALS['time_flush']);
422 // Only once per 10 seconds
423 if ($testTime >= $GLOBALS['flush_file_time']) {
424 // Flush check-point file
425 flushCheckPointFile($GLOBALS['modula_hash']);
428 // Time spend from last found block
429 $testTime = abs(microtime(TRUE) - $GLOBALS['found_time']);
431 // Is the last found time to far away?
432 if ($testTime >= $GLOBALS['restart_search_time']) {
433 // Count all root (genesis) hashes
434 $rootHashes = array();
435 foreach ($GLOBALS['found_hashes'] as $block) {
436 // "Walk" through all blocks
437 foreach ($block as $hash) {
438 if (!isset($hash['root_hash'])) {
440 die('INCONSISTENCY: hash=' . print_r($hash, TRUE));
443 if (isset($rootHashes[$hash['root_hash']])) {
445 $rootHashes[$hash['root_hash']]++;
448 $rootHashes[$hash['root_hash']] = 1;
453 // Find best root hash
456 foreach ($rootHashes as $hash => $count) {
458 //* NOISY-DEBUG: */ print ('hash=' . $hash . ',count=' . $count . ',bestRootHash=' . $bestRootHash . ',bestRootCount=' . $bestRootCount . PHP_EOL);
460 // Is a better one found?
461 if ($count > $bestRootCount) {
463 $bestRootHash = $hash;
464 $bestRootCount = $count;
469 print ('bestRootHash=' . $bestRootHash . ',bestRootCount=' . $bestRootCount . PHP_EOL);
471 // Search for latest best root hash
472 foreach ($GLOBALS['found_hashes'] as $block) {
473 // "Walk" through whole block and search for first appearance of best root hash
474 foreach ($block as $idx => $hash) {
475 // Is the root hash there?
476 if ($hash['root_hash'] == $bestRootHash) {
477 // Set found modula hash as new root and current modula hash
478 $GLOBALS['root_hash'] = $hash['nonce_hash'];
479 setModulaHash($hash['nonce_hash']);
480 print ('idx=' . $idx . ',modulaHash=' . $GLOBALS['root_hash'] . ' - Is now new root hash!' . PHP_EOL);
482 // Reset "found time" (when a hash was found)
483 $GLOBALS['found_time'] = microtime(TRUE);
485 // Re-initialize nonce
496 $GLOBALS['iteration']++;
497 $GLOBALS['iteration_second']++;
498 //print ('nonce=' . $GLOBALS['nonce'] . ',iteration=' . $GLOBALS['iteration'] . PHP_EOL);
499 //print ('nonceHash=' . $nonceHash . PHP_EOL);
500 //print ('sumNonce=' . $sumNonce . PHP_EOL);
501 //print ('sumModula=' . $GLOBALS['sum_modula'] . PHP_EOL);
504 // If the iteration is zero, then no hash is found
505 if ($GLOBALS['iteration'] == 0) {
507 $timeBadHashes += abs(microtime(TRUE) - $timeHash);
510 print('BAD:nonce=' . $GLOBALS['nonce'] . PHP_EOL);
512 // Nothing found, so calculate new nonce
517 // Add amount of hashes per block (multiple-hash)
518 $GLOBALS['hashes_block'] += $GLOBALS['iteration'] * $GLOBALS['hash_cycles'] + $GLOBALS['hash_cycles'];
521 addFoundHash($nonceHash);
524 // Time taken for one
525 $timeBlock = abs(microtime(TRUE) - $timeBlock);
528 $reward = abs($timeBlock - $timeBadHashes) / $hashrate * $GLOBALS['hashes_block'] / $GLOBALS['block_size'] * 1000;
529 print ('timeBlock=' . $timeBlock . ',timeBadHashes=' . $timeBadHashes . ',hashesPerBlock=' . $GLOBALS['hashes_block'] .',reward=' . $reward . PHP_EOL);
532 $GLOBALS['total_hashes'] += $GLOBALS['hashes_block'];
533 $GLOBALS['total_blocks']++;
534 $GLOBALS['hashes_block'] = 0;
537 $GLOBALS['found_hashes'][$GLOBALS['total_blocks']] = array();
539 // Calculate new nonce
542 // Add reward to total
543 $GLOBALS['total_reward'] += $reward;
545 // Calculate average block value
546 $blockValue = $GLOBALS['total_reward'] / $GLOBALS['total_blocks'] * $GLOBALS['total_hashes'] / ($GLOBALS['block_size'] * $GLOBALS['total_blocks']);
548 // Calculate reward per hour (= 3600 seconds)
549 $rewardPerHour = $GLOBALS['total_reward'] / abs(microtime(TRUE) - START_TIME) * 3600;
551 print ('totalReward=' . $GLOBALS['total_reward'] . ',blockValue=' . $blockValue . ',rewardPerHour=' . $rewardPerHour . PHP_EOL);